85.1 初学者重要提示
0 L1 F7 r2 b) k6 P% @6 m: f% F( Z QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。. [+ {. s5 ] c5 j5 h2 ?& a
STM32CubeProg下载算法制作和MDK下载算法制作基本是一样% W3 q( F+ Y. e/ m$ Q
本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。
" @4 p, ^- B% r: {7 [ a85.2 STM32CubeProg简介
2 j1 ]+ Q) } q4 w9 a2 G" \$ VSTM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。
( E6 D% ~$ ] s% E3 R, C- [& k6 I1 q, ~ T# C( D- f3 p
) V5 H) S; u) F
) Q, `! c$ m& n: a* r
85.3 STM32CubeProg下载算法基础知识' o2 Q9 S; I% h) o; r$ D8 d: d: Q
STM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。
; s% X& S. a: K+ H9 W; M5 B- g) l e. H+ s0 z: s
85.3.1 程序能够通过下载算法下载到芯片的核心思想7 x: l& ~/ M% d) o
认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。- O2 f: m+ A* { f
* }. E: K8 R% f& e0 o( f
85.3.2 算法程序中擦除操作执行流程: D2 x6 M/ ?1 N" _) a5 @9 M% {
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。
" A$ w- k7 s& e& k1 }
7 G7 e. p r# B擦除操作大致流程:
' z; i4 b" i' q: t1 Q* K' H+ p" D
% X! |, c# A0 v8 G% H8 H3 u7 O7 H: c
% a# S: n9 n% v1 f 加载算法到芯片RAM。: J2 \% \9 z, e4 N: C0 f! [$ d x1 C
执行初始化函数Init。7 W6 r& w- y V/ @! t. ?8 C
执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。
5 d: o6 L* ?: `5 U: p7 ^4 H* J 执行Uinit函数。
+ S6 m* M2 ~7 h5 M; v 操作完毕。5 \, a" q7 ^; o% y" q9 q
6 p$ b, V4 b6 t: k85.3.3 算法程序中编程操作执行流程- q5 Q; y* v5 W
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
( o' z# m6 g; Z/ u+ _3 S) h
! g2 L# i* _+ B编程操作大致流程:; P: ?0 S" W8 W' ~3 J# q
1 m( |- n* m2 _' V# R
' `5 o3 x3 q- n' r
# N- G, U: @# r, x @4 d/ w 针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。, k7 u2 H& G$ K9 ]8 K7 m8 C
查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:. a# m& ]) V) y8 Q, c; Y" ]( {2 Y
加载算法到RAM。
- Q9 f& X/ d' |& ^4 p3 m8 j 执行Init函数。( ]# @# c5 i( ^/ L5 e! D& b y6 C
加载用户到RAM缓冲。 X9 C1 I% U5 P* N2 G- M1 _
执行Program Page页编程函数。, f5 F* @( s6 `7 ^& w! U2 o
执行Uninit函数。& }/ g$ A3 M' |8 t: I
操作完毕。/ X9 _0 e* t: v( h* ?' C2 `0 q
0 |1 S9 U& L/ u( e1 S- s85.3.4 算法程序中校验操作执行流程" M+ @# S/ o9 C6 E. \0 t7 E5 W4 ?1 h7 U
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。: X$ M7 J" M" L- b' x! V9 U; H+ C
$ n7 k4 C1 e. U( U& A; w校验操作大致流程:
5 _9 c2 c6 {* E# p( W
$ H% n- S. a! p8 V6 v* f6 G3 @! @% v0 L+ X+ a6 u' U* L$ B Y3 k- }1 v$ y
0 u" d" C) D8 b" e1 y
校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。3 }. v: k% d1 F. U. v# b: D' {, t9 l9 v
查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:, S3 R' L1 e% |( l; c l+ k2 t$ E
加载算法到RAM。, @: y" S; |: S+ ]: S
执行Init函数。
' ?- i/ U) {9 k0 a6 r. f' { 查看校验算法是否存在$ y8 |6 W5 S: U& ?
如果有,加载应用程序到RAM并执行校验。( p3 s; Q& I% w$ I9 P
如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。
1 h% z. T. }' j 执行Uninit函数。
4 {+ B' _: \6 Z# P$ a' \ 替换BKPT(BreakPoint断点指令)为 B. 死循环指令。% ~" f4 ~* @4 x) g+ Y4 l8 K
执行RecoverySupportStop,回复支持停止。
+ U! X! L( p& h; G0 V) s# m$ F; ~ 执行DebugCoreStop,调试内核停止。: s0 t: f! [$ |
运行应用:
; f4 F- R v3 E) a: e5 @5 Y" |9 p 执行失败
7 H- |! j1 @2 `" |- i3 P1 i' N 执行成功,再执行硬件复位。
^7 ]( s' L* ~/ P* V' y 操作完毕,停止调试端口。; v% Z& d' U; k. w$ `/ v
9 T6 O9 i; z* ^: Z# m85.4 创建STM32CubeProg下载算法通用流程
5 Q+ C# X9 d8 A; N/ @3 w1 y( X下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。# ]' v E. E8 u1 a* v* f8 N( q% g
9 m( W: k3 E% f9 g# e l
85.4.1 第1步,使用STM32CubeProg提供好的程序模板( y, F1 F3 w/ a" t9 V
位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
3 c3 u0 a, M! y6 [9 q& F! Y# H) H+ w4 I; M
, R. M5 j8 N7 Q+ i; b
! ?6 D! \- u6 ^# G; T- r( B8 ]4 W6 S# H以M25P64为例(注,其余步骤就以这个为例子进行说明):
. v7 c8 Y; m- z6 d* `* k, H- T# e" y2 ?7 t, v
; I9 Q' }1 @. O# h
4 y; K7 K7 R: W- u8 Z4 m3 N
85.4.2 第2步,修改工程名
3 `% o, ~7 [% q) E6 j/ l4 G) O! pSTM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。6 f' X7 x& \/ N- M1 B
+ `4 K1 M+ _2 |% P85.4.3 第3步,修改使用的器件+ H+ y: s1 J8 u9 F( _4 ^- o
在MDK的Option选项里面设置使用的器件。+ b) c6 k* X" ?, M
5 o6 I) j$ a/ N! }4 O8 S
) [" n# b+ g$ {: B# q* x f8 w1 _
# k# R8 G- b# J. K# }! j c" _2 ?85.4.4 第4步,修改输出算法文件的名字
* G7 ?; j7 K z/ W这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。: R6 E5 P W$ z+ ~" y' w2 p
1 W, H: k) |/ U: b, I9 m
6 h0 o( U9 Q+ G
2 {* f! ^- G, o' a& @ {注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:
( \4 z5 d) W3 d3 _, O
. L3 K2 f9 ~ S/ P9 m% f4 ~) F; z) ?/ j) K5 y2 j
% P1 ~8 B1 ~& E+ L85.4.5 第5步,修改使用的库文件和头文件路径, R, _: R5 o, i- T& Z( w
根据大家使用的主控芯片,添加相应的库文件和头文件路径。
0 _! Z* F8 \2 u8 f1 `; F3 g' w# m2 l% a: S! `/ M
85.4.6 第5步,修改编程算法文件Loader_Src.c9 j; M% d! R( w" @. v
模板工程里面提供的是M25P64,部分代码如下:
0 d6 j& x" y1 E# G" E" \" S- @+ y, m9 T
- /**
9 j( m, k5 P) E - * Description :: D R! k& q" {$ g# L
- * Initilize the MCU Clock, the GPIO Pins corresponding to the
' P. F& H( x, V. N: I% n* O/ W - * device and initilize the FSMC with the chosen configuration
/ {) L( G) @9 m3 X! Y - * Inputs :
% V. H- J1 x3 F - * None
9 d3 l0 |6 M e5 l2 x/ B' x - * outputs :9 j/ v4 z) @9 M
- * R0 : "1" : Operation succeeded
8 o- h! S8 N! Z9 P2 K - * "0" : Operation failure& N8 _ Q a) D, l
- * Note: Mandatory for all types of device
0 Q- x& _3 U! ]2 Q" g, l9 v - */. o( o0 n5 s+ Q. V" `0 y
- int Init (void)
" t* y1 a0 {0 l# v& u2 o - { 1 N2 O/ B* K2 R
- SystemInit();
% }; |. M! |) q" q - sFLASH_Init();$ C1 ]- h& g/ V% c- a- {
- return 1;! q; k3 I1 f3 [2 U3 U4 N
- }
1 g5 r6 [8 I. U. m& E
1 R. k, G$ Y. j" S% Y' X
- g) z. h; K( T* _0 w# T- /**% k7 N9 ]8 `+ g8 S- ~
- * Description :7 {: W2 j3 J* p9 N" J+ x5 T+ E+ I
- * Read data from the device
?/ e/ s9 Y$ ]2 P: I) O - * Inputs :
" E& t# O# Z9 K* k5 j( S4 M; [9 n: X/ W - * Address : Write location) v* F4 Z" Q$ R2 {
- * Size : Length in bytes $ U$ E2 h; W8 w5 e9 O. S' H
- * buffer : Address where to get the data to write
, [8 d9 }) u E$ z! O: w - * outputs :
# j2 U8 \* d/ o+ j# T) O# q - * R0 : "1" : Operation succeeded) X) |' a" c4 E3 e5 g. e4 X1 H
- * "0" : Operation failure
6 p) ~% F5 z5 M4 K( O - * Note: Mandatory for all types except SRAM and PSRAM
" M, B' E0 L) A) l - */
6 T, o$ b9 }/ n$ T1 K9 v; k - int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)
& Z& O1 g) g9 B* u - { ' W- o) u/ a. _8 ^! w, R0 ]
- sFLASH_ReadBuffer(buffer, Address, Size);
7 Y* R- W8 z B6 G4 q( \ - return 1;0 g" J* M! L2 {* O4 G
- }
" [+ F7 @. t/ T: Q; W5 Q g
* Q+ g+ E5 m G% u
& ^( n# |& ^3 Q' F( K/ K* A- /**
3 o$ F' j3 Q% y4 W - * Description :
& w5 h F v% r# P3 z - * Write data from the device
/ Z$ V: ]3 m- g/ t - * Inputs :
* C0 `7 ~8 x2 {# G+ Q0 G - * Address : Write location! r0 Y% T2 |3 E M7 O$ m
- * Size : Length in bytes
* C* T" {, k \! o5 P0 j - * buffer : Address where to get the data to write' @- Q R6 X* n. ~$ z
- * outputs :/ e. o1 M' I/ X9 U
- * R0 : "1" : Operation succeeded
1 g. \3 K" m! o% m! ~4 O - * "0" : Operation failure
! y: B2 y& z3 B- f - * Note: Mandatory for all types except SRAM and PSRAM / N9 D6 {4 L! f, L2 h- t
- */9 r+ T: \5 |2 q6 W1 `, N3 q
- int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)
, `, l# M# W$ v: h/ Q9 B7 }! \/ h, x - {
2 Q' T% |+ }2 ^' s: \ - sFLASH_WriteBuffer(buffer, Address, Size);
" k7 _: B3 _1 m& O0 N8 U% ~ - return 1;* ]2 |2 A8 w. f" `& ?( A
- }
l7 l- _4 m2 Q3 h' @+ [9 w2 i! W
: j; P: p- T' o- $ _8 @$ `3 Z) M5 j! `) v9 p- a; y
- /**
9 d" n" t$ h' W' |8 N8 H. j - * Description :
$ M. z# |& m1 F3 k. U, k6 h2 j' { - * Erase a full sector in the device2 A: i2 P/ i5 z$ L; H0 \ v5 s
- * Inputs :. s7 e+ b( V0 ?# J& k
- * None; C$ O! w% ]$ `; g* n* f7 h
- * outputs :
% v9 L$ A$ N! S; ~8 M% A) R - * R0 : "1" : Operation succeeded
" s' q/ X) t; q* O7 B0 p - * "0" : Operation failure
$ P t6 g' w: s* i+ k - * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH9 c3 V0 W( O7 k2 e1 d
- */1 b' `+ E4 W4 ~4 f% k1 s5 K8 J$ V
- int MassErase (void)
5 O6 ^+ \& ^/ D( i( L0 z" H! n - { 4 V1 }. B; e2 m, K6 F
- sFLASH_EraseBulk();9 C4 e) I5 a# W7 W$ o) q* I
- return 1;
# r9 s; B, R1 C - }
复制代码 % j% n. l* Y6 h& e! n* S
85.4.7 第6步,修改配置文件Dev_Inf.c3 X3 h9 A% B. D% w/ _
模板工程里面提供简单的配置说明:
- v, O/ {) B' e6 J" a7 k0 A1 h% T! g+ I7 `
- #if defined (__ICCARM__)
+ n' ]& f) t3 _5 n0 m: y% { - __root struct StorageInfo const StorageInfo = {& S/ t5 [+ `% }; b: Q6 `& m
- #else+ u+ i. T/ |; E2 `; F' G# e
- struct StorageInfo const StorageInfo = {/ M4 P4 r" X2 N. y" \. @% T- b
- #endif
0 o" k7 n, t7 C4 O. V: k! \3 G - "M25P64_STM3210E-EVAL", // Device Name + version number
4 A+ Y! B9 k: I$ H - SPI_FLASH, // Device Type
4 O7 }( v3 L" F - 0x00000000, // Device Start Address* |, D. H: ` `8 p1 q4 b9 t
- 0x00800000, // Device Size in Bytes (8MBytes/64Mbits)5 Q4 l1 @/ u& ]% ~( w7 D
- 0x00000100, // Programming Page Size 16Bytes
2 h. H, m* P- v8 l5 G - 0xFF, // Initial Content of Erased Memory
' c6 _: Y6 X+ ]) H2 I5 Q# T - // Specify Size and Address of Sectors (view example below)
0 C" h* @7 c6 w) S - 0x00000080, 0x00010000, // Sector Num : 128 ,Sector Size: 64KBytes
$ E3 E3 L+ _8 P7 c( K - 0x00000000, 0x00000000,
# \/ q0 V4 [2 E% _ - };
复制代码
- Z% z* [9 a* l# R7 l注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。- F* m$ O% D5 x/ E/ Y
0 f! N; f! P3 x0 O3 r9 _% D85.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。
" z( \% }) w, S, tC和汇编的配置都勾选上:6 G# r9 @, B4 C( h
8 M( C# ?3 E6 r& |
2 S8 q& _+ d/ l9 Y3 I% s
" W6 h N$ W8 `0 I+ s9 P: Z
汇编:, D& w* W4 j( P& \1 N. {
! s% [0 ?: [9 f: T
6 y: I6 B; Y2 O0 ] y/ s
% @ U+ W. t5 r( o$ g( d& A2 P如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:
z' v) C5 K) h6 U
5 c7 R' b. g% l1 b(1)加载以响应运行事件。
! I' L6 K3 K& g7 b: {
$ N/ Z5 a" D0 w; r0 q(2)在不同情况下使用其他例程的不同组合加载到内存中。
# k0 X* h, v, |" @7 R6 W/ J& ^
& A6 X6 h# q* e6 _9 D7 t(3)在执行期间映射到不同的地址。
6 k+ u& n) x. l$ a- }8 |. T7 y. `/ N4 ^) a
使用Read-Write position independence同理,表示的可读可写数据段。
' l7 U; ~! Y) X2 r n8 u/ V: W6 N$ `$ a5 l5 M* V2 V" i
85.4.9 第8步,将程序可执行文件axf修改为stldr格式
; I1 c; k$ A& k通过下面的命令就可以将生成的axf可执行文件修改为stldr。4 i7 w4 m. ]( o3 z, _: ^% n
; y( _5 v! e/ @. A# i% A0 f5 v( T/ T+ E8 ~- D+ G* k
" n) Q3 U3 A$ Z( A
85.4.10 第9步,分散加载设置
. \' l6 i: y( c$ w我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。& B5 v6 h) }% m2 i+ q0 G
h" a1 W1 I; ^2 ?# R
, H. ]$ Z. z2 r U! v8 } l
3 t6 h2 K0 Q6 v9 A: }分散加载文件中的内容如下:
0 `/ O0 ^% b* T( ?1 L3 |6 \# Z' z5 X I: F2 A; D" T
- FLASH_LOADER 0x20000004 PI ; FlashLoader Functions
1 O6 n' G" x" X% j7 V! e3 { - {
, F2 f$ d% m* ]9 N - PrgCode +0 ; Code3 H' c7 n- `+ G$ Q L; l0 w1 i' b
- {
0 \# j4 q% `/ T: {9 j - * (+RO)
! d, p) j: |2 j5 I! L- P2 x# A - }; m# \; O# m, P. G5 k2 M" z
- PrgData +0 ; Data" w8 E, O; x9 \ \0 r
- {7 }8 C7 }, j6 O3 O( Q- ?
- * (+RW,+ZI)' t: L) m% ?& ~. R) |
- }; z& E1 S: o) Z
- }
& {/ s" H/ ~0 c: q9 g
* ?2 U- {9 I3 I& c- DEVICE_INFO +0 ; Device Info Z+ G9 Q- N+ N' w) y: B8 S' e* Q
- {
5 x6 h* _7 k, W, H b - DevInfo +0 ; Info structure$ e' j2 `; i6 y: r. I
- {
5 k, g& ]7 }9 n5 c& C1 _ - dev_inf.o
$ d4 n& \7 S8 l0 E% r: v3 a - }
$ Q& l" R( Q* b0 i% D8 X& C1 S - }
复制代码
- }* N5 u5 f* I8 c) x- D# E1 ~. y这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。: U/ e8 p* w+ r" [8 c. H
* p. T! }/ N0 [/ l8 i' O; u! m' _--diag_suppress L6305用于屏蔽L6503类型警告信息。
( I9 W! }, o( r% Y, T \2 X
& t( I" D& ~* V. x0 M& Y特别注意,设置了分散加载后,此处的配置就不再起作用了:* z1 Q6 c8 K- g
0 H G3 }- M/ i# L' r
1 e) M/ R' m! k% ~7 p# [. J* J; H8 I/ [
85.5 SPI Flash的STM32CubeProg下载算法制作8 X; B, i4 q& \& e3 Q
下面将SPI Flash算法制作过程中的几个关键点为大家做个说明。
; h. r& u9 ]8 }6 p8 e% q% Y, _. s) R9 C4 ~, u0 P3 g
85.5.1 第1步,制作前重要提示9 ?1 \9 ]% z) r! O4 K. f
这两点非常重要:6 b( R2 ~+ M% s# l: o
; M m3 f* z/ B
程序里面不要开启任何中断,全部查询方式。
' ]7 [1 u, X1 u" A2 w" w HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。) W4 C7 y3 k3 O; q
; Q* `7 _6 Q4 |85.5.2 第2步,准备一个工程模板
4 I6 r$ g- e6 F% N, J9 h 推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
# A; Z; _6 Q' q* f$ v* F: I% U5 S
. p' x8 v# s5 _/ R Z! f
* C% _0 }4 B6 ?3 M
$ x7 P" S$ Y- x; ~; a# A85.5.3 第3步,修改HAL库
/ e1 m" l/ x1 ~& y1 g这一步比较重要,主要修改了以下三个文件:
( |- C9 p4 b& |" B7 U' ]: p
+ k Y5 a) f ~9 W' A1 A+ ~/ k8 \! m, C" J# I$ x7 f
# G0 V- j q, R8 z, v6 f主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。8 [' ], }4 o k# B( {0 S
% x4 O" c% m0 N: j85.5.4 第4步,时钟初始化
4 E0 g; O c2 f, N* d' [我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:. N. X2 y0 v% k4 x3 m
0 [# a- G' e" ^+ _/ e) X1 ~( ^
- /*0 t+ L; o$ l6 ?" C- j9 I K, U
- *********************************************************************************************************
- o$ m+ V5 A9 o5 w4 W$ ~ - * 函 数 名: HAL_InitTick
: Q9 l: @1 N7 S, ~ - * 功能说明: 重定向,不使用
3 @( g' ` o4 ?7 A4 L2 s3 O - * 形 参: TickPriority
: ]- @0 L3 W! k9 }+ t) Q% N - * 返 回 值: 无/ G Z2 }7 e- s+ K9 B F
- *********************************************************************************************************
$ r; G& ]: s% j4 w8 h4 `3 G - */+ R( d7 S2 R9 ?
- HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)8 h1 y! `& k+ I: v& |& |1 X
- {' I! t; t6 y+ o
- return HAL_OK;
d1 u, d5 _- L: c* t9 R; \ - }
复制代码 ! p) F/ w% Y. W( a
然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:3 U- `9 u1 A! g1 |* h; d
5 v: A$ {' H; K" `0 z0 E: y- #if !defined (HSE_VALUE)
) |: y6 ~) n9 ~; F2 H, L - #define HSE_VALUE ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */7 A) O( m# {3 O! p
- #endif /* HSE_VALUE */
复制代码
: p& d- u& a: [- Y7 G$ ]: O8 Y" p4 k最后修改PLL:
' L2 n/ q% T% A
. c$ U, X( v1 O( E/ D+ m2 f- /*% l2 @' Q, b5 j% u8 b
- *********************************************************************************************************! F$ o4 G3 h9 k; h% k& N
- * 函 数 名: SystemClock_Config
- p7 D7 r0 l: l - * 功能说明: 初始化系统时钟
9 K! i* q: o5 U3 l/ ~; {$ p8 l: R - * System Clock source = PLL (HSE)1 e$ V3 W' j9 m$ B: _$ _
- * SYSCLK(Hz) = 400000000 (CPU Clock)
v+ X( [* R% n# J* s6 r1 O/ J - * HCLK(Hz) = 200000000 (AXI and AHBs Clock)( n% I; m& }2 S8 c8 A) L$ J; U% d7 k
- * AHB Prescaler = 2
; M/ q: B. \; ^% x1 p- c - * D1 APB3 Prescaler = 2 (APB3 Clock 100MHz)+ _8 R" d/ b" |' d
- * D2 APB1 Prescaler = 2 (APB1 Clock 100MHz)/ X5 [- r# E* e# \) f
- * D2 APB2 Prescaler = 2 (APB2 Clock 100MHz)
- c6 v& b8 ]5 \( f: S( w7 ]# T - * D3 APB4 Prescaler = 2 (APB4 Clock 100MHz)$ g5 y8 k! W* W0 |8 S
- * HSE Frequency(Hz) = 25000000
- H" Q. a( G Z3 E, r - * PLL_M = 5% |9 q( E1 ~9 h c; ^
- * PLL_N = 160, C9 `5 a$ K2 ]* v& [
- * PLL_P = 2
( H. X: C( f4 b( F9 @ - * PLL_Q = 4, D& V5 [" w4 a |# f s4 H
- * PLL_R = 2
4 c) L: c4 A* ~8 R/ U. n+ I - * VDD(V) = 3.3
+ E1 t% y+ s7 l# l' {3 I. q - * Flash Latency(WS) = 4, f5 k8 t: c f9 \2 w' i
- * 形 参: 无
# K2 {9 ]2 Y* v) k3 W- A6 u - * 返 回 值: 1 表示失败,0 表示成功
" d% L, G6 l t1 \: ]( |: B - *********************************************************************************************************0 i* w4 @/ x0 {# r7 s! U& b
- */( v8 G4 D( u! ^+ K& ~4 E
- int SystemClock_Config(void)
7 g+ N! `4 s4 @7 w - {9 ? D) e* M9 I W/ W. y- O2 y
- RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};& K- F% W! \8 B
- RCC_OscInitTypeDef RCC_OscInitStruct = {0};0 F' F) @. }# C9 b% A4 W
- HAL_StatusTypeDef ret = HAL_OK;
" A1 q% c4 m% C- S3 y
4 t4 X" ?; u' z2 W; ?- /* 锁住SCU(Supply configuration update) */
/ [& L: ^5 G- B) h' d - MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);/ ]4 M; x6 D0 X3 r+ e. F+ H
% m, z" n. g& g) i5 g- /* ( j) H" F( j& y4 H
- 1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
5 m9 Y' y+ Z, i6 ~3 I, ~; X - 详情看参考手册的Table 12的表格。" w* k/ O6 l9 T' s) M# B
- 2、这里选择使用VOS1,电压范围1.15V - 1.26V。5 k1 l8 Q8 i( |/ P5 n: p
- */
b( M) P' h* b$ s. y: \ - __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
' v1 e# c9 ]. P& s& ^- ?2 j - : Z( ?0 s$ A5 l+ T- e% z1 U; a
- while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
# m" @( `8 ~' \; s: {5 x; l
9 f, t) T1 ]8 \' z- F. j- y- /* 使能HSE,并选择HSE作为PLL时钟源 */
; E! Z2 y4 T% |5 e& r - RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
) s3 W- M4 G" N! Q' b - RCC_OscInitStruct.HSEState = RCC_HSE_ON;
/ v3 P/ P3 S% N; f - RCC_OscInitStruct.HSIState = RCC_HSI_OFF;. w& x3 P6 _- l) a4 E
- RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
. Z5 b! k2 k/ A* V - RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
% a8 Y5 N. x& @" J, E2 ] - RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
8 A- B. `) z( g. T3 Z6 F8 z - 0 U" F" M3 `- H8 b) Y
- RCC_OscInitStruct.PLL.PLLM = 5;- [. f! ]8 @* x1 b& W
- RCC_OscInitStruct.PLL.PLLN = 160;) M) m( G$ Y. ?- o" o
- RCC_OscInitStruct.PLL.PLLP = 2;! I2 j- `: S3 h4 \
- RCC_OscInitStruct.PLL.PLLR = 2;0 B" [1 [) j$ Q
- RCC_OscInitStruct.PLL.PLLQ = 4; ) E2 d4 q9 D. Y1 y; r# T! D
2 u# }) g; F0 D$ J9 e- RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
# J9 Y0 M# h/ C- A. k) t - RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;
. g6 t6 c1 T: E7 k - ret = HAL_RCC_OscConfig(&RCC_OscInitStruct); q, {7 V+ w, u
- if(ret != HAL_OK)
7 P9 U7 ^- Q* e7 k - {0 h* e" A) ~9 d; D+ ?9 R0 v
- return 1;
- j+ {) `' D% H, j( f7 z' n - }
: J' L6 S8 e5 I - / n p# d) q$ x. R4 p
- /*
4 k$ P5 m9 w; O1 _. c" D+ i* v - 选择PLL的输出作为系统时钟
% }/ J' a* o; T6 ? - 配置RCC_CLOCKTYPE_SYSCLK系统时钟
/ x4 U k- H5 T/ w - 配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
. b; U& b1 I/ X. o3 k3 b8 ?/ I - 配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线6 C- X& ^% F, V$ }5 j2 d
- 配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
# G4 \7 f* U: Q6 ?5 I. p* d4 {( y4 H, ] - 配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线) M2 B: H, ]# l
- 配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线 * |) U! _; n. e5 q8 B/ z: `
- */
- L" ^1 B, \3 X* ? - RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \8 `+ W( _$ Z% Q+ p( i
- RCC_CLOCKTYPE_PCLK2 | RCC_CLOCKTYPE_D3PCLK1);
# T, G$ \2 c* N4 H' N - ) K' ^/ F8 m& H% z6 c ]! k$ {' m5 t3 |
- RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
4 N# `: r% I1 J5 k- m - RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
/ e; A, M/ S/ \0 g4 }0 {! [; k, y - RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;4 r6 @: n1 L6 K8 F- x
- RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2; 1 q$ J' S+ k, k( _& Y% W$ t
- RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
) P- P; t4 s4 ^+ S/ P5 Z) B - RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; 0 f9 O$ U: Z; j2 ^- `3 i
- RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; W; Q% F8 T r9 J3 l9 L: }
5 r# T0 J4 I; z, [8 F+ z& r- /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */, n; C: K! \4 v0 t5 \
- ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4); E* a# p" N& ^5 M
- if(ret != HAL_OK)9 }9 r/ J1 O- o1 H* I
- {( v: Y6 v( }- Y S9 l
- return 1;
* o# W" k# z, b a# F* x* i - }9 `, G3 d" L ], l
- N7 @6 G# A, A* F# t6 g, i. t9 J
- /*. f9 E, [' h% [8 B% e
- 使用IO的高速模式,要使能IO补偿,即调用下面三个函数 " j7 t. K" g ~# L1 B1 V: z
- (1)使能CSI clock
. h4 Y& M% r& L& F/ D/ F% |, \ - (2)使能SYSCFG clock ~2 ]7 b) M* Z3 i& m2 j
- (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
# b/ {/ C% m9 V Y: ^. ~% e+ U - */5 R7 i* M6 ~; Q7 j
- __HAL_RCC_CSI_ENABLE() ;" ~9 v0 l) ^5 W i. |7 r2 {+ c
! j) B2 X. N5 K* i% F- K( M. m, y- __HAL_RCC_SYSCFG_CLK_ENABLE() ;
5 @8 n3 v7 [! A% `
: i0 g& C( X0 e; ^% i- HAL_EnableCompensationCell();
; I R2 V; _2 s' @- E; b7 k
/ _2 q& R, j8 t: V- d4 |- __HAL_RCC_D2SRAM1_CLK_ENABLE();
3 w) p3 O* F9 q% H* O - __HAL_RCC_D2SRAM2_CLK_ENABLE();: f) |0 W F6 w `
- __HAL_RCC_D2SRAM3_CLK_ENABLE();# j# a- j0 q6 R8 L
- * E9 v$ K5 J* C8 @* {
- return 0;. E6 }% {8 [" Y2 E) ~& l, {
- }
复制代码 * Z" c4 }. C; u/ v) Y0 S0 j# T/ ]' m
85.5.5 第5步,配置文件Dev_Inf.c的实现) D( {3 T* P' b2 \" W
配置如下:/ l! r4 I" a1 J$ t0 ?0 L
) W; v0 @: V8 h8 q
- #if defined (__ICCARM__)( l, x; `9 A r; X4 T: \
- __root struct StorageInfo const StorageInfo = {4 k& W2 o' y8 z# |" I6 r7 Z
- #else) Z) E# E5 [$ f6 G- v7 g
- struct StorageInfo const StorageInfo = {0 y8 A& Q* d }: N; Q
- #endif5 m% w' X0 o8 H+ U
- "ARMFLY_STM32H743_SPI_W25Q64", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */
) J2 u- N/ R1 r: r - NOR_FLASH, /* 设备类型 */+ C# _/ G" o4 N" P b% c8 x/ V
- 0xC0000000, /* Flash起始地址 */ l8 J" _* C: Z! |9 X
- 8 * 1024 * 1024, /* Flash大小,8MB */
) R$ i0 T2 i) T) q5 I0 v3 x - 4096, /* 编程页大小 */
0 _* n9 l2 T% x; T. ?# N - 0xFF, /* 擦除后的数值 */( ^0 z! D" l2 v
- 2048 , 4 * 1024, /* 块个数和块大小 */
$ w0 |. a! R ~7 ?; a - 0x00000000, 0x00000000,8 _+ \) I x$ W9 n0 c
- };
复制代码
) V$ i. h9 T4 o N' f注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:1 y) h# F8 z6 G3 H# @# t
8 D( `; |4 @5 W) f2 [
- [$ J2 `# B& U6 \3 [/ E; }
- P1 U- w1 j H85.5.6 第6步,编程文件Loader_Src.c的实现3 V+ }' J n; o4 }' V% h
下面将变成文件中实现的几个函数为大家做个说明:/ h: A! E. y8 t8 Y8 x t9 L0 _2 I
% m1 }6 x9 N8 v4 s5 ~ 初始化函数Init" }: q+ r5 r/ F( q7 I2 z
- /*4 R6 ]6 p+ `6 M3 k* d/ L4 `0 Z
- *********************************************************************************************************9 p8 d! y. |( d# Y3 Z2 ^
- * 函 数 名: Init( ~7 _9 j( c$ n
- * 功能说明: Flash编程初始化
( B0 @- T/ {9 b( n - * 形 参: 无
9 C E4 x9 X- R$ z: H+ x7 ?. L - * 返 回 值: 0 表示失败, 1表示成功. k9 Q2 v8 v0 w6 B j2 y/ n
- *********************************************************************************************************& G- B7 |% N: l/ E8 p# |3 z
- */
, O+ X; V: M2 P/ B1 {9 g! H. i% K* Q* U/ ^ - int Init(void)* \3 ^8 J' U6 r' D
- {
. U3 f, Y/ {* F5 k6 _ - int result = 0;$ h5 M! k1 i) q
* H* X" j" y& ~7 `% g+ r- /* 系统初始化 */! v) T5 G0 ]1 ^. C; K; ^& I. J
- SystemInit();
- b. I7 K2 O5 @" b( o7 V$ |8 S - I g9 P1 K0 Q6 C
- /* 时钟初始化 */
# A* P" l l* F! Y1 q' \ - result = SystemClock_Config();' x2 P. D3 N" R7 k: o% X
- if (result == 1)% y- k: B: ]8 }, u5 s! _
- {
2 |% Y( ^& |& }- i: F( y5 y - return 0;- V, s+ h* N5 }5 c; o
- }
# [" c: D' V' B* D' a - ; q% b, h) G5 u( _
- /* SPI Flash初始化 */; t& }3 {( k+ W1 L% r! ^- T
- bsp_InitSPIBus();
8 L2 i( n. K) w% ~" t* ~# a - bsp_InitSFlash();2 U {0 T) ~ T" J( K' u
- ( A9 l. o3 A3 W$ \" U0 g; U
- return 1;
9 D- o' V6 l" X" V6 d3 o: B - }
复制代码
5 F2 w }# @9 s m5 S' d$ f3 E; H 整个芯片擦除函数MassErase- T' D3 Q. B. E2 z* s
整个芯片的擦除实现如下:
' Y, _; C4 n/ {# T: `5 m" W( G* s
2 K7 p- W1 m/ T( P( I! S f. B- /*
* o* {& t# K C, | - *********************************************************************************************************- P# n ^( h! I" @
- * 函 数 名: MassErase
# N! Y8 X6 U4 B* b5 [& i( ? - * 功能说明: 整个芯片擦除
# E4 J- h6 J" \. i | - * 形 参: 无: G* G/ S% @4 t+ }
- * 返 回 值: 1 表示成功,0表示失败, C' S( d: n: i- H" T0 i
- *********************************************************************************************************4 I: R- Q# h$ g% d) u4 | A3 p
- */ P5 G3 i* u7 s' h5 | ^
- int MassErase(void)- q5 a Q' Q0 j; q4 z
- {
1 |' h1 s" ^ U - sf_EraseChip();
: l7 l) z; p: ^1 U - ~! V7 ]( ~3 J
- return 1;
/ y9 l7 ?- x, e" A. W2 ^* ?$ s - }
复制代码
) t& }- p, T) b3 v& W 扇区擦除函数SectorErase. }. R7 i) x5 `
- /** E: ~2 m1 K8 ~* Z; p: C
- *********************************************************************************************************
/ X7 }) \. Z1 x) d; |, L - * 函 数 名: SectorErase! S& p! j6 y" t( N9 X
- * 功能说明: EraseStartAddress 擦除起始地址. w" t. S# ?$ P# s* a# Z3 f* C7 [/ Z
- * EraseEndAddress 擦除结束地址
4 [) l) H& Z& f - * 形 参: adr 擦除地址
# ?/ @8 g& ]5 W! e& S - * 返 回 值: 1 表示成功,0表示失败# `9 x$ |4 f" y6 ~
- *********************************************************************************************************
: w: R( }6 \/ f) _" ~2 ?! R - */- r& z. j2 F5 O% F' B, R# F
- int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)- }) W9 @/ t! p( E; Y& }$ W
- {
/ i7 f6 g0 `! \. r) X" B - uint32_t BlockAddr;
c5 F9 p% m6 y; L2 n: ]
) n( E" }8 R1 _& j" {$ g- EraseStartAddress -= SPI_FLASH_MEM_ADDR;
3 n7 E" n+ z# R5 X2 Q* D - EraseEndAddress -= SPI_FLASH_MEM_ADDR;8 f% q: H, N" ^
- EraseStartAddress = EraseStartAddress - EraseStartAddress % 0x1000; /* 4KB首地址 */
' O6 _& T; v+ v" }( v
/ A$ d% ]* l: O1 L/ m- D+ h& D- while (EraseEndAddress >= EraseStartAddress)+ p8 Z+ D, t/ {$ E
- {, O2 l# z" A4 @3 b9 u( J4 @3 T |" J8 _
- BlockAddr = EraseStartAddress & 0x0FFFFFFF;
. n5 Y) y" F4 o* z5 T - 6 F, r1 V; ?& m: P2 L! l
- sf_EraseSector(BlockAddr); 5 r% `- z' O! I# L+ R4 W
$ q. R1 b3 n. T$ l3 U! p# Q5 @- EraseStartAddress += 0x1000;
3 ~% c- r! l# }" _' G4 B( n5 f - }
; x+ Z+ Y6 K$ q6 K5 f7 ~9 C4 x: F# ` - 8 U) G. l$ V! |. a" l9 F
- return 1;
5 @# c& _# u- r2 g9 c9 D - }
复制代码 ( a! ^9 d0 K+ @: |; {& S
这里要注意两点:$ I4 q0 Z/ C% ^% @+ _2 b/ ?
7 x, Z! ]" A5 w8 G L9 [; |! p7 U
(1) 程序里面的操作EraseStartAddress-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
! P8 a% \- E% T' q, m* B5 S
1 q! b) W1 ^* j# f F; p(2) 这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。# |5 D" v2 I. `; e/ l
$ b; k5 `' J" r/ f2 ` 页编程函数Write1 J& t; @, F; M1 ~9 r
页编程函数实现如下:
, R" J: v# S/ T, ?2 F" ~' |# f/ Y9 M$ Q* y8 Q- X d
- /*3 A& c9 u" I2 d7 A# i
- *********************************************************************************************************
( @5 | l7 y5 O- a3 J6 K1 W - * 函 数 名: Write
) n( i+ b5 I$ W - * 功能说明: 写数据到Device
; V) q1 k$ U d0 q - * 形 参: Address 写入地址
$ S4 V( |$ v3 ^; c7 d( X - * Size 写入大小,单位字节
0 H; d, T* Z/ S* S( k - * buffer 要写入的数据地址, U3 o* B# N+ i& T* I
- * 返 回 值: 1 表示成功,0表示失败- e) j9 j) G: J
- *********************************************************************************************************. g x3 v8 l" n( g. [& t" d. T
- */
0 Q# G6 j3 C- J, v: Z& v - int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)
! K% t' S+ T: ?* c, L. W) h0 d - { 4 k4 V- {% d, e3 q2 F7 X
- Address -= SPI_FLASH_MEM_ADDR;
) c/ t6 ~( U: m; _
" h* ]3 M, K; o" G& K% j) \4 }- sf_WriteBuffer(buffer, Address, Size);
7 U/ T1 @7 G# O- ~/ T6 D - 2 w8 A0 _0 }+ u7 E6 R, u7 k
- return 1;
3 C* |' D# Z4 P( m3 X8 o7 J - }
复制代码 7 d6 A5 `2 Y+ W3 P$ M1 |! t0 S
这里注意两点:
- O, Z' o. ]% |4 S) |6 J6 g. i
! d( G: C2 }2 B ?9 J H1 l(1) W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。$ W( T2 P' B0 l9 p% T1 }* \8 l
$ X3 t, F* h4 d; B( e(2) 程序里面的操作Address-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。$ ?% }% g) M/ G
, ^ K- i8 T2 l9 d
读取函数
+ A! G. |' [1 V4 {# p4 z- /*" f8 e, b# @) m# |2 \4 W( r
- *********************************************************************************************************
: |" O. B7 E4 | - * 函 数 名: Read
# Z& Z9 e1 `; q# I: U% I i - * 功能说明: 从SPI Flash读取数据
' F) q: y* p9 S+ \ o$ u. f. T& X - * 形 参: Address 读取地址
, y2 G; a; Z( E* c" @& Y: J# Z - * Size 读取大小,单位字节4 L, `0 a5 @4 c7 ]# q p$ f
- * buffer 读取存放的数据缓冲
& r; H/ @. M) D: X4 l1 @0 ] - * 返 回 值: 1 表示成功,0表示失败/ _0 u$ D' b$ V% X4 q2 u
- *********************************************************************************************************
, ]3 [1 u" C* D; W, X - */
2 F2 v' G/ W/ |; q9 k* v - int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer)
" J; a1 |# V$ { k - { * G; O1 n- Q' z; p5 E+ W4 [6 d/ s
- Address -= SPI_FLASH_MEM_ADDR;
8 w- Z& F" s& O* n z: F8 x - sf_ReadBuffer(Buffer, Address, Size);
7 _- h( h3 m" I0 i
3 O) V/ `0 o: T- n- return 1;
. h, T+ Y( G( E3 R2 Q - }
复制代码
+ b E' d4 l: E/ k' q 读取和校验函数3 y5 U% A4 p, j c
我们程序中未做校验函数。如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。3 F1 n! k1 J, g# q) G# E% o
/ X* n2 f. p9 r85.5.7 第7步,修改SPI Flash驱动文件(引脚,命令等)
0 Q% }! {- i6 O$ x最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:
7 s2 r" ]" | R* O. L+ X# ^7 d# \. R- j3 u
- /*/ \( r# \9 i2 B1 f
- *********************************************************************************************************
- a1 I+ x$ N* ] - * 时钟,引脚,DMA,中断等宏定义
2 p0 r3 ^! N% \9 H( |" M+ n! \2 w - *********************************************************************************************************
5 w* ~7 e9 o7 s - */- n' w8 a( k7 d4 M/ P3 E& l
- #define SPIx SPI1
/ T i9 G5 H# J$ _, Y - #define SPIx_CLK_ENABLE() __HAL_RCC_SPI1_CLK_ENABLE()
. U3 t+ x _( F; z( b s* d+ Q% v - #define DMAx_CLK_ENABLE() __HAL_RCC_DMA2_CLK_ENABLE()1 D& l' O8 d# x. z) o6 e
- ) i) ?' M) E: X2 b ]
- #define SPIx_FORCE_RESET() __HAL_RCC_SPI1_FORCE_RESET()
5 ]7 M1 G9 t( g! Y" ]' | - #define SPIx_RELEASE_RESET() __HAL_RCC_SPI1_RELEASE_RESET()
5 o( w; m$ f5 Z* ]% [3 R# O
1 [3 u5 P8 W& s: m; @$ B- #define SPIx_SCK_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
\2 [/ R) n$ A2 d+ `+ v - #define SPIx_SCK_GPIO GPIOB4 o8 u* [* A( b" ^3 f% v
- #define SPIx_SCK_PIN GPIO_PIN_32 I4 K4 b; H- b9 w/ q
- #define SPIx_SCK_AF GPIO_AF5_SPI1
8 @- Z! m% X- p' Q+ D2 L9 p
7 P- Z7 | I7 x6 O6 o$ u- #define SPIx_MISO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
c$ N. V) A" T - #define SPIx_MISO_GPIO GPIOB
+ g) R; z- C- R9 _* | - #define SPIx_MISO_PIN GPIO_PIN_4: ~+ P( b. ^. O1 j9 n* t
- #define SPIx_MISO_AF GPIO_AF5_SPI1( Y' u1 S1 E i9 C
- % o: o8 U& f. k4 q3 g( n- F$ c' l
- #define SPIx_MOSI_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()* q& J0 |0 ?: _6 z
- #define SPIx_MOSI_GPIO GPIOB( D0 }3 S8 d! M: m
- #define SPIx_MOSI_PIN GPIO_PIN_59 O7 Q1 q& X0 ~/ B O) V
- #define SPIx_MOSI_AF GPIO_AF5_SPI1
复制代码 ( ~4 @: D, q5 x" S. K
硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:
2 e( h; e, ]% y2 g$ |* a# u* l
. O; Q [, k4 T4 m* J+ F" G7 y主要是下面这几个:; j2 z: Q+ K. @3 n( O8 F
6 |" O6 q9 V2 ~- /* 串行Flash的片选GPIO端口, PD13 */, R) z) _0 z- a' T
- #define SF_CS_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE()" F7 d s) A& n
- #define SF_CS_GPIO GPIOD1 }7 ~; I+ n8 C2 u: V
- #define SF_CS_PIN GPIO_PIN_136 ]* K y- p9 E; i7 C8 |& c
7 n6 ?3 G* j) ^- H% e/ `% W0 s, ^- #define SF_CS_0() SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U) : }% j- t: \% ]- c& ^3 u
- #define SF_CS_1() SF_CS_GPIO->BSRR = SF_CS_PIN, \+ H* j4 T# j3 C3 f2 y" e j7 W
- " M. V, Z% Z: ^# o9 }
- #define CMD_AAI 0xAD /* AAI 连续编程指令(FOR SST25VF016B) */( R9 ~6 Z1 ]+ V
- #define CMD_DISWR 0x04 /* 禁止写, 退出AAI状态 */
& d' I; Y# E* _2 G! ~2 {2 l - #define CMD_EWRSR 0x50 /* 允许写状态寄存器的命令 */$ k. I4 `2 O5 E! r
- #define CMD_WRSR 0x01 /* 写状态寄存器命令 */8 P2 r. r u+ Z0 k/ a3 A
- #define CMD_WREN 0x06 /* 写使能命令 */2 Z1 A* J# B- q2 C8 R: y7 `
- #define CMD_READ 0x03 /* 读数据区命令 */
$ |& a/ e+ I0 P3 \5 c - #define CMD_RDSR 0x05 /* 读状态寄存器命令 */. q- j& @& l, B6 a
- #define CMD_RDID 0x9F /* 读器件ID命令 *// @8 f7 @- g! G+ c) |
- #define CMD_SE 0x20 /* 擦除扇区命令 */% {7 b, \; X% ?
- #define CMD_BE 0xC7 /* 批量擦除命令 */
8 b* x1 s3 L& G9 \' x/ d* k - #define DUMMY_BYTE 0xA5 /* 哑命令,可以为任意值,用于读操作 */
8 X3 h/ Z' |. G' e S - " V/ s* x5 r6 J# F H* t9 T
- #define WIP_FLAG 0x01 /* 状态寄存器中的正在编程标志(WIP) */
复制代码 % M5 w$ ~: w) q: _- [6 e
85.6 QSPI Flash的STM32CubeProg下载算法使用方法2 s M- N) s* o; \% v, y$ h" _; W: y
编译本章教程配套的例子,生成的算法文件位于此路径下: a4 G: X% t5 o2 e3 R
( r% H% G$ Q; i) ]2 ~6 A8 p F
! L+ r( N; V: @; |7 b- `; R. X6 Z$ I
85.6.1 下载算法存放位置- D. K }: [; }
生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:2 T. D( s @* {' `& Y+ u
2 B# }6 U) ]$ l: O' ~0 k$ ]" p
\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader% ?4 r; m$ E3 F/ A+ T7 L* M
5 T1 U6 Q: w: I: j
" e1 ]% z4 D& x# L9 M
- W; Q$ G( _4 s7 S* A B+ m85.6.2 STM32CubeProg下载配置
4 j" B& c7 d2 D1 {我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。# ?; H5 Z9 @4 Q1 k2 c
# v! Y( {" Z. A
7 J. O" Y1 f0 O& l4 ]8 i0 E/ V: z( P: X; A* ]3 d% X5 h
点击Connect后效果如下:8 U6 m* Y$ Y+ R1 V* n
" F: U2 {& ~7 J; j
8 _: Q. G# f0 o
/ K1 T& |* b4 v3 c% N, T" a/ N
在这里选择我们制作的下载算法:
- ^' ~6 c/ z* ~. K; t( n" P/ s
: M ?& O( C. P' y% P
! ]$ R! Q6 G8 F2 f6 j( U# D w) h1 f1 R7 r
任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming
5 |; S K. L! d: Q, l2 `/ |: ~/ \% W% n& l H# Q
3 \! \+ t- `. p9 [4 p* J f& L
# k% ^% k+ }# w7 I" A. B$ `下载完成后的效果如下:
0 B; }7 E: d& {, y9 V( S" s* Y6 h
9 t7 p( S4 {; f, j, }" i3 L2 M
; n1 Y% z8 L! C- |7 O. p% V! w+ P- j ^. V6 Z; d' d! x8 G
85.6.3 验证算法文件是否可以正常使用% [& ~4 N, `2 }6 P
为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。也可以使用STM32CubeProg直接读取:* F$ }$ o! H7 J+ @/ ^% r
; ~, I' } z$ H& Z- @( H/ ~4 x3 [* ~3 v" D8 t7 o0 ]0 G
1 x1 K) I4 v3 q8 O2 u85.7 实验例程说明
1 j, A2 M* r" v( {" Y本章配套例子:V7-066_SPI Flash的STM32CubeProg下载算法制作。! \) u) `% d+ W# Q5 _& ]- M
! s$ B; F6 n- r
编译本章教程配套的例子,生成的算法文件位于此路径下:
2 @( T# V/ N5 B2 A: K# g$ U4 X/ P9 V6 a2 d
* n) ]- m0 J% Q' m2 `' V2 j! K
" N9 _0 E, u' \8 q3 B0 q! @, o# ^85.8 总结, F; E7 S- y; y3 E2 h. ]/ Z
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。# {" {% L- u/ |% d+ H1 f' T# P
1 l$ K3 Y! r6 \
7 m. O4 i$ x/ b/ g7 J3 s
* C9 F! }! w& a |