
STM32系列的芯片里都有个特别的存储区---FLASH选项字区域,这里简单聊下该话题并顺便给出基于STM32F0芯片的代码实现及几点提醒。 . E9 O( A/ C% Q$ c f) B' @ a2 D# i/ U. C , N* y" d2 e* A; k# D9 _ 这块特定的FLASH存储区域,通常用来存放有关芯片内部FLASH读保护、写保护、看门狗使能方式、芯片启动、RAM校验、电源监控等配置信息。具体内容以及格式因不同的STM32系列可能有差异。 ) Z! t* o3 b4 d; w" @' j , b4 O/ K# @/ M1 J2 r3 }/ | 一般来讲,各个选项字由选项字节组成,各选项字节由原始字节项和互补字节项组成。如下图存储方式,绿色栏为原始选项字节,黄色栏代码互补选项字节。不同系列可能有差异。【下文中截图如无特别说明,均是来自STM32F0芯片的参考手册。】* \8 C1 _$ ^, t1 ]+ d! v: { 0 E" G, b. w0 n- y1 g 0 [ k1 t4 f& S' `' J ![]() 0 n6 I4 [$ r3 p/ b) V ; `9 a6 u3 {& L! G9 } FLASH选项字一般安排在某固定地址起始的一块连续的地址空间。下图就是STM32FO芯片内部FLASH选项字的地址及内容结构安排。这里包括读保护选项字节、用户选项字节、扇区写保护选项字节。6 z" D# l d3 ^5 B! C* h- Z) s 1 M( a! Z$ m* W6 n6 H # G, X* z1 g" I% o2 b ![]() 9 z; L1 e" a2 ]" R V3 l& l! |% f 对于出厂的芯片,FLASH选项字往往具有初始出厂值。下图就是STM32F0芯片出厂时FLASH选项字的初始值。 ![]() 3 p0 {9 B) t# l 但在我们的实际应用中往往需要结合实际应用情况,对FLASH选项字进行重新配置。而对FLASH选项字进行重新配置一般有两种方式:3 y- \) v6 T3 E* x$ f/ |* Q ?5 A) w- \# F! ?# j" ^ 第一种,通过编程烧录工具进行选项字的配置并写入。 0 y i+ {: l) p0 z9 V; [2 W9 K , q5 Z& i+ V7 } 比方使用STLinkUtiliy或STM32CubeProgrammer或其它类似编程工具来实现。这种方式相对较为简单,一般对操作人员往往有些要求。尤其在有意或无意弄错配置选项的情况下,没法快速发现并及时纠正。- D7 C& a/ \4 T. Z1 B$ Z3 z) I" z : R( a: v4 C4 z& C4 F. p ![]() ![]() , j3 y# p6 D, V1 ^5 I 另外一种方式就是用户在应用代码里根据应用需求做FLASH选项字的配置编程。 0 P# g% p8 s- Q6 s S9 [5 v 6 D3 z5 J. z, M7 q0 j. l$ | 这种方式,对开发人员来讲会增加了一些工作量。但烧录时只管烧录FLASH执行代码即可,关于芯片配置方面的信息在代码里自行完成。即使烧录时出现选项字的误操作,用户程序代码也可以将其自动纠正过来。 4 ]1 I' m: O1 K ! k. v. }- S' T7 i" ]. o 不论使用哪种方式修改FLASH选项字的配置,要想新的选项字信息真正起作用,还有个对FLASH选项字信息进行加载的环节,即将FLASH选项字的配置信息加载到选项字寄存器,从而作用于芯片的相关功能,也就是上面提到过的读保护、写保护、看门狗使能、电源控制、启动选择等功能信息。一般来说,将FLASH选项字信息加载到选择字寄存器需要借助系统复位或上电复位来完成。【当在调试状态下修改FLASH选项字时,它的加载需借助上电复位】 1 b& }( S' r4 P# {1 N* M ![]() 4 A# b- x9 I. z2 f 在进行FLASH选项字加载之前,芯片硬件会先对选项字信息进行基本地判断与确认,即将各个原始选项字节与互补选项字节的进行匹配比对。如果比对失败将产生出错事件,并将比对失败的选项字节【原始字节和互补字节】强制修改为0xFF或其它指定值,视不同的STM32系列而定。 # n6 Q! V# f& W 比方,下面就是STM32L4系列做选项字加载前发生原始项与互补项信息比对不匹配时的处理原则:9 S3 Y: c! A7 {# Q: C2 l' r4 v) [ ![]() 7 i; u. i0 N7 H8 J& f' b 关于FLASH选项字信息地编程修改,这里特别提醒几点: 7 {/ ^7 T6 ^! w 4 v }1 k- v5 l- h( E. a4 [! s$ Q1 U) d 第1点,当准备好要修改的选项字信息,在对FLASH选项字进行编程修改之前,需先对FLASH选项字区域进行擦除。发出擦除指令即可。擦除完毕之后再将新的选项字信息写入选项字区域。 5 i' _0 Y) ^0 _. t2 |6 e ![]() ![]() 那么为了保障选项字正确有序的编程写人,在做FLASH选项字的编程时,一定要保持电源的稳定。【比方说,代码里稍作延时等上电稳定后再操作】如果正在做FLASH选项字编程时发生电源电压剧烈波动或重启,很可能发生FLASH选项字区域被擦除了但又没有正确写入新的选项字信息的情况。而且,前面也提到了,如果选项字因为没有被完整地正确写入,在做FLASH选项字加载前,还会由于原始项与互补项做信息比对失败而被强行修改为OxFF或其它指定值。 q7 f3 l$ s! j3 l ; w G) _( m: y/ q& }# M - j: P0 E$ t" r! P6 d8 E' o g 下面两幅截图就是使用烧录工具软件【STM32CubeProgrammer】,在做flash选项字编程时通过模拟芯片被强行断电所发生的情形。 0 ]6 u# X; A; P: m. Q: } - G" g4 D3 i% M% V) [9 c8 Z 本来,开始做FLASH选项字编程时,RDP保护配置都是选择的LEVEL_0。在选项字编程时芯片被中途断电重新后连接所看到结果却是芯片被读保护了,其它配置字也被擦除了。从截图中可以看到,此时RDP=0xFF,既不是0xAA也不是0xCC,那么芯片的读保护等级就相应地变成了LEVEL_1读保护状态了。有人碰到此情形时往往感到纳闷不解,明明自己没有修改RDP的配置怎么RDP的保护等级变了呢?就是因为中途断电,选项字区域刚被擦除又还没来得及完整、正常写入。4 S# f- }4 h9 i# } ( A& P+ r$ {; v) ~5 P% O) E " b7 e( P5 B. N% r: D* {+ ?- C4 n ![]() ; |8 [4 J4 d- D% p- Y! N ![]() 当然,做FLASH选项字编程时除了要求电源稳定外,编程时序也需遵照手册介绍的来操作,以保证选项字编程的正确性。9 g/ o4 F( K0 I4 I$ @; h8 l * v4 q3 q7 t h% P! [) c + }$ `8 c2 o M8 g7 a, G1 Q0 w/ M# m2 ` 第2点,对于某些双BANK的STM32芯片,比如STM32L4,STM32F42X等系列,在做选项字编程调整时,也是先将两个BANK的用户选项页的内容进行擦除,然后依据用户准备好的选项寄存器的内容对所有选项字内容重新编程更新。5 q6 o/ g; K1 s/ t ![]() / ? s& M- |0 D4 o ) h& R! U5 D% a: L. R2 M 这里要注意的是,我们不能只是单独地针对双BANK其中的某一个BANK进行用户选项字调整,而是需要将BANK1和BANK2的配置信息都准备好并写入相应的选项字寄存器,从而实现对两个BANK的选项字信息的编程修改。 * x: \7 J% I( Q 8 u" O1 Z: C6 ?. Q- Z1 a 第3点,在做选项字编程过程中,如果修改读保护选项时,一定要清楚-----如果是从LEVEL_1调整为LEVEL_0将会发生整个芯片内部的FLASH内容被全部擦除的情况。另外,很多STM32系列支持LEVEL_2读保护,该保护等级具有不可逆性。如果选择该等级,除非你自己在芯片内部准备好了升级引导代码,否则你是不能再对片内代码内容做任何更新,更不可能基于该芯片再做调试了。 + i r7 K( M: C7 L- c& N " ]2 H; ]6 G l0 e) _ 好,关于STM32芯片的选项字的编程提醒就聊到这里。感觉上内容应该不多,但由于STM32家族拥有众多系列,同时各个系列间在选项字这部分内容又或多或少存在着差异,让内容突然庞杂了很多。这里只能抛砖引玉似地给些提醒,具体应用时请参考各个STM32系列的参考手册及编程手册。 # m3 I$ ~/ r9 @# K 下面就以STM32F072芯片为核心的Nucleo板,给出一个实现用户选项字编程的示例。) }2 X4 u& i- o/ P, R( q Q3 M. Y/ G7 t' R, Y! j. T 先看看STM32F072片内选项字区域的基本内容框架,如下图:7 i& w% H9 m% \0 A" q: L O0 ]- T) \2 U _- h0 N+ C( c# ~ - U* m* _9 {* B; n5 T/ p ![]() 6 ~0 @4 v: Q: q9 E" \4 B B 一、选项字内容的规划与准备: * J. ]" z6 v7 t& ]! ~/ B 这里我将RDP选项字节配置为0xbb,即LEVEL_1,则其互补选项字节内容就是0x44;将USER选项字节下面红色方框内的三个选项置1,另外两个保留位置1,那么USER选项字节的内容就是OxEA,它的互补字节就是0x15.4 l7 Z) i6 ?( @ + |+ |4 G5 G4 k' K6 X ( v: L( x7 v) L+ B ![]() : b5 E$ p* G, o' I2 z" K 选项域中的Data0字节配置为0x99,则其互补字节nData0则为0x66; 选项域中的Data1字节配置为0x88,则其互补字节nData0则为0x77; ; l7 X( i0 b8 k& d/ q! G) k) N5 I 按照上面规划准备好数据,如下图所示。各选项数据按原项和互补项组成半字写入。 6 ~6 T ]# }7 J7 H/ P" G3 z6 ? 【顺便说下,这里没有对扇区写保护做配置。当然要做也是完全可以的】2 a" @ ~/ p2 G Q& m W- R( q2 A * \7 W; { I m( U; ` ![]() & V- d r" V0 E- j7 K$ f H: ~ 二、编写相应程序代码,编译后下载到芯片: 0 p0 u, b$ r: o2 g% o% X ![]() 6 S6 C* L6 v$ x6 J' J 相关代码不长,较为简单。按照手册描述的来编写。我将上述代码分成6部分。" B$ H# g* u: v- m1 D 第1部分,做有关选项字节内容的判断,是否均为预期的选项配置内容。& A* U* j) [9 }6 Y& W8 L! s & a, b |9 O, z& ^9 d0 ?; F 3 j8 I8 v% i/ v 第2部分,对FLASH、OPTION编程做开锁操作。 P# r/ N4 `5 C4 l7 E# { : G$ ]6 v$ i' C# p& @) N! N 第3部分,对选项域进行擦除,然后退出擦除状态。 第4部分,对欲修改的选项字节进行编程修改。' A+ a0 {: _" ?( C; X9 X% D, r) _ 7 u9 i% U5 |3 J5 W* t9 w 第5部分,完成选项字的修改与编程后,上锁并退出。, D, G# E' o8 h0 _4 n B: {9 S' r5 Y$ v( @% L* \3 j" q G% L( Q; |( Y y- m k 第6部分,触发系统复位,将新的选择域内容加载到选项控制寄存器。: }4 y3 L( l4 s. f; x# j! z, F 三、运行程序,验证结果7 K9 c/ q$ B- g8 [& H- d 运行用户程序实现选项域的编程修改操作。可连接到STLINK Utlity之类的工具查看选项配置结果,看看是否跟预期规划的一致。 $ L( i3 s: Q5 _8 z$ Q$ K ![]() + Q) J, B! F+ ~7 n. k, Y( K5 x+ s. | : V# {+ @( S' _1 J 上图是通过STLINK Utility工具查看到的STM32F072芯片运行用户程序后的选项域的结果。不难看出,跟我们预期的结果一样【打勾的选项表示置1的选项】。我们还可以基于STLINK_Utility工具随意修改RDP选项字以外的其它选项内容,通过它做选项字的修改编程。然后再次运行用户程序,可以发现各个选项字内容又会回归到用户预设的配置内容。# |6 ?3 b5 y$ _2 d) l" e # ~+ i" \5 n, S( p8 y- _3 L6 a7 j |