ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

STM32官方Bootloader串口刷机与IAP升级实操指南

STM32官方Bootloader串口刷机与IAP升级实操指南 很多刚接触STM32F103C8T6最小系统板的朋友都会遇到同样的问题手里没买ST-Link只有一块USB转TTL小板能不能给芯片刷固件答案是可以的而且不仅能刷还能用它做IAP升级。这里说的就是芯片出厂时固化在系统存储区里的官方bootloader。它是ST官方在芯片出厂前写好的一段引导程序我们不用自己写bootloader就能通过串口把程序下载到Flash里。今天这篇的内容就是围绕STM32F103C8T6这颗芯片把官方bootloader的进入方式、上位机操作步骤、以及如何让应用在运行中自动进入升级模式完整梳理一遍。1. 官方bootloader在这里面扮演什么角色为什么能用它做IAP1.1 芯片里其实住了两套“启动程序”STM32F103C8T6的内部Flash容量是64KB部分批次实际容量更大但官方标称64KB地址从0x08000000开始。除了用户Flash芯片里还有一个独立的“系统存储器”地址在0x1FFFF000这个区域在出厂时就已经烧录了ST官方的一段bootloader程序用户程序没法擦除它也不建议尝试去改它。当芯片上电或复位时CPU会先去看BOOT0和BOOT1两个引脚的电平按表选择从哪里启动BOOT0BOOT1启动区域0x用户Flash0x08000000正常运行App10系统存储器0x1FFFF000执行官方bootloader11SRAM0x20000000一般调试用也就是说只要把BOOT0拉高再让BOOT1保持低电平复位后芯片就会进入官方bootloader。这时候我们用串口发指令给bootloader它就能帮我们把固件写到用户Flash里。这个模式不需要任何调试器只要有一根USB转TTL串口线就够了。1.2 官方bootloader能干什么不能干什么既然叫IAPIn Application Programming核心价值就是在应用运行过程中通过某个通信接口对Flash重新编程。官方bootloader解决的是“烧录”这个动作它支持USART1、USART2需要重映射、CAN、USB DFU等接口。对F103C8T6来说最常用、最不容易出问题的方式就是USART1也就是PA9和PA10这对串口引脚。它做不了的事情也很明确官方bootloader只认ST自己定义的协议我们不能往里面塞自定义的AES加密、CRC32校验、断点续传、版本回滚之类的东西。产品量产后如果要做OTA升级一般不会直接用官方bootloader而是自己写一个bootloader放在0x08000000再把App偏移到0x08008000以后。但作为开发期刷机、现场维护的手段官方bootloader足够简单可靠而且它不用占用户Flash空间属于“白嫖”芯片里已有的资源。2. 硬件准备BOOT0/BOOT1怎么接串口线怎么连2.1 关键引脚和接线关系F103C8T6的USART1对应PA9TX和PA10RX。USB转TTL小板和板子的连接方式必须是交叉连芯片引脚连接目标PA9USART1_TXUSB转TTL的RXPA10USART1_RXUSB转TTL的TXGNDUSB转TTL的GND这里有个很容易踩的坑很多USB转TTL的模块输出电平是5V而STM32F103C8T6是3.3V供电PA9和PA10不是所有引脚都是5V容忍的直接把RX接到5V的TX上长时间用有风险。比较稳妥的做法是用支持3.3V电平的USB转TTL模块或者在外面加一级电平转换。如果是板载CP2102、CH340这种自带3.3V输出的最小系统板那就直接接但最好不要用板上那个3.3V给外部大功率设备供电。另外一个值得注意的细节BOOT1这个引脚在F103C8T6上就是PB2。我们进入系统bootloader时要求BOOT01BOOT10所以要把BOOT1接GND别让它浮空。有些板子把BOOT1默认通过电阻接了地那就不需要额外处理。2.2 进入官方bootloader的两种姿势第一种是手动模式适合开发调试或现场维护。把BOOT0跳线帽跳到高电平按一下复位键芯片就进入bootloader。下载完固件后再把BOOT0跳回低电平复位一次App就跑起来了。整个过程不复杂但每次升级都要去拨跳线帽实在算不上“应用内升级”。第二种是自动模式适合产品已经装进外壳、不方便拆开碰跳线帽的场景。电路设计时把BOOT0接到一个可控的GPIO上或者通过三极管、继电器、模拟开关来控制BOOT0电平。App收到升级指令后先把BOOT0引脚拉高再执行软件复位。这样一来复位后的启动顺序就会走进系统bootloader。这个方案的硬件改动很小但能把“手动拨跳线”变成“程序触发”后面我会给一段可以直接参考的代码。2.3 复位时序和电源稳定性进入bootloader时BOOT0电平必须在复位信号释放之前稳定下来。实际操作中即使你先把BOOT0拉高再按复位键也有可能出现连接失败的情况尤其是外部电源纹波比较大的时候。我的经验是下载工具选好串口号后先给板子断电把BOOT0跳到高再上电然后立刻点连接。如果连接失败再按一次板子上的复位键往往就能连上。这是因为bootloader在等待主机的同步握手指令错过窗口期就会卡在那里。3. 上位机实操用STM32CubeProgrammer走一遍完整升级3.1 工具选型Flash Loader可以退休了ST早期官方用的是“Flash Loader Demonstrator”一个单独的PC软件界面比较老旧串口兼容性也一般。现在已经全面整合到STM32CubeProgrammer里了。我建议直接用STM32CubeProgrammer它是目前ST主推的一体化编程工具支持串口、USB、ST-Link、JTAG等方式界面也直观很多。无论你是想在Windows下点鼠标还是在Linux下跑命令行它都行。3.2 用STM32CubeProgrammer连接bootloader的完整步骤先把BOOT0拉高BOOT1接低用USB转TTL连好PA9和PA10然后给板上电。打开STM32CubeProgrammer按下面几步操作右上角选择“UART”模式。串口选择窗口里选中USB转TTL对应的COM口。波特率选115200数据位8停止位1校验位None流控None。如果界面上有“RTS/DTR”相关选项先不要勾选让它们保持默认。点“Connect”软件会尝试与bootloader握手。连接成功后主界面会显示芯片型号、Flash大小、当前UID等信息这就说明已经进入官方bootloader了。切到“Programming”页点击“Browse”选择要下载的hex文件或bin文件。如果是bin文件需要手动填写起始地址一般就是0x08000000如果是hex文件地址信息已经写在文件里不需要手动填。点“Download”等待进度条走完软件会提示下载完成。此时不要急着断电先把BOOT0跳回低电平或者在上位机里执行一次Reset后断开连接再按板子上的复位键就能看到新固件跑起来了。这里有一个常见误区很多人下载bin文件时不填起始地址结果程序写到0x08000000跳线也拨回来了复位后还是一团黑。bin文件是纯裸数据文件里没有地址信息所以起始地址一定要填对。hex文件自带地址记录工具会按地址写入不需要手动指定。3.3 下载成功不等于升级成功下载完成后建议做两个验证动作。一是连接状态下点一次“Read”或“Verify”对比一下芯片Flash里的内容和文件是否一致这一步能确保写入过程中没有因为串口误码导致数据不完整。二是断开连接后把BOOT0拉低复位确认App能正常启动、功能正常。如果App启动后跑飞或卡死问题通常不在bootloader而在于App本身的中断向量表位置。用官方bootloader把App写到0x08000000时App的链接地址就该是0x08000000这样中断向量表才能被CPU在复位后找到。只有当你自己写bootloader并把App放在0x08008000的时候才需要改IROM起始地址和VTOR。很多人把这两者搞混后面我会专门说。4. 让升级自动化App里拉高BOOT0再复位以及“直接跳转bootloader”的坑4.1 什么时候需要自动进入升级模式开发阶段手动拨跳线帽还能接受但产品一旦封箱用户不可能去拆外壳拨BOOT0。此时常见的做法是App运行中收到串口指令、按键组合或者云端升级指令后主动进入bootloader然后通过外部工具下载新固件。这里的核心就是让“App到bootloader”的过程自动完成。4.2 方案AGPIO控制BOOT0然后软件复位如果你在设计硬件时留了一个GPIO来控制BOOT0那软件流程会非常简单。这里我假设控制BOOT0的引脚接到了PA0实际请按你的原理图改。代码可以用HAL库写也可以直接操作寄存器核心逻辑如下#define BOOT0_CTRL_GPIO_PORT GPIOA #define BOOT0_CTRL_GPIO_PIN GPIO_PIN_0 void EnterSystemBootloader(void) { // 1. 停止关键外设避免复位前产生异常中断 HAL_UART_DeInit(huart1); HAL_UART_DeInit(huart2); HAL_ADC_DeInit(hadc1); HAL_TIM_Base_DeInit(htim1); // 2. 关闭全局中断 __disable_irq(); // 3. 恢复RCC时钟到默认状态避免bootloader拿到一个奇怪的外设时钟 HAL_RCC_DeInit(); // 4. 停掉SysTick SysTick-CTRL 0; SysTick-LOAD 0; SysTick-VAL 0; // 5. 把BOOT0引脚拉高 HAL_GPIO_WritePin(BOOT0_CTRL_GPIO_PORT, BOOT0_CTRL_GPIO_PIN, GPIO_PIN_SET); // 6. 软件复位 NVIC_SystemReset(); }这段代码里第5步很关键必须在复位前把BOOT0拉高因为复位一释放CPU马上采样BOOT0。如果代码执行到软件复位之后才想起来拉高那就晚了。第3步的HAL_RCC_DeInit很多人会忽略但我觉得很有必要尤其是App里改了系统时钟频率或者把HSE做主时钟的情况下官方bootloader自己会重新配置时钟如果RCC寄存器状态不是上电默认值可能会导致串口波特率计算错误进而握手失败。4.3 方案B直接从App跳转到0x1FFFF000为什么不推荐网上还有一种做法不控制BOOT0而是像跳转到App那样直接跳转到系统存储器的复位向量地址。原理上确实可行代码写出来也简单#define SYSTEM_BOOTLOADER_ADDRESS 0x1FFFF000 typedef void (*pFunction)(void); void JumpToSystemBootloader(void) { uint32_t boot_addr SYSTEM_BOOTLOADER_ADDRESS; pFunction jump_func; __disable_irq(); // 复位外设、关时钟和方案A一样做清理 // 取Bootloader初始栈顶指针重新设置MSP __set_MSP(*(volatile uint32_t *)boot_addr); // 取Reset Handler地址 jump_func (pFunction)(*(volatile uint32_t *)(boot_addr 4)); jump_func(); }但我不建议在生产项目里用这个方案原因有两个。一是F103的官方bootloader在启动时会假设自己是从复位后的初始状态开始运行的包括中断向量表、外设时钟、堆栈指针等。从App直接跳过去时很多外设寄存器还停留在App运行时的状态容易出现不可预期的现象比如bootloader可以握手但下载到一半卡死。二是这种跳转方式一旦遇到芯片版本更新或读保护配置变化兼容性很难保证。相比之下方案A老老实实地通过复位、让芯片按照硬件启动路径进入bootloader虽然看起来多了一步“软件复位”但它模拟的是最正常的上电启动过程稳定性要好得多。5. 踩坑与排查连接失败、下载失败、跳转失败的常见原因5.1 连接失败先别乱试按顺序查官方bootloader使用过程中最让人心烦的就是串口连接失败。我总结了一张表格基本覆盖了大部分情况故障现象可能原因解决思路点Connect后一直卡住提示超时串口线交叉接反检查PA9是否接到对方RXPA10是否接到对方TX提示无法打开COM口USB转TTL驱动未装好或串口被上位机占用换一个USB口关闭串口调试助手重插模块能打开串口但握手失败BOOT0没有真正拉高或复位时序没执行重新拉高BOOT0断电再上电或者按复位键握手成功但下载到一半卡死波特率不匹配或USB转TTL丢包严重降低波特率到9600或者换根短线检查共地下载完成但校验失败供电不足导致Flash写入掉电检查3.3V电源稳定性尽量用独立稳压供电连接时上位机自动判断到未知设备芯片还在运行App没有进入bootloader确认BOOT0和BOOT1电平重新复位我自己遇到过最隐蔽的一次USB转TTL模块的TXD和RXD丝印没有标错但它的电平是5VPA10输入口在官方bootloader握手时需要精确判断起始位5V高电平虽然不至于烧芯片却因为板子上的上拉电阻分压导致高电平被拉到3V以下握手屡屡失败。换了一个3.3V电平的模块一次成功。所以硬件电平匹配不能只看“能不能识别”还要看波形质量。5.2 最容易忽略的读保护和写保护很多批量生产的板子会开启Flash读保护即RDP等级设置为1或更高。一旦开启了读保护官方bootloader通常是能握手但会在“Read”或“Download”时拒绝访问Flash提示类似“Protection error”的信息。这时候需要在CubeProgrammer里选择“Option Bytes”页面把读保护等级降到0这个过程会擦掉整片Flash所以别在有重要数据的时候乱操作。还有一种是写保护对某个Flash扇区单独加了写保护。F103的选项字节里可以按扇区设置写保护如果你之前调试过擦写保护下载时会报“Write Protection”错误。解法同样是去“Option Bytes”页面取消对应扇区的写保护。这种坑通常在“昨天还能下载今天突然不行”的场景里出现原因十有八九是前一天调试时不小心改了选项字节。5.3 App不启动问题不一定在bootloader官方bootloader把固件下载到0x08000000之后App不启动很多人第一反应是“固件没烧进去”。但最常见的其实是中断向量表问题。如果你用STM32CubeMX生成工程时默认IROM1起始地址是0x08000000那没问题但如果你之前为了自写bootloader把IROM1改成了0x08008000然后又用官方bootloader直接下载App的所有中断向量就会落在0x08008000可CPU复位后只会从0x08000000开始找向量表于是App跑不起来。判断方法很简单下载一个空的、不带外设初始化的GPIO翻转程序到0x08000000如果LED会闪说明bootloader写入没问题问题就在App的链接地址。另外用官方bootloader下载App时不建议在App里手动改VTOR因为F103的VTOR默认指向0x08000000不需要额外设置如果你改了反而可能导致中断响应错乱。6. 什么场景该放弃官方bootloader自己写一个6.1 官方方案的边界很清晰官方bootloader最大的优点是省事最大的缺点是“不可控”。它的通信协议是固定的固件直接写进Flash没有版本判断没有回滚机制也没有断点续传。也就是说它适合做“线下升级”不适合做“远程升级”。如果你的产品要用4G/WiFi模块做OTA或者需要通过CAN总线升级官方bootloader基本帮不上忙因为它没有对应的协议解析能力也不会做业务层的校验。另外官方bootloader只有在BOOT0被正确配置时才会执行。如果你的产品已经完全密封又没有把BOOT0引到GPIO那出了厂就是死路一条——除非有ST-Link/JTAG口否则只能拆机。所以正规量产项目如果有可能需要现场升级我的建议是在电路板上预留BOOT0控制GPIO或者干脆自己写bootloader。6.2 自写bootloader其实只是“换了个入口”自己写bootloader听起来很高级但它和官方bootloader的本质区别只有一个入口程序由你控制。最常见的做法是在0x08000000放一个很小的bootloaderApp从0x08008000开始。bootloader启动后先检查升级标志、版本号、帧数据如果确认有升级任务就通过串口、CAN、WiFi等接口接收完整固件包校验无误后写入App区域最后跳转执行。这时候要特别注意两点。第一App工程需要把IROM1起始地址改成0x08008000大小改成0x078000这类的值同时启动文件里的中断向量表也会自动放到0x08008000。第二App代码里如果需要跳转到bootloader不能再用官方bootloader那套BOOT0控制逻辑而是直接复位后靠bootloader去检查升级标志位或者用软跳转函数跳回0x08000000。这个过程和本文前面提到的方法略有区别但思路是一致的到底是“用官方bootloader升级”还是“用自己写的bootloader升级”核心都是“Boot程序 通信接口 Flash写入”三段式结构。从我个人的实操经验来说开发初期我反而更喜欢先用官方bootloader把第一版自写bootloader烧进去然后再通过自写bootloader去更新App。这样既免去了反复接调试器的麻烦又能在早期把串口链路、Flash分区、跳转逻辑一次性跑通。等产品稳定后官方bootloader就退居幕后只作为产线和售后返修时的最后保险。给我一套完整的工具链和清晰的分工哪怕只靠一根USB转TTL也能把整机的升级链路做得又稳又省事。
返回列表