UKSVEP_23550.2/Core/Bootloader/bootloader.c
Razvalyaev 320cce09ec сделано (проверено на can):
отправка ошибок бутлоадера по uart/can
проверка crc принятой страницы
проверка на бесконечное попадание в hardfault

в целом структура бута все еще в процессе разработки
2025-09-11 16:57:20 +03:00

428 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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);
}