mirror of
https://github.com/EdgeTX/edgetx.git
synced 2025-07-21 15:25:12 +03:00
929 lines
23 KiB
C++
929 lines
23 KiB
C++
/****************************************************************************
|
|
* Copyright (c) 2014 by Michael Blandford. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the author nor the names of its contributors may
|
|
* be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
* THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
****************************************************************************
|
|
* Other Authors:
|
|
* - Andre Bernet
|
|
* - Bertrand Songis
|
|
* - Bryan J. Rentoul (Gruvin)
|
|
* - Cameron Weeks
|
|
* - Erez Raviv
|
|
* - Jean-Pierre Parisy
|
|
* - Karl Szmutny
|
|
* - Michal Hlavinka
|
|
* - Pat Mackenzie
|
|
* - Philip Moss
|
|
* - Rob Thomson
|
|
* - Romolo Manfredini
|
|
* - Thomas Husterer
|
|
*
|
|
****************************************************************************/
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* Headers
|
|
*----------------------------------------------------------------------------*/
|
|
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#if defined(PCBTARANIS)
|
|
#include "stm32f2xx_flash.h"
|
|
#endif
|
|
|
|
#include "board_taranis.h"
|
|
#include "../pwr.h"
|
|
#include "../lcd.h"
|
|
#include "../keys.h"
|
|
#include "../sdcard.h"
|
|
#include "../FatFs/ff.h"
|
|
#include "../FatFs/diskio.h"
|
|
#include "../translations/en.h"
|
|
|
|
#if defined(PCBTARANIS)
|
|
#define BOOTLOADER_TITLE " OpenTX Boot Loader v1.0"
|
|
#define BOOT_KEY_UP KEY_PLUS
|
|
#define BOOT_KEY_DOWN KEY_MINUS
|
|
#define BOOT_KEY_LEFT KEY_MENU
|
|
#define BOOT_KEY_RIGHT KEY_PAGE
|
|
#define BOOT_KEY_MENU KEY_ENTER
|
|
#define BOOT_KEY_EXIT KEY_EXIT
|
|
#define DISPLAY_CHAR_WIDTH 35
|
|
#elif defined(PCBSKY9X)
|
|
#define BOOTLOADER_TITLE "Boot Loader - Sky9x"
|
|
#endif
|
|
|
|
// states
|
|
enum BootLoaderStates {
|
|
ST_START,
|
|
ST_FLASH_MENU,
|
|
ST_DIR_CHECK,
|
|
ST_OPEN_DIR,
|
|
ST_FILE_LIST,
|
|
ST_FLASH_CHECK,
|
|
ST_FLASHING,
|
|
ST_FLASH_DONE,
|
|
ST_USB,
|
|
ST_REBOOT,
|
|
};
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* Local variables
|
|
*----------------------------------------------------------------------------*/
|
|
|
|
uint32_t FirmwareSize;
|
|
|
|
uint32_t Master_frequency;
|
|
volatile uint8_t Tenms;
|
|
uint8_t EE_timer;
|
|
|
|
TCHAR FlashFilename[60];
|
|
FIL FlashFile;
|
|
DIR Dj;
|
|
FILINFO Finfo;
|
|
|
|
TCHAR Filenames[20][50];
|
|
uint32_t FileSize[20];
|
|
uint32_t FnStartIndex;
|
|
uint32_t Valid;
|
|
|
|
uint32_t FlashSize;
|
|
|
|
uint32_t FlashBlocked = 1;
|
|
uint32_t LockBits;
|
|
|
|
uint32_t Block_buffer[1024];
|
|
UINT BlockCount;
|
|
|
|
#if defined(PCBSKY9X)
|
|
extern int32_t EblockAddress;
|
|
#endif
|
|
|
|
extern uint32_t EepromBlocked;
|
|
|
|
extern void init_spi(void);
|
|
extern void writeBlock(void);
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* Global functions
|
|
*----------------------------------------------------------------------------*/
|
|
|
|
#if defined(PCBSKY9X)
|
|
// Starts TIMER0 at full speed (MCK/2) for delay timing
|
|
// @ 36MHz this is 18MHz
|
|
// This was 6 MHz, we may need to slow it to TIMER_CLOCK2 (MCK/8=4.5 MHz)
|
|
void start_timer0()
|
|
{
|
|
register Tc *ptc;
|
|
|
|
PMC->PMC_PCER0 |= 0x00800000L; // Enable peripheral clock to TC0
|
|
|
|
ptc = TC0;// Tc block 0 (TC0-2)
|
|
ptc->TC_BCR = 0;// No sync
|
|
ptc->TC_BMR = 2;
|
|
ptc->TC_CHANNEL[0].TC_CMR = 0x00008001;// Waveform mode MCK/8 for 36MHz osc.(Upset be write below)
|
|
ptc->TC_CHANNEL[0].TC_RC = 0xFFF0;
|
|
ptc->TC_CHANNEL[0].TC_RA = 0;
|
|
ptc->TC_CHANNEL[0].TC_CMR = 0x00008040;// 0000 0000 0000 0000 1000 0000 0100 0000, stop at regC, 18MHz
|
|
ptc->TC_CHANNEL[0].TC_CCR = 5;// Enable clock and trigger it (may only need trigger)
|
|
}
|
|
|
|
void stop_timer0( void )
|
|
{
|
|
TC0->TC_CHANNEL[0].TC_CCR = TC_CCR0_CLKDIS; // Disable clock
|
|
}
|
|
|
|
void delay2ms()
|
|
{
|
|
TC0->TC_CHANNEL[0].TC_CCR = 5;// Enable clock and trigger it (may only need trigger)
|
|
while ( TC0->TC_CHANNEL[0].TC_CV < 36000 )// 2mS, Value depends on MCK/2 (used 18MHz)
|
|
{
|
|
// Wait
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(PCBTARANIS)
|
|
uint32_t isFirmwareStart(uint32_t *block)
|
|
{
|
|
if ((block[0] & 0xFFFC0000) != 0x20000000) {
|
|
return 0;
|
|
}
|
|
if ((block[1] & 0xFFF00000) != 0x08000000) {
|
|
return 0;
|
|
}
|
|
if ((block[2] & 0xFFF00000) != 0x08000000) {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
#elif defined(PCBSKY9X)
|
|
uint32_t isFirmwareStart( uint32_t *block )
|
|
{
|
|
if ((block[0] & 0xFFFE3000) != 0x20000000 ) {
|
|
return 0;
|
|
}
|
|
if ((block[1] & 0xFFF80000) != 0x00400000) {
|
|
return 0;
|
|
}
|
|
if ((block[2] & 0xFFF80000) != 0x00400000) {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
#if defined(PCBSKY9X)
|
|
|
|
uint32_t (*IAP_Function)(uint32_t, uint32_t);
|
|
|
|
void writeFlash(uint32_t *address, uint32_t *buffer) // size is 256 bytes
|
|
{
|
|
uint32_t FlashSectorNum;
|
|
uint32_t flash_cmd = 0;
|
|
|
|
/* Initialize the function pointer (retrieve function address from NMI vector) */
|
|
if ((uint32_t) address == FIRMWARE_START+BOOTLOADER_SIZE) {
|
|
if (isFirmwareStart(buffer))
|
|
FlashBlocked = 0;
|
|
else
|
|
FlashBlocked = 1;
|
|
}
|
|
|
|
if (FlashBlocked) {
|
|
return;
|
|
}
|
|
|
|
// Always initialise this here, setting a default doesn't seem to work
|
|
IAP_Function = (uint32_t (*)(uint32_t, uint32_t)) *(( uint32_t *)0x00800008);
|
|
FlashSectorNum = (uint32_t) address;
|
|
FlashSectorNum >>= 8;// page size is 256 bytes
|
|
FlashSectorNum &= 2047;// max page number
|
|
|
|
/* Send data to the sector here */
|
|
for (int i=0; i<FLASH_PAGESIZE/4; i++) {
|
|
*address++ = *buffer++;
|
|
}
|
|
|
|
/* build the command to send to EEFC */
|
|
flash_cmd = (0x5A << 24) | (FlashSectorNum << 8) | 0x03; //AT91C_MC_FCMD_EWP ;
|
|
|
|
__disable_irq();
|
|
/* Call the IAP function with appropriate command */
|
|
i = IAP_Function(0, flash_cmd);
|
|
__enable_irq();
|
|
}
|
|
|
|
uint32_t readLockBits()
|
|
{
|
|
// Always initialise this here, setting a default doesn't seem to work
|
|
IAP_Function = (uint32_t (*)(uint32_t, uint32_t)) *(( uint32_t *)0x00800008);
|
|
|
|
uint32_t flash_cmd = (0x5A << 24) | 0x0A;//AT91C_MC_FCMD_GLB ;
|
|
__disable_irq();
|
|
(void) IAP_Function( 0, flash_cmd );
|
|
__enable_irq();
|
|
return EFC->EEFC_FRR;
|
|
}
|
|
|
|
void clearLockBits()
|
|
{
|
|
uint32_t i;
|
|
uint32_t flash_cmd = 0;
|
|
|
|
// Always initialise this here, setting a default doesn't seem to work
|
|
IAP_Function = (uint32_t (*)(uint32_t, uint32_t)) *(( uint32_t *)0x00800008);
|
|
for ( i = 0; i < 16; i += 1 )
|
|
{
|
|
flash_cmd = (0x5A << 24) | ((128*i) << 8) | 0x09; //AT91C_MC_FCMD_CLB ;
|
|
__disable_irq();
|
|
/* Call the IAP function with appropriate command */
|
|
(void) IAP_Function( 0, flash_cmd );
|
|
__enable_irq();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void interrupt10ms(void)
|
|
{
|
|
Tenms |= 1; // 10 mS has passed
|
|
|
|
uint8_t enuk = KEY_MENU;
|
|
uint8_t in = ~readKeys();
|
|
|
|
for (int i = 1; i < 7; i++) {
|
|
uint8_t value = in & (1 << i);
|
|
keys[enuk].input(value, (EnumKeys) enuk);
|
|
++enuk;
|
|
}
|
|
}
|
|
|
|
#if defined(PCBSKY9X)
|
|
void init10msTimer()
|
|
{
|
|
register Tc *ptc;
|
|
register uint32_t timer;
|
|
|
|
PMC->PMC_PCER0 |= 0x02000000L; // Enable peripheral clock to TC2
|
|
|
|
timer = Master_frequency / 12800;// MCK/128 and 100 Hz
|
|
|
|
ptc = TC0;// Tc block 0 (TC0-2)
|
|
ptc->TC_BCR = 0;// No sync
|
|
ptc->TC_BMR = 0;
|
|
ptc->TC_CHANNEL[2].TC_CMR = 0x00008000;// Waveform mode
|
|
ptc->TC_CHANNEL[2].TC_RC = timer;// 10 Hz
|
|
ptc->TC_CHANNEL[2].TC_RA = timer >> 1;
|
|
ptc->TC_CHANNEL[2].TC_CMR = 0x0009C003;// 0000 0000 0000 1001 1100 0000 0000 0011
|
|
// MCK/128, set @ RA, Clear @ RC waveform
|
|
ptc->TC_CHANNEL[2].TC_CCR = 5;// Enable clock and trigger it (may only need trigger)
|
|
|
|
NVIC_EnableIRQ(TC2_IRQn);
|
|
TC0->TC_CHANNEL[2].TC_IER = TC_IER0_CPCS;
|
|
}
|
|
|
|
extern "C" void TC2_IRQHandler()
|
|
{
|
|
register uint32_t dummy;
|
|
|
|
/* Clear status bit to acknowledge interrupt */
|
|
dummy = TC0->TC_CHANNEL[2].TC_SR;
|
|
(void) dummy; // Discard value - prevents compiler warning
|
|
|
|
interrupt10ms();
|
|
|
|
}
|
|
#endif
|
|
|
|
#if defined(PCBTARANIS)
|
|
void init10msTimer()
|
|
{
|
|
// Timer14
|
|
RCC->APB1ENR |= RCC_APB1ENR_TIM14EN; // Enable clock
|
|
TIM14->ARR = 9999; // 10mS
|
|
TIM14->PSC = (PERI1_FREQUENCY * TIMER_MULT_APB1) / 1000000 - 1; // 1uS from 12MHz
|
|
TIM14->CCER = 0;
|
|
TIM14->CCMR1 = 0;
|
|
TIM14->EGR = 0;
|
|
TIM14->CR1 = 5;
|
|
TIM14->DIER |= 1;
|
|
NVIC_EnableIRQ(TIM8_TRG_COM_TIM14_IRQn);
|
|
}
|
|
|
|
extern "C" void TIM8_TRG_COM_TIM14_IRQHandler()
|
|
{
|
|
TIM14->SR &= ~TIM_SR_UIF;
|
|
interrupt10ms();
|
|
}
|
|
|
|
void init_hw_timer()
|
|
{
|
|
// Timer13
|
|
RCC->APB1ENR |= RCC_APB1ENR_TIM13EN; // Enable clock
|
|
TIM13->ARR = 65535;
|
|
TIM13->PSC = (PERI1_FREQUENCY * TIMER_MULT_APB1) / 10000000 - 1;// 0.1uS from 12MHz
|
|
TIM13->CCER = 0;
|
|
TIM13->CCMR1 = 0;
|
|
TIM13->EGR = 0;
|
|
TIM13->CR1 = 1;
|
|
}
|
|
|
|
// delay in units of 0.1 uS up to 6.5535 mS
|
|
void hw_delay(uint16_t time)
|
|
{
|
|
TIM13->CNT = 0;
|
|
TIM13->EGR = 1; // Re-start counter
|
|
while ( TIM13->CNT < time) {
|
|
// wait
|
|
}
|
|
}
|
|
|
|
//After reset, write is not allowed in the Flash control register (FLASH_CR) to protect the
|
|
//Flash memory against possible unwanted operations due, for example, to electric
|
|
//disturbances. The following sequence is used to unlock this register:
|
|
//1. Write KEY1 = 0x45670123 in the Flash key register (FLASH_KEYR)
|
|
//2. Write KEY2 = 0xCDEF89AB in the Flash key register (FLASH_KEYR)
|
|
//Any wrong sequence will return a bus error and lock up the FLASH_CR register until the
|
|
//next reset.
|
|
//The FLASH_CR register can be locked again by software by setting the LOCK bit in the
|
|
//FLASH_CR register.
|
|
void unlockFlash()
|
|
{
|
|
FLASH->KEYR = 0x45670123;
|
|
FLASH->KEYR = 0xCDEF89AB;
|
|
}
|
|
|
|
void waitFlashIdle()
|
|
{
|
|
while (FLASH->SR & FLASH_FLAG_BSY) {
|
|
wdt_reset();
|
|
}
|
|
}
|
|
|
|
#define SECTOR_MASK ((uint32_t)0xFFFFFF07)
|
|
|
|
void eraseSector(uint32_t sector)
|
|
{
|
|
waitFlashIdle();
|
|
|
|
FLASH->CR &= CR_PSIZE_MASK;
|
|
FLASH->CR |= FLASH_PSIZE_WORD;
|
|
FLASH->CR &= SECTOR_MASK;
|
|
FLASH->CR |= FLASH_CR_SER | (sector << 3);
|
|
FLASH->CR |= FLASH_CR_STRT;
|
|
|
|
/* Wait for operation to be completed */
|
|
waitFlashIdle();
|
|
|
|
/* if the erase operation is completed, disable the SER Bit */
|
|
FLASH->CR &= (~FLASH_CR_SER);
|
|
FLASH->CR &= SECTOR_MASK;
|
|
}
|
|
|
|
void writeFlash(uint32_t *address, uint32_t *buffer) // size is 256 bytes
|
|
{
|
|
uint32_t i;
|
|
|
|
if ((uint32_t) address == 0x08008000) {
|
|
if (isFirmwareStart(buffer)) {
|
|
FlashBlocked = 0;
|
|
}
|
|
else {
|
|
FlashBlocked = 1;
|
|
}
|
|
}
|
|
|
|
if (FlashBlocked) {
|
|
return;
|
|
}
|
|
|
|
if ((uint32_t) address == 0x08008000) {
|
|
eraseSector(2);
|
|
}
|
|
if ((uint32_t) address == 0x0800C000) {
|
|
eraseSector(3);
|
|
}
|
|
if ((uint32_t) address == 0x08010000) {
|
|
eraseSector(4);
|
|
}
|
|
if ((uint32_t) address == 0x08020000) {
|
|
eraseSector(5);
|
|
}
|
|
if ((uint32_t) address == 0x08040000) {
|
|
eraseSector(6);
|
|
}
|
|
if ((uint32_t) address == 0x08060000) {
|
|
eraseSector(7);
|
|
}
|
|
|
|
// Now program the 256 bytes
|
|
|
|
for (i = 0; i < 64; i += 1) {
|
|
/* Device voltage range supposed to be [2.7V to 3.6V], the operation will
|
|
be done by word */
|
|
|
|
// Wait for last operation to be completed
|
|
waitFlashIdle();
|
|
|
|
FLASH->CR &= CR_PSIZE_MASK;
|
|
FLASH->CR |= FLASH_PSIZE_WORD;
|
|
FLASH->CR |= FLASH_CR_PG;
|
|
|
|
*address = *buffer;
|
|
|
|
/* Wait for operation to be completed */
|
|
waitFlashIdle();
|
|
FLASH->CR &= (~FLASH_CR_PG);
|
|
|
|
/* Check the written value */
|
|
if (*address != *buffer) {
|
|
/* Flash content doesn't match SRAM content */
|
|
return;
|
|
}
|
|
/* Increment FLASH destination address */
|
|
address += 1;
|
|
buffer += 1;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
uint8_t *cpystr(uint8_t *dest, uint8_t *source)
|
|
{
|
|
while ((*dest++ = *source++))
|
|
;
|
|
return dest - 1;
|
|
}
|
|
|
|
FRESULT readBinDir(DIR *dj, FILINFO *fno)
|
|
{
|
|
FRESULT fr;
|
|
uint32_t loop;
|
|
do {
|
|
loop = 0;
|
|
fr = f_readdir(dj, fno); // First entry
|
|
|
|
if (fr != FR_OK || fno->fname[0] == 0) {
|
|
break;
|
|
}
|
|
if (*fno->lfname == 0) {
|
|
cpystr((uint8_t *) fno->lfname, (uint8_t *) fno->fname); // Copy 8.3 name
|
|
}
|
|
int32_t len = strlen(fno->lfname) - 4;
|
|
if (len < 0) {
|
|
loop = 1;
|
|
}
|
|
if (fno->lfname[len] != '.') {
|
|
loop = 1;
|
|
}
|
|
if ((fno->lfname[len + 1] != 'b') && (fno->lfname[len + 1] != 'B')) {
|
|
loop = 1;
|
|
}
|
|
if ((fno->lfname[len + 2] != 'i') && (fno->lfname[len + 2] != 'I')) {
|
|
loop = 1;
|
|
}
|
|
if ((fno->lfname[len + 3] != 'n') && (fno->lfname[len + 3] != 'N')) {
|
|
loop = 1;
|
|
}
|
|
|
|
} while (loop);
|
|
return fr;
|
|
}
|
|
|
|
uint32_t fillNames(uint32_t index)
|
|
{
|
|
uint32_t i;
|
|
FRESULT fr;
|
|
Finfo.lfname = Filenames[0];
|
|
Finfo.lfsize = 48;
|
|
fr = f_readdir(&Dj, 0); // rewind
|
|
fr = f_readdir(&Dj, &Finfo); // Skip .
|
|
fr = f_readdir(&Dj, &Finfo); // Skip ..
|
|
i = 0;
|
|
while (i <= index) {
|
|
fr = readBinDir(&Dj, &Finfo); // First entry
|
|
FileSize[0] = Finfo.fsize;
|
|
i += 1;
|
|
if (fr == FR_NO_FILE) {
|
|
return 0;
|
|
}
|
|
}
|
|
for (i = 1; i < 7; i += 1) {
|
|
Finfo.lfname = Filenames[i];
|
|
fr = readBinDir(&Dj, &Finfo); // First entry
|
|
FileSize[i] = Finfo.fsize;
|
|
if (fr != FR_OK || Finfo.fname[0] == 0) {
|
|
break;
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
FRESULT openFirmwareFile(uint32_t index)
|
|
{
|
|
cpystr(cpystr((uint8_t *)FlashFilename, (uint8_t *)FIRMWARES_PATH "/"), (uint8_t *) Filenames[index]);
|
|
f_open(&FlashFile, FlashFilename, FA_READ);
|
|
f_lseek(&FlashFile, BOOTLOADER_SIZE);
|
|
return f_read(&FlashFile, (BYTE *) Block_buffer, 4096, &BlockCount);
|
|
}
|
|
|
|
int menuFlashFile(uint32_t index, uint8_t event)
|
|
{
|
|
FRESULT fr;
|
|
|
|
lcd_putsLeft(4*FH, "\012Hold [ENT] to start loading" );
|
|
|
|
if (Valid == 0) {
|
|
// Validate file here
|
|
// return 3 if invalid
|
|
fr = openFirmwareFile(index);
|
|
fr = f_close(&FlashFile);
|
|
Valid = 1;
|
|
if (isFirmwareStart(Block_buffer) == 0) {
|
|
Valid = 2;
|
|
}
|
|
}
|
|
|
|
if (Valid == 2) {
|
|
lcd_putsLeft(4*FH, "\011No firmware found in the file!");
|
|
if (event == EVT_KEY_FIRST(BOOT_KEY_EXIT) || event == EVT_KEY_FIRST(BOOT_KEY_MENU)) {
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
if (event == EVT_KEY_LONG(BOOT_KEY_MENU)) {
|
|
fr = openFirmwareFile(index);
|
|
FirmwareSize = FileSize[index];
|
|
if (fr != FR_OK) {
|
|
return 0; // File open error
|
|
}
|
|
return 1;
|
|
}
|
|
else if (event == EVT_KEY_FIRST(BOOT_KEY_EXIT)) {
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
extern Key keys[];
|
|
|
|
static uint32_t PowerUpDelay;
|
|
|
|
int main()
|
|
{
|
|
uint8_t index = 0;
|
|
#if defined(PCBTARANIS)
|
|
uint8_t TenCount = 2;
|
|
#endif
|
|
uint8_t maxhsize = DISPLAY_CHAR_WIDTH;
|
|
FRESULT fr;
|
|
uint32_t state = ST_START;
|
|
uint32_t nameCount = 0;
|
|
uint32_t vpos = 0;
|
|
uint32_t hpos = 0;
|
|
uint32_t firmwareAddress = FIRMWARE_ADDRESS;
|
|
uint32_t firmwareWritten = 0;
|
|
|
|
#if defined(PCBTARANIS)
|
|
wdt_reset();
|
|
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; // Enable portA clock
|
|
#endif
|
|
|
|
pwrInit();
|
|
|
|
#if defined(PCBSKY9X)
|
|
MATRIX->CCFG_SYSIO |= 0x000000F0L; // Disable syspins, enable B4,5,6,7
|
|
#endif
|
|
|
|
#if defined(PCBSKY9X)
|
|
init_SDcard();
|
|
PIOC->PIO_PER = PIO_PC25; // Enable bit C25 (USB-detect)
|
|
start_timer0();
|
|
#endif
|
|
|
|
lcdInit();
|
|
#if defined(PCBSKY9X)
|
|
extern uint8_t OptrexDisplay;
|
|
OptrexDisplay = 1;
|
|
#endif
|
|
lcd_clear();
|
|
lcd_putsLeft(0, BOOTLOADER_TITLE);
|
|
lcd_invert_line(0);
|
|
lcdRefresh();
|
|
#if defined(PCBSKY9X)
|
|
OptrexDisplay = 0;
|
|
lcdRefresh();
|
|
#endif
|
|
|
|
#if defined(PCBTARANIS)
|
|
keysInit();
|
|
I2C_EE_Init();
|
|
init_hw_timer();
|
|
#endif
|
|
|
|
__enable_irq();
|
|
init10msTimer();
|
|
|
|
#if defined(PCBSKY9X)
|
|
EblockAddress = -1;
|
|
init_spi();
|
|
#endif
|
|
|
|
#if defined(PCBSKY9X)
|
|
uint32_t chip_id = CHIPID->CHIPID_CIDR;
|
|
FlashSize = ( (chip_id >> 8 ) & 0x000F ) == 9 ? 256 : 512;
|
|
#elif defined(PCBTARANIS)
|
|
FlashSize = 512;
|
|
#endif
|
|
|
|
#if defined(PCBSKY9X)
|
|
LockBits = readLockBits();
|
|
if (LockBits) {
|
|
clearLockBits();
|
|
}
|
|
#endif
|
|
|
|
#if defined(PCBTARANIS)
|
|
// SD card detect pin
|
|
sdInit();
|
|
unlockFlash();
|
|
usbInit();
|
|
usbStart();
|
|
#endif
|
|
|
|
for (;;) {
|
|
wdt_reset();
|
|
|
|
if (Tenms) {
|
|
wdt_reset(); // Retrigger hardware watchdog
|
|
|
|
if (EE_timer) {
|
|
if (--EE_timer == 0) {
|
|
#if defined(PCBSKY9X)
|
|
writeBlock();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
Tenms = 0;
|
|
|
|
lcd_clear();
|
|
lcd_putsLeft(0, BOOTLOADER_TITLE);
|
|
lcd_invert_line(0);
|
|
|
|
uint8_t event = getEvent();
|
|
|
|
if (usbPlugged()) {
|
|
state = ST_USB;
|
|
}
|
|
|
|
if (state == ST_START) {
|
|
lcd_putsLeft(2*FH, "\010Load Firmware");
|
|
lcd_putsLeft(3*FH, "\010Exit");
|
|
lcd_invert_line(2+vpos);
|
|
lcd_putsLeft(6*FH, INDENT "Or plug in a USB cable for mass storage");
|
|
if (event == EVT_KEY_FIRST(BOOT_KEY_DOWN) || event == EVT_KEY_FIRST(BOOT_KEY_UP)) {
|
|
vpos = (vpos+1) & 0x01;
|
|
}
|
|
else if (event == EVT_KEY_BREAK(BOOT_KEY_MENU)) {
|
|
if (vpos == 0)
|
|
state = ST_FLASH_MENU;
|
|
else
|
|
state = ST_REBOOT;
|
|
}
|
|
}
|
|
|
|
if (state == ST_USB) {
|
|
lcd_putsLeft(4*FH, "\026USB Connected");
|
|
if (usbPlugged() == 0) {
|
|
vpos = 0;
|
|
state = ST_START;
|
|
}
|
|
#if defined(PCBSKY9X)
|
|
usbMassStorage();
|
|
lcd_putc( 0, 6*FH, 'F' );
|
|
lcd_putc( 6, 6*FH, '0' + FlashBlocked );
|
|
lcd_putc( 0, 7*FH, 'E' );
|
|
lcd_putc( 6, 7*FH, '0' + EepromBlocked );
|
|
#endif
|
|
}
|
|
|
|
if (state == ST_FLASH_MENU) {
|
|
sdInit();
|
|
state = ST_DIR_CHECK;
|
|
}
|
|
|
|
else if (state == ST_DIR_CHECK) {
|
|
fr = f_chdir(FIRMWARES_PATH);
|
|
if (fr == FR_OK) {
|
|
state = ST_OPEN_DIR;
|
|
}
|
|
else {
|
|
lcd_putsLeft(2*FH, INDENT "No firmware in " FIRMWARES_PATH " directory");
|
|
if (event == EVT_KEY_FIRST(BOOT_KEY_EXIT) || event == EVT_KEY_FIRST(BOOT_KEY_MENU)) {
|
|
vpos = 0;
|
|
state = ST_START;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (state == ST_OPEN_DIR) {
|
|
index = 0;
|
|
fr = f_opendir(&Dj, ".");
|
|
if (fr == FR_OK) {
|
|
state = ST_FILE_LIST;
|
|
nameCount = fillNames(0);
|
|
hpos = 0;
|
|
vpos = 0;
|
|
}
|
|
}
|
|
|
|
if (state == ST_FILE_LIST) {
|
|
uint32_t limit = 6;
|
|
if (nameCount < limit) {
|
|
limit = nameCount;
|
|
}
|
|
maxhsize = 0;
|
|
for (uint32_t i=0; i<limit; i++) {
|
|
uint32_t x;
|
|
x = strlen(Filenames[i]);
|
|
if (x > maxhsize) {
|
|
maxhsize = x;
|
|
}
|
|
if (x > DISPLAY_CHAR_WIDTH) {
|
|
if (hpos + DISPLAY_CHAR_WIDTH > x) {
|
|
x = x - DISPLAY_CHAR_WIDTH;
|
|
}
|
|
else {
|
|
x = hpos;
|
|
}
|
|
}
|
|
else {
|
|
x = 0;
|
|
}
|
|
lcd_putsnAtt(INDENT_WIDTH, 16 + FH * i, &Filenames[i][x], DISPLAY_CHAR_WIDTH, 0);
|
|
}
|
|
|
|
if (event == EVT_KEY_REPT(BOOT_KEY_DOWN) || event == EVT_KEY_FIRST(BOOT_KEY_DOWN)) {
|
|
if (vpos < limit - 1) {
|
|
vpos += 1;
|
|
}
|
|
else {
|
|
if (nameCount > limit) {
|
|
index += 1;
|
|
nameCount = fillNames(index);
|
|
}
|
|
}
|
|
}
|
|
else if (event == EVT_KEY_REPT(BOOT_KEY_UP) || event == EVT_KEY_FIRST(BOOT_KEY_UP)) {
|
|
if (vpos > 0) {
|
|
vpos -= 1;
|
|
}
|
|
else {
|
|
if (index) {
|
|
index -= 1;
|
|
nameCount = fillNames(index);
|
|
}
|
|
}
|
|
}
|
|
#if !defined(PCBTARANIS)
|
|
else if (event == EVT_KEY_REPT(BOOT_KEY_RIGHT) || event == EVT_KEY_FIRST(BOOT_KEY_RIGHT)) {
|
|
if (hpos + DISPLAY_CHAR_WIDTH < maxhsize) {
|
|
hpos += 1;
|
|
}
|
|
}
|
|
else if (event == EVT_KEY_REPT(BOOT_KEY_LEFT) || event == EVT_KEY_FIRST(BOOT_KEY_LEFT)) {
|
|
if (hpos) {
|
|
hpos -= 1;
|
|
}
|
|
}
|
|
#endif
|
|
else if (event == EVT_KEY_BREAK(BOOT_KEY_MENU)) {
|
|
// Select file to flash
|
|
state = ST_FLASH_CHECK;
|
|
Valid = 0;
|
|
}
|
|
else if (event == EVT_KEY_FIRST(BOOT_KEY_EXIT)) {
|
|
state = ST_START;
|
|
vpos = 0;
|
|
}
|
|
|
|
lcd_invert_line(2 + vpos);
|
|
}
|
|
|
|
else if (state == ST_FLASH_CHECK) {
|
|
int result = menuFlashFile(vpos, event);
|
|
FirmwareSize = FileSize[vpos] - BOOTLOADER_SIZE;
|
|
if (result == 0) {
|
|
// canceled
|
|
state = ST_FILE_LIST;
|
|
}
|
|
else if (result == 1) {
|
|
// confirmed
|
|
firmwareAddress = FIRMWARE_ADDRESS + BOOTLOADER_SIZE;
|
|
firmwareWritten = 0;
|
|
state = ST_FLASHING;
|
|
}
|
|
}
|
|
|
|
if (state == ST_FLASHING) {
|
|
// Commit to flashing
|
|
uint32_t blockOffset = 0;
|
|
lcd_putsLeft(4*FH, "\032Loading...");
|
|
while (BlockCount) {
|
|
writeFlash((uint32_t *)firmwareAddress, &Block_buffer[blockOffset]);
|
|
blockOffset += FLASH_PAGESIZE/4; // 32-bit words
|
|
firmwareAddress += FLASH_PAGESIZE;
|
|
if (BlockCount > FLASH_PAGESIZE) {
|
|
BlockCount -= FLASH_PAGESIZE;
|
|
}
|
|
else {
|
|
BlockCount = 0;
|
|
}
|
|
}
|
|
firmwareWritten += 4; // 4K blocks
|
|
|
|
lcd_rect( 3, 6*FH+4, 204, 7);
|
|
lcd_hline(5, 6*FH+6, (200*firmwareWritten*1024)/FirmwareSize, FORCE);
|
|
lcd_hline(5, 6*FH+7, (200*firmwareWritten*1024)/FirmwareSize, FORCE);
|
|
lcd_hline(5, 6*FH+8, (200*firmwareWritten*1024)/FirmwareSize, FORCE);
|
|
|
|
fr = f_read(&FlashFile, (BYTE *)Block_buffer, sizeof(Block_buffer), &BlockCount);
|
|
if (BlockCount == 0) {
|
|
state = ST_FLASH_DONE;
|
|
}
|
|
if (firmwareWritten >= FlashSize - 32) {
|
|
state = ST_FLASH_DONE; // Backstop
|
|
}
|
|
}
|
|
|
|
if (state == ST_FLASH_DONE) {
|
|
lcd_putsLeft(4*FH, "\024Loading Complete");
|
|
if (event == EVT_KEY_FIRST(BOOT_KEY_EXIT) || event == EVT_KEY_BREAK(BOOT_KEY_MENU)) {
|
|
state = ST_START;
|
|
vpos = 0;
|
|
}
|
|
}
|
|
|
|
if (event == EVT_KEY_LONG(BOOT_KEY_EXIT)) {
|
|
state = ST_REBOOT;
|
|
}
|
|
|
|
if (--TenCount == 0) {
|
|
TenCount = 2;
|
|
lcdRefresh();
|
|
}
|
|
|
|
if (PowerUpDelay < 20) { // 200 mS
|
|
PowerUpDelay += 1;
|
|
}
|
|
else {
|
|
sdPoll10ms();
|
|
}
|
|
}
|
|
|
|
if (pwrCheck() == e_power_off && state != ST_FLASHING && state != ST_USB) {
|
|
pwrOff();
|
|
for (;;) {
|
|
// Wait for power to go off
|
|
}
|
|
}
|
|
|
|
if (state == ST_REBOOT) {
|
|
if ((~readKeys() & 0x7E) == 0) {
|
|
NVIC_SystemReset();
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|