структуризирован бутлоадер. работает
надо по протоколу подумать еще, доработать его и описать
This commit is contained in:
136
Core/Bootloader/Src/boot_can.c
Normal file
136
Core/Bootloader/Src/boot_can.c
Normal file
@@ -0,0 +1,136 @@
|
||||
#include "boot_can.h"
|
||||
#include "boot_gpio.h"
|
||||
|
||||
CAN_HandleTypeDef hcan_boot;
|
||||
|
||||
/**
|
||||
* @brief Инициализация CAN для бутлоадера
|
||||
*/
|
||||
void MX_BOOT_CAN_Init(void)
|
||||
{
|
||||
GPIO_InitTypeDef GPIO_InitStruct = {0};
|
||||
CAN_FilterTypeDef sFilterConfig;
|
||||
|
||||
/* Включаем тактирование */
|
||||
__RCC_CAN_BOOT_CLK_ENABLE();
|
||||
__RCC_CAN_PORT_CLK_ENABLE();
|
||||
|
||||
/* Настройка пинов RX/TX */
|
||||
GPIO_InitStruct.Pin = CAN_PIN_RX;
|
||||
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
|
||||
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
||||
HAL_GPIO_Init(CAN_PORT, &GPIO_InitStruct);
|
||||
|
||||
GPIO_InitStruct.Pin = CAN_PIN_TX;
|
||||
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
|
||||
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
|
||||
HAL_GPIO_Init(CAN_PORT, &GPIO_InitStruct);
|
||||
|
||||
/* Настройка CAN */
|
||||
hcan_boot.Instance = CAN_BOOT;
|
||||
hcan_boot.Init.Prescaler = CAN_SPEED_PRESCALER;
|
||||
hcan_boot.Init.Mode = CAN_MODE;
|
||||
hcan_boot.Init.SyncJumpWidth = CAN_SJW_1TQ;
|
||||
hcan_boot.Init.TimeSeg1 = CAN_SPEED_BS1;
|
||||
hcan_boot.Init.TimeSeg2 = CAN_SPEED_BS2;
|
||||
hcan_boot.Init.TimeTriggeredMode = DISABLE;
|
||||
hcan_boot.Init.AutoBusOff = DISABLE;
|
||||
hcan_boot.Init.AutoWakeUp = DISABLE;
|
||||
hcan_boot.Init.AutoRetransmission = ENABLE;
|
||||
hcan_boot.Init.ReceiveFifoLocked = DISABLE;
|
||||
hcan_boot.Init.TransmitFifoPriority = DISABLE;
|
||||
|
||||
if (HAL_CAN_Init(&hcan_boot) != HAL_OK)
|
||||
{
|
||||
Error_Handler();
|
||||
}
|
||||
|
||||
/* Настройка фильтра: пропускать все сообщения */
|
||||
sFilterConfig.FilterBank = 0;
|
||||
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
|
||||
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
|
||||
sFilterConfig.FilterIdHigh = 0x0000;
|
||||
sFilterConfig.FilterIdLow = 0x0000;
|
||||
sFilterConfig.FilterMaskIdHigh = 0x0000;
|
||||
sFilterConfig.FilterMaskIdLow = 0x0000;
|
||||
sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;
|
||||
sFilterConfig.FilterActivation = ENABLE;
|
||||
|
||||
if (HAL_CAN_ConfigFilter(&hcan_boot, &sFilterConfig) != HAL_OK)
|
||||
{
|
||||
Error_Handler();
|
||||
}
|
||||
|
||||
/* Запускаем CAN */
|
||||
if (HAL_CAN_Start(&hcan_boot) != HAL_OK)
|
||||
{
|
||||
Error_Handler();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Приём CAN: страница + CRC
|
||||
*/
|
||||
void Bootloader_CAN_Receive_Page(Bootloader_t *bl)
|
||||
{
|
||||
uint16_t bytes_received = 0;
|
||||
CAN_RxHeaderTypeDef canHeader;
|
||||
uint8_t canData[8];
|
||||
uint32_t start_tick = HAL_GetTick();
|
||||
|
||||
// Приём страницы прошивки
|
||||
while(bytes_received < PAGE_SIZE)
|
||||
{
|
||||
if(HAL_CAN_GetRxFifoFillLevel(bl->hcan, CAN_RX_FIFO0) > 0)
|
||||
{
|
||||
if(HAL_CAN_GetRxMessage(bl->hcan, CAN_RX_FIFO0, &canHeader, canData) == HAL_OK)
|
||||
{
|
||||
uint8_t len = canHeader.DLC;
|
||||
if(bytes_received + len > PAGE_SIZE)
|
||||
len = PAGE_SIZE - bytes_received;
|
||||
|
||||
memcpy(&bl->fw_buffer[bytes_received], canData, len);
|
||||
bytes_received += len;
|
||||
start_tick = HAL_GetTick(); // сброс таймаута
|
||||
LED_BOOT_TOOGLE();
|
||||
}
|
||||
}
|
||||
|
||||
// проверка таймаута
|
||||
if(HAL_GetTick() - start_tick >= FW_RECEIVE_TIMEOUT_MS)
|
||||
{
|
||||
bl->error.bit.timeout_receive = 1;
|
||||
bl->state = BL_STATE_ERROR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Приём CRC (4 байта)
|
||||
start_tick = HAL_GetTick(); // сброс таймаута
|
||||
while(1)
|
||||
{
|
||||
if(HAL_CAN_GetRxFifoFillLevel(bl->hcan, CAN_RX_FIFO0) > 0)
|
||||
{
|
||||
if(HAL_CAN_GetRxMessage(bl->hcan, CAN_RX_FIFO0, &canHeader, canData) == HAL_OK)
|
||||
{
|
||||
// CRC в первых 4 байтах пакета
|
||||
bl->fw_crc = (canData[0] << 24) |
|
||||
(canData[1] << 16) |
|
||||
(canData[2] << 8) |
|
||||
canData[3];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Таймаут
|
||||
if(HAL_GetTick() - start_tick >= FW_RECEIVE_TIMEOUT_MS)
|
||||
{
|
||||
bl->error.bit.timeout_receive = 1;
|
||||
bl->state = BL_STATE_ERROR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bl->fw_len = PAGE_SIZE;
|
||||
bl->state = BL_STATE_IDLE;
|
||||
}
|
||||
|
||||
92
Core/Bootloader/Src/boot_flash.c
Normal file
92
Core/Bootloader/Src/boot_flash.c
Normal file
@@ -0,0 +1,92 @@
|
||||
#include "boot_flash.h"
|
||||
uint32_t word_data;
|
||||
|
||||
|
||||
HAL_StatusTypeDef FLASH_Erase_App(void) //
|
||||
{
|
||||
HAL_StatusTypeDef res;
|
||||
uint32_t PageError = 0x00;
|
||||
|
||||
res = HAL_FLASH_Unlock();
|
||||
|
||||
if (res != HAL_OK) return res;
|
||||
|
||||
FLASH_EraseInitTypeDef EraseInitStruct;
|
||||
|
||||
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;// erase pages
|
||||
EraseInitStruct.Banks = 1;
|
||||
EraseInitStruct.PageAddress = MAIN_APP_START_ADR; //address
|
||||
EraseInitStruct.NbPages = MAIN_APP_NUM_OF_PAGE;// num of erased pages
|
||||
|
||||
res = HAL_FLASHEx_Erase(&EraseInitStruct, &PageError);
|
||||
|
||||
if (res != HAL_OK) return res;
|
||||
|
||||
res = HAL_FLASH_Lock();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
HAL_StatusTypeDef FLASH_Write_Page(uint32_t *Address, uint8_t *Data, int Data_size)
|
||||
{
|
||||
//GPIOB->ODR^=(0x2000);
|
||||
// GPIOB->ODR|=0x4000;
|
||||
|
||||
HAL_StatusTypeDef res;
|
||||
int data_cnt = 0;
|
||||
uint32_t adr;
|
||||
|
||||
|
||||
res = HAL_FLASH_Unlock();
|
||||
|
||||
if (res != HAL_OK) return res;
|
||||
|
||||
for (adr = *Address; adr < *Address + Data_size; adr = adr+4)
|
||||
{
|
||||
word_data = (
|
||||
Data[data_cnt]|
|
||||
Data[data_cnt+1]<<8|
|
||||
Data[data_cnt+2]<<16|
|
||||
Data[data_cnt+3]<<24);
|
||||
|
||||
res = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, adr, word_data);
|
||||
|
||||
if (res != HAL_OK) return res;
|
||||
|
||||
data_cnt +=4;
|
||||
}
|
||||
|
||||
*Address += Data_size;
|
||||
res = HAL_FLASH_Lock();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
HAL_StatusTypeDef FLASH_Write_Word(uint32_t Address, uint64_t Data) //Куда записывать
|
||||
{
|
||||
HAL_StatusTypeDef res;
|
||||
|
||||
res = HAL_FLASH_Unlock();
|
||||
|
||||
if (res != HAL_OK) return res;
|
||||
|
||||
res = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, (uint32_t)(Data));
|
||||
|
||||
if (res != HAL_OK) return res;
|
||||
|
||||
res = HAL_FLASH_Lock();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
24
Core/Bootloader/Src/boot_gpio.c
Normal file
24
Core/Bootloader/Src/boot_gpio.c
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "boot_gpio.h"
|
||||
|
||||
/**
|
||||
* @brief Инициализация GPIO для бутлоадера
|
||||
*/
|
||||
void MX_BOOT_GPIO_Init(void)
|
||||
{
|
||||
__RCC_LED_BOOT_CLK_ENABLE();
|
||||
|
||||
GPIO_InitTypeDef GPIO_InitStruct = {0};
|
||||
GPIO_InitStruct.Pin = LED_BOOT_Pin;
|
||||
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // Push-Pull выход
|
||||
GPIO_InitStruct.Pull = GPIO_NOPULL; // Без подтяжки
|
||||
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // Низкая скорость
|
||||
HAL_GPIO_Init(LED_BOOT_GPIO_Port, &GPIO_InitStruct);
|
||||
|
||||
for(int cnt = 0; cnt < 5; cnt++)
|
||||
{
|
||||
LED_BOOT_ON();
|
||||
for(int delay = 0; delay < 10000; delay++);
|
||||
LED_BOOT_OFF();
|
||||
for(int delay = 0; delay < 10000; delay++);
|
||||
}
|
||||
}
|
||||
150
Core/Bootloader/Src/boot_jump.c
Normal file
150
Core/Bootloader/Src/boot_jump.c
Normal file
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
* @file boot_jump.c
|
||||
* @brief Функции для перехода между бутлоадером и основным приложением,
|
||||
* управление ключом BOOT и проверка прошивки.
|
||||
*
|
||||
* Основные возможности:
|
||||
* - Настройка вектора прерываний для запуска приложения
|
||||
* - Управление ключом BOOT в Flash
|
||||
* - Проверка корректности прошивки перед прыжком
|
||||
* - Функции прыжка: Bootloader <-> Application
|
||||
*/
|
||||
#include "boot_jump.h"
|
||||
|
||||
|
||||
/**
|
||||
* @brief Инициализация приложения.
|
||||
* Устанавливает вектор прерываний на начало основного приложения.
|
||||
*/
|
||||
void App_Init(void)
|
||||
{
|
||||
__disable_irq();
|
||||
SCB->VTOR = MAIN_APP_START_ADR;
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Переход в бутлоадер.
|
||||
* Сбрасывает ключ BOOT и выполняет системный сброс.
|
||||
*/
|
||||
void JumpToBootloader(void)
|
||||
{
|
||||
// jump to boot
|
||||
ResetKey(); // сброс ключа (не erase, просто битый ключ)
|
||||
NVIC_SystemReset(); // сброс и переход в бутлоадер (т.к. нет ключа)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Переход к основному приложению.
|
||||
* Настраивает стек и переход к ResetHandler приложения.
|
||||
*/
|
||||
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 компилятор делал локальные переменные,
|
||||
// из-за чего при смене стека он не мог получить адрес для прыжка
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Сброс ключа BOOT в Flash.
|
||||
* Делает ключ «битым», чтобы MCU остался в бутлоадере при следующем перезапуске.
|
||||
*/
|
||||
void ResetKey(void)
|
||||
{
|
||||
HAL_FLASH_Unlock();
|
||||
|
||||
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, BOOTLOADER_KEY_ADR, 0);
|
||||
|
||||
HAL_FLASH_Lock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Установка ключа BOOT в Flash.
|
||||
* Указывает, что прошивка записана корректно.
|
||||
*/
|
||||
void SetKey(void)
|
||||
{
|
||||
HAL_FLASH_Unlock();
|
||||
|
||||
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, BOOTLOADER_KEY_ADR, BL_KEY_APP_WRITTEN);
|
||||
|
||||
HAL_FLASH_Lock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Чтение ключа BOOT из Flash.
|
||||
* @retval Значение ключа
|
||||
*/
|
||||
uint32_t ReadKey(void)
|
||||
{
|
||||
return (*(__IO uint32_t*)BOOTLOADER_KEY_ADR);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Стирание ключа BOOT в Flash (одна страница).
|
||||
*/
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Проверка валидности прошивки перед переходом к приложению.
|
||||
* Проверяет MSP и ResetHandler.
|
||||
* @retval HAL_OK - прошивка валидна
|
||||
* HAL_ERROR - прошивка повреждена или некорректна
|
||||
*/
|
||||
HAL_StatusTypeDef Verify_Firmware(void)
|
||||
{
|
||||
uint32_t msp = *((volatile uint32_t*)(MAIN_APP_START_ADR));
|
||||
uint32_t reset = *((volatile uint32_t*)(MAIN_APP_START_ADR + 4));
|
||||
|
||||
/* 1) Проверка MSP: должен быть указателем в SRAM */
|
||||
if ((msp < SRAM_START_ADR) || (msp > SRAM_END_ADR))
|
||||
{
|
||||
/* Некорректный стек — прошивка невалидна */
|
||||
return HAL_ERROR;
|
||||
}
|
||||
|
||||
/* 2) Проверка reset handler:
|
||||
- бит0 должен быть 1 (Thumb)
|
||||
- адрес без бита0 должен лежать в пределах flash (MAIN_APP_START_ADR .. FLASH_END_ADR)
|
||||
*/
|
||||
if ((reset & 0x1) == 0)
|
||||
{
|
||||
/* Не Thumb-при-старте — подозрительно */
|
||||
return HAL_ERROR;
|
||||
}
|
||||
|
||||
uint32_t reset_addr = (reset & (~1U)); /* выравненный адрес */
|
||||
if ((reset_addr < FLASH_START_ADR) || (reset_addr > FLASH_END_ADR))
|
||||
{
|
||||
/* Reset handler вне flash */
|
||||
return HAL_ERROR;
|
||||
}
|
||||
return HAL_OK;
|
||||
}
|
||||
189
Core/Bootloader/Src/boot_main.c
Normal file
189
Core/Bootloader/Src/boot_main.c
Normal file
@@ -0,0 +1,189 @@
|
||||
#include "bootloader.h"
|
||||
Bootloader_t boot = {0};
|
||||
|
||||
int main()
|
||||
{
|
||||
__disable_irq();
|
||||
SCB->VTOR = FLASH_BASE;
|
||||
__enable_irq();
|
||||
|
||||
|
||||
boot.state = BL_STATE_INIT;
|
||||
while (1)
|
||||
{
|
||||
Bootloader_Task(&boot);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function handles System tick timer.
|
||||
*/
|
||||
void SysTick_Handler(void)
|
||||
{
|
||||
/* USER CODE BEGIN SysTick_IRQn 0 */
|
||||
|
||||
/* USER CODE END SysTick_IRQn 0 */
|
||||
HAL_IncTick();
|
||||
/* USER CODE BEGIN SysTick_IRQn 1 */
|
||||
|
||||
/* USER CODE END SysTick_IRQn 1 */
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Boot_SystemClock_Config(void)
|
||||
{
|
||||
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
|
||||
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
|
||||
|
||||
/* Включаем внутренний генератор HSI */
|
||||
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
|
||||
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
|
||||
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
|
||||
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_OFF; // без PLL
|
||||
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
|
||||
{
|
||||
Error_Handler();
|
||||
}
|
||||
|
||||
/* Настройка шин AHB/APB */
|
||||
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|
||||
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
|
||||
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; // HSI = 8 MHz
|
||||
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // HCLK = 8 MHz
|
||||
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; // PCLK1 = 8 MHz
|
||||
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // PCLK2 = 8 MHz
|
||||
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
|
||||
{
|
||||
Error_Handler();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
/* Cortex-M3 Processor Interruption and Exception Handlers */
|
||||
/******************************************************************************/
|
||||
/**
|
||||
* @brief This function handles Non maskable interrupt.
|
||||
*/
|
||||
void NMI_Handler(void)
|
||||
{
|
||||
/* USER CODE BEGIN NonMaskableInt_IRQn 0 */
|
||||
|
||||
/* USER CODE END NonMaskableInt_IRQn 0 */
|
||||
/* USER CODE BEGIN NonMaskableInt_IRQn 1 */
|
||||
while (1)
|
||||
{
|
||||
}
|
||||
/* USER CODE END NonMaskableInt_IRQn 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function handles Hard fault interrupt.
|
||||
*/
|
||||
void HardFault_Handler(void)
|
||||
{
|
||||
/* USER CODE BEGIN HardFault_IRQn 0 */
|
||||
|
||||
/* Включаем тактирование PWR и BKP (APB1) и разрешаем доступ к BKP domain */
|
||||
/* Записываем напрямую в регистры RCC/APB1ENR и PWR->CR */
|
||||
SaveErrorCode(0xDEAD);
|
||||
NVIC_SystemReset();
|
||||
/* USER CODE END HardFault_IRQn 0 */
|
||||
while (1)
|
||||
{
|
||||
/* USER CODE BEGIN W1_HardFault_IRQn 0 */
|
||||
/* USER CODE END W1_HardFault_IRQn 0 */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function handles Memory management fault.
|
||||
*/
|
||||
void MemManage_Handler(void)
|
||||
{
|
||||
/* USER CODE BEGIN MemoryManagement_IRQn 0 */
|
||||
SaveErrorCode(0xBEEF);
|
||||
NVIC_SystemReset();
|
||||
/* USER CODE END MemoryManagement_IRQn 0 */
|
||||
while (1)
|
||||
{
|
||||
/* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
|
||||
/* USER CODE END W1_MemoryManagement_IRQn 0 */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function handles Prefetch fault, memory access fault.
|
||||
*/
|
||||
void BusFault_Handler(void)
|
||||
{
|
||||
/* USER CODE BEGIN BusFault_IRQn 0 */
|
||||
|
||||
/* USER CODE END BusFault_IRQn 0 */
|
||||
while (1)
|
||||
{
|
||||
/* USER CODE BEGIN W1_BusFault_IRQn 0 */
|
||||
/* USER CODE END W1_BusFault_IRQn 0 */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function handles Undefined instruction or illegal state.
|
||||
*/
|
||||
void UsageFault_Handler(void)
|
||||
{
|
||||
/* USER CODE BEGIN UsageFault_IRQn 0 */
|
||||
|
||||
/* USER CODE END UsageFault_IRQn 0 */
|
||||
while (1)
|
||||
{
|
||||
/* USER CODE BEGIN W1_UsageFault_IRQn 0 */
|
||||
/* USER CODE END W1_UsageFault_IRQn 0 */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function handles System service call via SWI instruction.
|
||||
*/
|
||||
void SVC_Handler(void)
|
||||
{
|
||||
/* USER CODE BEGIN SVCall_IRQn 0 */
|
||||
|
||||
/* USER CODE END SVCall_IRQn 0 */
|
||||
/* USER CODE BEGIN SVCall_IRQn 1 */
|
||||
|
||||
/* USER CODE END SVCall_IRQn 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function handles Debug monitor.
|
||||
*/
|
||||
void DebugMon_Handler(void)
|
||||
{
|
||||
/* USER CODE BEGIN DebugMonitor_IRQn 0 */
|
||||
|
||||
/* USER CODE END DebugMonitor_IRQn 0 */
|
||||
/* USER CODE BEGIN DebugMonitor_IRQn 1 */
|
||||
|
||||
/* USER CODE END DebugMonitor_IRQn 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function handles Pendable request for system service.
|
||||
*/
|
||||
void PendSV_Handler(void)
|
||||
{
|
||||
/* USER CODE BEGIN PendSV_IRQn 0 */
|
||||
|
||||
/* USER CODE END PendSV_IRQn 0 */
|
||||
/* USER CODE BEGIN PendSV_IRQn 1 */
|
||||
|
||||
/* USER CODE END PendSV_IRQn 1 */
|
||||
}
|
||||
|
||||
|
||||
void Error_Handler(void)
|
||||
{
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
91
Core/Bootloader/Src/boot_uart.c
Normal file
91
Core/Bootloader/Src/boot_uart.c
Normal file
@@ -0,0 +1,91 @@
|
||||
#include "boot_uart.h"
|
||||
#include "boot_gpio.h"
|
||||
|
||||
UART_HandleTypeDef huart_boot;
|
||||
|
||||
/**
|
||||
* @brief Инициализация UART для бутлоадера
|
||||
*/
|
||||
void MX_BOOT_UART_Init(void)
|
||||
{
|
||||
GPIO_InitTypeDef GPIO_InitStruct = {0};
|
||||
|
||||
/* Включаем тактирование */
|
||||
__RCC_UART_BOOT_CLK_ENABLE();
|
||||
__RCC_UART_PORT_CLK_ENABLE();
|
||||
|
||||
/* Настройка GPIO TX/RX */
|
||||
GPIO_InitStruct.Pin = UART_PIN_TX | UART_PIN_RX;
|
||||
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
|
||||
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
||||
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
|
||||
HAL_GPIO_Init(UART_PORT, &GPIO_InitStruct);
|
||||
|
||||
/* Настройка UART */
|
||||
huart_boot.Instance = UART_BOOT;
|
||||
huart_boot.Init.BaudRate = UART_SPEED;
|
||||
huart_boot.Init.WordLength = UART_WORDLENGTH_8B;
|
||||
huart_boot.Init.StopBits = UART_STOPBITS_1;
|
||||
huart_boot.Init.Parity = UART_PARITY_NONE;
|
||||
huart_boot.Init.Mode = UART_MODE_TX_RX;
|
||||
huart_boot.Init.HwFlowCtl = UART_HWCONTROL_NONE;
|
||||
huart_boot.Init.OverSampling = UART_OVERSAMPLING_16;
|
||||
|
||||
if (HAL_UART_Init(&huart_boot) != HAL_OK)
|
||||
{
|
||||
Error_Handler(); // твоя функция обработки ошибок
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Приём UART: страница + CRC
|
||||
*/
|
||||
void Bootloader_UART_Receive_Page(Bootloader_t *bl)
|
||||
{
|
||||
uint16_t bytes_received = 0;
|
||||
uint8_t crc_buf[4];
|
||||
HAL_StatusTypeDef res;
|
||||
uint32_t start_tick = HAL_GetTick(); // старт таймера
|
||||
|
||||
// Приём данных страницы
|
||||
while(bytes_received < PAGE_SIZE)
|
||||
{
|
||||
uint8_t byte = 0;
|
||||
res = HAL_UART_Receive(bl->huart, &byte, 1, FW_RECEIVE_TIMEOUT_MS); // блокирующий приём 100ms
|
||||
|
||||
if(res == HAL_OK)
|
||||
{
|
||||
bl->fw_buffer[bytes_received++] = byte;
|
||||
start_tick = HAL_GetTick(); // сброс таймера при успешном приёме
|
||||
LED_BOOT_TOOGLE();
|
||||
}
|
||||
else
|
||||
{
|
||||
bl->error.bit.timeout_receive = 1;
|
||||
bl->state = BL_STATE_ERROR; // превышен таймаут
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Приём CRC (4 байта)
|
||||
for(uint8_t i = 0; i < 4; i++)
|
||||
{
|
||||
res = HAL_UART_Receive(bl->huart, &crc_buf[i], 1, FW_RECEIVE_TIMEOUT_MS);
|
||||
if(res == HAL_OK)
|
||||
{
|
||||
start_tick = HAL_GetTick(); // сброс таймера
|
||||
}
|
||||
else
|
||||
{
|
||||
bl->error.bit.timeout_receive = 1;
|
||||
bl->state = BL_STATE_ERROR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Сохраняем CRC в структуру
|
||||
bl->fw_crc = (crc_buf[0] << 24) | (crc_buf[1] << 16) | (crc_buf[2] << 8) | crc_buf[3];
|
||||
bl->fw_len = bytes_received;
|
||||
bl->state = BL_STATE_IDLE;
|
||||
}
|
||||
493
Core/Bootloader/Src/bootloader.c
Normal file
493
Core/Bootloader/Src/bootloader.c
Normal file
@@ -0,0 +1,493 @@
|
||||
/******************************************************************************
|
||||
* @file bootloader.c
|
||||
* @brief Бутлоадер STM32 — реализован как конечный автомат (state machine).
|
||||
*
|
||||
* @details
|
||||
* Логика работы:
|
||||
* - Структура Bootloader_t содержит текущее состояние и ошибки.
|
||||
* - После сброса проверяются предыдущие ошибки.
|
||||
* - Проверяется BOOT KEY, чтобы определить: запускать основное приложение или оставаться в бутлоадере.
|
||||
* - Основной цикл — state machine:
|
||||
* INIT — проверка ключа, инициализация периферии
|
||||
* IDLE — ожидание команд по UART/CAN
|
||||
* RECEIVE_UART/RECEIVE_CAN — приём страницы прошивки
|
||||
* WRITE — запись страницы во Flash
|
||||
* ERASE — стирание приложения
|
||||
* JUMP_TO_APP — проверка прошивки и переход к приложению
|
||||
* JUMP_TO_BOOT— возврат в бутлоадер
|
||||
* RESET — программный сброс
|
||||
* ERROR — обработка ошибок и уведомление внешнего интерфейса
|
||||
* - Команды прошивки (BootloaderCommand_t) обрабатываются через Receive_FW_Command().
|
||||
* - Проверка целостности данных осуществляется через CRC32.
|
||||
*
|
||||
* Подключение бутлоадера в основном приложении:
|
||||
* 0) Подключить boot_jump.h и boot_jump.c для взаимодействия с бутлоадером:
|
||||
* @code #include "boot_jump.h" @endcode
|
||||
* 1) В начале main() вызвать App_Init(), чтобы установить VTOR на
|
||||
* начало приложения:
|
||||
* @code App_Init(); @endcode
|
||||
* 2) Для перехода в бутлоадер (например, при ошибке или обновлении):
|
||||
* @code JumpToBootloader(); @endcode
|
||||
******************************************************************************/
|
||||
#include "bootloader.h"
|
||||
#include "boot_gpio.h"
|
||||
#include "boot_flash.h"
|
||||
#include "boot_uart.h"
|
||||
#include "boot_can.h"
|
||||
#include "boot_jump.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Глобальные переменные для HAL-периферии
|
||||
// -----------------------------------------------------------------------------
|
||||
HAL_StatusTypeDef res_hal;
|
||||
CAN_TxHeaderTypeDef TxHeaderBoot;
|
||||
CAN_RxHeaderTypeDef RxHeaderBoot;
|
||||
uint32_t TxMailBoxBoot = 0;
|
||||
uint8_t TXDataBoot[8] = {0};
|
||||
uint32_t led_err_lasttick = 0;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Локальные (static) функции
|
||||
// -----------------------------------------------------------------------------
|
||||
static uint8_t Receive_FW_Command(Bootloader_t *bl);
|
||||
static uint32_t CRC32_Compute(const uint8_t* data, uint32_t length);
|
||||
|
||||
/**
|
||||
* @brief Проверка после сброса MCU.
|
||||
* Определяет причину предыдущего сброса, проверяет ошибки и при необходимости
|
||||
* выставляет соответствующие биты в структуре ошибок бутлоадера.
|
||||
*/
|
||||
void Bootloader_StartCheck(Bootloader_t *bl)
|
||||
{
|
||||
uint32_t ErrCodeBoot = 0;
|
||||
uint32_t ErrCntBoot = 0;
|
||||
|
||||
// Проверка watchdog reset (IWDGRSTF или WWDGRSTF в RCC->CSR)
|
||||
if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST) || __HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST))
|
||||
{
|
||||
//SaveErrorCode(0x0D0D);
|
||||
__HAL_RCC_CLEAR_RESET_FLAGS(); // Очистить флаги сброса, чтобы не повторялось
|
||||
}
|
||||
|
||||
// Чтение сохранённого кода ошибки и количества сбоев
|
||||
ErrCodeBoot = GetErrorCode();
|
||||
ErrCntBoot = GetErrorCnt();
|
||||
// Если ошибок было больше 5, фиксируем тип ошибки
|
||||
if(ErrCntBoot > 5)
|
||||
{
|
||||
ClearErrorCode();
|
||||
if(ErrCodeBoot == 0xDEAD) // HardFault
|
||||
{
|
||||
ResetKey();
|
||||
bl->error.bit.hardfault_cycle = 1;
|
||||
bl->state = BL_STATE_ERROR;
|
||||
}
|
||||
else if(ErrCodeBoot == 0xBEEF) // MemManage
|
||||
{
|
||||
ResetKey();
|
||||
bl->error.bit.memmanage_cycle = 1;
|
||||
bl->state = BL_STATE_ERROR;
|
||||
}
|
||||
/*else if(ErrCodeBoot == 0x0D0D) пока хз надо ли
|
||||
{
|
||||
ResetKey();
|
||||
bl->error.bit.watchdog_reset = 1; // Добавь бит в структуру BootloaderError_
|
||||
bl->state = BL_STATE_ERROR;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Инициализация периферии бутлоадера (UART, CAN, системный такт).
|
||||
* Привязывает дескрипторы HAL к структуре бутлоадера и задаёт
|
||||
* начальный адрес приложения.
|
||||
*/
|
||||
void Bootloader_Init(Bootloader_t *bl)
|
||||
{
|
||||
HAL_Init();
|
||||
|
||||
Boot_SystemClock_Config();
|
||||
MX_BOOT_UART_Init();
|
||||
MX_BOOT_CAN_Init();
|
||||
|
||||
// Привязка дескрипторов к структуре бутлоадера
|
||||
bl->huart = &huart_boot;
|
||||
bl->hcan = &hcan_boot;
|
||||
bl->TxHeader.DLC = 8;
|
||||
bl->TxHeader.StdId = 123;
|
||||
bl->addr = MAIN_APP_START_ADR; // адрес начала приложения
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Основной цикл работы бутлоадера (машина состояний).
|
||||
* Обрабатывает состояния INIT, IDLE, RECEIVE, WRITE, ERASE, JUMP и ERROR.
|
||||
*/
|
||||
void Bootloader_Task(Bootloader_t *bl)
|
||||
{
|
||||
int receive_uart_flag;
|
||||
|
||||
switch (bl->state)
|
||||
{
|
||||
case BL_STATE_INIT:
|
||||
/*
|
||||
* Состояние инициализации.
|
||||
* - включаем индикацию (LED),
|
||||
* - проверяем ошибки,
|
||||
* - читаем "ключ" (метку, что приложение уже записано).
|
||||
* Если ключ установлен -> сразу переход в приложение.
|
||||
* Иначе -> переходим в режим ожидания команд от хоста (IDLE),
|
||||
* инициализируем интерфейсы (CAN/UART, CRC и т.д.).
|
||||
*/
|
||||
bl->prev_state = bl->state;
|
||||
MX_BOOT_GPIO_Init();
|
||||
Bootloader_StartCheck(bl);
|
||||
|
||||
if ((ReadKey() == BL_KEY_APP_WRITTEN))
|
||||
{
|
||||
bl->state = BL_STATE_JUMP_TO_APP;
|
||||
}
|
||||
else
|
||||
{
|
||||
bl->state = BL_STATE_IDLE;
|
||||
Bootloader_Init(bl);
|
||||
}
|
||||
break;
|
||||
|
||||
case BL_STATE_IDLE:
|
||||
/*
|
||||
* Состояние ожидания команд.
|
||||
* - если ошибка уже зафиксирована -> переход в ERROR,
|
||||
* - если это первый вход в IDLE -> отправляем "готов" (0x00) по CAN/UART,
|
||||
* - далее слушаем команды от хоста (erase, write, jump и т.п.).
|
||||
* Неизвестная команда -> ошибка.
|
||||
*/
|
||||
if(bl->error.all)
|
||||
{
|
||||
bl->prev_state = bl->state;
|
||||
bl->state = BL_STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (Receive_FW_Command(bl) == 0xFF)
|
||||
{
|
||||
bl->error.bit.unknown_cmd = 1;
|
||||
bl->state = BL_STATE_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case BL_STATE_RESET:
|
||||
/*
|
||||
* Состояние сброса.
|
||||
* Вызывает системный reset через NVIC -> контроллер запускается заново.
|
||||
*/
|
||||
NVIC_SystemReset();
|
||||
break;
|
||||
|
||||
case BL_STATE_ERASE:
|
||||
/*
|
||||
* Состояние стирания Flash.
|
||||
* - сбрасываем "ключ" приложения,
|
||||
* - стираем область памяти под приложение,
|
||||
* - при успехе возвращаемся в IDLE,
|
||||
* - при ошибке отмечаем ошибку стирания и уходим в ERROR.
|
||||
* По завершению гасим LED.
|
||||
*/
|
||||
bl->prev_state = bl->state;
|
||||
EraseKey();
|
||||
if (FLASH_Erase_App() == HAL_OK)
|
||||
{
|
||||
HAL_Delay(50);
|
||||
bl->state = BL_STATE_IDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
bl->error.bit.erase_err = 1;
|
||||
bl->state = BL_STATE_ERROR;
|
||||
}
|
||||
LED_BOOT_OFF();
|
||||
break;
|
||||
|
||||
case BL_STATE_RECEIVE_UART:
|
||||
case BL_STATE_RECEIVE_CAN:
|
||||
/*
|
||||
* Состояние приёма страницы прошивки от хоста.
|
||||
* - различаем, пришло ли по UART или CAN,
|
||||
* - отправляем ACK (0x00),
|
||||
* - читаем блок данных (страницу) в буфер,
|
||||
* - после приёма проверяем CRC полученного блока,
|
||||
* - если CRC не совпадает -> очищаем буфер, фиксируем ошибку и уходим в ERROR.
|
||||
* По завершению приёма гасим LED.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
LED_BOOT_OFF();
|
||||
break;
|
||||
|
||||
case BL_STATE_WRITE:
|
||||
/*
|
||||
* Состояние записи страницы прошивки во Flash.
|
||||
* - пытаемся записать буфер в указанную область памяти,
|
||||
* - если успешно -> возвращаемся в IDLE (ждём следующего блока),
|
||||
* - если ошибка -> фиксируем ошибку записи и уходим в ERROR.
|
||||
* После завершения гасим LED.
|
||||
*/
|
||||
bl->prev_state = bl->state;
|
||||
if (FLASH_Write_Page(&bl->addr, bl->fw_buffer, bl->fw_len) == HAL_OK)
|
||||
{
|
||||
bl->state = BL_STATE_IDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
bl->error.bit.write_err = 1;
|
||||
bl->state = BL_STATE_ERROR;
|
||||
}
|
||||
LED_BOOT_OFF();
|
||||
break;
|
||||
|
||||
case BL_STATE_JUMP_TO_APP:
|
||||
/*
|
||||
* Состояние перехода в приложение.
|
||||
* - выполняем проверку корректности прошивки (Verify_Firmware),
|
||||
* - если проверка пройдена -> устанавливаем "ключ" приложения,
|
||||
* чтобы пометить прошивку как валидную,
|
||||
* - если проверка не пройдена -> ошибка verify и переход в ERROR.
|
||||
* В случае успеха вызываем JumpToApplication(), передавая управление основному коду.
|
||||
*/
|
||||
bl->prev_state = bl->state;
|
||||
if (Verify_Firmware() == HAL_OK)
|
||||
{
|
||||
EraseKey();
|
||||
SetKey();
|
||||
}
|
||||
else
|
||||
{
|
||||
bl->error.bit.verify_err = 1;
|
||||
bl->state = BL_STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
JumpToApplocation();
|
||||
break;
|
||||
|
||||
case BL_STATE_JUMP_TO_BOOT:
|
||||
/*
|
||||
* Состояние возврата в bootloader.
|
||||
*/
|
||||
bl->prev_state = bl->state;
|
||||
JumpToBootloader();
|
||||
break;
|
||||
|
||||
case BL_STATE_ERROR:
|
||||
/*
|
||||
* Состояние ошибки.
|
||||
* - при первом входе в ERROR отправляем код ошибки (0xFF + код ошибки),
|
||||
* - продолжаем слушать команды, чтобы можно было сбросить или стереть Flash,
|
||||
* - мигаем LED раз в 500 мс для визуальной индикации ошибки.
|
||||
*/
|
||||
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;
|
||||
|
||||
if (Receive_FW_Command(bl) == 0xFF)
|
||||
{
|
||||
bl->error.bit.unknown_cmd = 1;
|
||||
bl->state = BL_STATE_ERROR;
|
||||
}
|
||||
|
||||
if(HAL_GetTick() - led_err_lasttick > 500)
|
||||
{
|
||||
led_err_lasttick = HAL_GetTick();
|
||||
LED_BOOT_TOOGLE();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/*
|
||||
* Попадание в неизвестное состояние.
|
||||
* Считается ошибкой: ставим unknown_cmd и переходим в ERROR.
|
||||
*/
|
||||
bl->error.bit.unknown_cmd = 1;
|
||||
bl->state = BL_STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Устанавливает новое состояние бутлоадера в зависимости от команды.
|
||||
* @param bl: указатель на структуру бутлоадера
|
||||
* @param cmd: команда бутлоадера (BootloaderCommand_t)
|
||||
* @param uart_flag: 1 — команда пришла по UART, 0 — по CAN
|
||||
* @retval 0x00 — команда успешно обработана, 0xFF — неизвестная команда
|
||||
*/
|
||||
static 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_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 - ошибка
|
||||
*/
|
||||
static 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
|
||||
if((bl->state != BL_STATE_IDLE) && (bl->state != BL_STATE_ERROR))
|
||||
{
|
||||
LED_BOOT_ON();
|
||||
}
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Вычисление CRC32 блока данных.
|
||||
* @param data: указатель на массив данных
|
||||
* @param length: длина массива в байтах
|
||||
* @retval CRC32 вычисленное значение
|
||||
*/
|
||||
static 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Конфигурация системного тактирования (должна быть переопределена пользователем).
|
||||
*/
|
||||
__WEAK void Boot_SystemClock_Config(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Обработчик ошибок (может быть переопределён пользователем).
|
||||
*/
|
||||
__WEAK void Error_Handler(void)
|
||||
{
|
||||
while(1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user