84.1 初学者重要提示
+ c6 ~5 T* d. k( z1 l" q SPI Flash的相关知识点可以看第78章和79章。
5 n4 {# x' M: E# l/ w SPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
* y+ g1 ^7 n: U4 Z' d- h84.2 MDK下载算法基础知识
- c4 s0 `/ [; Z" XFlash编程算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。MDK本身支持的各种器件都自带下载算法,存放在MDK各种器件的软件包里面,以STM32H7为例,算法存放在\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软件包版本不同,数值2.6.0不同),但不支持的需要我们自己制作,本章教程为此而生。( N0 b; G. a2 f: H% j
`4 v: U/ C9 ?# ^3 I4 T
84.2.1 程序能够通过下载算法下载到芯片的核心思想
8 M* S& a8 w- \, N; U* N! o; Q认识到这点很重要:通过MDK创建一批与地址信息无关的函数,实现的功能主要有初始化,擦除,编程,读取,校验等,然后MDK调试下载阶段,会将算法文件加载到芯片的内部RAM里面(加载地址可以通过MDK设置),然后MDK通过与这个算法文件的交互,实现程序下载,调试阶段数据读取等操作。
# g% [2 }$ f( n; U* f8 q* Q7 L: Z$ `% _: L/ i G
84.2.2 算法程序中擦除操作执行流程
# O+ R( R8 i2 _3 W ]擦除操作大致流程:
. c/ O! X9 m2 @( |3 b9 v
& K5 h! u$ k( O( I- K+ O% g) V3 K2 b
( v' X! C8 `! q" j8 `/ G7 [ i7 K/ M: u9 K9 {
加载算法到芯片RAM。' c& T; ?3 G5 \2 i7 o$ x; S
执行初始化函数Init。+ e8 K: ?; q7 o7 k/ x2 ~! V5 O
执行擦除操作,根据用户的MDK配置,这里可以选择整个芯片擦除或者扇区擦除。8 i; }7 e5 H) H; C& }
执行Uinit函数。 r4 ^1 _2 P; E6 h9 \3 l/ I
操作完毕。
1 t, X3 W6 i6 k% U, F84.2.3 算法程序中编程操作执行流程
! R, E9 W( @/ h8 L/ R; `! c编程操作大致流程:
! O. J* S& Z( o0 o; N, a) ?* k* o8 N6 F7 t7 [! n
; {4 G* `+ V* \" V6 m' Q
/ u: ^$ f* O% M" Z/ X1 [. y
针对MDK生成的axf可执行文件做Init初始化,这个axf文件是指的大家自己创建应用程序生成的。
+ s& T* z9 W. F 查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
0 d3 ^+ r- I& w+ B# _# o: H 加载算法到RAM。
3 E* z! s: j/ M5 T& l8 U% w( ? 执行Init函数。$ d" O5 k0 T# A1 m4 n4 d' O: H
加载用户到RAM缓冲。
# ^ Z# R' h3 k 执行Program Page页编程函数。) T9 d* q/ Y7 v n
执行Uninit函数。
4 q" x- @3 c1 f 操作完毕。 C' e5 e8 D( N0 H3 r; q: Y# Q/ {
84.2.4 算法程序中校验操作执行流程
+ ^- F5 \; S/ w" U. {# ~1 \校验操作大致流程:# L9 K6 t% z$ ]8 b& R0 U
+ y8 \' I& o- m
) }9 |9 ^' Y" ?
- ?5 X( s- p7 h7 g) I/ s 校验要用到MDK生成的axf可执行文件。校验就是axf文件中下载到芯片的程序和实际下载的程序读出来做比较。
6 W+ S. d0 A/ e7 v( i 查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
& i5 J* s B" _; U, J 加载算法到RAM。
6 z! t2 p* T2 L' Q0 t) T4 I 执行Init函数。
3 z& t$ r! y1 P0 { 查看校验算法是否存在7 S$ V3 Y! i1 y2 J4 I
如果有,加载应用程序到RAM并执行校验。: r. f/ ?( f; v$ ^
如果没有,计算CRC,将芯片中读取出来的数据和RAM中加载应用计算输出的CRC值做比较。( q' ^ q$ Z) l$ n, D- h5 i. v
执行Uninit函数。
6 R% @: B6 H: ~ 替换BKPT(BreakPoint断点指令)为 B. 死循环指令。9 d$ r( E. _6 d0 i, c6 l* B
执行RecoverySupportStop,恢复支持停止。
0 F' i. y, s( H$ ~: E 执行DebugCoreStop,调试内核停止。
0 g3 O6 Y8 c# m9 B% c/ A 运行应用:! _" w! l" `8 I' q/ j, n
执行失败。. J8 a1 ?* p+ u4 P2 o
执行成功,再执行硬件复位。
5 Z. O5 v$ F( o& Z# ^; U 操作完毕,停止调试端口。. H! B. d; P- d% o" H( i
84.3 创建MDK下载算法通用流程
/ U" W8 k( x+ ^' y& I$ W! z下面是MDK给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。
7 e( I) H0 a2 |
o3 l8 A; T! D2 X84.3.1 第1步,使用MDK提供好的程序模板/ I. y4 ~& D# I9 D- z- r
位于路径:\Keil\ARM\Pack\ARM\CMSIS\version\Device\_Template_Flash。
0 B3 b1 u* ]) k7 x. w5 y' _
1 Q( \/ l% O W* X0 D效果如下:
/ {! g& E4 T6 M2 o
2 R" h& C' R: V0 q% K5 b2 Y U; w: A0 N0 ]
# w. \ B+ z) d7 K
84.3.2 第2步,修改工程名
$ [1 Y5 U" S; z% T) CMDK提供的工程模板原始名字是NewDevice.uvprojx,大家可以根据自己的需要做修改。比如修改为MyDevice.uvprojx。, L6 L e5 H4 ]
: C ?6 ?5 ~9 d4 S2 j2 w! G84.3.3 第3步,修改使用的器件
; P1 ?2 i' X1 p! @" S在MDK的Option选项里面设置使用的器件。- v7 w" }& x$ i8 u2 ~3 a$ H
n d7 |, A0 B9 q$ j, u
1 [3 D! X0 k) f7 V$ N
, P" b1 A. e6 k/ a* { H y$ ~84.3.4 第4步,修改输出算法文件的名字+ X. R a/ }+ S: k }. {+ G
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.flm。
9 U+ [3 e3 h& r, `& n+ O3 O1 {5 m- ^8 ~5 \4 `- S3 Y3 ~
; H, m' n! C) w3 ]; A/ y+ M% m! Q
: \2 S/ o: f+ L8 }$ y/ _注:MDK这里设置的名字与下面位置识别出来的算法名无关:
# e. @; E* I/ e |4 ^+ a _( U* N
7 {& F _- A3 \$ O9 _6 e0 `! E+ G: ~5 Y ]# v6 E
1 J r/ G6 s" r: X# G, Y8 Q
这个名字是在FlashDev.c里面定义的。
; G) X9 J8 {7 P4 `. B+ L+ E/ f8 o( Z- z1 w4 H6 h1 Z- e) C. d7 m
84.3.5 第5步,修改编程算法文件FlashPrg.c- f3 e* v" R: ~/ f
模板工程里面仅提供了接口函数,内容需要用户自己填。
D, L: f6 w8 z0 J$ d2 J0 n r$ b
- /* " Y V9 N+ o; t2 d) L8 c) V
- Mandatory Flash Programming Functions (Called by FlashOS):
l$ u) K+ k" m' A% N - int Init (unsigned long adr, // Initialize Flash
G& E3 J+ q1 S8 o8 _ - unsigned long clk,4 F3 k% O6 | L% h6 ]
- unsigned long fnc);: d: X" N# B$ x I% m
- int UnInit (unsigned long fnc); // De-initialize Flash7 r8 d" ]. O+ u6 J5 s) a; b
- int EraseSector (unsigned long adr); // Erase Sector Function
, q; l! e* G! P5 r - int ProgramPage (unsigned long adr, // Program Page Function
+ w, _$ B" A9 Q/ U" ?0 U: e. U - unsigned long sz,( p, H1 e9 x' A# R- t4 D
- unsigned char *buf);- K# e$ R" c9 B" C
/ ?$ f* S9 Y9 p- Optional Flash Programming Functions (Called by FlashOS):% [1 S7 s% v& H6 X4 y; j
- int BlankCheck (unsigned long adr, // Blank Check
; N9 {* {) A$ q* _2 _ - unsigned long sz,
/ o! i6 ?' l; b' p1 X8 \3 {7 Q8 | - unsigned char pat);
6 U+ p5 u/ z2 M - int EraseChip (void); // Erase complete Device
8 O# G1 }+ W) f% Z - unsigned long Verify (unsigned long adr, // Verify Function
% j8 @5 P( p5 ~5 `' B% w6 `2 N8 @* X - unsigned long sz,
' p! l) u: a: K _: r3 y x - unsigned char *buf);5 U* `7 S# h$ x r% P( b. s, A" U5 k
5 d; }+ I6 J/ G3 c- - BlanckCheck is necessary if Flash space is not mapped into CPU memory space
: m4 Y! q6 I! @% S# r: k' u9 { - - Verify is necessary if Flash space is not mapped into CPU memory space! T! E/ ~3 Q' z' I
- - if EraseChip is not provided than EraseSector for all sectors is called* v' K+ R2 z' l
- */
M2 s( x/ w# ?8 P - # G8 Y3 c9 u: C! n+ ~$ p& q/ v, Z7 r
- /*
, g( S2 G$ w. T) T# U" m - * Initialize Flash Programming Functions
0 g5 D$ G2 u. b) ^9 {5 Y8 e; J9 i7 i - * Parameter: adr: Device Base Address
! p, |4 B6 b1 l7 X4 g5 y+ M - * clk: Clock Frequency (Hz)/ \8 S# v5 F2 a6 l
- * fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify)
1 e0 O/ Z* o) n; [, U- e' v& y - * Return Value: 0 - OK, 1 - Failed6 ^. e. {8 w5 E, n
- */
# R5 p* K. R+ Z6 p4 ^ - ' f# W. D3 g" m( O+ p/ V% E
- int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {
) I! k6 N( s) a) Q* [/ O - + q; _1 ^: |% D
- /* Add your Code */
5 d: ^8 G1 F( J* D2 ^ - return (0); // Finished without Errors" Z" f5 A. Z( Q0 Q$ W" z
- }) y0 B* o( ]5 A& ?+ t3 n, x
- 4 f9 ]! c- A0 Z
- 3 I! @( V- D( H9 A5 c+ E
- /*( Z( f# z3 y( c
- * De-Initialize Flash Programming Functions1 t# z! i7 H% }: y, _, e
- * Parameter: fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify)
* K- P5 }( v2 ?% H3 \ - * Return Value: 0 - OK, 1 - Failed
5 }0 u, Y( A( ?& o0 ? - */1 l2 W& u$ m4 Q. U1 B3 g) F2 f
- : e! R- h, [9 x7 U/ E' p3 l
- int UnInit (unsigned long fnc) {" ]+ y3 L2 s0 {& K) _
- ' }% }) k& F3 }( c8 G! E
- /* Add your Code */( D9 \ ` g" r
- return (0); // Finished without Errors
i3 g8 v1 b) Y. T& t' y5 G - }3 h, o+ J' C& B; \
" T, N& `: t' q* l( s* l/ z6 t$ E. q
. C# ^+ X/ L# h5 Y: s6 [0 t Z4 C" |- /* Y* f( B0 v& u& D5 }( U/ A! b8 t% y
- * Erase complete Flash Memory
' V! W1 n0 O' I$ X- a - * Return Value: 0 - OK, 1 - Failed+ b! U- [9 Y2 [' s% {
- */
6 R3 G! V4 D' O4 ?0 P1 @ - 7 j& h. g$ m3 g3 u& ]7 @1 e P9 r O* M
- int EraseChip (void) {
- J' i8 ~( Z8 T2 W3 g
7 _3 L8 t$ e1 D! f0 W) G) e% ^. j- /* Add your Code */
2 Q( V% l5 \6 D# t, p8 y4 _1 l4 D g - return (0); // Finished without Errors+ y- C3 d" C+ W* K2 d+ t* h8 d
- }. t1 C# C4 w5 I% I- U0 r
- " T* B( Z8 c$ [+ Y( A$ D1 X$ i% V. Y
- & J5 I8 `* b# g5 ~( {
- /** T! S9 D3 e1 m( Z' {
- * Erase Sector in Flash Memory- \& y8 T( G! g1 y- N# @
- * Parameter: adr: Sector Address
3 {, @5 o$ H, E% w" e - * Return Value: 0 - OK, 1 - Failed' z: Y2 O8 s6 n% o* v6 H- C; w0 q
- */5 _ d+ [3 F, p5 H& W8 O8 I- J
- ( _$ M5 [7 o0 o. z* u2 |. s
- int EraseSector (unsigned long adr) {
+ @5 C& s* c- z' R4 h1 ?$ G' z - ! l c$ ]5 `7 }7 ^; E
- /* Add your Code */; C( D7 a, V) |# @7 Q& S- b
- return (0); // Finished without Errors
3 q* V5 B* Y5 M* u8 ~0 l - }/ l( t1 Q4 v5 H$ ~9 U! t
; Y c+ u7 l' H& C" b- 3 ~) e9 V8 V- c* G* F
- /*2 S+ W2 v# ` u' Z
- * Program Page in Flash Memory
5 q# h# u1 u( D% R - * Parameter: adr: Page Start Address
5 X9 t+ w% A. @& Z( ] - * sz: Page Size
9 X3 n8 V" `; G9 N. { - * buf: Page Data$ f6 B3 A: I& A2 n. C. S1 |
- * Return Value: 0 - OK, 1 - Failed
/ x* ]( I0 S/ ] - */
, T' \8 j4 |; n - 9 K. |0 G9 O) `. y) S2 \# O9 W0 z4 y
- int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {% X& u/ q9 U; l# b( |
6 {' r- n2 M$ b5 R9 B5 f/ I- /* Add your Code */6 y( y+ {5 q4 L @ F. K
- return (0); // Finished without Errors$ c: r. X ^: p `
- }
复制代码
3 U7 s- @$ F3 }: x5 }84.3.6 第6步,修改配置文件FlashDev.c
! f% C- Q/ B7 D3 `! o模板工程里面提供简单的配置说明:! o$ X7 o6 i' F4 Y9 n
$ D" M* J+ @7 F& A) |; a- @5 |2 Q- struct FlashDevice const FlashDevice = {
* {1 |. T M; ^ - FLASH_DRV_VERS, // Driver Version, do not modify!
3 U1 t- h4 Z) P$ @ - "New Device 256kB Flash", // Device Name 6 t9 z2 }0 j4 `; P/ ~& }8 Z
- ONCHIP, // Device Type+ m( f* m b& f: `- q
- 0x00000000, // Device Start Address Z: L' F9 b% {+ |6 ?
- 0x00040000, // Device Size in Bytes (256kB)) o1 S4 t1 p0 }7 S: m' n: b
- 1024, // Programming Page Size1 F/ |9 F* ^7 r/ ^2 l6 f3 a% W& r
- 0, // Reserved, must be 0
+ p# n6 I ]1 U - 0xFF, // Initial Content of Erased Memory% P/ x; Y3 T; G& u: n0 M4 d7 I2 V- G
- 100, // Program Page Timeout 100 mSec0 D; U& t( d7 [
- 3000, // Erase Sector Timeout 3000 mSec2 G5 @5 T2 J. X. U. d
- ]( }7 ?' i* \) S; q! ]# i" p- S- // Specify Size and Address of Sectors; [' u/ o) g, D9 H+ C% O
- 0x002000, 0x000000, // Sector Size 8kB (8 Sectors)
: H5 F/ G# E7 P" S5 U - 0x010000, 0x010000, // Sector Size 64kB (2 Sectors)
; z1 `) d" S' ? {# W - 0x002000, 0x030000, // Sector Size 8kB (8 Sectors)- x- e! V* c2 b. ~9 K% |: Q
- SECTOR_END
! N% u+ V* C" {; |7 L! M$ ] - };
复制代码 $ z6 m9 _& G4 i5 v: v _
注:名字New Device 256kB Flash就是我们第4步所说的。MDK的Option选项里面会识别出这个名字。
- G. M w+ a' j- a! b4 N& ^( N1 \8 L: M# d% l
84.3.7 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关& A( @5 Y& y- c( u! `% T+ [ i1 j4 H
C和汇编的配置都勾选上:
; @) d% J2 U6 L' _% ^' ]8 z0 l |$ \7 k
/ f4 p9 N e1 j; X: ?9 J; v5 M7 L# [
! X9 O) S" I( r* q6 [ n" Z
* a! H; q0 Q! ?% f6 a' |汇编:
, n9 X( T. o: h- G. N% V3 _6 |5 J
0 o! o x; {! t' }
0 y6 S1 {+ @* s( h2 `4 n如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免用户不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:7 ?- h0 x% U$ p% U3 A
2 Y: Q* h" H7 b) X ~. E: I
(1)加载以响应运行事件。
0 h- r0 l9 f0 G) z8 ]8 A* {: l1 o$ o" m" q3 n u2 @5 @* I: w& q
(2)在不同情况下使用其他例程的不同组合加载到内存中。
( }4 c2 d9 S; X& i6 {3 _: g9 G
1 ~; z2 ~% k. F1 X5 S x* i( k( g. K% ?1 c(3)在执行期间映射到不同的地址。; s) I2 I. P5 D6 V8 s5 @
7 O V5 l3 ?- Z s. r
使用Read-Write position independence同理,表示的可读可写数据段。, p- M, l$ A1 r/ _% d! b6 I
3 J1 s5 b4 \3 n' v' f0 e4 Z
84.3.8 第8步,将程序可执行文件axf修改为flm格式
0 U2 M! a" ^+ _( K& _* E; r通过下面的命令就可以将生成的axf可执行文件修改为flm。
' Y8 H( b" d% g- X4 w- d! m; v0 u/ P
3 U& R5 G0 `1 O! B s. w9 e0 f
6 o6 f' t" U; _
84.3.9 第9步,分散加载设置
& L9 T- z( r p, w5 m我们这里的分散加载文件直接使用MDK模板工程里提供好的即可,无需任何修改。8 h! C, ?7 _- f( n, P
6 {* ^- Y) U1 T) T
! t! D8 d0 Q) b4 I2 o0 r, _( c3 P" D/ M+ T$ U
分散加载文件中的内容如下:
$ e) A% j2 p$ a9 z2 y. |
1 ?& f) ]& D# ~" E7 z) f' r- ; Linker Control File (scatter-loading)) l( [* B8 n4 J; _8 u; c1 a
- ;9 h- E; g2 s$ D& D9 J
; [$ _# ~8 U( Z* x- PRG 0 PI ; Programming Functions8 m; h- U, ^/ N
- {% F4 a, `. L& z/ X1 P2 @ s
- PrgCode +0 ; Code
: t+ q% ?$ ^ `% ~+ j2 ~2 E2 @$ R - {: L. W( A3 t! U7 r$ R) K9 ~
- * (+RO): ^$ ?! M3 [4 x% S( o0 d! r+ ~
- }
7 p7 C: e* k! m' C& g- v" \ - PrgData +0 ; Data, S5 y5 o% I7 p- i4 Y9 \3 h
- {
/ w& s9 {, i% N! h1 s8 x; `8 f - * (+RW,+ZI)
, G g* p* d! g8 `8 [4 a! e - }
( ~, R5 A; X8 o( H) }! E r - }
" V) r* R% \: p( _, m- V' l, w - 4 L/ u' c4 h: Y B9 k/ g1 m( l
- DSCR +0 ; Device Description. l: t) T3 b1 [' ~. ]
- {
. D2 ^ U- f: ?1 H: |& w# T - DevDscr +0
! g: D' c$ r* g3 W - {* i- Z* ` u7 O% q6 f* m
- FlashDev.o
* ]( ], B, r5 U) K& `- w - }( [2 r" \' @+ S' r! j! L1 f9 N
- }
复制代码
1 m. z4 U0 j, H! b* W$ E9 G--diag_suppress L6305用于屏蔽L6503类型警告信息。
1 ^$ V, C* L! A. m. j, `, z4 q N+ h, \' M% M
特别注意,设置了分散加载后,此处的配置就不再起作用了:/ c$ I8 j- n% F% J: B' H0 u. N6 P
2 \, _1 `3 T8 I. p2 B
1 w! J* Z/ c$ ~8 J( P1 I; I
3 C; V$ X8 ?" x6 P* j
84.4 SPI Flash的MDK下载算法制作
8 {2 h$ s, L8 c# g$ l下面将QSPI Flash算法制作过程中的几个关键点为大家做个说明。' ]; R2 b; ]1 ?$ I8 S5 V1 A
5 Z& ~& J5 S- \' k8 ^9 Q/ H" }84.4.1 第1步,制作前重要提示" L' \5 Q% x2 A; t
这两点非常重要:) T8 e; l& c; a: }6 [9 n3 d
9 m2 ^; q$ ^3 \$ c& K/ H. @) T 程序里面不要开启任何中断,全部查询方式。3 G2 a# {& p. v9 G
HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。
5 n( a8 c" H8 a84.4.2 第2步,准备一个工程模板: \, N8 f! `+ v5 e
推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
. s* f% }* P. B A6 q1 \0 _2 {* x; v
2 q- ^0 B( B: u
- _/ p! b' W% K1 [
9 n2 f1 e+ G1 U2 W% e9 Y5 ]* l84.4.3 第3步,修改HAL库
0 t6 D$ ?8 ?; u W+ y; H这一步比较重要,主要修改了以下三个文件:
$ H) x& B) I, K) ]$ S4 I( {0 }! c6 z! m
3 H# ?8 R: I3 t1 L# ^; b/ j
5 U2 m6 w2 H. i- c8 g主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。
/ K3 {8 v6 a. E; {, C6 c+ x. I' K: N! C# V( T8 Z9 Q
84.4.4 第4步,时钟初始化
& U' J$ [. n! R! Y- k, P0 B/ }我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:
: F6 ^! E& ]. c+ A# j) L. z/ p: `4 ~( n# k O$ w; Q+ q
- /*( {9 M, b; I2 N- ^: ]
- *********************************************************************************************************
1 T# O, D+ O! x! c: f - * 函 数 名: HAL_InitTick0 B; U' B/ ^$ k; E
- * 功能说明: 重定向,不使用& m: ^. z; S+ t8 Z
- * 形 参: TickPriority# ]9 q$ t8 J2 w4 v$ A- n" q
- * 返 回 值: 无2 J: T6 v' ^1 S. I a) c2 q* B6 `
- *********************************************************************************************************
. E; {9 A& P: F4 l6 p! r - */
d8 N) l0 z/ m. U9 _6 L9 ]# U9 g5 R - HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
; T: j. N# Y7 v1 b" n: j- g - {
( h9 P" a! P& }# Z( h; [6 F4 i" Z - return HAL_OK;
) z: l2 A3 n( k) M3 o! T6 | r, p - }
) D' R2 e6 X. N0 f6 n1 Q+ B - 然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:7 t& y' S9 l5 B/ u6 {
- 9 z, l* E; ^% Y7 Z/ z
- #if !defined (HSE_VALUE)
5 F, j* {0 E, `# Z1 x* g - #define HSE_VALUE ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */3 W: O T1 a. U5 z0 U' a
- #endif /* HSE_VALUE */
; a H+ b0 Q& w7 Z, F - 最后修改PLL:
7 n4 x% j. T+ w - " s% N+ q v2 f
- /*
- u2 h7 P' w: H - *********************************************************************************************************) f5 [1 ^# Q. U4 X1 p, d
- * 函 数 名: SystemClock_Config# ?% X. S _4 g
- * 功能说明: 初始化系统时钟2 W; G* Q: u+ ]
- * System Clock source = PLL (HSE)7 q8 _+ @" y' D2 @0 f2 `
- * SYSCLK(Hz) = 400000000 (CPU Clock)8 F/ h- D! P; t% G- U7 r _9 C" G/ f
- * HCLK(Hz) = 200000000 (AXI and AHBs Clock)
6 q7 y" y* @9 `0 H# M: y( e - * AHB Prescaler = 29 {& e D+ q3 |4 L3 S6 o# |
- * D1 APB3 Prescaler = 2 (APB3 Clock 100MHz), _2 w- K T* l6 M9 s8 z8 h) D
- * D2 APB1 Prescaler = 2 (APB1 Clock 100MHz)- {3 P+ K# i1 [1 m% ^! c i
- * D2 APB2 Prescaler = 2 (APB2 Clock 100MHz)' H) v! d+ F, Q
- * D3 APB4 Prescaler = 2 (APB4 Clock 100MHz)2 h S) p! o3 K
- * HSE Frequency(Hz) = 25000000
6 {# }+ d! M. U! ] - * PLL_M = 5
6 R2 L: h7 y2 _5 ]- H; c - * PLL_N = 160 y6 a, I+ N6 ?6 h
- * PLL_P = 2
& h* `4 O; Z6 L: E0 q+ ] - * PLL_Q = 4
9 }+ s8 s! Q& z8 J: S - * PLL_R = 2
5 i9 y4 x! g9 u' d( ]$ s% ^. l - * VDD(V) = 3.3# z% U7 c' t% F2 J8 ]
- * Flash Latency(WS) = 4
, h H! v5 W$ k+ z. | - * 形 参: 无
1 n5 ~; v1 @/ t6 G+ R - * 返 回 值: 1 表示失败,0 表示成功/ L3 y( s8 b2 s9 M
- *********************************************************************************************************. O7 h! M0 [$ Z# `) O. j( w
- */- ~- G$ t3 v6 b. N
- int SystemClock_Config(void)4 | _' t$ Y2 r5 J9 y* o x0 ?
- {
% z. ~2 G2 X' G* } - RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
5 n2 G( k+ a, v h* V0 q - RCC_OscInitTypeDef RCC_OscInitStruct = {0};
: o" h: y$ ?) C* D0 w% s G - HAL_StatusTypeDef ret = HAL_OK;% h( Y, g+ C6 ?
- 5 V9 ]0 p- O `2 J
- /* 锁住SCU(Supply configuration update) */5 L; f7 u# Q+ p( o% ~- @4 t
- MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);8 M' | R" k- ]' u; g
* M8 T1 z% A5 |, I' I' J- /*
& `6 J+ U0 [9 I( v, m. h. A - 1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,1 l$ _7 o/ e) a T
- 详情看参考手册的Table 12的表格。
' B* T8 r# w+ h( a3 q |9 h - 2、这里选择使用VOS1,电压范围1.15V - 1.26V。; |) T5 [% O6 J* t: a, B8 N; i
- */( j- @+ Y5 y0 t# e
- __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
m# [; K8 Z A- R9 {: r - $ z! H1 ~1 c$ K
- while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}7 {% D% g) a$ O1 k0 o) J
' C+ C2 h. U$ n, g- /* 使能HSE,并选择HSE作为PLL时钟源 */+ `/ K; x& x" l! n- U O
- RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;2 b% i- R* Y! r7 {; X
- RCC_OscInitStruct.HSEState = RCC_HSE_ON;
6 \9 ~9 \0 z% E* j$ C - RCC_OscInitStruct.HSIState = RCC_HSI_OFF;* P' f3 E' \) m& l# c7 Q. _' S* N
- RCC_OscInitStruct.CSIState = RCC_CSI_OFF;' @4 {& i" Y' h2 Y9 s" H; m
- RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
7 O* H8 p Y3 p! y3 ~/ S - RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;9 A& S/ n2 U# {, s
+ k6 o4 P8 l& \* c- RCC_OscInitStruct.PLL.PLLM = 5;
7 N) c3 {# ?: X9 G - RCC_OscInitStruct.PLL.PLLN = 160;( k2 }, v+ f3 ^ \# q$ s
- RCC_OscInitStruct.PLL.PLLP = 2;
( G# g+ I* x. v0 _5 ` - RCC_OscInitStruct.PLL.PLLR = 2;
' a [( I" Q' Y4 f - RCC_OscInitStruct.PLL.PLLQ = 4;
8 ]2 w+ W1 G: L& X0 l) f - ~+ L" W2 Z2 ~ f) ?# V
- RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
7 K ^, w3 k0 W' {( G3 k - RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2; $ x& A0 \& c1 n* b% t/ Y7 f
- ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
' x/ D2 P% }4 K' `: k7 D - if(ret != HAL_OK)2 _# L5 U1 x- v- L6 K
- {. q7 a; U$ L6 i+ g+ I1 a4 N
- return 1;
% f2 q+ z6 R: R6 I- N. w0 V - }8 t ^6 Q% U% n4 `6 D4 `8 c
- # w g7 }6 X" e- E
- /*
8 p9 x) ~( f$ h- Y/ S3 t - 选择PLL的输出作为系统时钟
$ ?/ a1 S/ e- E+ t: d; [ - 配置RCC_CLOCKTYPE_SYSCLK系统时钟( F9 ^8 j: h, [
- 配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线0 X9 v! {: @/ g! p; ?/ V! W, i4 f( l
- 配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线% j; g( t2 [/ `
- 配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
% u+ ^+ u" D9 m; q' d4 y - 配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
0 r: }4 ], o5 K9 l: }" c4 H - 配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线
, A5 T0 }0 [1 y! G% }/ u - */
. _2 N Y9 G; X$ L. U& O - RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
' Z) `6 _7 |' N, d3 _9 n# y - RCC_CLOCKTYPE_PCLK2 | RCC_CLOCKTYPE_D3PCLK1);& p- }% A2 w0 K0 P
' l8 X3 h# F6 ?% G- RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;$ z4 `+ k$ z! S# k3 B8 I1 l4 J
- RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;9 R" M# E8 ]# z5 H7 g* ?% {) m5 C
- RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;* {0 ~: l$ H) J1 @: q+ U+ z
- RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2; ) p" c# v( `; d+ t
- RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; 9 V1 D2 s- x6 a* ~$ w; H E0 w- \
- RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
4 w& r% _9 H0 O3 t - RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; 5 \3 ]! |6 N' P
- 9 A# F* Q: v3 Q/ m
- /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
. v! E! b5 x) \1 w% Z3 } - ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
: x8 z' B* h2 E4 B$ T1 D- o( v! h - if(ret != HAL_OK)
5 z4 |1 u; O2 f4 `& a8 G$ G$ y - {/ n% W, [9 @) [0 s$ i, P% p& L$ S
- return 1;
$ q. ~7 u+ u* p$ L# W8 k+ o - }: H7 T; t' b) |& r
- ; {- o. N, B* f+ ^: l) l( U2 U5 p
- /*( w0 }( X( a R9 m6 ^; l+ X
- 使用IO的高速模式,要使能IO补偿,即调用下面三个函数 ! ^" }1 L* d% h; C, G# L" c
- (1)使能CSI clock
; D8 R" R& W6 _* r6 C - (2)使能SYSCFG clock
5 U" ?- R3 \( B' x% g: u# h - (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0, q/ { s, B. ^6 O" l q( p* k
- */' Z6 O5 z* t- H' P
- __HAL_RCC_CSI_ENABLE() ;7 z, W" w9 F; N" w$ T. C
2 ?! E- ^; L; Y; p0 {& M. V" m0 r+ T- __HAL_RCC_SYSCFG_CLK_ENABLE() ;: G8 P! h0 V( c) T; C8 o( \
- , L' ]3 G9 y, V Q, y! g
- HAL_EnableCompensationCell();
8 o }4 D- S; r
E- F" a. }# M% O' `2 a5 b* k* p- __HAL_RCC_D2SRAM1_CLK_ENABLE();
* M. e ^( z& z* e - __HAL_RCC_D2SRAM2_CLK_ENABLE();
, z& g, I) K7 Z4 n5 w; b- X: V - __HAL_RCC_D2SRAM3_CLK_ENABLE();! j V8 j7 }# r& D( e
- 7 `4 d, z# R5 F6 U4 u2 T/ v
- return 0;6 V, r T, X/ r" W: f6 b$ h$ R3 I
- }
复制代码 6 O6 S, V. N% [" t- g6 n; }2 s
84.4.5 第5步,配置文件FlashDev.c的实现
- R# F* l7 M. r; p配置如下:9 \# P) D3 J6 p* p
6 s+ T2 D" u% u2 r( G# N- struct FlashDevice const FlashDevice = {. Q5 P* c% u5 f! }$ R( t. O' h
- FLASH_DRV_VERS, /* 驱动版本,勿修改,这个是MDK定的 */ y! I) R1 R0 f d; _) Z3 q0 R
- "ARMFLY_STM32H743_SPI_25Q64", /* 算法名,添加算法到MDK安装目录会显示此名字 */9 k' }# G0 z! q# k: G
- EXTSPI, /* 设备类型 */) u6 F' E6 ^) B8 z5 G$ f
- 0xC0000000, /* Flash起始地址 */1 ]& Z+ s3 r6 w- R& U2 i
- 8 * 1024 * 1024, /* Flash大小,8MB */' Z" U+ L, g: P( N, W, e' ^
- 4096, /* 编程页大小 */! D. L( I: I$ M# X3 M
- 0, /* 保留,必须为0 */
$ H& m* H5 j8 G; v; P - 0xFF, /* 擦除后的数值 */
& l) \* c; K$ o* w& H - 6000, /* 页编程等待时间 */; @( y& i4 C* K! g7 X
- 6000, /* 扇区擦除等待时间 */
) M( l; D7 n G) T6 w2 v. e - 4 * 1024, 0x000000, /* 扇区大小,扇区地址 */* m! c0 e! Z: L/ P: I7 D- j: j5 [
- SECTOR_END
( }! l w Y* | - };
复制代码
0 W' s# `# V1 c3 ^7 k# P注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:' v% O/ `7 W8 Z$ q* _+ O( O/ G
8 f( ~" x$ S1 M% y
1 h1 U8 H. G3 g1 |! |
% W5 M* A4 U' c' I' a. P: I8 A. x/ i84.4.6 第6步,编程文件FlashPrg.c的实现: H+ M1 n2 P" y
下面将文件中实现的几个函数为大家做个说明:
: _- J) m L, e7 m
( e4 F! ?( C) c# j% V/ U. G) P9 J 初始化函数Init
/ d9 m1 g6 I" v* i5 s. \0 \- b1 V5 |- /*
: R4 K1 R: e4 ^+ ]5 o9 a - *********************************************************************************************************9 Y+ G6 [' t+ Q2 p6 z
- * 函 数 名: Init) Y- a3 O/ \! t5 J4 r: K9 Y
- * 功能说明: Flash编程初始化' B5 T$ S9 H: L4 K" `
- * 形 参: adr Flash基地址,芯片首地址。+ D9 M6 P( G4 Y" X5 R+ }+ F
- * clk 时钟频率" E* A& }" d \4 ~+ B
- * fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify
& F- @# G9 V2 V0 q d- Q - * 返 回 值: 0 表示成功, 1表示失败4 K" D# W. }1 s: l7 Z
- *********************************************************************************************************/ b- X% n9 l2 Q y
- */7 o# l( B& A% D& H
- int Init (unsigned long adr, unsigned long clk, unsigned long fnc)
% G- ]$ i) w4 T6 C, W( J$ k2 h - {& y8 X% V+ L* h( L0 U
- int result = 0;5 s- ^; m3 ]. a! p+ i7 ]
- , o/ J3 m* ?7 k
- /* 系统初始化 */
9 r. J( K7 ]/ }; a8 I - SystemInit(); ! B0 {" ?% D; \/ t* p6 h' o) L
- 4 I8 [2 R3 C; A
- /* 时钟初始化 */0 @6 [4 V* a! I6 a2 i
- result = SystemClock_Config();
1 n+ p# }* T- \. y5 t6 Z% H2 |1 f - if (result != 0)
- Q% ]+ z6 A8 \8 [& M - {( U0 Y6 p9 u; [
- return 1; ( c2 M4 R8 t, d2 o
- }1 h# Y: F) [& {
: i% X0 x1 F( @7 J- d- bsp_InitSPIBus();: s, w4 }1 ~: N/ |6 E, F
- bsp_InitSFlash();1 l+ D0 {" [3 e
; c2 y7 l8 G4 B- e; `- return 0;; k" z3 o% f# d! f. K' W
- }
复制代码 + q i8 A# U i8 F# f/ I
复位初始化函数Uinit
! |7 z0 S6 i& j/ \擦除,编程和校验函数后都会调用此函数,我们这里未使用。. @( N& I/ d( X" K( r$ G! l! p
, B, _' a* _% N5 N9 K
- /*3 _/ D; X8 p+ w4 m8 |1 ]
- *********************************************************************************************************. `) |! b8 S. k- V2 }7 f
- * 函 数 名: UnInit L: v2 `3 U8 l, D# f
- * 功能说明: 复位初始化8 g2 ?+ X4 X4 ^
- * 形 参: fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify
5 N+ X- I5 Z/ [# m# x - * 返 回 值: 0 表示成功, 1表示失败
1 M' z- j }) f8 l* C - ********************************************************************************************************* b# U# U; E3 {/ `9 d
- */3 D$ d* T3 B% k+ ?, N% k3 v
- int UnInit (unsigned long fnc)
' A6 d: Q% J- C/ n8 i+ B4 Q# E0 T - {
0 k Q0 Q7 p% T) {! n0 ^ - return (0);
5 ^; {$ v2 B/ P8 C7 Y - }
复制代码 ) [# r/ m/ R* T. ]
复位初始化这里,直接将其设置为内存映射模式。! _0 c% r% E1 |
& R7 C( ?9 `- p1 d
整个芯片擦除函数EraseChip$ Q4 m& ~" i/ w6 t4 q- K
如果大家配置勾选了MDK Option选项中此处的配置,会调用的整个芯片擦除:- O$ @& v f8 O) t, A, }8 c
8 |2 p- t7 S8 ^8 G. ?- g! v
" \1 `, F u; n8 y& `1 q+ _1 z2 e6 q/ d
实际应用中不推荐大家勾选这里,因为整个芯片擦除太耽误时间。' c4 L) g( P- s9 j2 T% \& K Q
) {8 e. v1 r$ I6 G/ ~+ c: u
另外,如果大家的算法工程里面没有添加此函数,MDK会调用扇区擦除函数来实现,直到所有扇区擦除完毕。
0 h1 K/ b2 l1 R& W6 S
" F/ E. a% b: y1 s- /*$ Q C0 j1 [0 N$ \
- *********************************************************************************************************/ R3 x' g+ H6 c6 o
- * 函 数 名: EraseChip
: I+ t+ |( Q5 ~( V2 v - * 功能说明: 整个芯片擦除
! J; L* I# |; m" I - * 形 参: 无
m+ o# B. o5 L# ` - * 返 回 值: 0 表示成功, 1表示失败; B% E0 q& a; _4 h* t ^# j
- *********************************************************************************************************) {" y u$ w7 ^6 U& @
- */. E) K! Y' h5 _4 W
- int EraseChip (void) 1 S B( B# `+ n9 p
- { : z* x- t4 x/ x4 Z
- sf_EraseChip();8 ~8 {; W8 A* F4 \
- return 0; ! k- D& V! F2 T" I3 Q
- }
复制代码
6 Y. _& u* ] [ 扇区擦除函数EraseSector$ t/ p0 K& h9 t$ U7 P" v
如果大家配置勾选了MDK Option选项中此处的配置,会调用扇区擦除:
# v+ t* h5 ]. ^3 K+ Y N
' e O5 G/ |! }4 `- i
- V" N/ @5 n2 D, [
1 F2 i. L+ H, D# h- /*3 r0 C3 Z ^3 w/ b
- *********************************************************************************************************
5 i; T% o* x' J! u3 q" U - * 函 数 名: EraseSector( a4 O. Y! ]: P& k5 d
- * 功能说明: 扇区擦除+ h% b+ \9 N4 D" s% S
- * 形 参: adr 擦除地址
; k$ C; M7 M9 P$ I. ~" ^! X - * 返 回 值: 无1 z& f8 I+ a5 ~: A# h1 o* V
- *********************************************************************************************************: B6 p" J' c9 E W8 X2 Y1 K
- */
3 A1 J& F8 d9 i; | r& H- b - int EraseSector (unsigned long adr) # m: s/ P. A% W, u3 ~( g% }
- {
/ \5 \$ W. l8 D" s" q, U - adr -= SPI_FLASH_MEM_ADDR;
- E7 z c* I& a' L0 b4 p* v; D8 L
; z l: k8 r3 a; M+ S, p- sf_EraseSector(adr);
) n5 ?/ n. j i9 u5 b" q5 ?7 v - : D! m/ P& g9 p a9 v6 `& a
- return 0; ) m3 o/ i/ \: X( o
- }
复制代码
. U4 z# R. V/ c% o* |这里要注意两点:
O. j n! l% q4 f5 S) J' g& {# z: |) j9 \
(1) 程序里面的操作adr -= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。特别注意,我们这里的0xC0000000是随意设置的,因为STM32H7的标准SPI外设并不支持内存映射。* u1 @4 U1 D$ Z9 {
8 l1 e4 e) v9 d5 y) O$ L
(2) 这里执行的擦除大小要前面FlashDev.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。4 g p; j; W s- e: N
' h8 Q3 O2 Y2 E7 {" Z6 L 页编程函数ProgramPage! Y$ m7 I2 L1 a: B) ?$ j4 H. e
页编程函数实现如下:; M% i6 D0 h, S6 ^0 i$ W1 e
" J$ M& X9 n: \: E( `
- /*! Q0 ^ V F4 E2 d j
- *********************************************************************************************************
) t( T8 ]7 R$ m1 n0 N - * 函 数 名: ProgramPage4 A% \; P$ ~7 c, O6 Q* s
- * 功能说明: 页编程
; c' l& Y1 K' U, U% H. b5 M - * 形 参: adr 页起始地址! m1 G! F/ O* @$ c
- * sz 页大小* c& M% N( \6 f
- * buf 要写入的数据地址
/ r, c5 [7 D3 c, S - * 返 回 值: 无
F( B5 c$ k- \# C l - *********************************************************************************************************6 [6 D" R+ I3 y( F" G8 l9 ?! r
- */
: P( }9 `- a8 Z7 { - int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf)
3 X$ L5 x/ U+ G' D( T - {3 M5 h% b5 ?$ k+ w4 c
- adr -= SPI_FLASH_MEM_ADDR;
+ ^: G" x" k- M
) o! T+ k) U% t: ?0 d- \2 O& a- sf_WriteBuffer(buf, adr, sz);
. x' E5 | x! l. q - / S$ K) c) J5 Z, _
- return (0);
8 s7 W- c* {8 e8 M1 `1 N" T - }
复制代码
# h( `5 u% R: M: a! R, }: i4 w这里注意两点:
9 R% J6 @8 Q+ [, y* I) o7 b1 h! H4 S" [+ C# K% O$ H$ [! ~, G- {, Z
(1) W25Q64的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。/ C7 v e/ W( \0 V6 m! _! W- F
4 {7 W& Y3 M; }+ f/ K+ t
(2) 程序里面的操作adr -= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。9 D& d7 B4 Y) R
3 g3 u7 P4 D' w
读取和校验函数; @0 t- f% E+ n4 l
校验函数实现如下:
; H$ y3 B& Q! x/ Q+ v
, r0 N7 W7 |. R4 h @% Y- /*; D' X+ V! M3 G# c! b8 R) _
- *********************************************************************************************************' ]+ b$ ]4 ?$ t9 d2 |4 G
- * 函 数 名: Verify4 X4 ~0 _8 A& r/ n" Q& j- b
- * 功能说明: 校验" c+ ~8 |' E! L( X' C( i4 l2 u s }
- * 形 参: adr 起始地址
8 y5 y6 m: u8 v/ |2 b - * sz 数据大小* d6 k8 H3 ~+ K
- * buf 要校验的数据缓冲地址
7 x- t" X$ @' O( D - * 返 回 值: -
6 _1 ~' n/ f$ e6 l* q - *********************************************************************************************************
; f" m: z& R+ j2 G& R1 {' s1 T - */0 a* T7 k) |% }: p. D# U: V2 Q, A
- unsigned char aux_buf[4096];6 [7 O) B c2 M
- unsigned long Verify (unsigned long adr, unsigned long sz, unsigned char *buf)5 k" h5 e/ A+ c: N; m$ V) K$ q
- { K. s0 s5 Y% ^- r# r
- int i;9 |. ~. O/ d4 |* b! B
9 A1 m- f0 f1 K* B" Y- b; y1 \( o- adr -= SPI_FLASH_MEM_ADDR;
6 V8 `7 P" f+ |( j - ( ~( ?; a7 {6 T& w0 [! _
- sf_ReadBuffer(aux_buf, adr, sz);3 D* c Q9 B* n5 k; }" Q
Y2 v+ `9 {' ^, \- for (i = 0; i< sz; i++)
, F3 k" U0 C2 [* s" T$ i7 K - {
: K! O' J9 m0 R2 L1 H - if (aux_buf<i style="font-style: italic;"> != buf) 0 j/ K9 y7 C. ?7 @; F( P# {
- </i>return (adr+i); /* 校验失败 */ - }; @/ x+ Y+ z* R3 ?
- }
) l7 D* x& ~9 a" ^1 |* J - 3 W! b1 W6 v) w" r
- adr += SPI_FLASH_MEM_ADDR;
s8 B( m* ^. ~/ V/ E, g - return (adr+sz); /* 校验成功 */
, o/ U- ?7 v/ h- {" d5 R: F - }
复制代码
% N1 Y6 P! i* u( G84.4.7 第7步,修改SPI Flash驱动文件(引脚,命令等)) p% \, S0 `- |" q
最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:* D. a7 }# n% I' F5 w
/ s) r6 u4 G) k- /*
6 e, w) K( G- Z) a4 e - *********************************************************************************************************
- U! e" `3 T+ o7 A0 q; G - * 时钟,引脚,DMA,中断等宏定义
. K- `1 \* t) E/ v - *********************************************************************************************************
/ Z/ Q4 x- A' D, T - */9 D8 n; o. Z( Q5 Y, W7 w" v0 b
- #define SPIx SPI1
2 D# r1 I' l( O6 x - #define SPIx_CLK_ENABLE() __HAL_RCC_SPI1_CLK_ENABLE()0 D- }6 s6 G7 Y/ I% Q
- #define DMAx_CLK_ENABLE() __HAL_RCC_DMA2_CLK_ENABLE()' ^" K/ R, N! Y9 f0 Y
- ) s+ ~5 [: Z+ z. W7 r! D0 ]
- #define SPIx_FORCE_RESET() __HAL_RCC_SPI1_FORCE_RESET()
$ j1 H' ?5 C# o4 p! {# o9 p - #define SPIx_RELEASE_RESET() __HAL_RCC_SPI1_RELEASE_RESET()- Q3 W* ?2 c: Y( _: e; J
. \9 U: H9 g( A7 m! J. ` C4 k- #define SPIx_SCK_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()# B6 h1 Z# X- j: l! y/ S
- #define SPIx_SCK_GPIO GPIOB
! {% G# W; f$ x - #define SPIx_SCK_PIN GPIO_PIN_3
7 y& L; p5 y2 A - #define SPIx_SCK_AF GPIO_AF5_SPI16 Y( M, B. ` }$ Q8 L: _. B
1 s& ?* z) ~- ]' L( I0 S0 W8 [( H- #define SPIx_MISO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()) s J, K+ P; o" ]" ?. H; @
- #define SPIx_MISO_GPIO GPIOB
9 [8 @- W% ?, ~, b - #define SPIx_MISO_PIN GPIO_PIN_4; s6 o7 m2 ]3 C2 d7 A: {5 P
- #define SPIx_MISO_AF GPIO_AF5_SPI1
# O5 M# B2 P2 l
$ p/ H& w- p; b! h( Q( I- #define SPIx_MOSI_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
0 h3 H6 i6 G e2 G8 z: S3 T" N - #define SPIx_MOSI_GPIO GPIOB8 G6 s7 _1 _. W, q* g% V$ t
- #define SPIx_MOSI_PIN GPIO_PIN_5
7 `3 _: U5 n7 A! l7 ~ - #define SPIx_MOSI_AF GPIO_AF5_SPI1
复制代码
5 C' v, P% o+ z- I. m; L7 {硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:
2 @# f' }& ^' V$ R7 r5 p( H: {
, Y. R H0 x" c主要是下面这几个:
% U3 g5 D7 m% L8 D. g
' e$ g/ U5 ?+ g8 R2 {- /* 串行Flash的片选GPIO端口, PD13 */) U: n& a8 m$ {
- #define SF_CS_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE()# T7 G6 R7 w' Z% q3 F
- #define SF_CS_GPIO GPIOD% Q$ \" h" {4 a! H, M- B
- #define SF_CS_PIN GPIO_PIN_13
3 R3 f1 h t3 W7 t9 K' Q+ O7 O - ( m: r2 B$ a& \8 |- P: v
- #define SF_CS_0() SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U) 9 S/ \: A* W: H" H
- #define SF_CS_1() SF_CS_GPIO->BSRR = SF_CS_PIN2 @+ F- _) y, |8 w! A' z& F
- ! L! W! b+ G* y4 ^* U$ K
- #define CMD_AAI 0xAD /* AAI 连续编程指令(FOR SST25VF016B) */
: E- F& ^1 n' T+ K - #define CMD_DISWR 0x04 /* 禁止写, 退出AAI状态 */2 ~4 x1 ~' z/ ~& O# K+ }) S1 @
- #define CMD_EWRSR 0x50 /* 允许写状态寄存器的命令 */' A2 F$ C; `9 [. y5 r# P ?
- #define CMD_WRSR 0x01 /* 写状态寄存器命令 */
$ Y5 y& y& ~4 u2 y5 V - #define CMD_WREN 0x06 /* 写使能命令 */
8 L( N, A3 V0 O3 B1 ^, `: ]0 b - #define CMD_READ 0x03 /* 读数据区命令 */6 m- X" W( l- l$ r* L. ]. \' v
- #define CMD_RDSR 0x05 /* 读状态寄存器命令 */1 ~& A4 ]8 d( |) B+ I; _
- #define CMD_RDID 0x9F /* 读器件ID命令 */: P) P8 U1 Y) |- C
- #define CMD_SE 0x20 /* 擦除扇区命令 */
) p5 A. w9 ] G8 U+ P- a3 T - #define CMD_BE 0xC7 /* 批量擦除命令 */+ ?9 B: h; r$ O ]) a+ E9 N
- #define DUMMY_BYTE 0xA5 /* 哑命令,可以为任意值,用于读操作 */
4 q% M3 E1 S. {, X4 E' B( g
8 x5 z( X$ @3 m8 s! O- #define WIP_FLAG 0x01 /* 状态寄存器中的正在编程标志(WIP) */
复制代码
/ j0 D4 h, u6 Q8 j84.5 SPI Flash的MDK下载算法使用方法
& D1 S$ \/ j9 }3 y5 X6 e& |编译本章教程配套的例子,生成的算法文件位于此路径下:1 z+ P1 @2 N: x
4 y. `2 k1 A3 N! f
8 g3 S! Q6 c6 x7 c; j E
5 V9 @: l9 ?6 J. c1 J84.5.1 下载算法存放位置
, i( l* |& Q. f/ J* ~, D) D生成算法文件后,需要大家将其存到MDK安装目录,有两个位置可以存放,任选其一,推荐第2种:
1 O; S% l/ Y/ R
; u: j1 O! k/ Y/ @' q2 q1 Z$ v1 \+ J 第1种:存放到MDK的STM32H7软包安装目录里面:\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软包版本不同,数值2.6.0不同)。
' p* d7 X' C! p! ]7 k& g 第2种:MDK的安装目录 \ARM\Flash里面。+ u- n% w) D5 p* Y
9 U2 Z' y1 P6 U# \/ O/ i
6 o- j3 ^' E9 J& { O84.5.2 下载配置7 a/ g1 O, F4 c9 @4 E
注意这里一定要够大,否则会提示算法文件无法加载:6 }1 k+ v# c( w! X' [4 z! d
/ G! `+ {6 V- } E8 ]% V7 v9 D
5 G: ^; X9 M1 }' _; }9 t
我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。推荐使用AXI SRAM(地址0x24000000),因为这块RAM空间足够大。3 d5 U d% Y+ W
" e/ p2 Z1 \. R0 @! Y1 N' A$ X如果要下载程序到QSPI Flash里面,需要做如下配置:5 n6 M* X1 Y6 z4 ]) r3 |
5 d& B' Y8 G% e. n
% m( E$ s) @, b' U& {4 F
+ ?. \: N. v! i84.5.3 验证算法文件是否可以正常使用; X, u( G1 p" b$ l5 Q- S5 X9 E
为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。
1 J8 E5 n: {1 K9 e2 _
& @2 Z" q; b+ T84.6 实验例程说明
1 s) n; u7 f( O; E- {* r本章配套例子:V7-065_SPI Flash的MDK下载算法制作8 D9 e* O- W- P* V$ A0 v/ o
( u& ]+ B& J2 q3 [编译后,算法文件会存到此路径下:- f, C$ B( k, _9 w: ^
$ U. Z8 S! c; N: ^" x2 s
1 ^8 {2 E* ]. U' {9 C k4 m5 C# J
6 a! u9 l: ]8 J2 Y% p5 @84.7 总结
" |" y! ]% J, H! \本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。
0 A0 m1 H! v0 A' u
) d! j$ `+ F2 f, v( U9 A$ z+ Q9 ^9 A. P2 A8 i
|