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