70.1 初学者重要提示
9 k3 U- l; J+ Z s' u& N 本章2.5小节里面的Flash三级读保护是重点,务必要掌握明白。) L- `0 ~2 _7 N, U
STM32H743XI有两个独立的BANK,一个BANK的编程和擦除操作对另一个BANK没有任何影响。但是如果用户应用程序和要擦写的Flash扇区在同一个BANK,在执行擦写操作时,用户应用程序将停止运行,包括中断服务程序。
0 |5 O O& O' f- g STM32H7的两个Flash BANK是256bit带宽,CPU访问是采用的两个64bit AXI总线。
! H2 f6 n; o; r% g7 U* k7 q8 J HAL库的内部Flash编程函数HAL_FLASH_Program固定编写32字节数据。2 n9 v8 d) I8 ~" l, x. K
70.2 内部Flash基础知识
5 Q! d9 T- U5 F- q' h: |70.2.1 内部Flash的硬件框图
. y) \, ]# s0 q认识一个外设,最好的方式就是看它的框图,方便我们快速的了解内部Flash的基本功能,然后再看手册了解细节。) x% O1 b8 j7 X$ g/ B/ A' ]
9 G9 m) y* h G; |9 G! t+ A; ]' T7 s4 E. w" K
' x, r' x( }2 Y) I: S6 K
通过这个框图,我们可以得到如下信息:
& c* C* T& X3 T1 U; n7 a! w1 O" F+ X3 U/ l
sys_ck时钟输入
& \$ a5 o3 J) Z6 w8 q( ?8 uD1域总线时钟。& i4 D0 I. N; g' v2 v- R0 n- R$ k
4 [% O p" Z0 D po_rst输入( d+ U; R: g: u
Power on reset 上电复位。* n7 [5 e& T$ N7 F! W9 `: y
4 j* @% d* B/ O* R* Q d1_rst输入' U9 t' d. J! z; s* X- i( N( Y
D1域系统复位。
- |% B/ r1 e: e* R- ^. t$ V6 t6 m; W' E/ F7 J; x" z; _
flash_it输出
; X9 L, F1 o) {' b9 F$ z6 }4 fflash中断请求输出。
~3 ]$ [( u3 u& t
6 v3 q( \& L+ g4 T1 K+ QSTM32H7的两个Flash BANK是独立的,读写和擦除互补影响,256bit带宽,CPU访问是采用的两个64bit AXI总线。
8 Q; w2 F/ k6 W7 k4 a L4 c. J Q0 a/ W& y r
70.2.2 内部Flash框架
0 l9 N: j2 b6 `; K2 U# ]1 s关于内部Flash的框架,了解以下几个知识点即可:
9 u+ z2 `8 J9 W' M; h7 G! ]! k( P' n# Z8 @# I0 K; P$ F; ? g, \
256bit为单位,即32字节,并且每个单位配10bit的ECC校验位。正是这个原因要求大家对Flash进行编程时,必须以32字节为单位。+ s) L1 X6 n' `: {6 Q
两个独立的BANK,每个BANK有1MB容量。并且每个BANK的扇区大小固定为128KB,即8个扇区。
1 B+ j/ V4 S# K6 O( m" V4 B
" D$ r6 M& L3 O* [3 K- S4 O% x# J0 T$ D( V
7 U' Z* e( l+ l; U
BANK1的地址范围:0x0800 0000到0x080F FFFF。3 f; j# f7 M& T% ~$ l1 H* ~
: { ^' L3 r- y) hBANK2的地址范围:0x0810 0000到0x081F FFFF。' d- z+ q8 n1 x0 Y& d
& O! L0 F/ j7 t2 K! C70.2.3 内部Flash读操作0 M- _1 i& g2 n
STM32H7的内部Flash读操作跟内部RAM的读操作是一样的,支持64bit,32bit,16bt和8bit,使用比较简单。这里我们重点普及一个知识点,H7的内部Flash在不同主频下需要做的延迟参数:' Q" T8 c0 @# {; {! x1 N
5 ^( \2 M3 l9 j$ I5 v0 n1 i4 N$ O' N( |7 J; _) ^, ^1 c
4 K# ]( F5 g( x3 c对于上面的表格,大家可以看到,当延迟等待设置为0的时候,即无等待,单周期访问,速度可以做到70MHz。增加1个Flash周期后,访问速度可以做到140MHz。当增加到3个或4个Flash周期后,最高速度可以做到225MHz。- R' r- \+ ^& a- S* N @
0 p% H; B- M8 w了解了这个知识点后,再来看下面的时序,非常具有参考意义:# g3 K% }( Q1 o- Z* P
* ~. p9 p8 n$ v, C4 E注:ACLK、ARADDR、ARVALID、RDATA、RVALID 和RLAST是AXI总线信号。Flash读和Flash数据是 Flash 接口信号。; ?7 u. c4 c/ Z* \+ i7 t8 L
, K0 F6 m5 X0 e# ]
' u z5 s' y; P: r( @8 S# o" \6 [% m" u) h
关于这个时序要要认识到以下几点:* _0 @/ W* H- l5 B) l x
1 b) ~) P1 a2 J8 W* e AXI总线发起读取信号后,Flash端等待了3个时钟周期(注意延迟三个周期,支持的Flash速度),之后连续读取了4个64bit数据。
9 Z2 l, K5 j' Y: C 由于AXI总线是64bit的,所以1次读取就可以读出64bit数据,连续读取4次后,就是256bit,即Flash接口的一组数据,因为H7的Flash接口带宽是256bit的。
/ H" ]9 ]4 P) A* F 如果不开Flash Cache的情况下,连续读可以提升性能。
! ^7 P3 v* t/ @. z4 S4 O. ]
8 N; \8 Y. c; O! X% B# |! A4 S7 h下面是连续读取8个64bit数据的时序图,跟连续读取4个64bit数据基本是一样的,只是多读取了4组数据。
8 J2 R4 ~9 ^2 \- C
& n( c: _ } k" g# W4 u" `8 J9 N* c [* }8 f- {& W
5 P I9 }! u/ R/ Q7 H8 T9 ?6 j; H
70.2.4 内部Flash写入和擦除操作0 [* X. ^' t+ v* f+ g
最重要的知识点放在开头说:STM32H7内部Flash的写操作地址必须是32字节对齐(此地址对32求余数为0),写入的数据量也必须是32字节整数倍,不足32字节整数倍,补0也要是整数倍。
0 ^, p2 R' w, D% v
/ F! G1 e2 e- M- I0 z- M这里我们重点了解Flash的写入和擦除流程。Flash的写入扇区流程如下:
+ |/ {+ r* P' i8 @% I5 u$ {* M. f0 D7 ~0 B/ a
先保证这块扇区空间之前已经擦除过了。
1 v/ a) W" ^0 U( _解锁Flash,通过HAL库的函数HAL_FLASH_Unlock实现。
" o) ]3 x" Y- [$ o6 U* N/ N( P- M) I检查是否写保护,使能Flash可以编程,然后对其进行编程操作,编程完毕后,等待编程完成,然后禁止Flash编程位。具体操作可以通过HAL库的函数HAL_FLASH_Program实现。
2 n! }5 z. B' p+ J$ p! X5 B
- |/ n- E5 P) U9 o1 {) AFlash的擦除流程如下:
* n0 u J$ S$ \6 o9 I: ^解锁Flash,通过HAL库的函数HAL_FLASH_Unlock实现。
7 R. w0 T4 E- r3 J' u) G如果是BANK1或者BANK2需要擦除,调用函数FLASH_MassErase,然后等待擦除完成,完成之后关闭BANK1和BANK2的擦除请求位BER1/BER2
2 |' L# }; z) `2 i" T如果是扇区擦除,调用函数FLASH_Erase_Sector,然后等待擦除完成,完成之后关闭扇区的擦除请求位SER。. [9 u& @; i$ A. r/ K8 D" `
! [; p& f. x' y3 g9 @4 E" |( N4 }
w% Z( z4 Q0 l" B0 Z7 x+ p70.2.5 内部Flash读保护: e. g- }; p; Y' P& r! r/ D
内部Flash支持三级读保护RDP(read out protection)。
$ m2 n1 {$ c$ _7 y7 e8 g1 T6 `) @0 {, g6 {7 k
Level 0(无保护)! n- V2 S" k. ~, }' t( b8 Z
默认设置,所有读写和擦除操作都可以正常支持。. k- `' n+ {2 T( }
7 E& C1 z# F7 a2 V7 x
Level 1 (Flash连接保护) n2 S- a2 j. g- c
可以防止连接调试器时读取Flash内容,或者RAM中存有恶意获取代码,也是禁止的。因此只要调试器连接芯片,或者从内部RAM启动运行代码,都是禁止访问内部Flash的。$ V ?- L( n1 i# C
如果没有检测到从内部RAM启动和系统bootloader启动且没有连接调试器,对用户Flash的读写和擦除操作都是允许的,并且其它安全存储区也是可以访问的。否则是禁止访问的,一旦检测到对Flash的读请求,将产生总线错误。' k$ }( z3 M% h* c) Q8 `1 m) Z4 ?4 I
如果将Level 1切换到Level 0时,用户Flash区和安全区域将被擦除。6 y# O6 w1 Q; G$ [. |6 y4 Q, Z2 w
+ S3 M- C" _3 U/ n% B( d Level 2(设备保护和自举保护)
5 b7 b1 K+ G g( U3 A+ ~7 i 所有调试特性被关闭。4 }6 Y& q& M0 g1 ]
禁止从RAM启动。
& X0 G. O+ B! r4 ?- u 除了选项字节里面的SWAP位可以配置,其它位都无法更改。
. ]# O* |7 ? ]8 a" t 禁止了调试功能,且禁止了从RAM和系统bootloader启动,用户Flash区是可以执行读写和擦除操作的,访问其它安全存储区也是可以的。
Z* j% Z$ p) Z& U% \4 b# t0 G& D; ?" Z; t6 ^
特别注意:Level2修改是永久性的,一旦配置为Level2将不再支持被修改。
' g; x" U/ E8 M- J W- |# U2 T
( g- X; I% a% |8 e. t如果大家要设置读保护的话,使用HAL的API可以设置,也可以使用STM32CubeProg设置:# F% J) g) Q. R; h
# k' {; J3 r' ]1 E* H$ ^# ^9 ?! I4 f
4 p4 L% p# ~+ f3 w3 `& T3 U% W. G; J) G2 W) K, d& b" ^9 A( ]
70.2.6 内部Flash选项字节/ z8 y0 t8 ?! V; y) F/ O, g8 ~2 K: y
Flash选项字节主要用于boot地址设置,安全保护,Flash扇区保护等,涉及到的选项比较多。如果大家打算了解这一部分的话,使用STM32CubeProg进行设置即可,也比较方便。
9 j T) x! |8 a' V7 B1 v
2 s! o9 W: V2 t& U3 F4 I
) N% X' v8 ^8 r7 Q7 t" {
; y4 w0 p" W' o0 w70.2.7 内部Flash的ECC校验
" t# A" F( @8 ~ z这里先说下为什么内部Flash要带ECC校验,因为随着芯片的制造工艺水平越高,带电粒子产生的位翻转就越多,此时的ECC是必须要有的,一般可以纠正1-2个bit,安全等级高的Flash类存储器和RAM类都是必须要带ECC的。: O. Z! u) W& q/ c" d0 N1 O9 U
& ?; a1 P* |7 s6 T7 X对于STM32H7带的ECC校验,一般不需要用户去管理。! ~! ~; j9 d1 w w9 ~- e& B" `$ @. {
+ R; F6 ]7 @6 a
70.3 内部Flash的HAL库用法
" w/ q! V: Y: _0 `, `, k; M6 V70.3.1 内部Flash结构体FLASH_TypeDef- }2 [$ d6 `: l$ t& u# n7 \* |
内部Flash相关的寄存器是通过HAL库中的结构体FLASH_TypeDef定义的,在stm32h743xx.h中可以找到这个类型定义:' Q! u. ~; E% W4 D9 { D
& }9 S- I/ T- k3 B: \
- typedef struct, R: n% Z+ \6 ] I X8 n* z
- {8 X3 T/ n' X$ T, k1 i! w0 G
- __IO uint32_t ACR;
* P/ z# y+ [; J7 q l- v - __IO uint32_t KEYR1;
' P: x4 A6 B9 G1 R# d/ L - __IO uint32_t OPTKEYR;
1 @) |' a% m7 v2 k - __IO uint32_t CR1;
% ~- ?4 m! i+ ` - __IO uint32_t SR1;
' K* I9 }7 i. B' l3 l t6 r$ P* { - __IO uint32_t CCR1;
3 F% b! h+ P- r w: P6 {1 v8 n - __IO uint32_t OPTCR; ! O' X7 R, q$ q& V9 e
- __IO uint32_t OPTSR_CUR;
( @, j+ C1 H- N - __IO uint32_t OPTSR_PRG;
! C4 o( _$ W7 s - __IO uint32_t OPTCCR;
9 t5 ~: f' N6 s, {9 y2 L - __IO uint32_t PRAR_CUR1;
, J0 ]5 Q0 h8 m5 x- s# [" Y - __IO uint32_t PRAR_PRG1; & D% T g( t" a/ |. F
- __IO uint32_t SCAR_CUR1; + u E6 ^0 u/ \7 i
- __IO uint32_t SCAR_PRG1;
1 m5 ^) [8 C2 d3 R - __IO uint32_t WPSN_CUR1; 3 x' @( \1 r6 C9 e" d
- __IO uint32_t WPSN_PRG1;
; E8 O: J( E' x* c+ P - __IO uint32_t BOOT_CUR; 4 ` M/ d" p+ o& N2 R% O
- __IO uint32_t BOOT_PRG; ( D- S$ z, S2 F) g2 }: I/ j
- uint32_t RESERVED0[2]; 6 k; x$ H9 ~3 w' L( F6 N& m* T
- __IO uint32_t CRCCR1;
- g" G. Y; b+ J( @ - __IO uint32_t CRCSADD1; 4 ?/ h% K- [6 S5 w: w0 p
- __IO uint32_t CRCEADD1;
: [/ ~& A1 k c X: W - __IO uint32_t CRCDATA; & X' C. _3 r8 R2 t3 ^7 o# z( J
- __IO uint32_t ECC_FA1; & B! {9 z+ L1 J1 G C
- uint32_t RESERVED1[40]; / Q$ |7 M! f+ U) {
- __IO uint32_t KEYR2;
8 d C) V0 m5 Y! ~ - uint32_t RESERVED2;
/ W' Y( k9 [' r4 ?2 k& o - __IO uint32_t CR2; ( n3 \& g d1 [/ d' `
- __IO uint32_t SR2; ?& f6 {2 d6 U' S! z6 c6 o2 y
- __IO uint32_t CCR2; : f* b1 E \$ g+ _ ^" x
- uint32_t RESERVED3[4];
/ e) ]2 o( Z) e1 {/ I/ e: j - __IO uint32_t PRAR_CUR2;
# Q4 l. H! g$ X; |6 x+ Z - __IO uint32_t PRAR_PRG2;
$ D% r# ?7 K$ O# \5 t - __IO uint32_t SCAR_CUR2;
+ m$ }3 e1 a; ?7 y" | - __IO uint32_t SCAR_PRG2; 2 P; \7 q3 {: |0 D
- __IO uint32_t WPSN_CUR2; 2 m. q: A1 w6 R9 g6 b e* j
- __IO uint32_t WPSN_PRG2; # B0 ^! ]8 K1 C6 _; o7 w# _
- uint32_t RESERVED4[4];
9 b9 E. k% H$ n/ X - __IO uint32_t CRCCR2; 9 u# K$ j8 f: V+ q' Y, l
- __IO uint32_t CRCSADD2;
5 u# a, k4 @ E% A8 f( j4 R - __IO uint32_t CRCEADD2; * o0 ]/ t; g. O9 V3 ^9 C3 ~
- __IO uint32_t CRCDATA2;
7 r) x2 f4 J" r$ v7 @ - __IO uint32_t ECC_FA2; / K( ]& i/ v2 U" q E9 q# K6 f
- } FLASH_TypeDef;
复制代码 # L) ?/ [5 ~( c3 G6 f
这个结构体的成员名称和排列次序和CPU的寄存器是一 一对应的。( i& G& W" C% M5 O* O9 ]6 G
; O+ Y, Y7 u) ^# }- }( h5 m
__IO表示volatile, 这是标准C语言中的一个修饰字,表示这个变量是非易失性的,编译器不要将其优化掉。core_m7.h 文件定义了这个宏:
9 I% n- {! ?& \3 N. v- #define __O volatile /*!< Defines 'write only' permissions */
; G3 j$ x7 \, D: R3 g - #define __IO volatile /*!< Defines 'read / write' permissions */
复制代码 0 z5 r5 q1 S0 Q5 f; P' q! ^
下面我们看下Flash的定义,在stm32h743xx.h文件。 D/ k& X# f3 H+ d6 l
7 J; D/ Y2 N- I- g% m' H+ p$ B" b" ?
- #define PERIPH_BASE (0x40000000UL)
8 q" l( p- h2 S+ \! ?$ D* W8 b8 J - #define D1_AHB1PERIPH_BASE (PERIPH_BASE + 0x12000000UL) v$ |, q8 D( j/ W" k+ W
- #define FLASH_R_BASE (D1_AHB1PERIPH_BASE + 0x2000UL)
; g! T( P" q6 H5 A - #define FLASH ((FLASH_TypeDef *) FLASH_R_BASE) <----- 展开这个宏,(FLASH_TypeDef *)0x52002000
复制代码
+ d! s/ Z, c2 n1 f' j6 D我们访问Flash的CR1寄存器可以采用这种形式:FLASH->CR1 = 0。
) c' Y2 h n7 s9 S: }2 {# p5 U& O3 ?7 B5 U4 e
70.3.2 内部Flash擦除结构体FLASH_EraseInitTypeDef
5 [- q6 K, o; ^1 C8 y5 u9 h' ~* G下面是做内部Flash擦除的结构体,用到的地方比较多:2 Y" p: g" o0 X
: X$ I4 c/ B/ K, y: h7 `
- typedef struct- ?/ y: r& Y& O1 M1 n
- {2 A$ _! B5 K- J1 p; i/ }/ T
- uint32_t TypeErase; 0 ]& ?5 A. M4 R n
- uint32_t Banks; 4 O% m- v3 N- ~$ m* u! R
- uint32_t Sector; 6 f- c' D: i* v0 R; W2 `- w
- uint32_t NbSectors; 4 X: F2 u, t3 D* \5 l6 l! ?
- uint32_t VoltageRange; , T. {: s6 _2 i
- } FLASH_EraseInitTypeDef;
复制代码
. M" f8 Q* y1 [2 R r$ Y下面将结构体成员逐一做个说明:
0 w+ v, M3 M/ b+ u; p8 ~8 q TypeErase
, m; a4 Q/ G# i# |4 ^" L
) i) E6 U6 \, _: T( f- t" G
. d7 D' N7 y0 G* {, T用于选择BANK擦除还是扇区擦除,H743有两个BANK,每个BANK有个8个扇区,每个扇区128KB。具体支持的参数如下:
1 S* ]& w- Y' D
$ f* d8 N$ P2 k, i5 T- #define FLASH_TYPEERASE_SECTORS 0x00U /* 扇区方式擦除 */. ]- v* `; ]4 C# y* ]
- #define FLASH_TYPEERASE_MASSERASE 0x01U /* BANK方式擦除 */
复制代码
; g1 _5 @7 L( n1 Z. S& M% h' j& | Banks
/ R* {$ J% |1 {# f) Q# c用于选择要擦除的BANK,或者两个BANK都选择:2 g/ F2 n! z* F, f5 F1 Y
9 K- z# j; y- X4 y; V( z6 n
- #define FLASH_BANK_1 0x01U /* Bank 1 */" i" j( F* l6 O1 m# V
- #define FLASH_BANK_2 0x02U /* Bank 2 */
( a5 H8 u9 `4 h1 X - #define FLASH_BANK_BOTH (FLASH_BANK_1 | FLASH_BANK_2) /* Bank1 和 Bank2 */
复制代码 . N2 ]4 w0 E- {/ E3 E& J; }' G
Sector' j* ~2 R% p4 m: f9 E1 l }
用于选择要擦除的扇区:
1 u+ M1 N: [ b* i- #define FLASH_SECTOR_0 0U /* Sector Number 0 */
; A. G/ D8 j- x* \6 y0 K/ V - #define FLASH_SECTOR_1 1U /* Sector Number 1 */
9 b" Z$ V4 [/ ~& n - #define FLASH_SECTOR_2 2U /* Sector Number 2 */8 d; a" m' Z9 N
- #define FLASH_SECTOR_3 3U /* Sector Number 3 */( l! X; _& W) T1 U, {
- #define FLASH_SECTOR_4 4U /* Sector Number 4 */
4 Y1 V# q+ q E5 H6 F - #define FLASH_SECTOR_5 5U /* Sector Number 5 */
; D' V* B* Z; \4 L+ W: _ - #define FLASH_SECTOR_6 6U /* Sector Number 6 */2 o, F7 o, _ C2 e2 e, @7 g; M
- #define FLASH_SECTOR_7 7U /* Sector Number 7 */
复制代码
2 |3 u/ ~/ B/ }- Y, X NbSectors8 @; R4 ]5 F/ b5 R% I4 `
用于设置要擦除的扇区个数,对于STM32H743来说,范围1到8。3 Z. l. l& K$ ?; E" P0 ^
! N5 n5 S+ q! @. }0 F9 K u' S) p VoltageRange
% Q, r9 ]9 M7 [5 t% F用于设置编程的并行位数,电压不同,位数不同:
1 x& n' |% X: |- u- #define FLASH_VOLTAGE_RANGE_1 0x00000000U /* Flash program/erase by 8 bits */
/ W* v' W# M; F9 l - #define FLASH_VOLTAGE_RANGE_2 FLASH_CR_PSIZE_0 /* Flash program/erase by 16 bits *// m) B/ L+ l3 T, q
- #define FLASH_VOLTAGE_RANGE_3 FLASH_CR_PSIZE_1 /* Flash program/erase by 32 bits */
s+ M/ [. g& l& K! N! r - #define FLASH_VOLTAGE_RANGE_4 FLASH_CR_PSIZE /* Flash program/erase by 64 bits */
复制代码
8 C6 S! y# O1 p6 t# N( Z" ~( ~+ D/ i; c- M5 ?6 x
70.3.3 内部Flash的操作总结* ?' M7 i* @+ P& k' g' b9 j0 s
7 [8 w; g/ J3 s. ]$ w! H6 Y9 I
3 H6 `# u$ ]. i- e; W8 ?
使用方法由HAL库提供: _0 {2 E; C6 Q( V2 g }) I4 N
Flash编程函数操作流程
" ]/ U$ ^' l0 l$ u1 |& [1 Y. t9 Q0 g Flash解锁函数HAL_FLASH_Unlock。
9 j# C( Z: i! k Flash查询方式编程HAL_FLASH_Program。6 V% C0 N" n9 t. A& l; P
Flash中断方式编程HAL_FLASH_Program_IT。
; v Y1 a2 b! l Flash上锁函数HAL_FLASH_Lock。
; M+ C! O9 G2 w- @ A1 |* B
4 u; [0 _* u' p/ }0 Z选项字节编程流程4 A8 Y$ D* Z- `4 E( v
选项字节解锁函数HAL_FLASH_OB_Unlock。
1 D3 g5 Z) D6 o& r8 \ 选项字节加载函数HAL_FLASH_OB_Launch。
8 S) n9 w w, l0 T# D 选项字节编程函数HAL_FLASHEx_OBProgram。
2 g9 T" _& I* s) U4 C2 d 选项字节加锁函数HAL_FLASH_OB_Lock。
: f( Q: M) [: {/ g5 Y8 H- y
& p+ [5 N9 p, f. @9 V( `% W* S Flash擦除流程
' u3 e: I7 l- F& ?0 ? Flash解锁函数HAL_FLASH_Unlock。& V- V. n& Z* V0 b. J5 p
Flash查询方式擦除HAL_FLASHEx_Erase。
+ W1 T: N8 A5 b1 s# D0 c Flash中断方式擦除HAL_FLASHEx_Erase_IT。
2 ^1 ^1 {, q0 J% a Flash上锁函数HAL_FLASH_Lock。
2 y+ g; f* { K( ^- ~( f$ O: Q6 o' T
5 \* X& W" F- y& p70.4 内部Flash源文件stm32h7xx_hal_flash.c
, R% z# _/ J$ S( }. c, i9 y) E此文件涉及到的函数较多,这里把几个常用的函数做个说明:
% |) f. \" y }& h$ x) J- ^% _0 s- @ E; e, g( W F
HAL_FLASH_Unlock
( l. O( m9 y5 U% `6 W$ ? HAL_FLASH_Lock
; d# \( }8 W% y5 s P* z! O HAL_FLASHEx_Erase: W( m) {8 @/ T+ I
HAL_FLASH_Program
! H9 S- c, x4 b& D- ?; u# q: L0 v; t* `4 Y! Q+ A
70.4.1 函数HAL_FLASH_Lock5 k" c0 K6 ~" c+ }0 u' {6 Y8 {& a
函数原型:2 n% F& v: ?( H) ~1 u
4 Z- ^7 Q& m3 C
- HAL_StatusTypeDef HAL_FLASH_Lock(void), f3 z5 Y( b1 S/ n+ ~
- {
/ e% T p, Q- s" x2 v - /* 设置FLASH Bank1控制寄存器Lock位,即禁止访问 */5 l5 N% I: N& L# A7 k" y# G' @0 O
- SET_BIT(FLASH->CR1, FLASH_CR_LOCK);
5 g6 P3 p u. P+ q7 M# U - 2 L$ {7 ^: n5 U o2 K9 {2 p
- /* 验证Flash Bank1是否已经被锁住 */
}$ x! `, A9 f0 n& X - if (READ_BIT(FLASH->CR1, FLASH_CR_LOCK) == 0U)
5 {1 X) D8 H$ h* i( ^, `# c" O$ G - {
# i6 j: L" F9 |8 g. N - return HAL_ERROR;) Z+ B: ^) `: @& g
- }
1 H. P. E# ]' s
% f- O- N, ]9 `3 E R8 y- /* 设置FLASH Bank2控制寄存器Lock位,即禁止访问 */. y, e2 }9 P& l' ~7 [
- SET_BIT(FLASH->CR2, FLASH_CR_LOCK);2 O. p; Y; S0 {6 C# q& ^+ w
. F: O7 m3 T& S7 F- /* 验证Flash Bank2是否已经被锁住 */3 F& I2 [+ u2 L+ A) n
- if (READ_BIT(FLASH->CR2, FLASH_CR_LOCK) == 0U)
* M$ ]# }% G- K1 W' l, m; i - {; l# p, z, P8 D4 H S) R+ o
- return HAL_ERROR;6 _1 ` ^5 ^8 [4 _' d; {
- }6 u1 S% @3 Z, t: R
# ^" W* Q- \1 O% l) L; @- return HAL_OK;) Q1 s0 p8 Y+ H9 G" ?
- }
复制代码
5 v! g* H8 S2 s, }! m5 v) ]( a函数描述:
" S Z3 C$ Q6 a5 F; u+ ~0 ~) J9 F2 N
用于Flash加锁,加锁后将不能对Flash进行编程和擦除。5 Y; s( z: o5 w8 g' c5 n5 E
4 q0 R. {+ f x5 O! \70.4.2 函数HAL_FLASH_Unlock3 V0 D& R F" M. a$ [
函数原型:
' h. s2 c# O0 ?' e- A- HAL_StatusTypeDef HAL_FLASH_Unlock(void)
; r1 z5 `' t3 |2 y# Q5 B - {5 }# `% b, X6 `& t* Q, R/ w
- if(READ_BIT(FLASH->CR1, FLASH_CR_LOCK) != 0U)
6 r& D) [0 Y3 _ r! d3 I2 b7 r - {2 e8 T7 p! \0 z* v, x
- /* 允许访问Flash Bank1 */
5 a9 ^$ r$ C0 E& L! f H& o - WRITE_REG(FLASH->KEYR1, FLASH_KEY1);
9 n3 _* S7 R W) w. d( H9 W: K - WRITE_REG(FLASH->KEYR1, FLASH_KEY2);
, U' Y- R* |3 k$ _; Y - 6 R* x! V6 y4 H! h* ^" r% e5 }& v- J8 W
- /* 验证是否已经解锁 */ W3 X7 P* S( [8 y+ Y( z
- if (READ_BIT(FLASH->CR1, FLASH_CR_LOCK) != 0U)
( K( l# B& @$ O - {# X6 K6 b. G$ B# {" Z2 W
- return HAL_ERROR;
% X9 J+ O# \1 X& q* l( v - }9 D; f, L# |) C& j
- }
2 Z9 `0 C! y1 I1 H7 W - 4 \; v" {7 `- `" R
- if(READ_BIT(FLASH->CR2, FLASH_CR_LOCK) != 0U): I$ \ L" _7 {' Q( d$ x! e7 i
- {1 F4 f: ~* w2 k" Y) w/ p, f
- /* 允许访问Flash Bank2 */( ?% \$ w+ c1 Q- Y
- WRITE_REG(FLASH->KEYR2, FLASH_KEY1);2 p( H1 p5 n/ p4 t$ G
- WRITE_REG(FLASH->KEYR2, FLASH_KEY2);1 _- m3 k; E f* o$ `
, f% F# o+ m+ M2 e, k4 x2 v0 }- /* 验证是否已经解锁 */) l/ n5 L+ x0 u7 o
- if (READ_BIT(FLASH->CR2, FLASH_CR_LOCK) != 0U)
( Y8 _3 @0 c) w: O3 h6 M. M! C - {) f) e! h/ P/ n! {; [9 z* q$ S
- return HAL_ERROR;
9 y5 Z/ J, c* V7 l - }
' ?; S9 h+ K# ^ - }
. O1 `7 c8 C2 V. [% q( K8 u9 D) i3 I3 N
7 a% j/ n% v# |# y1 R7 B* C$ w- |$ b# r- return HAL_OK;7 n5 {/ E& ^+ e: O
- }
复制代码 3 w: O. L% _0 h0 p( Y' e& {) d3 C4 o
函数描述:
2 m4 |; ^( t3 H2 F) ?! s9 B- {! g8 f# I, G3 V
此函数用于Flash解锁,解锁后可以对Flash进行擦除和编程。, t) e- ?6 h) ~$ g7 }. E; a3 K) _
: c% M2 v* W5 n: }0 b4 M
70.4.3 函数HAL_FLASH_Program/ m3 V; }% ]& g9 ^) Z6 S9 L
函数原型:
0 P$ E, D! C8 Z3 ^0 j9 w; f6 m( r9 W( _. L0 d# L6 z
- HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t FlashAddress, uint32_t DataAddress)* V2 e+ D' ]- e3 V; S7 g
- {
9 k* i; x9 l* _! N - HAL_StatusTypeDef status;0 B7 h& u) c4 Z5 f
- __IO uint32_t *dest_addr = (__IO uint32_t *)FlashAddress;
! L( s. h, w" i( p( {2 [, m - __IO uint32_t *src_addr = (__IO uint32_t*)DataAddress;
# h6 g' s5 t; [+ ]( Z( G2 v! V1 t - uint32_t bank;8 b- r9 G7 r$ ^3 L4 k0 x& i
- uint8_t row_index = FLASH_NB_32BITWORD_IN_FLASHWORD;
8 N" o3 z0 M3 S, A4 d( b$ K, G
/ v5 M# o6 G3 X( e4 T) @5 r5 e$ L) M1 |- /* 检测参数 */$ F! ?( {$ F: d' N
- assert_param(IS_FLASH_TYPEPROGRAM(TypeProgram));
* }4 |7 s( D& T - assert_param(IS_FLASH_PROGRAM_ADDRESS(FlashAddress));
1 j$ n" z- r# @. f - F: g( H: Q9 q5 C2 A- v
- /* 上锁 */
2 @' N$ q z2 P- \7 b - __HAL_LOCK(&pFlash);7 h3 [0 J* w3 @. B3 `9 k/ D
8 J% x; p6 U$ u: u7 u- g' Y8 e4 R: m- #if defined (FLASH_OPTCR_PG_OTP)
$ e' d/ {2 l" R& P; B3 H% y - if((IS_FLASH_PROGRAM_ADDRESS_BANK1(FlashAddress)) || (IS_FLASH_PROGRAM_ADDRESS_OTP(FlashAddress)))
1 R' F( Q' _7 S$ A0 b4 b - #else
9 I0 j/ R8 c+ B$ ^9 e. H2 U$ D - if(IS_FLASH_PROGRAM_ADDRESS_BANK1(FlashAddress))* E5 p' K' z5 U6 H& @4 X
- #endif
& I, Y( h c# \9 Q# ?3 L - {
9 C8 _0 ^+ y; A - bank = FLASH_BANK_1;
, r* [ Q: c$ O2 `+ X - }
8 \% r3 a9 C! q" L - else
. m; o M! L$ \ - {7 d7 \2 t; J1 e7 H; y& Z5 r
- bank = FLASH_BANK_2;
& C) y( Z5 S* k8 r - }
/ F( E7 r& ]( l - 9 ~; K& m6 ~( t* B% x( }
- /* 错误标识,无错误 */: P3 U5 b2 D1 ?) D2 P
- pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;' s2 } I4 `, A# i+ K/ r: k, X
- * y. i' {- K' h% I' x2 A; G
- /* 等待操作完成 */
: ?" d, [6 }, `, K - status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE, bank);
! Q( n$ j. d0 {) z
4 ~4 E/ X+ p# h, l# \/ F- if(status == HAL_OK)' i. C- E3 d F ~ h
- {
/ q. u7 Z' L! Q& p - if(bank == FLASH_BANK_1)
% R- i. D, F; ~8 ?( _ - {, [& u0 H5 y0 \8 d/ _2 L/ f7 ~
- #if defined (FLASH_OPTCR_PG_OTP) R' v/ D$ b2 F6 z
- if (TypeProgram == FLASH_TYPEPROGRAM_OTPWORD)' F$ |; e) y/ {/ L
- {& ~) ]- I( _- X
- /* 设置OTP寄存器的PG位,使能可以编程 */
$ w/ f' S2 P0 h& T6 V8 _ - SET_BIT(FLASH->OPTCR, FLASH_OPTCR_PG_OTP);
9 j, }% T/ C8 Q7 s* |, {! _ - }; r3 Y1 B; B) Y3 t
- else: P! N! T! E0 Q0 \
- #endif
, U% a% `4 }. | T - {, h7 p! f; O. H% \% g
- /* 设置PG位,使能可编程 */
9 i! \4 L8 v, n0 h1 ~ g0 l - SET_BIT(FLASH->CR1, FLASH_CR_PG);, P, L; P3 G% H: e0 S
- }7 ]0 m ^% ~& a2 q
- }5 f" F2 Q2 Y* h. Q# o: z
- else3 A$ @! j ~- a' f' [$ [
- {
1 J) O+ ^9 |2 x( n t - /* 设置PG位 */
( Q3 g& k @8 G' t) d - SET_BIT(FLASH->CR2, FLASH_CR_PG);. Q: M7 J0 w6 @9 p) V
- }7 b' @1 F, N8 b( G
4 e P2 P$ H- o# R2 M: c2 Z- k* O/ N- __ISB();4 M7 g4 X3 J/ R& `. {% ?7 [& W
- __DSB();1 t0 X! z" j! y: w
- 0 L! D( y2 o* L' j; `+ b) o
- #if defined (FLASH_OPTCR_PG_OTP)
: z. ^+ v! P+ I7 p1 A$ m - if (TypeProgram == FLASH_TYPEPROGRAM_OTPWORD)
) Q5 @. `8 P/ K7 p6 ~. k - {/ d( S. Z$ P* z+ W3 Y
- /* 编程OTP(16 bits) */- }0 N& ~1 O, F r
- *(__IO uint16_t *)FlashAddress = *(__IO uint16_t*)DataAddress;9 c% `6 k9 N4 R3 m [' b3 {
- }
4 X! T1 [' M% |& N7 M6 {9 z - else
3 y0 N! d$ X* \ - #endif
. ?; M l$ Q, a - {: d. v0 B, ]+ g3 U
- /* 对Flash进行编程 */
" Q7 y6 r0 t7 O9 H - do
2 Z$ H7 x( D9 J# z( j) X - {' {: W3 C5 Y0 y" d0 g- k
- *dest_addr = *src_addr;% ] a6 X: _& p; A" e$ X/ }1 A+ X
- dest_addr++;7 R' G8 D9 h; e8 h/ k& K
- src_addr++;
3 m, S. X, a! ]# H8 } - row_index--;; @3 D& l: o/ A
- } while (row_index != 0U);
/ L5 N$ |) i" L) ?& ~1 J - }- a7 w/ j& S1 {- r3 M3 v
- . t' U3 D. q C: y/ L0 v
- __ISB();
/ J% u; W6 H+ q- B: G9 l! ?6 p - __DSB();
7 x! W) Q+ X: t* t$ ]6 r4 n - : L! O. w+ H- v7 c/ E5 Y- O5 }
- /* 等待最后一次操作完成 */
6 \4 S4 y2 O$ e3 H, |" y' q; O% u - status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE, bank);5 V. I) }1 W [: G0 G
# F8 h8 w6 k6 H8 Y- #if defined (FLASH_OPTCR_PG_OTP)# t z5 X1 w3 B* r' p
- if (TypeProgram == FLASH_TYPEPROGRAM_OTPWORD)9 b( k1 m4 z% u6 a- u
- {
$ d2 l5 {9 k- r$ H& I/ ? - /* 如果编程操作完成,关闭OTP PG位 */- `- t, h. k, r
- CLEAR_BIT(FLASH->OPTCR, FLASH_OPTCR_PG_OTP);
$ N) i6 e1 P/ d* I1 p - }" D# W. n3 ]3 s5 ~# e1 F
- else/ G" M% a% ?3 Y; M7 B/ A& a9 s4 d, q$ ^
- #endif
$ U. a/ {. ~. E$ E - {
8 T+ K. _: b0 ~% z. K0 M; y: c - if(bank == FLASH_BANK_1)
3 O5 T+ K1 V: T - {
, a/ N$ b# l) o7 {; g - /* 如果操作完成,关闭PG位 */
$ A$ ~3 Y3 B# W7 @+ D - CLEAR_BIT(FLASH->CR1, FLASH_CR_PG);1 ^/ m4 R# T- X
- }2 E$ j* F7 V1 n6 I; \4 Q
- else5 J( }! z! `9 d. J# N
- {
+ _+ _/ J. T; |- Y% d9 ] - /* 如果操作完成,关闭PG位 */
& ]/ c% T3 q8 b5 ` - CLEAR_BIT(FLASH->CR2, FLASH_CR_PG);, Q0 R- M+ n4 b5 n& J, F/ n
- }
8 `1 R; E/ k7 q- v - }
4 Y) O, J# r1 H6 L! ?: R - }2 z m$ g+ X& i
- , z- g- T* ]. p8 H7 U D. N
- /* 解锁 */
. } h' {1 V# ~) U - __HAL_UNLOCK(&pFlash);
% n- ?; X- i7 Y+ ^ - / Y( M+ E7 b& Y( K" u4 R! C
- return status;. O6 i7 i; ]' }/ O9 U
- }
8 _( H1 b8 C, S0 i1 r
7 [' c0 c- v N9 T& o2 w ^
复制代码
5 g. H6 E0 Z% N) J* t, `* t函数描述:
6 R& R1 R" `/ @- Y) f此函数主要用于Flash编程,固定编程32个字节数据。
' P, L) j2 ]6 i+ i' a# u& g/ d# s! |
+ k5 Q' L5 w Q2 ?8 Y0 A函数参数:0 C+ m" C' e5 n( b8 f2 G
第1个参数是要编程的Flash类型,支持两种参数:
. I/ }7 T7 C6 [8 Y% W FLASH_TYPEPROGRAM_FLASHWORD,用于芯片内部Flash编程。
! Q) Y" C9 e& @ FLASH_TYPEPROGRAM_OTPWORD,用于芯片内部OTP存储区编程,当前的H743并没有这个区域,所以可以忽略。
# x, k M. m# t5 K3 Y# O# J0 t# d q6 L5 R* O4 |
第2个参数是要编程的Flash地址。5 O* W; z Y3 c, D% e- R, ~
$ f* I% \; n; a* i" L& a6 b第3个参数是要编程到Flash的数据地址。
) @. H8 ]4 n( s! D4 L1 j; g! P返回值,返回HAL_TIMEOUT表示超时,HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示串口忙,正在使用中。
- K. \1 C. G" U9 s; s+ [3 k" I1 c( d% T2 r) _: v, G. Y
. X$ Z, F1 O# C* }% r( d1 m Q
注意事项:
* n4 @& H3 v4 ?$ L3 ?- H& { 第2个参数的Flash地址要是32字节对齐的,即此地址对32求余等于0。
" N+ w: i6 r5 S7 c) S 第3个参数务必要是32字节的整数倍。
X ]1 f N7 x0 j/ v2 H# Z, `6 Z) ~( J4 `! R0 d, [
70.4.4 函数HAL_FLASHEx_Erase+ Z" ]- M$ ^+ i. `) r9 C% a) S
函数原型:
0 N: X! P& H( L1 v- S- A, R- L. }- z
; x: R t+ j, {4 |; n- HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *SectorError)2 U4 p' a; |% j% q% l& |" e) v
- {
0 u. [, P* Z6 z6 t5 n" l2 R - HAL_StatusTypeDef status = HAL_OK;
- j/ ]9 c: N3 _2 a0 B% P( W( G1 K - uint32_t sector_index;
8 y% G, |' X" a- o - 9 l; s; _% z' j9 o) G p, Y9 O
- /* 检查参数 */
0 t/ ~- d( k! z {# n" D: E - assert_param(IS_FLASH_TYPEERASE(pEraseInit->TypeErase));
" R N2 u' }1 K! M - assert_param(IS_FLASH_BANK(pEraseInit->Banks));' {/ t7 Y3 x( N3 I$ I* M* w
- |7 y1 m$ |7 ]1 l7 E: V# M. h- /* 上锁 */
# J$ h9 N3 A1 y ?% O5 C - __HAL_LOCK(&pFlash);
5 N% R- ^1 q2 [* G - ; D; ?7 u' P* v) `* Q
- /* 无错误 */
1 O( e3 n# B' ` Z/ s5 h. D* U - pFlash.ErrorCode = HAL_FLASH_ERROR_NONE; |/ \+ @7 p* y+ R; C+ D
- 8 Y! F/ j2 C5 i' Y3 C! h0 B
- /* 等待BANK1的操作完成 */
4 b$ F0 O% m/ N5 Q. t/ ?2 X - if((pEraseInit->Banks & FLASH_BANK_1) == FLASH_BANK_1)( @) V7 |8 E2 L }0 |
- {
0 ?- w; P; @9 X; |. Z! ?% K7 Q - if(FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE, FLASH_BANK_1) != HAL_OK)
" t) P! E0 o5 Y% ?) f7 u \1 ^ - {
" y" r% X6 [0 Z- G9 x9 n- \" l - status = HAL_ERROR;; ?5 l+ y# C. V3 q$ @
- }+ \7 t- e3 S0 ~) E5 h7 G5 m4 u! Z
- }
3 A0 o+ `: d7 ?: C. f' y% { - 8 M, Q$ S0 w3 Z
- /* 等待BANK2的操作完成 */
) {3 j! r) B0 D w/ T0 H9 k - if((pEraseInit->Banks & FLASH_BANK_2) == FLASH_BANK_2)
7 v% U+ y2 G4 T% `4 E3 u# |7 e' R9 v - {
0 P: P" Y9 i- V5 M5 \ - if(FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE, FLASH_BANK_2) != HAL_OK)
+ P' O0 u/ J6 g, d - {. E. k* V# Z) A; c1 x0 V
- status = HAL_ERROR;0 z, M3 D/ Z, n, d8 T
- }3 ` L) c& ?, F, u- g- f# ~
- }
* i* E: b% @! p+ I% I# q! F- y
' u" K* \" e6 G5 q! o; t% \% |, p- if(status == HAL_OK)8 u/ V [& Q5 i) y6 Z; b
- {
: Y7 b2 h2 u* ~( [& w, [6 I - if(pEraseInit->TypeErase == FLASH_TYPEERASE_MASSERASE)6 c" w2 E4 `9 }1 E- M# k
- {% c" M2 E1 Q( d) g6 Y
- /* 整个BANK1或者BANK2擦除 */
& v2 J0 t* I/ ~- p9 d# |& m - FLASH_MassErase(pEraseInit->VoltageRange, pEraseInit->Banks);
6 n3 W. |# d4 N+ b, K0 Y - 0 D$ v+ Z8 W* }
- /* 等待操作完成 */; \) r2 }( Q# t- A9 |) M
- if((pEraseInit->Banks & FLASH_BANK_1) == FLASH_BANK_1)
: F( P T+ n9 f4 Z - {0 F% i" |% U3 g1 s
- if(FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE, FLASH_BANK_1) != HAL_OK)/ f: a8 o7 ]' K; k" j
- {
# _. B9 ~, _" a1 q - status = HAL_ERROR;/ n x0 A5 N; q+ e& g0 |( d
- }
4 a1 R0 i% k$ G' `+ I7 R: n5 Y( ` - /* 如果擦除完成,关闭BANK1的BER位 */$ p) {. ?, s( Q, @
- FLASH->CR1 &= (~FLASH_CR_BER);6 i/ p: H7 l$ H# t( ]$ O
- }
! e9 U. s v T: T/ Q - if((pEraseInit->Banks & FLASH_BANK_2) == FLASH_BANK_2)
7 N- `' V3 T9 _; ^/ t# J5 r! u5 n - {6 B6 g8 {, L5 D; n. j, N$ V) n
- if(FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE, FLASH_BANK_2) != HAL_OK)
: T+ E* l1 S# v. r* ? - {
& q3 }6 ] Z5 O# N - status = HAL_ERROR;7 G; D$ b! N& v+ y
- }
! n) y0 e9 \: u0 p/ q. S - /* 如果擦除操作完成,关闭BANK2的BER位 */" G4 x; s t1 p9 Z! J+ J
- FLASH->CR2 &= (~FLASH_CR_BER);1 q- h: v+ |7 f. o! U( C' H# u. \5 `
- }
; }& |# e6 `' a* {: ~8 L6 K - }, U# ?+ k3 @0 l. \2 e8 v5 S
- else7 B% W# ]- X/ X# I) m+ H
- {5 k8 h. i7 B- O8 k
- /* 初始化扇区错误码 */; \/ E- Q8 q6 Q" ^: E" J
- *SectorError = 0xFFFFFFFFU;
/ j* W: G& }1 R$ K8 Q
- T& {/ I2 s, H6 M& y2 K% T- /* 扇区方式擦除 */
7 Y" z! [* w( j - for(sector_index = pEraseInit->Sector; sector_index < (pEraseInit->NbSectors + pEraseInit->Sector);" x" [& ?5 r9 _/ T& Q( _$ ^
- sector_index++)
. P" P, h# p0 B) ~, A - {
7 {5 _2 `0 B! L: j& ?9 x G - FLASH_Erase_Sector(sector_index, pEraseInit->Banks, pEraseInit->VoltageRange);
* Q! L0 Y2 Q3 x. ]1 b - - _6 @6 K5 [/ a9 o6 U; Q) J
- if((pEraseInit->Banks & FLASH_BANK_1) == FLASH_BANK_1), c5 X+ B% x9 {4 v. Z; u
- {. T' I6 ?. \5 s+ ^) \6 J: I
- /* 等待BANK1操作完成 */
2 H/ F, ]9 N; K0 Z - status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE, FLASH_BANK_1);) q$ u: }" m s0 B5 f* b
- # f$ k+ p7 j" n; O) b
- /* 如果擦除操作完成,禁止SER位 */0 V) S5 E1 T0 S( T
- FLASH->CR1 &= (~(FLASH_CR_SER | FLASH_CR_SNB));
" m( Z) l$ ]4 k* z1 a% ~ - }. z( V6 s4 t6 r* |
- if((pEraseInit->Banks & FLASH_BANK_2) == FLASH_BANK_2)2 W$ ?! H. x; w5 _$ |
- {
" H# S4 c2 M- E3 i3 R0 J3 k4 B; Q - /* 等待BANK2操作完成 */3 r8 Q) _# t: \/ o m
- status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE, FLASH_BANK_2);9 B1 Z- c+ ^% Y2 w
- $ w$ T( |6 b( E
- /* 如果擦除操作完成,禁止SER位 */2 y- f$ v# ^. w! U
- FLASH->CR2 &= (~(FLASH_CR_SER | FLASH_CR_SNB));9 n9 o3 J2 k6 y4 y' m- K
- }, p, a6 R! F4 y. s, \
- 3 P" ], H6 r! z
- if(status != HAL_OK)
8 J- ^+ l8 b9 [! m. C/ ?# _ - {
$ H/ y2 r2 n; @- O- s# G# ?4 ~ - /* 如果擦除出错,停止后续擦除,返回擦除异常的扇区号 */! b; Y6 o* \, g( R% \) q0 G
- *SectorError = sector_index;1 `" n4 R: o8 P+ ]$ a
- break;3 w7 m+ k( u* h" c4 \+ v
- }
! Z9 L/ X5 f- v1 q - }
( F4 [( F+ v: W2 \9 v4 C - }
+ A& {0 y& m0 Q: u. S7 I N - }8 J B, ]2 k" y% ^' _
& f! d1 N3 @8 R7 N- /* 解锁 */
t- V& x8 X ~5 m0 c8 D6 g6 c - __HAL_UNLOCK(&pFlash);
! d7 j# ?$ S! F" j9 [ - - S3 Z$ `( B1 V6 [
- return status;1 n! R7 d9 _$ c9 r: n8 w: {
- }& i* I& `; R* C2 l- |! d
复制代码
! G, \$ t5 T& M7 r函数描述:
6 ?- f% S& d7 J. ]用于内部Flash的批量擦除(BANK擦除)和扇区方式擦除。
4 M* I% S# j- X. X' i; o# `9 `
' ~) l* _' V4 |2 i% w( p函数参数:
" t$ I" T8 f4 }1 v. { 第1个参数是FLASH_EraseInitTypeDef类型结构体指针变量。$ Y, J/ |' g& M2 x! g4 Y6 q2 B* ]% X3 e
第2个参数是错误码返回,返回0xFFFFFFFF表示全部正确,返回其它值是擦除过程中的错误扇区。
% ^: c6 ]* C5 T* M M0 A, h 返回值,返回HAL_ERROR表示参数错误,HAL_OK表示发送成功,HAL_BUSY表示忙,正在使用中。
% v3 `- Q* y- q* n& e6 I" A9 g5 U% t# W- z# w* ?/ l
70.5 总结) p5 w5 k9 g) ]- k' W
本章节就为大家讲解这么多,对于内部Flash编程来说,掌握本章节的这些知识点就够用了,更多的知识点可以看STM32H7的参考手册学习。; x: A' J2 J0 j7 s" h
& @9 y" d5 M, U! E
0 Z7 A, }- C7 x1 n
2 _, b, [+ L" C2 G8 H' R
|