diff --git a/Core/Bootloader/boot_can.h b/Core/Bootloader/Inc/boot_can.h similarity index 100% rename from Core/Bootloader/boot_can.h rename to Core/Bootloader/Inc/boot_can.h diff --git a/Core/Bootloader/boot_flash.h b/Core/Bootloader/Inc/boot_flash.h similarity index 82% rename from Core/Bootloader/boot_flash.h rename to Core/Bootloader/Inc/boot_flash.h index 9e810e7..ddacec8 100644 --- a/Core/Bootloader/boot_flash.h +++ b/Core/Bootloader/Inc/boot_flash.h @@ -4,10 +4,11 @@ #include "bootloader.h" - +// FOR APP FLASHING HAL_StatusTypeDef FLASH_Erase_App(void); HAL_StatusTypeDef FLASH_Write_Page(uint32_t *Address, uint8_t *Data, int Data_size); + +// SERVICE HAL_StatusTypeDef FLASH_Write_Word(uint32_t Address, uint64_t Data); -uint8_t *FLASH_Read(uint32_t add); #endif //__BOOT_FLASH_H \ No newline at end of file diff --git a/Core/Bootloader/Inc/boot_gpio.h b/Core/Bootloader/Inc/boot_gpio.h new file mode 100644 index 0000000..359b075 --- /dev/null +++ b/Core/Bootloader/Inc/boot_gpio.h @@ -0,0 +1,14 @@ +#ifndef __BOOT_GPIO_H +#define __BOOT_GPIO_H + +#include "bootloader.h" + + +#define LED_BOOT_ON() CLEAR_BIT(LED_BOOT_GPIO_Port->ODR, LED_BOOT_Pin) +#define LED_BOOT_OFF() SET_BIT(LED_BOOT_GPIO_Port->ODR, LED_BOOT_Pin) +#define LED_BOOT_TOOGLE() LED_BOOT_GPIO_Port->ODR ^= LED_BOOT_Pin + + +void MX_BOOT_GPIO_Init(void); + +#endif //__BOOT_GPIO_H \ No newline at end of file diff --git a/Core/Bootloader/Inc/boot_jump.h b/Core/Bootloader/Inc/boot_jump.h new file mode 100644 index 0000000..54bf2ee --- /dev/null +++ b/Core/Bootloader/Inc/boot_jump.h @@ -0,0 +1,26 @@ +#ifndef __BOOT_JUMP_H +#define __BOOT_JUMP_H + +#include "bootloader.h" + +/* Инициализация приложения */ +void App_Init(void); +/* Переход в бутлоадер */ +void JumpToBootloader(void); +/* Переход к основному приложению */ +void JumpToApplocation(void); + +/* Сброс ключа BOOT в Flash */ +void ResetKey(void); +/* Установка ключа BOOT в Flash */ +void SetKey(void); +/* Чтение ключа BOOT из Flash */ +uint32_t ReadKey(void); +/* Стирание ключа BOOT в Flash */ +void EraseKey(void); + +/* Проверка валидности прошивки перед переходом к приложению */ +HAL_StatusTypeDef Verify_Firmware(void); + + +#endif //__BOOT_JUMP_H \ No newline at end of file diff --git a/Core/Bootloader/Inc/boot_project_setup.h b/Core/Bootloader/Inc/boot_project_setup.h new file mode 100644 index 0000000..2a60280 --- /dev/null +++ b/Core/Bootloader/Inc/boot_project_setup.h @@ -0,0 +1,74 @@ +#ifndef __BOOT_SETUP_H +#define __BOOT_SETUP_H + +#include "stm32f1xx_hal.h" + +// ======================== BOOTLOADER CONFIG ======================== + +// ---------- MAIN APPLICATION defines ---------- +// Адрес и страницы Flash для основного приложения +// MAIN_APP_START_ADR – начало кода основного приложения +#define MAIN_APP_START_ADR (uint32_t)0x0800C000UL +#define MAIN_APP_PAGE 21 // страница, с которой начинается приложение +#define MAIN_APP_NUM_OF_PAGE 250-MAIN_APP_PAGE // количество страниц, отведённых под приложение + +// ---------- KEY defines ---------- +// Адрес и страница Flash для хранения ключа бутлоадера +// Ключ используется для проверки, записано ли приложение корректно +#define BOOTLOADER_KEY_ADR (uint32_t)0x08009800UL // физический адрес ключа +#define BOOTLOADER_KEY_PAGE 20 // страница флеш, на которой хранится ключ + +// ---------- RECEIVE defines ---------- +// Настройки приёма прошивки +#define FW_RECEIVE_TIMEOUT_MS 500 // таймаут приёма одного байта прошивки (мс) +#define PAGE_SIZE 2048 // размер блока (страницы) прошивки для приёма и записи в Flash + // должен быть таким, чтобы размер страниц Flash был кратен ему + +// ---------- LED defines ---------- +#define LED_BOOT_Pin GPIO_PIN_5 +#define LED_BOOT_GPIO_Port GPIOB + +// ======================== RCC (CLOCK) defines ======================== + +// Макросы для включения тактирования периферии бутлоадера +#define __RCC_LED_BOOT_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() // тактирование UART +#define __RCC_UART_BOOT_CLK_ENABLE() __HAL_RCC_USART3_CLK_ENABLE() // тактирование UART +#define __RCC_UART_PORT_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() // тактирование порта UART +#define __RCC_CAN_BOOT_CLK_ENABLE() __HAL_RCC_CAN1_CLK_ENABLE() // тактирование CAN +#define __RCC_CAN_PORT_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() // тактирование порта CAN + +// ======================== UART defines ======================== + +// Аппаратный UART и скорость передачи +#define UART_BOOT USART3 +#define UART_SPEED 256000 +#define UART_BOOT_IRQn USART3_IRQn + +// Порт и пины UART +#define UART_PORT GPIOB +#define UART_PIN_TX GPIO_PIN_10 +#define UART_PIN_RX GPIO_PIN_11 + +// ======================== CAN defines ======================== + +// Аппаратный CAN и режим работы +#define CAN_BOOT CAN1 +#define CAN_MODE CAN_MODE_NORMAL + +// Настройка скорости CAN при 8 MHz +// ------------|-----------|-----------|----------- +// CAN speed | Prescaler | BS1 | BS2 +// ------------|-----------|-----------|----------- +// 125 kbps | 4 | 13TQ | 2TQ +// 250 kbps | 2 | 13TQ | 2TQ +// 500 kbps | 1 | 13TQ | 2TQ +#define CAN_SPEED_PRESCALER 4 +#define CAN_SPEED_BS1 CAN_BS1_13TQ +#define CAN_SPEED_BS2 CAN_BS2_2TQ + +// Порт и пины CAN +#define CAN_PORT GPIOA +#define CAN_PIN_RX GPIO_PIN_11 +#define CAN_PIN_TX GPIO_PIN_12 + +#endif //__BOOT_SETUP_H diff --git a/Core/Bootloader/boot_uart.h b/Core/Bootloader/Inc/boot_uart.h similarity index 100% rename from Core/Bootloader/boot_uart.h rename to Core/Bootloader/Inc/boot_uart.h diff --git a/Core/Bootloader/bootloader.h b/Core/Bootloader/Inc/bootloader.h similarity index 55% rename from Core/Bootloader/bootloader.h rename to Core/Bootloader/Inc/bootloader.h index fc908f2..a73d04b 100644 --- a/Core/Bootloader/bootloader.h +++ b/Core/Bootloader/Inc/bootloader.h @@ -4,23 +4,51 @@ #include "boot_project_setup.h" #include "string.h" - -#define BL_KEY_APP_WRITTEN 0xAAAA5555 /* --- Настройка: подставьте значения для вашей MCU --- */ /* Адрес начала приложения (используется в вашем коде) */ #ifndef MAIN_APP_START_ADR #error "MAIN_APP_START_ADR must be defined" #endif +/* Flash boundaries: подставьте реальные границы флеш-памяти вашего MCU */ +#ifndef FLASH_START_ADR +#define FLASH_START_ADR MAIN_APP_START_ADR +#endif +#ifndef FLASH_END_ADR +#define FLASH_END_ADR FLASH_BASE + (*((uint16_t*)FLASHSIZE_BASE) * 1024U) +#endif -/* Если нужен другой полином/поведение CRC, используйте вашу функцию CRC32_Compute. - В вашем коде уже есть CRC32_Compute, поэтому будем её использовать. */ - - +/* SRAM boundaries: подставьте реальные адреса SRAM вашей MCU */ +#ifndef SRAM_START_ADR +#define SRAM_START_ADR 0x20000000UL +#endif +#ifndef SRAM_END_ADR +#define SRAM_END_ADR 0x2003FFFFUL +#endif + + +/** + * @brief Значение ключа, указывающее что основное приложение записано + */ +#define BL_KEY_APP_WRITTEN 0xAAAA5555 + + + + +/** @brief Получить сохранённый код ошибки из BKP */ #define GetErrorCode() BKP->DR1 +/** @brief Получить счетчик ошибок из BKP */ #define GetErrorCnt() BKP->DR2 +/** + * @brief Сохранение кода ошибки и инкремент счетчика + * @param code Код ошибки + * @details + * Включаем тактирование PWR и BKP (APB1) и разрешаем доступ к BKP domain + * Записываем напрямую в регистры RCC/APB1ENR и PWR->CR + * Записываем код ошибки и счётчик ошибок + */ #define SaveErrorCode(code) do{ \ RCC->APB1ENR |= (RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN); \ PWR->CR |= PWR_CR_DBP; \ @@ -28,6 +56,9 @@ GetErrorCnt() = GetErrorCnt() + 1; \ }while(0u); +/** + * @brief Очистка кода ошибки и счетчика ошибок + */ #define ClearErrorCode(code) do{ \ RCC->APB1ENR |= (RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN); \ PWR->CR |= PWR_CR_DBP; \ @@ -36,9 +67,8 @@ }while(0u); -// ERROR DEFINES /** - * @brief Комманды бутлоадера + * @brief Команды для управления бутлоадером */ typedef enum { CMD_ERASE = 0x01, ///< Команда на стирание прошивки @@ -50,7 +80,7 @@ typedef enum { }BootloaderCommand_t; /** - * @brief Состояние бутлоадера + * @brief Состояния конечного автомата бутлоадера */ typedef enum { BL_STATE_INIT = 0, ///< Состояние: инициализация @@ -61,7 +91,7 @@ typedef enum { BL_STATE_RECEIVE_CAN, ///< Состояние: прием прошивки по CAN BL_STATE_WRITE, ///< Состояние: запись данных BL_STATE_ERROR, ///< Состояние: ошибка - BL_STATE_RESET, ///< Состояние: сброс контролллера + BL_STATE_RESET, ///< Состояние: сброс контролллера BL_STATE_JUMP_TO_BOOT, ///< Состояние: запуск приложения } BootloaderState_t; @@ -69,21 +99,24 @@ typedef enum { * @brief Ошибки бутлоадера */ typedef union { - uint16_t all; + uint16_t all; ///< Все ошибки одним числом struct { - unsigned hardfault_cycle:1; - unsigned memmanage_cycle:1; - unsigned watchdog_reset:1; - unsigned unknown_cmd:1; - unsigned erase_err:1; - unsigned write_err:1; - unsigned verify_err:1; - unsigned timeout_receive:1; - unsigned crc_err:1; - }bit; + unsigned hardfault_cycle:1; ///< Прерывание HardFault + unsigned memmanage_cycle:1; ///< Прерывание MemManage + unsigned watchdog_reset:1; ///< Watchdog сброс + unsigned unknown_cmd:1; ///< Неизвестная команда + unsigned erase_err:1; ///< Ошибка стирания + unsigned write_err:1; ///< Ошибка записи + unsigned verify_err:1; ///< Ошибка проверки прошивки + unsigned timeout_receive:1; ///< Таймаут приёма + unsigned crc_err:1; ///< Ошибка CRC + } bit; } BootloaderError_t; +/** +* @brief Дескриптор бутлоадера + */ typedef struct { BootloaderState_t state; ///< текущее состояние бутлоадера BootloaderError_t error; @@ -91,7 +124,7 @@ typedef struct { uint32_t addr; ///< текущий адрес прошивки uint8_t fw_size; ///< размер прошивки - uint8_t fw_buffer[PAGE_SIZE]; ///< буфер для приема прошивки (UART/CAN) + uint8_t fw_buffer[PAGE_SIZE]; ///< буфер для приема прошивки (UART/CAN) uint32_t fw_len; ///< длина принятого пакета uint32_t fw_crc; ///< контрольная сумма прошивки @@ -101,15 +134,12 @@ typedef struct { BootloaderState_t prev_state; ///< предыдущее состояние бутлоадера } Bootloader_t; -extern uint32_t TxMailBoxBoot; -extern uint8_t TXDataBoot[8]; - -void ResetKey(void); -void JumpToBootloader(void); - -void App_Init(void); +/* Основная задача бутлоадера */ void Bootloader_Task(Bootloader_t *bl); +/* Настройка тактирования */ +void Boot_SystemClock_Config(void); +/* Хендлер ошибки */ void Error_Handler(void); #endif //__BOOTLOADER_H \ No newline at end of file diff --git a/Core/Bootloader/boot_can.c b/Core/Bootloader/Src/boot_can.c similarity index 95% rename from Core/Bootloader/boot_can.c rename to Core/Bootloader/Src/boot_can.c index 19528b1..c033edb 100644 --- a/Core/Bootloader/boot_can.c +++ b/Core/Bootloader/Src/boot_can.c @@ -1,9 +1,10 @@ #include "boot_can.h" +#include "boot_gpio.h" CAN_HandleTypeDef hcan_boot; /** - * @brief Инициализация CAN для бутлоадера (по defines) + * @brief Инициализация CAN для бутлоадера */ void MX_BOOT_CAN_Init(void) { @@ -67,9 +68,9 @@ void MX_BOOT_CAN_Init(void) } } -// ----------------------------- -// CAN: приём страницы + CRC с таймаутом -// ----------------------------- +/** + * @brief Приём CAN: страница + CRC + */ void Bootloader_CAN_Receive_Page(Bootloader_t *bl) { uint16_t bytes_received = 0; @@ -90,7 +91,8 @@ void Bootloader_CAN_Receive_Page(Bootloader_t *bl) memcpy(&bl->fw_buffer[bytes_received], canData, len); bytes_received += len; - start_tick = HAL_GetTick(); // сброс таймера + start_tick = HAL_GetTick(); // сброс таймаута + LED_BOOT_TOOGLE(); } } @@ -102,8 +104,8 @@ void Bootloader_CAN_Receive_Page(Bootloader_t *bl) return; } } - // Приём CRC (4 байта) + start_tick = HAL_GetTick(); // сброс таймаута while(1) { if(HAL_CAN_GetRxFifoFillLevel(bl->hcan, CAN_RX_FIFO0) > 0) diff --git a/Core/Bootloader/boot_flash.c b/Core/Bootloader/Src/boot_flash.c similarity index 91% rename from Core/Bootloader/boot_flash.c rename to Core/Bootloader/Src/boot_flash.c index ec9c743..694ab25 100644 --- a/Core/Bootloader/boot_flash.c +++ b/Core/Bootloader/Src/boot_flash.c @@ -1,6 +1,5 @@ #include "boot_flash.h" -//uint32_t PAGE_OFFSET = ((uint32_t)((4-1) * 0x0400)); -uint32_t PAGE_NUMB = 127; +uint32_t word_data; HAL_StatusTypeDef FLASH_Erase_App(void) // @@ -31,31 +30,6 @@ HAL_StatusTypeDef FLASH_Erase_App(void) // -uint8_t *FLASH_Read(uint32_t add) -{ - return (uint8_t *)add; -} - -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; -} -uint32_t word_data; - - - HAL_StatusTypeDef FLASH_Write_Page(uint32_t *Address, uint8_t *Data, int Data_size) { @@ -92,3 +66,27 @@ HAL_StatusTypeDef FLASH_Write_Page(uint32_t *Address, uint8_t *Data, int Data_si 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; +} + + diff --git a/Core/Bootloader/Src/boot_gpio.c b/Core/Bootloader/Src/boot_gpio.c new file mode 100644 index 0000000..832f858 --- /dev/null +++ b/Core/Bootloader/Src/boot_gpio.c @@ -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++); + } +} diff --git a/Core/Bootloader/Src/boot_jump.c b/Core/Bootloader/Src/boot_jump.c new file mode 100644 index 0000000..08b6527 --- /dev/null +++ b/Core/Bootloader/Src/boot_jump.c @@ -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; +} diff --git a/Core/Bootloader/boot_main.c b/Core/Bootloader/Src/boot_main.c similarity index 86% rename from Core/Bootloader/boot_main.c rename to Core/Bootloader/Src/boot_main.c index 3af2bdf..2240e5e 100644 --- a/Core/Bootloader/boot_main.c +++ b/Core/Bootloader/Src/boot_main.c @@ -4,9 +4,10 @@ Bootloader_t boot = {0}; int main() { __disable_irq(); - SCB->VTOR = 0x08000000; + SCB->VTOR = FLASH_BASE; __enable_irq(); - + + boot.state = BL_STATE_INIT; while (1) { @@ -86,10 +87,7 @@ void HardFault_Handler(void) /* Включаем тактирование PWR и BKP (APB1) и разрешаем доступ к BKP domain */ /* Записываем напрямую в регистры RCC/APB1ENR и PWR->CR */ - RCC->APB1ENR |= (RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN); - PWR->CR |= PWR_CR_DBP; - BKP->DR1 = 0xDEAD; // записываем код ошибки - BKP->DR2 = BKP->DR2 + 1; // счётчик ошибок + SaveErrorCode(0xDEAD); NVIC_SystemReset(); /* USER CODE END HardFault_IRQn 0 */ while (1) @@ -105,12 +103,7 @@ void HardFault_Handler(void) void MemManage_Handler(void) { /* USER CODE BEGIN MemoryManagement_IRQn 0 */ - /* Включаем тактирование PWR и BKP (APB1) и разрешаем доступ к BKP domain */ - /* Записываем напрямую в регистры RCC/APB1ENR и PWR->CR */ - RCC->APB1ENR |= (RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN); - PWR->CR |= PWR_CR_DBP; - BKP->DR1 = 0xBEEF; // записываем код ошибки - BKP->DR2 = BKP->DR2 + 1; // счётчик ошибок + SaveErrorCode(0xBEEF); NVIC_SystemReset(); /* USER CODE END MemoryManagement_IRQn 0 */ while (1) diff --git a/Core/Bootloader/boot_uart.c b/Core/Bootloader/Src/boot_uart.c similarity index 71% rename from Core/Bootloader/boot_uart.c rename to Core/Bootloader/Src/boot_uart.c index 3bebac2..f4ef541 100644 --- a/Core/Bootloader/boot_uart.c +++ b/Core/Bootloader/Src/boot_uart.c @@ -1,10 +1,10 @@ #include "boot_uart.h" +#include "boot_gpio.h" UART_HandleTypeDef huart_boot; /** * @brief Инициализация UART для бутлоадера - * @note Использует USART3, PB10 (TX), PB11 (RX) */ void MX_BOOT_UART_Init(void) { @@ -37,9 +37,10 @@ void MX_BOOT_UART_Init(void) } } -// ----------------------------- -// UART: приём страницы + CRC -// ----------------------------- + +/** + * @brief Приём UART: страница + CRC + */ void Bootloader_UART_Receive_Page(Bootloader_t *bl) { uint16_t bytes_received = 0; @@ -51,42 +52,35 @@ void Bootloader_UART_Receive_Page(Bootloader_t *bl) while(bytes_received < PAGE_SIZE) { uint8_t byte = 0; - res = HAL_UART_Receive(bl->huart, &byte, 1, 100); // блокирующий приём 100ms + 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 { - // проверка таймаута - if(HAL_GetTick() - start_tick >= FW_RECEIVE_TIMEOUT_MS) - { - bl->error.bit.timeout_receive = 1; - bl->state = BL_STATE_ERROR; // превышен таймаут - return; - } - // иначе просто ждем следующего байта + 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, 100); + res = HAL_UART_Receive(bl->huart, &crc_buf[i], 1, FW_RECEIVE_TIMEOUT_MS); if(res == HAL_OK) { start_tick = HAL_GetTick(); // сброс таймера } else { - if(HAL_GetTick() - start_tick >= FW_RECEIVE_TIMEOUT_MS) - { - bl->error.bit.timeout_receive = 1; - bl->state = BL_STATE_ERROR; - return; - } + bl->error.bit.timeout_receive = 1; + bl->state = BL_STATE_ERROR; + return; } } diff --git a/Core/Bootloader/Src/bootloader.c b/Core/Bootloader/Src/bootloader.c new file mode 100644 index 0000000..53120bb --- /dev/null +++ b/Core/Bootloader/Src/bootloader.c @@ -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); +} + diff --git a/Core/Bootloader/boot_project_setup.h b/Core/Bootloader/boot_project_setup.h deleted file mode 100644 index 27e975a..0000000 --- a/Core/Bootloader/boot_project_setup.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef __BOOT_SETUP_H -#define __BOOT_SETUP_H - -#include "stm32f1xx_hal.h" - -// === BOOTLOADER defines === -// KEY defines -#define BOOTLOADER_KEY_ADR (uint32_t)0x08009800UL -#define BOOTLOADER_KEY_PAGE 20 - -// MAIN APP defines -#define MAIN_APP_START_ADR (uint32_t)0x0800C000UL -#define MAIN_APP_PAGE 21 -#define MAIN_APP_NUM_OF_PAGE 250-MAIN_APP_PAGE - - - -/* Flash boundaries: подставьте реальные границы флеш-памяти вашего MCU */ -#ifndef FLASH_START_ADR -#define FLASH_START_ADR 0x08000000UL -#endif -#ifndef FLASH_END_ADR -/* пример: 512KB flash -> 0x08080000. Поменяйте под ваш MCU */ -#define FLASH_END_ADR 0x080FFFFFUL -#endif - -/* SRAM boundaries: подставьте реальные адреса SRAM вашей MCU */ -#ifndef SRAM_START_ADR -#define SRAM_START_ADR 0x20000000UL -#endif -#ifndef SRAM_END_ADR -/* пример: 128KB SRAM -> 0x2001FFFF. Поменяйте под ваш MCU */ -#define SRAM_END_ADR 0x2003FFFFUL -#endif - - - - - -// RECEIVE defines -#define FW_RECEIVE_TIMEOUT_MS 5000 // таймаут приёма страницы -#define PAGE_SIZE 2048 // страницы принимаются размером с page_size (размер страниц флеш должен быть кратен PAGE_SIZE) - - - - -// === RCC defines === -#define __RCC_UART_BOOT_CLK_ENABLE() __HAL_RCC_USART3_CLK_ENABLE() -#define __RCC_UART_PORT_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() -#define __RCC_CAN_BOOT_CLK_ENABLE() __HAL_RCC_CAN1_CLK_ENABLE() -#define __RCC_CAN_PORT_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() - - -// === UART defines === -#define UART_BOOT USART3 -#define UART_SPEED 256000 -#define UART_BOOT_IRQn USART3_IRQn - -#define UART_PORT GPIOB // usart port -#define UART_PIN_TX GPIO_PIN_10 -#define UART_PIN_RX GPIO_PIN_11 - - -// === CAN defines === -#define CAN_BOOT CAN1 -#define CAN_MODE CAN_MODE_NORMAL -// Presacler = 1 для 500 kbps при 8 MHz -#define CAN_SPEED_PRESCALER 4 -#define CAN_SPEED_BS1 CAN_BS1_13TQ -#define CAN_SPEED_BS2 CAN_BS2_2TQ - -#define CAN_PORT GPIOA // can port -#define CAN_PIN_RX GPIO_PIN_11 -#define CAN_PIN_TX GPIO_PIN_12 - -#define CAN_BOOT_IRQn USB_LP_CAN1_RX0_IRQn - -#endif //__BOOT_SETUP_H \ No newline at end of file diff --git a/Core/Bootloader/bootloader.c b/Core/Bootloader/bootloader.c deleted file mode 100644 index e76de62..0000000 --- a/Core/Bootloader/bootloader.c +++ /dev/null @@ -1,450 +0,0 @@ -#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}; - -uint32_t ErrCodeBoot = 0; -uint32_t ErrCntBoot = 0; - -static uint8_t Receive_FW_Command(Bootloader_t *bl); -static void SetKey(void); -static uint32_t ReadKey(void); -static void EraseKey(void); -static void JumpToApplocation(void); -static uint32_t CRC32_Compute(const uint8_t* data, uint32_t length); -void Boot_SystemClock_Config(void); -static HAL_StatusTypeDef Verify_Firmware(void); - -void Bootloader_Init(Bootloader_t *bl) -{ - HAL_Init(); - - Boot_SystemClock_Config(); - MX_BOOT_UART_Init(); - MX_BOOT_CAN_Init(); - -} - -void App_Init(void) -{ - __disable_irq(); - SCB->VTOR = 0x0800C000; - __enable_irq(); -} - - - -void Bootloader_StartCheck(Bootloader_t *bl) -{ - - // Проверка 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(); - 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; - }*/ - } -} - - -void Bootloader_Task(Bootloader_t *bl) -{ - int receive_uart_flag; - switch (bl->state) - { - case BL_STATE_INIT: - bl->prev_state = bl->state; - Bootloader_StartCheck(bl); - // Проверяем ключ, чтобы понять запускать приложение или программирование - if ((ReadKey() == BL_KEY_APP_WRITTEN)) - { - 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; - break; - - case BL_STATE_IDLE: - 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; - // Ждем команды по 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 - { - HAL_Delay(50); - 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_JUMP_TO_APP: - 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: - 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; - } - -} - - -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 - - return ret_val; -} - -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; -} - -static 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; -} - -// key functions -void ResetKey(void) -{ - HAL_FLASH_Unlock(); - - HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, BOOTLOADER_KEY_ADR, 0); - - HAL_FLASH_Lock(); -} - -void JumpToBootloader(void) -{ - // jump to boot - ResetKey(); // сброс ключа (не erase, просто битый ключ - NVIC_SystemReset(); // сброс и переход в бутлоадер (т.к. нет ключа) -} - - -static void SetKey(void) -{ - HAL_FLASH_Unlock(); - - HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, BOOTLOADER_KEY_ADR, BL_KEY_APP_WRITTEN); - - HAL_FLASH_Lock(); -} - -static uint32_t ReadKey(void) -{ - return (*(__IO uint32_t*)BOOTLOADER_KEY_ADR); -} - -static 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(); - -} - - - -static 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 компилятор делал локальные переменные, - // из-за чего при смене стека он не мог получить адрес для прыжка -} - - - -__WEAK void Boot_SystemClock_Config(void) -{ -} - - -__WEAK void Error_Handler(void) -{ - while(1); -} - diff --git a/Core/Src/can.c b/Core/Src/can.c index 7c2cbce..96e6f66 100644 --- a/Core/Src/can.c +++ b/Core/Src/can.c @@ -23,7 +23,7 @@ /* USER CODE BEGIN 0 */ #include "message.h" #include "gpio.h" -#include "bootloader.h" +#include "boot_jump.h" void CAN_filterConfig(void); CAN_TxHeaderTypeDef TxHeader; diff --git a/Core/Src/main.c b/Core/Src/main.c index dd60a1e..6f0b36d 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -29,7 +29,7 @@ #include "package.h" #include "message.h" #include "lampa.h" -#include "bootloader.h" +#include "boot_jump.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ diff --git a/MDK-ARM/uksvep_2_2_v1.uvoptx b/MDK-ARM/uksvep_2_2_v1.uvoptx index 10150ff..02f32d1 100644 --- a/MDK-ARM/uksvep_2_2_v1.uvoptx +++ b/MDK-ARM/uksvep_2_2_v1.uvoptx @@ -148,24 +148,7 @@ UL2CM3(-S0 -C0 -P0 -FD20000000 -FC1000 -FN1 -FF0STM32F10x_512 -FS08000000 -FL080000 -FP0($$Device:STM32F103RC$Flash\STM32F10x_512.FLM)) - - - 0 - 0 - 49 - 1 -
42
- 0 - 0 - 0 - 0 - 0 - 1 - ..\Core\Bootloader\bootloader.c - - \\uksvep_2_2_v1\../Core/Bootloader/bootloader.c\49 -
-
+ 0 @@ -207,6 +190,11 @@ 1 CMD_GOTOBOOT + + 8 + 1 + hcan + @@ -427,7 +415,7 @@ 0 1300 1 -
134222002
+
134222082
0 0 0 @@ -438,22 +426,6 @@ \\bootloader\../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_can.c\1300 - - 1 - 0 - 850 - 1 -
134221508
- 0 - 0 - 0 - 0 - 0 - 1 - ../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_flash.c - - \\bootloader\../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_flash.c\850 -
@@ -496,12 +468,22 @@ 1 TXDataBoot + + 8 + 1 + \\bootloader\../Core/Bootloader/boot_main.c\boot.error.bit.unknown_cmd + + + 9 + 1 + \\bootloader\../Core/Bootloader/boot_main.c\boot.error.bit.write_err + 1 0 - 0x08011800 + 0x0800c000 0 @@ -556,30 +538,10 @@ - - System Viewer\BKP - 35903 - - - System Viewer\CAN - 35904 - - - System Viewer\FLASH - 35900 - System Viewer\GPIOB 35905 - - System Viewer\PWR - 35901 - - - System Viewer\RCC - 35902 - 1 @@ -1008,7 +970,7 @@ 0 0 0 - ..\Core\Bootloader\boot_project_setup.h + ..\Core\Bootloader\Inc\boot_project_setup.h boot_project_setup.h 0 0 @@ -1020,8 +982,8 @@ 0 0 0 - ..\Core\Bootloader\boot_main.c - boot_main.c + ..\Core\Bootloader\Src\bootloader.c + bootloader.c 0 0 @@ -1032,20 +994,20 @@ 0 0 0 - ..\Core\Bootloader\bootloader.c - bootloader.c + ..\Core\Bootloader\Src\boot_main.c + boot_main.c 0 0 5 35 - 5 + 1 0 0 0 - ..\Core\Bootloader\bootloader.h - bootloader.h + ..\Core\Bootloader\Src\boot_can.c + boot_can.c 0 0 @@ -1056,20 +1018,20 @@ 0 0 0 - ..\Core\Bootloader\boot_can.c - boot_can.c + ..\Core\Bootloader\Src\boot_flash.c + boot_flash.c 0 0 5 37 - 5 + 1 0 0 0 - ..\Core\Bootloader\boot_can.h - boot_can.h + ..\Core\Bootloader\Src\boot_gpio.c + boot_gpio.c 0 0 @@ -1080,47 +1042,23 @@ 0 0 0 - ..\Core\Bootloader\boot_flash.c - boot_flash.c + ..\Core\Bootloader\Src\boot_jump.c + boot_jump.c 0 0 5 39 - 5 - 0 - 0 - 0 - ..\Core\Bootloader\boot_flash.h - boot_flash.h - 0 - 0 - - - 5 - 40 1 0 0 0 - ..\Core\Bootloader\boot_uart.c + ..\Core\Bootloader\Src\boot_uart.c boot_uart.c 0 0 - - 5 - 41 - 5 - 0 - 0 - 0 - ..\Core\Bootloader\boot_uart.h - boot_uart.h - 0 - 0 - diff --git a/MDK-ARM/uksvep_2_2_v1.uvprojx b/MDK-ARM/uksvep_2_2_v1.uvprojx index aa60635..72631d0 100644 --- a/MDK-ARM/uksvep_2_2_v1.uvprojx +++ b/MDK-ARM/uksvep_2_2_v1.uvprojx @@ -341,7 +341,7 @@ USE_HAL_DRIVER,STM32F103xE - ../Core/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy;../Drivers/CMSIS/Device/ST/STM32F1xx/Include;../Drivers/CMSIS/Include;..\Core\Bootloader + ../Core/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy;../Drivers/CMSIS/Device/ST/STM32F1xx/Include;../Drivers/CMSIS/Include;..\Core\Bootloader\Inc @@ -665,12 +665,17 @@ boot_project_setup.h 5 - ..\Core\Bootloader\boot_project_setup.h + ..\Core\Bootloader\Inc\boot_project_setup.h + + + bootloader.c + 1 + ..\Core\Bootloader\Src\bootloader.c boot_main.c 1 - ..\Core\Bootloader\boot_main.c + ..\Core\Bootloader\Src\boot_main.c 2 @@ -723,45 +728,81 @@ - - bootloader.c - 1 - ..\Core\Bootloader\bootloader.c - - - bootloader.h - 5 - ..\Core\Bootloader\bootloader.h - boot_can.c 1 - ..\Core\Bootloader\boot_can.c - - - boot_can.h - 5 - ..\Core\Bootloader\boot_can.h + ..\Core\Bootloader\Src\boot_can.c boot_flash.c 1 - ..\Core\Bootloader\boot_flash.c + ..\Core\Bootloader\Src\boot_flash.c - boot_flash.h - 5 - ..\Core\Bootloader\boot_flash.h + boot_gpio.c + 1 + ..\Core\Bootloader\Src\boot_gpio.c + + + boot_jump.c + 1 + ..\Core\Bootloader\Src\boot_jump.c + + + 2 + 0 + 0 + 0 + 0 + 1 + 2 + 2 + 2 + 2 + 11 + + + 1 + + + + 2 + 0 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 0 + 2 + 2 + 2 + 2 + 2 + 0 + 0 + 2 + 2 + 2 + 2 + 2 + + + + + + + + + boot_uart.c 1 - ..\Core\Bootloader\boot_uart.c - - - boot_uart.h - 5 - ..\Core\Bootloader\boot_uart.h + ..\Core\Bootloader\Src\boot_uart.c @@ -1105,7 +1146,7 @@ USE_HAL_DRIVER,STM32F103xE - ..\Core\Inc;../Drivers/STM32F1xx_HAL_Driver/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy;../Drivers/CMSIS/Device/ST/STM32F1xx/Include;../Drivers/CMSIS/Include;..\Core\Bootloader + ..\Core\Inc;../Drivers/STM32F1xx_HAL_Driver/Inc;../Drivers/STM32F1xx_HAL_Driver/Inc/Legacy;../Drivers/CMSIS/Device/ST/STM32F1xx/Include;../Drivers/CMSIS/Include;..\Core\Bootloader\Inc @@ -1447,52 +1488,42 @@ boot_project_setup.h 5 - ..\Core\Bootloader\boot_project_setup.h - - - boot_main.c - 1 - ..\Core\Bootloader\boot_main.c + ..\Core\Bootloader\Inc\boot_project_setup.h bootloader.c 1 - ..\Core\Bootloader\bootloader.c + ..\Core\Bootloader\Src\bootloader.c - bootloader.h - 5 - ..\Core\Bootloader\bootloader.h + boot_main.c + 1 + ..\Core\Bootloader\Src\boot_main.c boot_can.c 1 - ..\Core\Bootloader\boot_can.c - - - boot_can.h - 5 - ..\Core\Bootloader\boot_can.h + ..\Core\Bootloader\Src\boot_can.c boot_flash.c 1 - ..\Core\Bootloader\boot_flash.c + ..\Core\Bootloader\Src\boot_flash.c - boot_flash.h - 5 - ..\Core\Bootloader\boot_flash.h + boot_gpio.c + 1 + ..\Core\Bootloader\Src\boot_gpio.c + + + boot_jump.c + 1 + ..\Core\Bootloader\Src\boot_jump.c boot_uart.c 1 - ..\Core\Bootloader\boot_uart.c - - - boot_uart.h - 5 - ..\Core\Bootloader\boot_uart.h + ..\Core\Bootloader\Src\boot_uart.c