
前言 看过另一篇实战经验《STM32F091 空片使用 System Bootloader 下载代码》之后,就会知道 STM32F091 有一项特殊的功能,就是在空片的情况下既可以使用 ST-Link 等编程工具进行编程,也可以使用 System Memory 中的 Bootloader 进行下载代码。这完善了整个编程体系,给用户在编程方案的选择上带来很大的方便。但是,在某些特定应用上,好心也可能会干坏事哦。下面就来聊聊这个事是怎么来的,并且找出相应的对策。 问题6 h) C$ u1 q3 s- s9 f# y8 q 某客户在其产品的设计中,使用了 STM32F091RCT6。客户使用 ST-Link 对 STM32F091RCT6 进行编程,发现对空片进行编程之后,必须要重新上电才能运行用户代码;但是如果不是空片,则编程后就可以直接运行用户代码。由于客户的测试系统是直接烧写完芯片后在不断电的情况直接进入测试模式,如果空片烧写需要断电的话,带来一定的麻烦。客户希望搞明白这件事,并希望找到办法,能在空片编程后也可以直接运行用户代码。6 ?8 s# J5 j9 Z; b% s ( p1 X* G8 k- n) P$ D 调研 1.还原问题6 o) t; D6 ^5 Q3 m. }/ W 在这里,使用带有 STM32F091RCT6 的 NUCLEO-F091RC 板来进行问题还原,将此 Nucleo 板通过 USB 线连接到电脑。打开 STM32 ST-LINK Utility,点击“Connect to the target”按钮进行芯片连接,连接后打开一个准备好的 LED 灯闪烁的.hex 文件代码,点击“Program verify”按钮准备进行编程。2 b, H3 m! t& O* |4 g6 ~3 [3 }% Y' @ ![]() ' D/ d+ T/ ^8 e# W! r 在这里,我们勾选了“Reset after programming”,目的在于编程后对芯片进行复位,可以运行用户代码。然后点击“Start”按钮开始进行编程。 编程之后,按道理可以看到 LED 灯闪烁的,但是并没有出现。需要给 MCU 进行断电后,重新上电才能看到 LED 灯闪烁。也就是说需要一次上电复位才能运行用户代码。 ' e y/ {) R3 l/ v4 |" y0 v " Z. @" s. _( ]* W+ y! m3 t+ a 2.分析问题 先来回顾一下 STM32F091 的参考手册 RM0091 对于 Empty Check 的描述:# p, u6 ~, K) ^4 i+ P. E ( g+ j3 a/ i1 J2 B ![]() 1 t9 ~2 H- r( z 首先,芯片内部存在一个查空标志,用来标志芯片是否为空片。这个标志位在 BOOT0 脚被定义到从 Main Flash memory 启动的时候使用。当这个标志位被置“1”的时候,此芯片被认为是空的,系统将从 System memory 中启动 Bootloader,以允许用户进行代码下载,即使现在 BOOT0 脚定义的是从 Main Flash memory 启动。此标志位只在载入 Option bytes 时更新:当地址 0x0800 0000 读出的内容为 0xFFFF FFFF 时,此标志位置“1”,否则为“0”。这意味着当烧写完一个空片后需要在系统复位后执行用户代码的话,是必须要重新上电以产生或者在 FLASH_CR 寄存器中置位 OBL_LAUNCH 来启动 Option byte loader reset,以清除此查空标志。 现在就可以来分析目前所遇到的情况了:; I$ ~- }/ q- e; i d; X 当空片通过 SWD 连接到 ST-Link 进行烧写的情况下,由于上电时空片检测检测到此芯片为空片,查空标志被置位,所以系统此时从 System memory 中启动 Bootloader 开始运行。通过简单的 SWD 接口对芯片进行编程,勾选的“Reset after programming”将在编程结束后在 RESET 引脚上产生一个复位信号,但是不幸的是这个复位并不能清除查空标志,导致复位后仍然从 System memory 中启动 Bootloader,而没有运行用户代码,也就是我们之前遇到的现象。 8 u" H8 F! Q. v* g) q2 \ k 一般情况下,我们都可以通过重新上电来产生 POR 以清除查空标志,从 Main Flash memory 启动运行用户代码。但是,客户目前的这种特殊需求就会带来一定的麻烦。还有一种应用也会比较麻烦,也就是使用锂电池的产品,而且这个电池直接焊接到用户板上,无法方便地进行断电上电。此时,若是空片是焊接在板子上进行在线编程,那么,问题来了。空片编程之后,由于不方便进行断电,而无法完成 POR 的动作,不能运行用户代码也就无法实现一个 Option byte loader reset。查空标志无法清除,程序运行将锁死在 System memory 的 Bootloader。. J) D. V" W: e! d. R# p" i $ f. D5 S* `/ o, B& H% N % O# V |+ W2 O* n/ z2 H 3.问题解决3 X' `, n0 E; Y; Q( F' v3 b 这种问题呢,解决方法当然有很多种,下面来大概地探讨一下: 1) 从生产上来解决:芯片在编程器上进行单独编程,之后再上板子,避开空片烧写后没有 POR。 2) 从硬件上来解决:使用一个跳线,或者使用其他方式,比如在夹具上想办法,以达到通过人工的断电再连通上电,实现一个 POR。需要在 PCB 板上预留。大家可自行选择对策。但是这会增加生产上的麻烦,降低效率。. M$ l6 l" K4 U5 ?0 z d 3) 从编程方法来解决:不使用 ST-Link 进行编程,直接使用 Bootloader 进行串口升级,升级后跳转到 Main Flash memory 去运行用户代码。需要在用户代码中加入将 Main Flash memory 映射到 0x0000 0000 的代码。! U) q) f& b- D- u 4) 前面几种方式大家一看就明白如果去解决了。但是,如果一定要使用 ST-Link 通过 SWD 进行烧写的话,就另当别论, 我们下面来探讨这种方式。 一般看到这种问题,直观思维就是思考是否有办法,可以在 ST-LINK 烧写后通过一定的 ST-LINK 命令跳转到用户代码去运行用户代码。方法看起来可行,但是有点复杂。第一,STM32 ST-LINK Utility 没有提供类似的功能,需要用户自行使用 ST-LINK_CLI 命令;第二,需要在用户代码中加入别忘了将 Main Flash memory 映射到 0x0000 0000 的代码;第三,由于查空标志未清除,需担心意外的复位信号或干扰,导致复位后又跑回 System Memory,还需要在用户代码中加入“每次运行都判断是否为 Option Bytes Loader reset,如果不是,就直接执行一次 Option Bytes Loader reset 以清除查空标志”。: @1 w! n6 { }2 {/ p' P 我们的直观思维都是出现问题解决问题,但是看了上面的描述,这样的解决办法还真有点麻烦。那有没有什么其他简单的办法呢?答案是有的,我们不要把思维停留在出现问题解决问题上,而是如何去避免产生问题。下面来理一理思路:7 f$ o9 V; B4 J* p2 M 这个问题的根源在于查空标志的存在,所以需要思考的是怎么避免查空标志的影响?7 o! _9 `. p; ?! r/ l; Q8 P 来看一下查空标志产生的条件:: _6 X5 I+ _! ?& K8 n) U+ f" h a) 使用了 BOOT0 引脚$ Z6 U) j. t4 @. k% u b) BOOT0 引脚为低电平,启动区域指向 Main Flash memory% V3 \! |7 t( r4 D7 [/ F1 | c) 读取 0x0800 0000 地址的值为 0xFFFF FFFF 由于是空片编程,所以第三种条件是肯定是成立的;由于硬件设计,BOOT0 引脚的电平也不方便改来改去;所以需要把关注点放在第一个条件上——“使用了 BOOT0 引脚”。由于 STM32F091 的特性,刚好有机会可以不使用BOOT0 引脚,而是直接使用选项字节,所以解决的办法有了。 步骤如下:; f+ \ N0 X* t. X5 M% p6 k i. 打开 STM32 ST-LINK Utility,点击“Connect to the target”按钮进行连接; ii. 从菜单“Target → Option Bytes”调出选项字节对话框 M$ S; H9 l/ d' i$ { ![]() + K. P* H1 ]3 b; |# v& o+ r w+ d& Y$ y 将“nBoot0_SW_Cfg”的打勾去掉,点击“Apply”,改成使用选项字节中的 nBoot0 和 nBoot1 来控制启动区域 iii. 再打开需要烧写的代码文件,点击“Program Verify”按钮,对话框中勾选“Reset after programming”,点击“Start”完成烧写动作就可以了。 ![]() 这样就可以看到用户代码已经在运行了,是不是很简单。 如果,希望更简单的完成,可以使用 ST-LINK_CLI,写一个批处理文件,包含以下动作:9 {! T2 Y6 ]0 r6 c! G+ ~
结论5 l- z( y! @( `3 d# f' ] 由于查空检测机制,导致 STM32F091 空片在使用 ST-LINK 编程后,不断电的情况下复位将回到 System Memory,无法进入Main Flash memory 去运行用户代码。所以,在特殊应用中,如果无法进行断电再上电,需要使用办法对这种机制进行破坏。 " N ?1 K5 N0 V5 `1 R5 r- R( h, s 处理4 W/ k# u5 r' }) y# R( |$ h; m' @ 将 Boot 启动配置为用选项字节进行控制,而不是使用 Boot0 引脚,以此来破坏查空机制的影响。 建议 对于问题的解决,一般从两个方向进行思考:一是出现了问题再来找解决问题的办法;二是如何避免出现问题。很多时候,由于思维惯性,很多工程师可能会更喜欢直接从第一种方向去思考问题;然而,事实上,如果能从第二种方向思考,阻止问题的产生,那才是最好的办法。 |