
对于 STM32 用户,经常会涉及到通过用户启动程序实现对用户应用程序的更新升级。一般来讲,用户启动程序主要用来跟外界通信,获取新的用户程序代码并实现对用户代码区的应用程序升级。用户应用程序是指实现各种用户功能的代码。& `7 e! A& q- d3 v& b$ j+ l 在这个过程中,往往需要做从用户引导程序区(以下简称【BOOT 区】)到用户应用程序区(以下简称【APP 区】)的跳转,有时可能还需实现从用户应用程序区跳回到用户启动程序区,甚至不同用户程序区的互相跳转等操作。在这些跳转过程中,常常有人的开发工作在此遇到阻碍,甚至破费周折。 STM32F0 代码区跳转6 ~3 v8 f" ]6 h: v5 ]" R P! I8 w8 u 在此我们以 STM32F0 为例,就芯片内不同程序区的跳转问题做些交流与介绍,限于篇幅,这里仅直接介绍具体操作和注意事项,不做过多拓展介绍。相关知识点可阅读 STM32 芯片参考手册、STM32 相关内核编程手册。 : o& K# _$ Z0 |: l0 z 下面介绍中提及的集成编译环境是指 ARM MDK,硬件基于 STM32F072RB Nucleo 开发板。后面我将逐一介绍不同跳转操作的基本流程和注意事项,涉及以下三种情况:& E, ~3 ~! Q& i9 \( Z5 ` 从【BOOT 区】跳转到【APP 区】 从【APP 区】跳转到另外新【APP 区】5 n# P, C/ k, L3 h3 h+ P+ g+ k 从【APP 区】跳回【BOOT 区】 一般来讲,不同区段的执行代码我们通过建立不同的工程项目来实现,最终将不同区段执行代码写入芯片。这里假定 BOOT 区对应的内部 FLASH 地址段为 0x8000000—0x8004000,APP1 区对应的内部 FLASH 地址段为 0x8004000—0x8008000, APP2 区对应的内部 FLASH 地址段为 0x8008000—0x800C000.! U t" J5 V1 n& t6 o. r! c7 c' W4 i ![]() % T \7 ]% p& C2 G0 \ 从【BOOT 区】跳转到【APP 区】 7 G# O2 F: q' h9 G) Y% z- y" l" n3 P 先说从 BOOT 区跳转到 APP 区。这个跳转代码比较简洁、简单,注意跳转前要关闭刚才程序区开启过的所有中断使能,保证所有中断请求位都被清除,不是简单的关闭总中断,否则往往隐患多多。BOOT 区相关跳转代码如下:6 P1 o8 c: Q. i' m2 S% {0 ` n& D
这个从 BOOT 区到 APP 区的跳转最终能否成功,关键还是取决于 APP 区代码相关配置及准备工作。假设这里的 APP 区是上面提到的 APP1 区,内部 FLASH 地址段为 0x8004000—0x8008000,那么在 MDK 的 option 项里的 memory 配置板块要做正确配置,即 flash 空间与 ram 空间的配置,如下图所示: ( d/ h" Z- k7 j ![]() IROM1 的配置就是 APP1 代码摆放的起始空间地址及长度。IRAM 的配置要注意先保留 48个字的空间用来存放中断矢量表的内容。因为 stm32F0 芯片的中断矢量表的大小就是 48个字(即 0xc0 字节)。至于剩下的内部 RAM 空间大小由芯片本身的 RAM 容量决定(这里是基于 STM32F072RB 芯片,其内部 RAM 总容量为 0x40000)。 ' ^3 N0 U2 h' G2 {# c 另外一件很重要的事情就是做中断矢量表的拷贝。在 APP1 区的 main()程序开头部分,将放在 flash 程序空间起始部分的连续 48 个中断矢量地址表拷贝到内部 RAM 的起始地址段。即将矢量表从 0x8004000 地址开始拷到 0x20000000 开始的连续 48 个字空间(前面提到的存储配置正是为了配合这个拷贝操作)。基于 MDK 环境的参考代码如下: L6 m. L) w3 @0 m. G" d* g
. A% y* s3 H# ]% o; J 。。。。。。 后面为用户功能代码。。。。。。 上面代码中绿色语句就是实现中断矢量表从内部 flash 到内部 RAM 的拷贝,而红色语句则是为了实现将程序执行的 0 地址域的重映射,即将程序运行的 0 地址从内部 flash 的0x8000000 通过重映射机制切换到 0x20000000,为的是在 APP1 区发生中断时 CPU 能从正确的地方准确获取相应中断矢量地址去执行中断服务程序。 到此,从 BOOT 区跳转到 APP1 区就算完成了。 从【APP 区】调转到新【APP 区】 * C! _8 f. M5 O8 a' C 那么,如果想从 APP1 区跳转到另外 APP2 代码区呢?这个跟从 BOOT 区跳转到 APP1 区类似。在 APP1 区的跳转代码这里就不说了,地址给对、代码写对就好。APP2 区的代码也同样必须做中断矢量表的拷贝和 0 地址域的重映射。但因为在 APP1 代码里已经做过了 0 地址的重映射,所以就不必重复做了。 3 x+ B- `3 T+ ~& H" r 7 {* q w4 j9 P* F) e7 o: e 假定 APP2 代码区的内部 flash 空间安排在为 0x8008000—0x800C000。则 MDK 里 memory布局配置如下:: N8 F0 J" o, p+ L i2 z ![]() 0 l8 u8 i* v! ^/ E3 T- l# O* { 从【APP 区】跳转到【BOOT 区】5 c& Q' w% k5 k 5 P+ V! n/ k5 X. [' R% S 有时我们还希望或需要程序能从 APP 区跳回用户 BOOT 区,那如何操作呢?对于 STM32F0芯片而言,程序执行区从 APP 区跳回 BOOT 区跟从 BOOT 区跳到 APP 区还不太一样,经常有人在这个跳转过程中卡壳,对于跳得出而跳不回感到难以理解。 假设从 APP2 区跳回 BOOT 区,在 APP2 区做跳转准备时除了给定正确的跳转地址外,另一个要做的就是将之前通过重映射将 0 地址程序空间从内部 SRAM 切换回内部 flash 区。实际应用中,我们往往因为忽视了这点,跳回去后一碰到中断就问题来了。另外,从 APP8 K( S& B4 @/ ?6 V" [4 r4 y& V9 R 区跳回 BOOT 区无须矢量表的拷贝操作。所以在 APP2 区执行跳转前需将 0 地址重映射回内部 flash 空间,通过运行如下库代码完成: __HAL_SYSCFG_REMAPMEMORY_FLASH(); . f' C- _( M M4 B 几点重要经验总结0 E" l+ Z8 [9 [4 s+ i 通过以上几种不同情况下操作过程的描述,我们可以知道,想要避免 STM32F0 在代码调转中出错,应该遵循以下几条关键的经验:1 x& {6 m- b& C& i+ _9 Q$ o7 s 从【BOOT 区】跳转到【APP 区】,在【APP 区】要做中断矢量表的拷贝和将 0 地址从内部 flash 切换到内部 SRAM 起始地址。 6 z# i0 A9 p# @ 从【APP 区】跳转到其它新的【APP 区】,只需在新的【APP 区】的代码里再做中断矢量表的拷贝,并保证相关存储配置的正确。 从【APP 区】跳回【BOOT 区】,该过程无矢量表的拷贝,只需将 0 地址执行域重新映射回内部flash 区。 不论从什么区跳往什么区,跳转前禁用当前用户打开过的所有中断使能、确保无未处理的中断请求存在或在跳转过程中发生中断。* B0 v# \1 F, K# e E 以上操作流程主要针对基于 COMTEX M0 内核的 STM32F0 系列芯片。7 b6 H8 o* N- o7 C+ n9 O0 c + |9 O9 P2 p# @5 i6 i3 K * S3 j; k9 I( b8 U/ R( Q 8 \5 i2 C1 D5 f" b7 _ 2 P" p1 u$ H! _ |