| 
 
| 一、简单介绍 Bootloader 是嵌入式设备中负责引导和更新的关键程序。汽车的 ECU 在进行 OTA 升级时,Bootloader 负责下载、校验和写入新固件,确保出现异常时能回退到旧版本保障行车安全;手机系统更新依赖 Bootloader来刷机、验证签名并启动系统内核;电脑中的 BIOS 或 UEFI 同样是一类 Bootloader,用于初始化硬件并加载操作系统。通过统一的下载接口和安全机制,Bootloader 让设备可以远程维护、在线更新并防止恶意篡改,从而在降低生产和维护成本的同时,保证系统在整个生命周期中的稳定与安全。
 
 
   
 归根结底,bootloader也是一个工程,和application性质相同
 
 笔者实现的简易bootloader的作用主要有
 
 擦除现存app
 接收新的app固件
 烧写到flash中
 跳转到新app
 二、开发思路
 笔者是基于STM32F103CBT6开发,和常见的STM32F103C8T6相比,flash变成128kb
 
 
   
 STM32单片机的flash启动模式下,启动地址总是0x8000000,这也是自己的bootloader的起始地址
 
 
   
 bootloader大小为12616字节,需要12个page多一点点(0.32 page)
 
 因此把判断标志放在page 12的开头,即地址0x08003400
 
 App的起始地址为0x08003800,(page13的开头)
 
 内存分布为
 
 
   
 boot检测flag信号为true表明需要升级,擦除App并烧写新的固件;否则就表明不需要升级,直接跳转到App即可
 
 
   
 三、实战开发
 App
 cubemx开启时钟,UART1和UART2,LED
 
 
   
 App工程需要设置起始地址,使用keil的话如下设置
 
 
   
 system_stm32f1xx.c文件中设置向量表偏移地址
 
 
   
 App闪烁LED并在串口2发送App running作为演示
 
 
   
 App的main函数代码如下
 
 /* USER CODE END 0 */
 /**
 * @brief  The application entry point.
 * @retval int
 */
 int main(void)
 {
 /* USER CODE BEGIN 1 */
 /* USER CODE END 1 */
 /* MCU Configuration--------------------------------------------------------*/
 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
 HAL_Init();
 /* USER CODE BEGIN Init */
 /* USER CODE END Init */
 /* Configure the system clock */
 SystemClock_Config();
 /* USER CODE BEGIN SysInit */
 /* USER CODE END SysInit */
 /* Initialize all configured peripherals */
 MX_GPIO_Init();
 MX_USART1_UART_Init();
 MX_USART2_UART_Init();
 /* USER CODE BEGIN 2 */
 req.TypeErase = FLASH_TYPEERASE_PAGES;
 req.Banks = FLASH_BANK_1;
 req.PageAddress = APP_REQUEST_ADDRESS;
 req.NbPages = 1;
 /* go to wait state for getting next data from PC*/
 
 updateReq = *(uint8_t*)(APP_REQUEST_ADDRESS);
 HAL_UART_Receive_IT(&huart1, rx, 6);
 
 /* USER CODE END 2 */
 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
 HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
 HAL_UART_Transmit(&huart2, tx, 14, 10);
 HAL_Delay(100);
 /* USER CODE END WHILE */
 /* USER CODE BEGIN 3 */
 }
 /* USER CODE END 3 */
 }
 
 
 Boot
 cubemx开启时钟,串口
 
 
   
 boot工程起始地址就是0x8000000,就不需要偏移了
 
 设置地址宏和升级请求标志
 
 #define APP_START_ADDRESS 0x08003800
 #define APP_REQUEST_ADDRESS 0x08003400
 
 updateReq = *(uint8_t*)(APP_REQUEST_ADDRESS);
 
 
 判断是否需要升级
 
 if (updateReq == 1 || updateReq == 0xFF)
 {
 state = STATE_INIT;
 HAL_FLASH_Unlock();
 HAL_FLASHEx_Erase(&req, &error);
 }
 else
 {
 state = STATE_JUMP;
 }
 
 
 在主循环内进行状态的切换
 
 笔者制作了一个配套的上位机
 
 
   
 上位机按下boot则发送由帧头0xAA+0x22222222+CRC组成的升级请求,App检测到后就设置标志位并复位
 
 
   
 上位机按下flash则进行新固件的传输,在boot中进行接收和flash的写入
 
 case STATE_FLASH:
 {
 /**
 * flash code
 */
 HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, APP_START_ADDRESS + flashPackCnt * 4, *(uint32_t*) (prx + 1));
 flashPackCnt++;
 
 /* flash finish, notify PC by sending 0x77 */
 tx[0] = 0x77;
 HAL_UART_Transmit_IT(&huart1, tx, 1);
 
 /* go to wait state for getting next data from PC*/
 HAL_UART_Receive_IT(&huart1, rx, 6);
 state = STATE_WAIT;
 
 if ((flashPackCnt) * 4 == dataSize)
 {
 state = STATE_END;
 
 HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, APP_REQUEST_ADDRESS, 0);
 }
 
 break;
 }
 
 
 上位机发送0x20+四字节的固件包+CRC,单片机接收后发送0x77作为应答信号,这里笔者作为演示,就直接明文传输了,实际应用一般会有加密措施和握手等步骤
 
 
   
 烧写完毕后跳转到新的App中
 
 case STATE_JUMP:
 {
 deinitEverything();
 uint32_t stacktop = *((__IO uint32_t *)APP_START_ADDRESS);
 __set_MSP(stacktop);
 
 app_func_t app_func = (app_func_t)(*((__IO uint32_t *)(APP_START_ADDRESS + 4)));
 
 app_func();
 }
 
 
 
 
 boot的main函数代码如下
 
 /* USER CODE END 0 */
 /**
 * @brief  The application entry point.
 * @retval int
 */
 int main(void)
 {
 /* USER CODE BEGIN 1 */
 /* USER CODE END 1 */
 /* MCU Configuration--------------------------------------------------------*/
 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
 HAL_Init();
 /* USER CODE BEGIN Init */
 /* USER CODE END Init */
 /* Configure the system clock */
 SystemClock_Config();
 /* USER CODE BEGIN SysInit */
 /* USER CODE END SysInit */
 /* Initialize all configured peripherals */
 MX_GPIO_Init();
 MX_USART1_UART_Init();
 /* USER CODE BEGIN 2 */
 updateReq = *(uint8_t*)(APP_REQUEST_ADDRESS);
 erase.TypeErase = FLASH_TYPEERASE_PAGES;
 erase.Banks = FLASH_BANK_1;
 erase.PageAddress = APP_START_ADDRESS;
 erase.NbPages = 40;
 req.TypeErase = FLASH_TYPEERASE_PAGES;
 req.Banks = FLASH_BANK_1;
 req.PageAddress = APP_REQUEST_ADDRESS;
 req.NbPages = 1;
 uint32_t error;
 if (updateReq == 1 || updateReq == 0xFF)
 {
 state = STATE_INIT;
 HAL_FLASH_Unlock();
 HAL_FLASHEx_Erase(&req, &error);
 }
 else
 {
 state = STATE_JUMP;
 }
 /* USER CODE END 2 */
 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
 switch (state)
 {
 case STATE_INIT:
 {
 /* prepare for size info by interrupt */
 HAL_UART_Receive_IT(&huart1, rx, 6);
 /* tell host the chip is in boot now */
 tx[0] = 0x77;
 HAL_UART_Transmit_IT(&huart1, tx, 1);
 /* go to idle */
 state = STATE_IDLE;
 break;
 }
 case STATE_IDLE:
 {
 break;
 }
 case STATE_SIZE:
 {
 break;
 }
 case STATE_ERASE_BEGIN:
 {
 break;
 }
 case STATE_WAIT:
 {
 break;
 }
 case STATE_FLASH:
 {
 /**
 * flash code
 */
 HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, APP_START_ADDRESS + flashPackCnt * 4, *(uint32_t*) (prx + 1));
 flashPackCnt++;
 /* flash finish, notify PC by sending 0xAA */
 tx[0] = 0x77;
 HAL_UART_Transmit_IT(&huart1, tx, 1);
 /* go to wait state for getting next data from PC*/
 HAL_UART_Receive_IT(&huart1, rx, 6);
 state = STATE_WAIT;
 if ((flashPackCnt) * 4 == dataSize)
 {
 state = STATE_END;
 HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, APP_REQUEST_ADDRESS, 0);
 }
 break;
 }
 case STATE_END:
 {
 
 break;
 }
 case STATE_JUMP:
 {
 deinitEverything();
 uint32_t stacktop = *((__IO uint32_t *)APP_START_ADDRESS);
 __set_MSP(stacktop);
 app_func_t app_func = (app_func_t)(*((__IO uint32_t *)(APP_START_ADDRESS + 4)));
 app_func();
 }
 
 default:
 break;
 }
 /* USER CODE END WHILE */
 /* USER CODE BEGIN 3 */
 }
 /* USER CODE END 3 */
 }
 
 
 调试读取内存,0x8003800处的内容和编译的App的hex内容一致
 
 
   
 由于升级请求被擦除,下一次复位就直接跳转至App运行了
 ————————————————
 版权声明:本文为CSDN博主「田甲」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
 原文链接:https://blog.csdn.net/weixin_56719449/article/details/151936705
 
 
 | 
 |