#include "bootloader.h" #include "boot_flash.h" #include "boot_uart.h" #include "boot_can.h" CAN_TxHeaderTypeDef TxHeader; CAN_RxHeaderTypeDef RxHeader; uint32_t TxMailBox = 0; // num of used mail void SetKey(void); uint32_t ReadKey(void); void ResetKey(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); void Bootloader_Init(void) { HAL_Init(); Boot_SystemClock_Config(); MX_BOOT_UART_Init(); MX_BOOT_CAN_Init(); } HAL_StatusTypeDef res_hal; void Bootloader_Task(Bootloader_t *bl) { switch (bl->state) { case BL_STATE_INIT: // Проверяем ключ, чтобы понять запускать приложение или программирование if (ReadKey() == BL_KEY_APP_WRITTEN) { bl->state = BL_STATE_JUMP_TO_APP; break; // не инициализируем, а сразу прыгаем в приложение } else { bl->state = BL_STATE_IDLE; } // Инициализация периферии Bootloader_Init(); bl->huart = &huart_boot; bl->hcan = &hcan_boot; bl->addr = MAIN_APP_START_ADR; break; case BL_STATE_IDLE: // Ждем команды по UART или CAN if(bl->state != bl->prev_state) { static uint8_t Data[8] = {0}; static uint32_t TxMail = {0}; Data[0] = 0xFA; HAL_CAN_AddTxMessage(bl->hcan, &bl->TxHeader, Data, &TxMail); HAL_UART_Transmit() } if (Receive_FW_Command(bl) == 0xFF) // функция обработки команд, возвращает 1 если ошибка { bl->state = BL_STATE_ERROR; } break; case BL_STATE_ERASE: ResetKey(); if (FLASH_Erase_App() == HAL_OK) // твоя функция стирания MAIN_APP_PAGE..NUM { bl->state = BL_STATE_IDLE; } else { bl->state = BL_STATE_ERROR; } break; case BL_STATE_RECEIVE_UART: // В этом состоянии мы просто принимаем страницу + CRC Bootloader_UART_Receive_Page(bl); break; case BL_STATE_RECEIVE_CAN: Bootloader_CAN_Receive_Page(bl); break; case BL_STATE_WRITE: if (FLASH_Write_Page(&bl->addr, bl->fw_buffer, bl->fw_len) == HAL_OK) // запись блока во Flash { bl->state = BL_STATE_IDLE; // ждём следующего блока } else { bl->state = BL_STATE_ERROR; } break; case BL_STATE_VERIFY: if (/*Verify_Flash_CRC(bl->fw_crc)*/0 == HAL_OK) { ResetKey(); SetKey(); // отметка, что прошивка записана bl->state = BL_STATE_IDLE; } else { bl->state = BL_STATE_ERROR; } break; case BL_STATE_JUMP_TO_APP: JumpToApplocation(); break; case BL_STATE_ERROR: // обработка ошибок (можно светодиод, повторная инициализация, лог по UART) Error_Handler(); break; default: bl->state = BL_STATE_ERROR; break; } bl->prev_state = bl->state; } /** * @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; // --------------------------- // Чтение команды по UART // --------------------------- res = HAL_UART_Receive(bl->huart, &cmd, 1, 10); // таймаут 10 ms if (res == HAL_OK) { switch(cmd) { case CMD_ERASE: // команда: стереть Flash bl->state = BL_STATE_ERASE; return 0x00; case CMD_START_RECEIVE: // команда: принять блок bl->state = BL_STATE_RECEIVE_UART; 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; default: return 0xFF; // неизвестная команда } } // --------------------------- // Чтение команды по CAN // --------------------------- CAN_RxHeaderTypeDef canHeader; uint8_t canData[8]; if (HAL_CAN_GetRxFifoFillLevel(bl->hcan, CAN_RX_FIFO0) > 0) { if (HAL_CAN_GetRxMessage(bl->hcan, CAN_RX_FIFO0, &canHeader, canData) == HAL_OK) { cmd = canData[0]; // предполагаем, что команда в первом байте switch(cmd) { case CMD_ERASE: bl->state = BL_STATE_ERASE; return 0x00; case CMD_START_RECEIVE: // запускаем прием первой страницы + crc, затем после приема всего массива ждем следующей комманды 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; default: return 0xFF; } } } #ifdef TEST_CAN TxHeader.StdId = 0x200; // ID OF MESSAGE TxHeader.ExtId = 0; // STANDART FRAME (NOT EXTENTED) TxHeader.RTR = CAN_RTR_DATA; // TRANSMIT DATA OR TxHeader.IDE = CAN_ID_STD; // STANDART FRAME TxHeader.DLC = 8; // DATA SIZE TxHeader.TransmitGlobalTime = DISABLE; //THIS MODE IS NOT USED, SO DISABLE uint8_t asd[8] = "ABCDEFGL"; res_hal = HAL_CAN_AddTxMessage(&hcan_boot, &TxHeader, asd, &TxMailBox); // add to mail for transmit HAL_Delay(1000); #endif return 0x00; // если команды нет, ничего не делаем, остаёмся в BL_STATE_IDLE } // reset/set key function 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 ResetKey(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) { // jump to main app //Задаётся адрес программы со смещением от начала вектора прерываний uint32_t app_jump_adr; app_jump_adr=*((volatile uint32_t*)(MAIN_APP_START_ADR+4)); void(*GoToApp)(void); //Деинициализация HAL HAL_DeInit(); GoToApp = (void (*) (void)) app_jump_adr; //Перенос вектора прерываний на начало зашитой программы __disable_irq(); __set_MSP(*((volatile uint32_t*)MAIN_APP_START_ADR)); __enable_irq(); //Переход к выполнению зашитой программы GoToApp(); } 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(); } } void Error_Handler(void) { NVIC_SystemReset(); }