STM32自定义Bootloader开发及RS485在线编程问题咨询
自定义STM32 RS-485 Bootloader开发疑问解答
背景与方案概述
两块搭载STM32的开发板通过RS-485互联(各板配备UART转RS-485收发器),因无法修改PCB使用ST官方Bootloader及BOOT0引脚,需开发自定义Bootloader。
Flash分区规划
- B-1 Flash分为3部分:
- 20KB:Bootloader
- 120KB:B-2应用缓冲区
- 360KB:B-1主应用
- B-2 Flash分为2部分:
- 20KB:Bootloader
- 100KB:主应用
Bootloader执行逻辑
// B1: starts from bootloader for (5 seconds) { // check for the boot command from UART if (command received) { // send boot and reset command to B-2 and receive ack // start the boot mode while (boot mode not aborted) { // receive command packet containing address if (command == header && address == B1) { // prompt for the main .hex file and store it in B-1 partition } else if (command == header && address == B2) { // prompt for the main .hex file and store it in B-2 partition // send header and the app to B-2 using rs-485 port } else if (command == abort) { break from while loop } } } else { // do nothing } } // jump to main application
疑问与解答
疑问1:无GPIO连接时,能否通过Flash标志位激活B-2的Boot模式?
完全可行,具体实现思路:
- 在B-2的Flash中预留一块独立的标志存储区域(建议选Bootloader分区内的空闲扇区,或单独划分一个小扇区,确保不会被主应用或Bootloader的擦写操作覆盖)
- B-2的Bootloader启动后,优先检查该标志位:
- 若标志位为预设的“Boot模式触发值”,则跳过主应用跳转,进入RS-485等待接收的Boot状态
- 若标志位正常,则直接跳转至主应用
- 触发流程:B-1发送Boot命令时,先通过RS-485向B-2的主应用发送指令,让B-2主应用写入标志位,随后执行软复位;B-2复位后进入Bootloader,检测到标志位即进入Boot模式,操作完成后需清除标志位,避免下次启动误触发
疑问2:能否直接将UART输入数据流写入应用Flash区域?
不能直接按示例代码操作,STM32 Flash写入有严格的硬件约束,示例代码存在以下问题:
- Flash写入前必须擦除:STM32 Flash只能从1改写为0,写入前必须先擦除对应扇区(扇区大小依STM32型号而定),直接写入会导致数据异常或硬件错误
- 地址访问限制:Flash属于只读存储区域,不能直接将UART接收的指针指向Flash地址,需先将数据接收至RAM缓冲区,再通过Flash编程API写入
- 无数据校验:逐字节直写缺乏校验机制,传输过程中出现错误无法及时发现
正确的操作流程示例:
// 1. 先将UART数据接收至RAM缓冲区 uint8_t ram_buf[APP_BUFFER_SIZE]; for (uint32_t i = 0; i < size_of_app; i++) { HAL_UART_Receive(&uartPort, &ram_buf[i], 1, timeout); } // 2. 校验数据完整性(如CRC、校验和) if (verify_data(ram_buf, size_of_app) != OK) { // 校验失败,执行错误处理 return; } // 3. 擦除目标Flash扇区 uint32_t flash_addr = B2_APP_FLASH_MEMORY_PARTITION_START_ADDRESS; FLASH_EraseInitTypeDef erase_init; erase_init.TypeErase = FLASH_TYPEERASE_SECTORS; erase_init.Sector = get_sector_from_address(flash_addr); // 根据地址获取对应扇区 erase_init.NbSectors = get_sector_count(size_of_app); // 计算需要擦除的扇区数量 erase_init.VoltageRange = FLASH_VOLTAGE_RANGE_3; // 匹配芯片供电电压范围 uint32_t sector_error; if (HAL_FLASHEx_Erase(&erase_init, §or_error) != HAL_OK) { // 擦除失败,执行错误处理 return; } // 4. 将RAM缓冲区数据写入Flash for (uint32_t i = 0; i < size_of_app; i += 4) { // 按字写入,提升效率 uint32_t data = *(uint32_t*)(ram_buf + i); if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, flash_addr + i, data) != HAL_OK) { // 写入失败,执行错误处理 return; } }
内容的提问来源于stack exchange,提问作者mehdi
相关产品推荐
相关产品推荐

