отправка ошибок бутлоадера по uart/can проверка crc принятой страницы проверка на бесконечное попадание в hardfault в целом структура бута все еще в процессе разработки
428 lines
12 KiB
C
428 lines
12 KiB
C
#include "bootloader.h"
|
||
#include "boot_flash.h"
|
||
#include "boot_uart.h"
|
||
#include "boot_can.h"
|
||
|
||
HAL_StatusTypeDef res_hal;
|
||
CAN_TxHeaderTypeDef TxHeaderBoot;
|
||
CAN_RxHeaderTypeDef RxHeaderBoot;
|
||
uint32_t TxMailBoxBoot = 0;
|
||
uint8_t TXDataBoot[8] = {0};
|
||
int receive_uart_flag = 0;
|
||
|
||
void SetKey(void);
|
||
uint32_t ReadKey(void);
|
||
void EraseKey(void);
|
||
void Boot_SystemClock_Config(void);
|
||
void JumpToApplocation(void);
|
||
|
||
void Bootloader_UART_Receive_Page(Bootloader_t *bl);
|
||
void Bootloader_CAN_Receive_Page(Bootloader_t *bl);
|
||
|
||
uint32_t CRC32_Compute(const uint8_t* data, uint32_t length);
|
||
|
||
void Bootloader_Init(Bootloader_t *bl)
|
||
{
|
||
HAL_Init();
|
||
|
||
Boot_SystemClock_Config();
|
||
MX_BOOT_UART_Init();
|
||
MX_BOOT_CAN_Init();
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
uint32_t code = 0;
|
||
uint32_t cnt = 0;
|
||
void Bootloader_Task(Bootloader_t *bl)
|
||
{
|
||
|
||
switch (bl->state)
|
||
{
|
||
case BL_STATE_INIT:
|
||
code = LoadErrorCode();
|
||
cnt = LoadErrorCnt();
|
||
|
||
bl->prev_state = bl->state;
|
||
// Проверяем ключ, чтобы понять запускать приложение или программирование
|
||
if ((ReadKey() == BL_KEY_APP_WRITTEN) && (cnt <= 5))
|
||
{
|
||
bl->state = BL_STATE_JUMP_TO_APP;
|
||
break; // не инициализируем, а сразу прыгаем в приложение
|
||
}
|
||
else
|
||
{
|
||
bl->state = BL_STATE_IDLE;
|
||
}
|
||
|
||
// Инициализация периферии
|
||
Bootloader_Init(bl);
|
||
bl->huart = &huart_boot;
|
||
bl->hcan = &hcan_boot;
|
||
bl->TxHeader.DLC = 8;
|
||
bl->TxHeader.StdId = 123;
|
||
bl->addr = MAIN_APP_START_ADR;
|
||
|
||
if(cnt > 5)
|
||
{
|
||
ClearErrorCode();
|
||
if(code == 0xDEAD) // HardFault
|
||
{
|
||
ResetKey();
|
||
bl->error.bit.hardfault_cycle = 1;
|
||
TXDataBoot[0] = 0xAA;
|
||
TXDataBoot[1] = (bl->error.all >> 8) & 0xFF;
|
||
TXDataBoot[2] = bl->error.all & 0xFF;
|
||
res_hal = HAL_CAN_AddTxMessage(bl->hcan, &bl->TxHeader, TXDataBoot, &TxMailBoxBoot);
|
||
res_hal = HAL_UART_Transmit(bl->huart, TXDataBoot, 1, 100);
|
||
}
|
||
else if(code == 0xBEEF) // MemManage
|
||
{
|
||
ResetKey();
|
||
bl->error.bit.memmanage_cycle = 1;
|
||
TXDataBoot[0] = 0x55;
|
||
TXDataBoot[1] = (bl->error.all >> 8) & 0xFF;
|
||
TXDataBoot[2] = bl->error.all & 0xFF;
|
||
res_hal = HAL_CAN_AddTxMessage(bl->hcan, &bl->TxHeader, TXDataBoot, &TxMailBoxBoot);
|
||
res_hal = HAL_UART_Transmit(bl->huart, TXDataBoot, 1, 100);
|
||
}
|
||
}
|
||
|
||
break;
|
||
|
||
case BL_STATE_IDLE:
|
||
if((bl->state != bl->prev_state) && (bl->prev_state != BL_STATE_ERROR))
|
||
{
|
||
TXDataBoot[0] = 0x00;
|
||
res_hal = HAL_CAN_AddTxMessage(bl->hcan, &bl->TxHeader, TXDataBoot, &TxMailBoxBoot);
|
||
res_hal = HAL_UART_Transmit(bl->huart, TXDataBoot, 1, 100);
|
||
}
|
||
bl->prev_state = bl->state;
|
||
// Ждем команды по UART или CAN
|
||
if (Receive_FW_Command(bl) == 0xFF) // функция обработки команд, возвращает 1 если ошибка
|
||
{
|
||
bl->error.bit.unknown_cmd = 1;
|
||
bl->state = BL_STATE_ERROR;
|
||
}
|
||
break;
|
||
|
||
case BL_STATE_RESET:
|
||
NVIC_SystemReset();
|
||
break;
|
||
|
||
case BL_STATE_ERASE:
|
||
bl->prev_state = bl->state;
|
||
EraseKey();
|
||
if (FLASH_Erase_App() == HAL_OK) // твоя функция стирания MAIN_APP_PAGE..NUM
|
||
{
|
||
bl->state = BL_STATE_IDLE;
|
||
}
|
||
else
|
||
{
|
||
bl->error.bit.erase_err = 1;
|
||
bl->state = BL_STATE_ERROR;
|
||
}
|
||
break;
|
||
|
||
case BL_STATE_RECEIVE_UART:
|
||
case BL_STATE_RECEIVE_CAN:
|
||
receive_uart_flag = (bl->state == BL_STATE_RECEIVE_UART) ? 1 : 0;
|
||
|
||
TXDataBoot[0] = 0x00;
|
||
bl->prev_state = bl->state;
|
||
if(receive_uart_flag)
|
||
{
|
||
res_hal = HAL_UART_Transmit(bl->huart, TXDataBoot, 1, 100);
|
||
Bootloader_UART_Receive_Page(bl);
|
||
}
|
||
else
|
||
{
|
||
res_hal = HAL_CAN_AddTxMessage(bl->hcan, &bl->TxHeader, TXDataBoot, &TxMailBoxBoot);
|
||
Bootloader_CAN_Receive_Page(bl);
|
||
}
|
||
uint32_t crc_calculated = CRC32_Compute((uint8_t *)bl->fw_buffer, bl->fw_len);
|
||
if(crc_calculated != bl->fw_crc)
|
||
{
|
||
for(int i = 0; i < bl->fw_len; i++)
|
||
{
|
||
bl->fw_buffer[i] = 0;
|
||
}
|
||
bl->error.bit.crc_err = 1;
|
||
bl->state = BL_STATE_ERROR;
|
||
}
|
||
break;
|
||
|
||
case BL_STATE_WRITE:
|
||
bl->prev_state = bl->state;
|
||
if (FLASH_Write_Page(&bl->addr, bl->fw_buffer, bl->fw_len) == HAL_OK) // запись блока во Flash
|
||
{
|
||
bl->state = BL_STATE_IDLE; // ждём следующего блока
|
||
}
|
||
else
|
||
{
|
||
bl->error.bit.write_err = 1;
|
||
bl->state = BL_STATE_ERROR;
|
||
}
|
||
break;
|
||
|
||
case BL_STATE_VERIFY:
|
||
bl->prev_state = bl->state;
|
||
if (/*Verify_Flash_CRC(bl->fw_crc)*/0 == HAL_OK)
|
||
{
|
||
EraseKey();
|
||
SetKey(); // отметка, что прошивка записана
|
||
bl->state = BL_STATE_IDLE;
|
||
}
|
||
else
|
||
{
|
||
bl->error.bit.verify_err = 1;
|
||
bl->state = BL_STATE_ERROR;
|
||
}
|
||
break;
|
||
|
||
case BL_STATE_JUMP_TO_APP:
|
||
bl->prev_state = bl->state;
|
||
JumpToApplocation();
|
||
break;
|
||
|
||
case BL_STATE_JUMP_TO_BOOT:
|
||
bl->prev_state = bl->state;
|
||
JumpToBootloader();
|
||
break;
|
||
|
||
|
||
case BL_STATE_ERROR:
|
||
if(bl->state != bl->prev_state)
|
||
{
|
||
TXDataBoot[0] = 0xFF;
|
||
TXDataBoot[1] = (bl->error.all >> 8) & (0xFF);
|
||
TXDataBoot[2] = bl->error.all & (0xFF);
|
||
res_hal = HAL_CAN_AddTxMessage(bl->hcan, &bl->TxHeader, TXDataBoot, &TxMailBoxBoot);
|
||
res_hal = HAL_UART_Transmit(bl->huart, TXDataBoot, 1, 100);
|
||
}
|
||
bl->prev_state = bl->state;
|
||
// Ждем команды по UART или CAN
|
||
if (Receive_FW_Command(bl) == 0xFF) // функция обработки команд, возвращает 1 если ошибка
|
||
{
|
||
bl->error.bit.unknown_cmd = 1;
|
||
bl->state = BL_STATE_ERROR;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
bl->error.bit.unknown_cmd = 1;
|
||
bl->state = BL_STATE_ERROR;
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
uint8_t SetBootState(Bootloader_t *bl, BootloaderCommand_t cmd, uint8_t uart_flag)
|
||
{
|
||
switch(cmd)
|
||
{
|
||
case CMD_ERASE: // команда: стереть Flash
|
||
bl->state = BL_STATE_ERASE;
|
||
return 0x00;
|
||
case CMD_START_RECEIVE: // команда: принять блок
|
||
if(uart_flag)
|
||
bl->state = BL_STATE_RECEIVE_UART;
|
||
else
|
||
bl->state = BL_STATE_RECEIVE_CAN;
|
||
return 0x00;
|
||
case CMD_WRITE: // команда: записать блок
|
||
bl->state = BL_STATE_WRITE;
|
||
return 0x00;
|
||
case CMD_VERIFY: // команда: проверка прошивки
|
||
bl->state = BL_STATE_VERIFY;
|
||
return 0x00;
|
||
case CMD_GOTOAPP: // команда: прыжок в приложение
|
||
bl->state = BL_STATE_JUMP_TO_APP;
|
||
return 0x00;
|
||
case CMD_RESET: // команда: прыжок в приложение
|
||
bl->state = BL_STATE_RESET;
|
||
return 0x00;
|
||
case CMD_GOTOBOOT: // команда: прыжок в бутлоадер
|
||
bl->state = BL_STATE_JUMP_TO_BOOT;
|
||
return 0x00;
|
||
|
||
default:
|
||
return 0xFF; // неизвестная команда
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief Обработка команд прошивки по UART или CAN
|
||
* @param bl: указатель на структуру бутлоадера
|
||
* @retval 0x00 - команда принята и обработана, 0xFF - ошибка
|
||
*/
|
||
uint8_t Receive_FW_Command(Bootloader_t *bl)
|
||
{
|
||
BootloaderCommand_t cmd = 0;
|
||
HAL_StatusTypeDef res = HAL_ERROR;
|
||
uint8_t ret_val = 0x00;
|
||
|
||
// ---------------------------
|
||
// Чтение команды по UART
|
||
// ---------------------------
|
||
res = HAL_UART_Receive(bl->huart, &cmd, 1, 10); // таймаут 10 ms
|
||
if (res == HAL_OK)
|
||
{
|
||
ret_val = SetBootState(bl, cmd, 1);
|
||
}
|
||
|
||
// ---------------------------
|
||
// Чтение команды по CAN
|
||
// ---------------------------
|
||
uint8_t canData[8];
|
||
if (HAL_CAN_GetRxFifoFillLevel(bl->hcan, CAN_RX_FIFO0) > 0)
|
||
{
|
||
if (HAL_CAN_GetRxMessage(bl->hcan, CAN_RX_FIFO0, &RxHeaderBoot, canData) == HAL_OK)
|
||
{
|
||
cmd = canData[0]; // предполагаем, что команда в первом байте
|
||
ret_val = SetBootState(bl, cmd, 0);
|
||
}
|
||
}
|
||
|
||
#ifdef TEST_CAN
|
||
TxHeaderBoot.StdId = 0x200; // ID OF MESSAGE
|
||
TxHeaderBoot.ExtId = 0; // STANDART FRAME (NOT EXTENTED)
|
||
TxHeaderBoot.RTR = CAN_RTR_DATA; // TRANSMIT DATA OR
|
||
TxHeaderBoot.IDE = CAN_ID_STD; // STANDART FRAME
|
||
TxHeaderBoot.DLC = 8; // DATA SIZE
|
||
TxHeaderBoot.TransmitGlobalTime = DISABLE; //THIS MODE IS NOT USED, SO DISABLE
|
||
uint8_t asd[8] = "ABCDEFGL";
|
||
res_hal = HAL_CAN_AddTxMessage(&hcan_boot, &TxHeaderBoot, asd, &TxMailBoxBoot); // add to mail for transmit
|
||
HAL_Delay(1000);
|
||
#endif
|
||
|
||
return ret_val;
|
||
}
|
||
|
||
uint32_t CRC32_Compute(const uint8_t* data, uint32_t length)
|
||
{
|
||
const uint32_t polynomial = 0x04C11DB7;
|
||
uint32_t crc = 0xFFFFFFFF;
|
||
|
||
for(uint32_t i = 0; i < length; i++)
|
||
{
|
||
crc ^= ((uint32_t)data[i] << 24);
|
||
for(uint8_t j = 0; j < 8; j++)
|
||
{
|
||
if(crc & 0x80000000)
|
||
crc = (crc << 1) ^ polynomial;
|
||
else
|
||
crc <<= 1;
|
||
}
|
||
}
|
||
|
||
return crc ^ 0xFFFFFFFF;
|
||
}
|
||
|
||
|
||
// key functions
|
||
void ResetKey(void)
|
||
{
|
||
HAL_FLASH_Unlock();
|
||
|
||
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, BOOTLOADER_KEY_ADR, 0);
|
||
|
||
HAL_FLASH_Lock();
|
||
}
|
||
|
||
void SetKey(void)
|
||
{
|
||
HAL_FLASH_Unlock();
|
||
|
||
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, BOOTLOADER_KEY_ADR, BL_KEY_APP_WRITTEN);
|
||
|
||
HAL_FLASH_Lock();
|
||
}
|
||
|
||
uint32_t ReadKey(void)
|
||
{
|
||
return (*(__IO uint32_t*)BOOTLOADER_KEY_ADR);
|
||
}
|
||
|
||
void EraseKey(void)
|
||
{
|
||
FLASH_EraseInitTypeDef EraseInitStruct;
|
||
HAL_FLASH_Unlock();
|
||
uint32_t PageError = 0x00;
|
||
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;// erase pages
|
||
EraseInitStruct.PageAddress = BOOTLOADER_KEY_ADR; //address
|
||
EraseInitStruct.NbPages = 0x01;// num of erased pages
|
||
|
||
HAL_FLASHEx_Erase(&EraseInitStruct, &PageError);
|
||
HAL_FLASH_Lock();
|
||
|
||
}
|
||
|
||
|
||
|
||
void JumpToApplocation(void)
|
||
{
|
||
//Деинициализация HAL
|
||
HAL_DeInit();
|
||
|
||
//Перенос вектора прерываний на начало зашитой программы
|
||
__disable_irq();
|
||
__set_MSP(*((volatile uint32_t*)MAIN_APP_START_ADR));
|
||
__enable_irq();
|
||
|
||
//Переход к выполнению зашитой программы
|
||
__ASM volatile(
|
||
"ldr r0, [%0, #4]\n" // r0 = *(MAIN_APP_START_ADR + 4)
|
||
"bx r0\n" // переход по адресу в r0
|
||
:
|
||
: "r"(MAIN_APP_START_ADR)
|
||
: "r0"
|
||
);
|
||
//Note: asm потому что при O0 компилятор делал локальные переменные,
|
||
// из-за чего при смене стека он не мог получить адрес для прыжка
|
||
}
|
||
|
||
|
||
void JumpToBootloader(void)
|
||
{
|
||
// jump to boot
|
||
ResetKey(); // сброс ключа (не erase, просто битый ключ
|
||
NVIC_SystemReset(); // сброс и переход в бутлоадер (т.к. нет ключа)
|
||
}
|
||
|
||
|
||
// Сохранение кода ошибки
|
||
// Чтение после ресета
|
||
uint32_t LoadErrorCode(void)
|
||
{
|
||
return BKP->DR1;
|
||
}
|
||
|
||
uint32_t LoadErrorCnt(void)
|
||
{
|
||
return BKP->DR2;
|
||
}
|
||
|
||
void ClearErrorCode(void)
|
||
{
|
||
PWR->CR |= PWR_CR_DBP;
|
||
BKP->DR1 = 0;
|
||
BKP->DR2 = 0;
|
||
}
|
||
|
||
|
||
__WEAK void Boot_SystemClock_Config(void)
|
||
{
|
||
}
|
||
|
||
|
||
__WEAK void Error_Handler(void)
|
||
{
|
||
while(1);
|
||
}
|
||
|