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