ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

STM32L071KZ Bootloader刷写Flash失败排查与解决

STM32L071KZ Bootloader刷写Flash失败排查与解决 先说结论最近用STM32L071KZ做低功耗采集节点固件升级走的是BOOT0拉高进ST系统Bootloader再用串口刷Flash。本来以为这条路最省事结果实际调试时被“Flash Issue”折腾了一整晚串口工具能连上读出来的Flash却是全0xFF用STM32CubeProgrammer擦除直接报“erase failed! cannot access memory internal command error”好不容易擦除成功烧录校验又失败最后程序刷进去了复位后却死活不跑。这篇文章就把这类问题的排查思路、根因分析和可复现的解决方案完整写出来给正在拿STM32L071KZ或者其他STM32L0系列做Bootloader刷写、量产烧录、救砖的朋友一个参考。做嵌入式开发遇到“MCU Flash下载失败”并不稀奇但通过ST Bootloader刷写时的失败和用ST-Link/J-Link在IDE里直接烧录的失败原因往往不是一回事。Bootloader场景下的问题大多是选项字节保护、引脚配置、通信链路时序、擦写对齐这几类排查顺序和方法完全不一样。下面按我实际踩坑的顺序来写。1. 现象复盘ST Bootloader下Flash问题的三类典型表现1.1 第一类握手成功但Flash读出来是空白我一开始用的是STM32CubeProgrammer的UART模式连接Boot0拉高后复位软件很顺利就识别到了目标芯片Get ID返回了L0系列的ID说明Bootloader已经正常工作。但当我尝试读取主Flash区域时整个0x08000000区域返回的全是0xFF跟全新空片一样。这个现象的迷惑性非常强。表面看像是Flash里本来就没程序或者程序丢了但实际情况是读取操作被保护机制拦住了。STM32L0系列的保护机制不止RDP读保护一种还有WRP写保护、PCROP代码读出保护。当某个区域被保护之后通过Bootloader发Read Memory命令可能会读到全0xFF而不是报错这正是它容易误导人的地方。还有一种更朴素的可能读地址填错了。L071KZ的Flash起始地址是0x08000000容量192KB有效范围到0x0802FFFF。如果上位机在输入地址时用了0x8000000少个0或者填了0x08030000开始越界地址读回来自然也是0xFF。这类问题虽然低级但实际工作中很常见。1.2 第二类Erase命令报错擦除进行不下去这是我最头疼的一类。CubeProgrammer连接正常点击“Full chip erase”之后进度条走了一小段就弹错“erase failed! cannot access memory internal command error”。这个错误信息本身挺误导人的它没有直接说“写保护”而是说“内存访问失败”。实际上在STM32L0系列上除了RDP读保护之外Flash写保护WRP才是擦除失败的头号原因。L0的WRP是按页组配置的一旦某个区域的写保护选项字节被置位Bootloader执行Erase命令时就会因为访问到受保护区域而中止。另外一个容易忽略的原因是供电电压。Flash擦写需要电荷泵VDD过低时Flash控制器会拒绝执行擦除操作。L071KZ虽然标称1.8V到3.6V工作但Flash擦写对电压余量是有要求的如果板子用纽扣电池供电或者USB转TTL模块输出电流不够电压被拉低擦除就会间歇性失败。1.3 第三类写入校验失败或者写完程序不跑擦除过了之后烧录过程也可能在中途翻车。我遇到的报错是校验和失败具体表现是写入过程显示成功但“Verify”阶段发现读回来的数据和bin文件对不上。这类问题在Bootloader场景下通常是这几个原因写入地址没有按8字节对齐。STM32L0的Flash编程是双字64位操作一次写入必须8字节对齐长度也必须是8的整数倍。如果hex文件里某个段的首地址不对齐Bootloader就会写失败或者写入错误数据。上位机校验算法和Bootloader不一致。ST Bootloader返回的校验是基于XOR的如果自研上位机用的是CRC或者简单累加校验结果自然对不上。写保护依然存在但只保护了部分区间。整片擦除可能碰巧成功写入时某些页仍然受WRP保护数据写不进去校验就失败。程序刷完复位不跑也是经典问题。最常见原因就是Boot0引脚还拉在高电平复位之后又进了Bootloader其次则是选项字节里的nBOOT0、nBOOT1、nBOOT_SEL配置和你的启动期望不一致。2. 排查前的底层基础L0 Flash架构与Bootloader行为2.1 先搞懂L071KZ的Flash结构排查才有方向STM32L071KZ属于STM32L0超低功耗系列Cortex-M0内核主频32MHz。Flash容量192KBSRAM 20KB封装是LQFP32。这颗芯片的Flash架构有几个特点直接关系到Bootloader刷写页大小是128字节不是常见的1KB或者2KB。擦除操作以页为单位Bootloader的擦除命令也是按页来。编程必须是双字64位操作也就是一次写入8个字节。所以不管是官方工具还是自研上位机写入长度都必须是8的整数倍。Flash地址范围是0x08000000到0x0802FFFF共1536页。选项字节Option Bytes位于独立的区域配置RDP、WRP、PCROP、启动源等。选项字节本身也是Flash修改时同样需要擦写。理解这几点之后再看Bootloader写Flash失败的问题就清晰多了地址越界、未对齐、写到保护页、供电不足导致Flash控制器中止这些都能从Flash自身的限制中找到答案。L071KZ的Flash在Bootloader场景下还有一个特殊点写Flash时必须先擦除。Flash写入只能把1写成0不能把0写成1所以往一个已经是0x00的区域写0xFF是无效的。很多自研上位机只做了Write Memory命令没预先擦除导致写进去的数据和预期完全不符。2.2 系统Bootloader的进入条件和通信协议L0系列出厂时在系统存储区有一段Bootloader固件地址在0x1FF00000起始处。要让MCU上电后运行这段固件需要满足启动条件。对L071KZ来说最直接的方式是把BOOT0引脚拉高再复位。这里有一个容易踩的坑L0的启动源选择不仅受BOOT0引脚控制还受选项字节里的nBOOT_SEL位影响。如果nBOOT_SEL配置为“使用选项字节nBOOT0”而不是“使用BOOT0引脚”那么就算外部把BOOT0拉高了芯片也不一定会进Bootloader。很多板子出厂时烧录过选项字节可能改过这个位导致你拉高BOOT0没反应。ST Bootloader支持USART、I2C、SPI三类接口L071KZ没有USB所以没有DFU。USART协议在应用笔记AN3155里定义I2C对应AN4221SPI对应AN4286。基本流程都是上电后Bootloader等待主机发送0x7F同步字节收到后返回ACK0x79然后主机再发命令帧命令帧由命令码、地址/数据、XOR校验组成。调试时我建议记住一个关键点Bootloader运行不依赖外部晶振它内部用HSI16时钟。这意味着如果系统程序起不来是因为外部晶振、PLL配置问题Bootloader刷写通常不会受影响。反过来想如果程序刷进去之后运行不起来而Bootloader读写Flash一切正常那问题大概率就在应用固件本身的时钟配置而不是Flash内容损坏。2.3 选项字节的“一票否决权”在STM32L0上选项字节对Flash访问的控制权是绝对的。排查Bootloader刷写问题第一个要查的就是选项字节状态。RDP读保护Level 0是无保护Level 1是禁止调试接口访问Flash同时Bootloader的读写Flash能力也会受限Level 2是永久保护不可解除。如果芯片被设成Level 1CubeProgrammer连接时有时还能识别芯片但读取Flash会得到异常数据或者全0xFF。WRP写保护按页组保护Flash区域受保护区域不能擦除、不能写入。L0的WRP粒度是4页一组也就是512字节一组。选项字节里有多组WRP配置位。PCROP代码读出保护保护区域内的代码不能通过Bootloader读取但执行不受影响。如果程序和数据正好落在PCROP区域Read Memory读回来就是空数据。解保护的操作本身也有风险。把RDP从Level 1降回Level 0时芯片会自动执行一次全片擦除所有Flash内容都会清空。如果你只是想临时读一下Flash内容没做备份改完选项字节之后数据就没了。我在调试时就因为这个动作丢过一版测试固件教训非常深刻。3. 实操排查从连接失败到Flash写失败的完整定位流程3.1 连接不上时的硬件排查先解决最基础的问题Bootloader连不上。在ST Bootloader场景里USART连接不上是最常见的原因也最琐碎。第一确认BOOT0电平真的拉高了。不要相信电路图用万用表量一下引脚实测电压。有的板子BOOT0没直接引出是通过跳线帽或者焊盘短接的时间久了氧化会导致接触不良。更隐蔽的是有些低功耗板子BOOT0引脚还并联了电容上电瞬间电压爬升太慢MCU启动时BOOT0还没到高电平导致从主Flash启动了。第二检查TXD和RXD有没有接反。USB转TTL模块的TXD要接MCU的RXDRXD接MCU的TXD。这个错误我犯过不止一次尤其是用杜邦线临时飞线的时候线序一乱就白折腾。第三注意电平匹配。如果USB转TTL模块是5V电平而L071KZ是3.3V供电5V的TXD输出可能会通过MCU的引脚内部保护二极管倒灌电流轻则通信不稳定重则损坏芯片。建议用3.3V电平的模块或者确保模块有电平转换。第四复用哪个串口引脚要看AN2606。STM32L0的Bootloader支持多个USART引脚映射但具体到LQFP32封装能用的是哪些引脚需要查AN2606里对应型号的表格。不要想当然认为PA9/PA10一定就行L0的引脚映射比较灵活有的封装下Bootloader的USART可能映射到其他引脚。第五连接时要卡准Bootloader的握手窗口。Bootloader上电后只会等待有限时间的同步字节如果上位机打开太慢窗口就过了。CubeProgrammer连接时可以先点Connect然后在软件开始等待时手动按一下板子的复位键。更省事的做法是预留一个NRST控制信号或者用支持DTR/RTS自动复位的USB转TTL模块。第六别用老掉牙的Flash Loader Demonstrator。热搜词里提到的Flash Loader Demonstrator 2.2.0是很多年前的官方工具对L0系列的支持不够完善在新系统上还容易出现驱动问题。新项目直接换STM32CubeProgrammer省心得多。3.2 连接成功但Flash操作失败的寄存器级定位当CubeProgrammer能连上芯片但Flash擦除或写入报错时不要急着反复重试先读一下Flash控制器的状态寄存器确认硬件层面的实际原因。L0系列的Flash状态寄存器里有几个位特别关键BSYFlash忙标志。擦写过程中置1如果长时间不结束说明Flash控制器卡死了。WRPRTERR写保护错误。置1说明本次写入/擦除操作触碰了WRP保护区域。PGAERR编程对齐错误。地址没按双字对齐时会置位。SIZERR长度错误。写入长度不是8字节整数倍时触发。PGSERR编程序列错误。比如没擦除就写或者写0到非0xFF区域。用CubeProgrammer连接后可以在内存视图中直接查看选项字节区域对比RDP、WRP的状态。如果发现WRP相关位被置位用软件里的“Option Bytes”页面修改配置把写保护取消再试。如果寄存器显示BSY一直为1大概率是Flash控制器处于异常状态最常见原因是擦写过程中供电跌落导致控制器半途停止。解决方法是断电、恢复正常供电、重新上电再连一次。如果反复出现就要检查板子的电源电路了我遇到过一次LDO选型余量不足擦写Flash瞬间电流增大电压掉到2.7V以下然后各种随机失败。3.3 换接口交叉验证区分通信问题和Flash问题排查这种问题我习惯用“交叉验证法”不换芯片、不换固件只换Bootloader的通信接口。L071KZ的Bootloader同时支持USART、I2C和SPI。如果USART连接之后Flash读写报错可以试试用I2C或者SPI接口连接同一个芯片做同样的Flash擦除操作。如果I2C/SPI能正常擦除说明Flash本身和保护设置都没问题故障点就锁定在USART通信链路比如电平不稳、波特率偏差、引脚映射错误。如果换了接口之后报一模一样的错误那问题就在Flash保护配置或者供电不是通信的问题。这个方法的逻辑很简单Bootloader的通信接口是独立的但Flash控制器是同一个。通信接口的故障只会影响该接口的数据传输不会影响Flash操作本身的结果。Flash操作的结果是跨接口一致的。我实际测试中用USART连接时偶尔会出现握手失败切到I2C之后非常稳定。后来查出是USART1的引脚和板上的一个按键复用了按键按下时把RX电平拉低导致握手数据出错。这种硬件复用问题靠看代码很难发现但换接口一测就暴露了。4. 解决与复现用CubeProgrammer完成一次可靠的Flash刷写4.1 GUI操作从连接到烧录的完整步骤解决完上面的问题之后我把刷写流程固化成一个标准操作每次刷写L071KZ都按这个顺序走基本不再翻车。第一步连接前准备。给板上电确认VDD在3.0V以上BOOT0通过1k电阻接到3.3V串口模块的TXD、RXD、GND分别接好共地必须保证。第二步打开STM32CubeProgrammer选择UART模式选择正确的串口号波特率建议先用9600这种低波特率等稳定后再尝试115200。接口参数保持默认的8E18数据位、偶校验、1停止位这是AN3155协议的标准配置。如果选错奇偶校验握手阶段就会失败。第三步点击Connect。连接成功后软件右侧会显示芯片型号、设备ID、Flash大小等信息。此时先别急着烧录点击“Option Bytes”页面查看RDP级别和WRP状态。如果RDP不是Level 0先改为Level 0并执行注意此时Flash会被全片擦除。如果WRP有保护区域全部取消。第四步点击“Erase”标签选择“Full chip erase”执行一次全片擦除。擦除完成后用“Read”功能读回整个Flash确认所有区域都是0xFF再进行烧录。第五步在“Download”页面选择固件文件hex文件会自动解析地址bin文件需要手动指定烧录地址L071KZ的Flash起始地址是0x08000000。勾选“Verify after download”点击“Start”开始烧录。第六步烧录完成后把BOOT0跳线恢复到低电平手动复位板子程序应该正常启动。4.2 命令行烧录与量产脚本手工点GUI在调试阶段没问题但一旦涉及量产就得用命令行工具。CubeProgrammer自带的STM32_Programmer_CLI支持完整的烧录流程可以写到批处理脚本里实现一键烧录。下面是一个典型的生产烧录脚本STM32_Programmer_CLI.exe -c portCOM7 br115200 -e all -w firmware.hex -v -rst参数含义拆解一下-c portCOM7指定串口。如果量产中串口号不固定可以用portauto或者结合设备管理器里的COM号做对应。br115200波特率实测115200在短连接线小于15cm下很稳定。-e all执行全片擦除。如果只需要擦除部分区域可以指定地址范围比如-e 0x08000000 0x08010000。-w firmware.hex写入固件。写bin文件时要加-a 0x08000000指定地址。-v烧录后校验。-rst烧录完成后复位芯片。量产时还可以加一步设置RDP Level 1防止固件被读出来STM32_Programmer_CLI.exe -c portCOM7 br115200 -ob RDP0xBB这里0xBB就是Level 1的配置值。需要特别注意设置RDP Level 1之后再想解除会触发全片擦除。而且量产工具如果每次都设置RDP后续返修时再连接就会受到读保护限制必须先解保护再烧录所以解锁和加锁的动作要设计成独立选项别写死在同一条命令里。4.3 自研上位机按AN3155协议刷写时的坑如果项目需要自己写上位机通过Bootloader刷写L071KZ下面几个坑是绕不开的。坑一串口参数必须是8E1。AN3155的USART协议标准配置是8数据位、偶校验、1停止位。上位机配置成8N1虽然有时候也能握手成功但后续数据帧的奇偶校验对不上会导致随机失败。我一开始就是被这个坑的改完串口参数之后立刻稳定。坑二地址和长度必须遵守Flash对齐规则。发送Write Memory命令时起始地址要8字节对齐数据长度必须是8的整数倍且最大不超过256字节。如果bin文件最后一段不满8字节要补0xFF填充不能直接把剩余几个字节发出去。坑三擦除命令有两条。AN3155定义了Erase0x43和Extended Erase0x44两条命令。老版本Erase命令一次只能擦除有限页数Extended Erase支持更灵活的范围。L0的Bootloader对这两条命令的支持程度不完全一样建议上位机优先用Extended Erase兼容性更好。坑四注意擦除和编程的时间。Bootloader在擦除或写入Flash时是不会响应新的同步字节的。上位机要么在命令之间插入足够延时要么等待ACK超时后重新发同步字节。我实测STM32L071KZ擦除一页大约需要几十毫秒全片擦除要几秒上位机超时时间至少给10秒否则整个流程走一半就报超时。坑五收到ACK不代表数据写完了。Bootloader是先接收数据再执行Flash编程编程完成后才返回结果。所以要在发送完整个数据帧之后等待一个额外的响应作为“写完成”信号。如果上位机只发了数据不管结果下一帧就可能因为Flash还在忙而失败。5. 常见问题速查表与避坑清单5.1 故障速查表基于最近这次实战我整理了下面这张速查表遇到问题直接对号入座现象可能原因处理方案Bootloader连接失败BOOT0没拉高、串口线接反、奇偶校验不对、握手窗口错过量BOOT0电平交叉检查TXD/RXD串口改8E1先点Connect再手动复位能连接但读Flash全0xFFRDP读保护、PCROP保护、地址越界读选项字节解除保护或修正地址范围擦除失败报cannot access memoryWRP写保护、供电电压过低、Flash控制器忙取消WRP检查VDD断电重启后重试写入校验失败地址未8字节对齐、WRP保护、校验算法不一致按8字节对齐重写取消保护用XOR校验烧录成功但程序不跑BOOT0仍为高、nBOOT_SEL配置不对、应用固件时钟配置恢复BOOT0低电平检查选项字节启动源检查外部晶振启动配置刷写过程中随机失败供电不足、串口线过长、电磁干扰独立供电缩短串口线降波特率到9600Keil里报flash download failed - cortex-m3芯片型号选错、Flash算法缺失、RDP保护检查Keil Device选择添加STM32L0xx Flash算法解除RDP5.2 避坑清单以下几项是我这轮调试下来觉得最值得提醒同行的经验不要把BOOT0直接接3.3V中间串个1k电阻。这样方便用万用表量电平也方便飞线。直连的话万一接线短路MCU的启动引脚容易受损。BOOT0和NRST引出来做成两个测试点。哪怕板子空间再紧张也值得。有了这两个测试点后期刷写、救砖、调试都方便。量产板还可以做成双排针配合夹具一键烧录。烧录前先把Flash读一遍。如果板子上有旧固件先读出来备份再擦除。不要一上来就Erase不然旧程序丢了后悔都来不及。不要在擦写进行中断电。Flash擦写中途断电轻则数据损坏重则选项字节错乱导致芯片锁死。量产时如果供电不稳建议加电容或者用带过流保护的烧录器。检查选项字节的nBOOT_SEL。如果在Bootloader场景下BOOT0拉高没反应第一反应就是看这个位而不是反复换串口工具。尽量用独立电源给板子供电不要从USB转TTL模块取电。USB转TTL模块的输出电流通常只有几十毫安到一百毫安Flash擦写瞬间的电流尖峰容易把电压拉掉。5.3 IDE直接下载失败和Bootloader问题要分开排查热搜词里有一个“error: flash download failed - cortex-m3”这个错误很多人都遇到过但它和ST系统的Bootloader本质上没有关系属于两条排查线路。如果是在Keil MDK或者IAR里用ST-Link/J-Link直接下载时遇到这个错误常见原因是Keil里Device型号没选对比如把L071KZ选成了L073或者别的L0型号Flash算法加载错误。Flash Download页面的Programming Algorithm列表里没有添加对应的L0 Flash算法。芯片被RDP Level 1保护调试口被禁用调试器无法访问Flash。这类问题不要往Bootloader上靠直接查IDE配置和解保护就行。我有一块板子就是之前测试时设置了RDP Level 1后来在Keil里下载报这个错用CubeProgrammer连ST-Link解掉RDP之后恢复。6. 写在最后这次折腾给我留下的三个习惯折腾了一整晚之后我给自己定了几条规矩现在做L0系列项目基本不再被Flash刷写问题卡住。第一拿到新板子先不做功能调试先把选项字节完整读出来保存一份。这样后面无论出了什么问题都能对照最初的选项字节配置快速判断是软件改了它还是硬件异常改了它。第二所有涉及Bootloader刷写的测试统一用CubeProgrammer的日志功能。每次操作都导出完整日志里面会记录错误码和寄存器状态比肉眼盯屏幕靠谱得多。第三量产工装里保留一个“仅解锁”模式专门用于处理返修板。返修板进线后先解保护、全片擦除、再重新烧录避免因为RDP保护导致整块板报废。最后分享一个小技巧如果板子上BOOT0引脚实在没法操作可以试试用I2C或者SPI接口进Bootloader。只要这几个引脚留出来了就能绕过USART引脚占用的限制依然完成刷写。L0的Bootloader支持多接口这也是它的一个隐藏优势。希望这次的踩坑记录能帮大家少走几个弯路。
返回列表