81.1 初学者重要提示
9 f. @* k& `! `; N QSPI Flash的相关知识点可以看第78章和79章。% _$ @! I! `8 [ Q. R. M
QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
" M5 H. p: O8 R; J- d$ D; c STM32CubeProg下载算法制作和MDK下载算法制作基本是一样
5 v3 s, n: `; @! Y# B& p- v# M 本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。
7 ~( K8 D( q$ G81.2 STM32CubeProg简介$ }% h i0 o. @& S I2 h
STM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。0 f/ o. e5 U# [$ P
2 ~+ n. Z" q, N6 D& M9 P! T" {. X
9 \/ P3 |2 x7 V9 [, E3 S8 z1 s: e- ?1 S9 o; p
81.3 STM32CubeProg下载算法基础知识+ Q, X. `) a( ]) R3 G
STM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。 N C4 f+ o& J1 ^4 B' w$ e6 @
) V: g% Q, f* K' K6 H5 l81.3.1 程序能够通过下载算法下载到芯片的核心思想9 f/ ?% ~8 i7 v. a2 _' u
认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。9 ~2 C# t0 X% k. {
: H. T+ _3 [3 X& \81.3.2 算法程序中擦除操作执行流程
9 v3 [$ V, T/ w5 h注:下面是MDK的算法执行流程,STM32CubeProg是类似的。- b% k/ k* N4 b; O
. j7 G% t7 c- M2 ^3 k" u
擦除操作大致流程:9 D3 P% I) p! C
i6 o) Q+ k o+ s$ ]: F& g- d& j( ?8 ~0 L
& W( q0 i: N! X9 x+ ` 加载算法到芯片RAM。; j" R% U4 T, y( y" l
执行初始化函数Init。 r2 P1 u6 d3 M, o2 \' J# T
执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。
9 `% Y+ \; x1 v 执行Uinit函数。
+ y& A6 H/ V; g) f0 I; v( g0 E4 a 操作完毕。1 b5 y- u" ]9 {; N. R) C! x# H
81.3.3 算法程序中编程操作执行流程
! c% T3 n3 G, c0 h) f% O, B V注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。& Z" n9 x2 l1 X% l# N% w" d
2 v, q5 t7 z+ L; G6 G编程操作大致流程:
# o2 \1 A" w' G; R8 V4 r3 g: p" |7 C2 J+ N- I- C% J5 g( S$ k5 l
" L/ t( g" [4 i* j/ a! V% ]/ c- ^
! i" d( W3 Q- z, z/ v2 j
针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。
1 F7 C4 |2 O1 I3 a8 ? 查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
9 J; D4 J9 ^4 W! I 加载算法到RAM。& t. [% A' A7 y0 [* U+ E
执行Init函数。9 @( w7 J5 F& O3 j* x- |5 D) N
加载用户到RAM缓冲。
* `+ p) _$ c2 l2 ~ 执行Program Page页编程函数。! C8 d0 l( c! E& b. Z, a P( e/ z# t2 [6 S
执行Uninit函数。3 m3 _2 @* u: ^/ y* h' a
操作完毕。
8 m3 C4 U1 a6 A/ c1 g! ]) Z81.3.4 算法程序中校验操作执行流程
( A. X& \0 r; M( q! ?注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
`- C/ v7 s! k* v& j( s1 U4 s" Z% {6 E% W: d
校验操作大致流程:3 x' X8 ^6 m% B% i3 H1 ?& G6 P: _4 \
$ t0 f( R; r0 }
5 P5 I# {- V3 t& e
. t. i6 w$ b1 [ 校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。; w3 A8 U3 J: s- o8 ^* t v9 d
查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
/ ?+ \" t+ ^2 I) O, ` 加载算法到RAM。
/ l. N3 G! L. s1 U( c+ c6 t 执行Init函数。
" M ]: N" \, A, F5 U6 R+ [% l 查看校验算法是否存在
3 L( _8 _3 T3 p 如果有,加载应用程序到RAM并执行校验。
3 Z+ b$ j/ z4 Z1 ]7 y 如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。. l$ F: Z4 W2 B$ h" \! ^$ U
执行Uninit函数。
' w/ y4 t, R1 j6 Y0 a% B' m+ @& k 替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
& C7 k! E% i3 \8 v. ~ 执行RecoverySupportStop,回复支持停止。
$ X6 \! |1 ?* K' r/ [1 _( N3 ~ 执行DebugCoreStop,调试内核停止。
& \ v% [5 z$ W1 P4 B 运行应用:
6 s! l) t# F( n) ]# U2 B 执行失败, N J, v. C; z4 j C. s
执行成功,再执行硬件复位。9 b% [! [% ~+ }+ x% I: a9 w% [3 Z
操作完毕,停止调试端口。
3 o \& f. ]% P9 S0 S3 X6 G4 h81.4 创建STM32CubeProg下载算法通用流程- n- @; B- P) E' k9 x, K1 E# t) u# F
下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。$ D5 q' @5 n, c& s& k8 n# }
5 Q1 ], z B# s' W3 H Q3 g8 r1 V. t
81.4.1 第1步,使用STM32CubeProg提供好的程序模板
5 ~+ u/ [2 A( M& @1 H) M8 R位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader5 G; q0 u4 |' s& F/ l, t
: K" G. g$ K) o* g" E/ x- F& c7 ^
+ D' Q* n0 [, ~) a" v9 }
/ X: _- G) l' G+ ?- Z1 y: D以M25P64为例(注,其余步骤就以这个为例子进行说明):% }6 M$ Y3 Z* }
5 g* {" Z I2 A' E. k% L
9 o! j0 N9 C: {
5 L8 Z: M. K$ y9 s2 |9 g
81.4.2 第2步,修改工程名
1 h$ o0 C# M& N O0 D+ LSTM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。
0 Y) b8 n4 [! v5 ~) e
" J- T- D: }% x$ b2 L: s) M( I/ ?# _+ y81.4.3 第3步,修改使用的器件
. a: M: t# V; O, E6 i在MDK的Option选项里面设置使用的器件。; H S) g0 h1 t7 _
' i- h! r, G0 G0 i
! C, n. q- a" b& w# P7 p. [. W; K5 ^& y
81.4.4 第4步,修改输出算法文件的名字
* ~; V6 h0 S/ q, z) q$ u1 N这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。
1 ?9 I4 w5 K% s" h N/ ~9 P8 C3 H' g* P: r( ~7 b; M( P3 M- ?
2 T0 n8 }- [; v+ N7 E1 Y# `3 u
0 `+ o' X L$ s. I
注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:
- L2 ^& ]2 e! f' x
/ Q9 G3 [ t/ ^9 o# j/ a* @; e. C7 g* K
81.4.5 第5步,修改使用的库文件和头文件路径
' i5 W2 X- q# d7 g根据大家使用的主控芯片,添加相应的库文件和头文件路径。
: q' |7 J& l3 D! ]. o7 Q. [; p% W) y' p) w% \ G9 c
81.4.6 第5步,修改编程算法文件Loader_Src.c/ l$ L) h1 S! u) ^# j
模板工程里面提供的是M25P64,部分代码如下:
2 I- _# F* H& l6 s8 u: E9 k* w# W# S3 G1 c3 k
- /**8 j& N6 N0 H; n7 _& L
- * Description :
3 O. Z9 ~) k% z4 ~; h' | - * Initilize the MCU Clock, the GPIO Pins corresponding to the; E/ s0 O# M9 q0 @- T' X
- * device and initilize the FSMC with the chosen configuration ; B I4 i7 t j! [
- * Inputs :% }4 Y2 f; P1 e
- * None0 H) n% y* G* Y6 B6 C
- * outputs :" L" @1 t% B1 z* n( P
- * R0 : "1" : Operation succeeded- b. @: D; q* n' }! G" c
- * "0" : Operation failure* u2 j' L+ L o* w; c/ f
- * Note: Mandatory for all types of device
3 C' @/ t3 H3 c1 R( w - */
( f: U9 g% X; Z3 M" g' a$ i - int Init (void)
1 x' ~+ j( J+ X4 h! Y; P6 R - { 8 X6 E7 e( ]# c# R7 S6 [
- SystemInit();; o0 M- D. |! O. t- W0 K c
- sFLASH_Init();2 t. P: u1 U& Q, c
- return 1;
8 \8 l, K+ x3 w, S$ A0 @ - }
" G; w1 H1 F% Y( H4 b
+ G* y2 O, m9 H- / q) w/ c5 ~0 Z. Y% q+ Z
- /** ^3 _* m+ {( Y2 E
- * Description :% u7 l* ]9 E4 ?; _( [& k5 N
- * Read data from the device
% `1 @, S C1 n7 E* B! |9 O - * Inputs :. g: o& I6 c7 l) u; Z# Y; K Q
- * Address : Write location% k* q# @- b- v# J" D
- * Size : Length in bytes ' J; Z8 I% r) X
- * buffer : Address where to get the data to write6 D3 D/ T/ s; I5 z1 h
- * outputs :
1 p$ Z( B5 o5 U6 C* d - * R0 : "1" : Operation succeeded# Y# ?: z% ~3 O% k: y4 q5 Q# G* c& y
- * "0" : Operation failure
4 j' r0 F1 y( B5 p - * Note: Mandatory for all types except SRAM and PSRAM b6 P6 k1 R0 c- |+ k
- */
) I- B a$ ]' m. ]4 Q3 I - int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)
2 P& {2 R* {% x1 r2 U& r1 G. Y& F - {
2 F, O% K' f1 C3 O* U \ - sFLASH_ReadBuffer(buffer, Address, Size);# E* D6 V: F# d& H( ^
- return 1;
$ s+ w# C$ O0 F$ Q9 @1 h; { - }
J* C' B* x1 `! J - 6 J; w/ ^% M4 ~5 v/ h2 d
- F& w6 `0 k# Y. g2 @" r+ D* n- /**
& V# W* ~/ {" G4 a+ I - * Description :3 C, E/ f; z# Q/ ?
- * Write data from the device - w/ b0 }' \1 D
- * Inputs :
/ B) P+ `! K: D( m; Y - * Address : Write location) F3 w0 ^* t. I$ a$ h- b
- * Size : Length in bytes
% y) h; W5 b5 L; D" D7 }( l - * buffer : Address where to get the data to write
: `, N* V2 c) _: M# W- l) Q - * outputs :5 f' \4 f' u# |1 y
- * R0 : "1" : Operation succeeded6 d4 P$ W' c9 p( \; R7 G9 f0 N
- * "0" : Operation failure
7 g+ [+ B0 c" D8 t - * Note: Mandatory for all types except SRAM and PSRAM
: u! p @4 y- r; H( F4 E" |9 O - */
! b5 h v- P9 I" w - int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)' _1 L* ^% K6 ?, L+ W) g n: m
- {; K8 e- A! \( l1 Q. j7 C7 z$ d9 [1 ^" o
- sFLASH_WriteBuffer(buffer, Address, Size);1 ?; I& j( H' D7 w5 j3 r
- return 1;
9 P9 F0 b4 Y) O H - }
# `+ e1 `1 ]9 I9 `( |
6 I2 M/ H d p9 N5 B( o- # f$ r- R, I& j+ c( a& c8 p
- /**( w0 K) Y9 S4 N; f& f6 h
- * Description :
2 e9 B+ \, W3 J! N. H - * Erase a full sector in the device A9 y2 C$ I4 D4 Z5 O9 K
- * Inputs :
7 Y* L/ u7 s q. h3 B* P# @6 ` - * None. d# J+ R8 E5 G9 n$ \0 j
- * outputs :
* w- t/ ?$ Y8 s" g - * R0 : "1" : Operation succeeded- R6 l- z \4 J# F: D3 C J
- * "0" : Operation failure0 h: z3 X7 F/ u! z% f, `
- * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH
: x; \ G( c0 A0 y/ u! V5 R - */
! B+ e- f. e$ G. R - int MassErase (void)9 M& A) w4 D/ Q8 C
- {
^, B$ ?3 m4 N - sFLASH_EraseBulk();
* ^, a, v6 V3 [8 Q - return 1;
* F% p2 p$ G7 ^ - }
复制代码 . O8 j$ M; a5 K, O4 H V
81.4.7 第6步,修改配置文件Dev_Inf.c4 h3 M' s4 }! x7 Y4 c; u
模板工程里面提供简单的配置说明:- s: X# P( F2 a; K* t
+ \( j! Y7 e0 x2 X. d M( f- #if defined (__ICCARM__): G* i5 L, E3 ~0 x% F3 h6 T
- __root struct StorageInfo const StorageInfo = {+ R) f1 R$ n$ W5 _* J5 Z4 [
- #else
3 T, q. j& C, B - struct StorageInfo const StorageInfo = {! v! S% Q1 \7 f1 O
- #endif7 x. T: ]6 T' }& d7 R
- "M25P64_STM3210E-EVAL", // Device Name + version number
$ ^& y. C( E h5 X0 i - SPI_FLASH, // Device Type
! V, m: T! B2 L+ |. v/ Q/ p - 0x00000000, // Device Start Address
3 B8 Y) x2 t& x. Z+ c3 ~9 U - 0x00800000, // Device Size in Bytes (8MBytes/64Mbits)) l7 p) e7 ~2 {" v. u( ?- X
- 0x00000100, // Programming Page Size 16Bytes
5 N D9 e! d4 }4 \ - 0xFF, // Initial Content of Erased Memory3 r- h* O0 ~+ ^7 F5 s
- // Specify Size and Address of Sectors (view example below)
4 v; K2 p, [, r - 0x00000080, 0x00010000, // Sector Num : 128 ,Sector Size: 64KBytes , _0 g( X w. G# p- r# w
- 0x00000000, 0x00000000,( u% X& ]4 m+ E/ G& n- _
- };
复制代码 2 j5 e; `: m" G& }2 W
注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。
/ u- e4 r' g, C; Y8 w( b
7 U" R; q. f5 J% z7 G- [- I- a: R6 q# N ?" P
; {' Z+ C$ q2 s6 O. m
81.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。! P- f3 x1 F; { n
C和汇编的配置都勾选上:9 Y& I7 X$ V& G M. x4 ^6 ~" ^
5 w7 ^* [3 B0 y. c0 Q" S5 k
) v; M+ d6 k2 J% w4 I& u# J h$ Q' o2 D: {0 ?
汇编:8 d1 ~$ j! A' [& T# M6 E& t- d
/ A5 J2 |/ d2 H0 V# l, H- }7 R
0 f) F5 o% X- S( i# ]
; r0 T( b, ?' q' P+ F j3 U
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:. E* J( A0 R- j
. m+ }6 `, @+ l, u: _4 o. [4 c(1)加载以响应运行事件。
: h7 m' K7 ^% e9 T6 v+ R; c) P) N, F- X# J% a/ B
(2)在不同情况下使用其他例程的不同组合加载到内存中。
8 n5 J6 a& y, o, G# t9 I9 z; y# n/ {( f& X# }! o
(3)在执行期间映射到不同的地址。
0 v! y, h' O) n! S7 d$ D, m. P. O9 @, q: i/ {9 c$ V0 Q3 _
使用Read-Write position independence同理,表示的可读可写数据段。
7 l4 y! a, L& H0 P
' _ C' L! N& P) L4 E5 ]" ?81.4.9 第8步,将程序可执行文件axf修改为stldr格式0 @- Y0 ]; w+ Y/ u) J! V1 e. s [' m
通过下面的命令就可以将生成的axf可执行文件修改为stldr。
* j+ @& y2 g& I- m7 G0 i1 i+ i0 C0 t' V" e8 d6 |1 j9 i1 I
& _" p7 }" m4 s$ Q
1 E$ }7 _- S# b: W E
81.4.10 第9步,分散加载设置
5 i, `, G5 [1 `, H我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。
& Y9 U6 g8 ?7 }2 j, a2 K+ Z- Z, N
) o! p# x5 x: g8 S- R7 h
5 q- I8 ? @/ k* ^! c2 f4 I+ ~' r& A9 b
分散加载文件中的内容如下:
- ?0 R- C5 B2 u1 P5 `
& T; b4 h# d# m1 K- FLASH_LOADER 0x20000004 PI ; FlashLoader Functions6 r( y. a+ a" Y# D( {
- {
" x7 P) L m" I Y/ f8 M+ K) Z - PrgCode +0 ; Code" ~% E5 M% x1 P$ _( ]: {
- {3 U/ I/ J) C" x; W, \2 D3 _
- * (+RO)5 ~' \5 p* c. e- m) ]
- }
! v2 G& S, g# r1 p9 ~ g+ A - PrgData +0 ; Data9 U2 Q' R7 @1 v8 h
- {
( [$ q* i( c4 g5 f) ~8 c$ C - * (+RW,+ZI)
* m" ?5 H M8 F/ O7 M - }/ K7 y) o ?$ H2 s9 R7 i! W& d
- }& q$ z8 A5 \* n* i7 k
+ e+ ]; O6 J0 X6 ]& R* r1 E% M- DEVICE_INFO +0 ; Device Info/ H% Q. t, ?; G, X
- {7 L, V4 f1 L% j b% o
- DevInfo +0 ; Info structure' H1 x) @9 j) g6 x. ]
- {3 V, }( t$ \" h0 G" J
- dev_inf.o
" m* D b- w) U. g8 A6 z4 ^ - }+ E* B4 ~5 l8 N5 }# J) Y
- }
复制代码 $ ^5 K' L6 Q. {% d$ n5 |- I9 S
这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。3 m0 [8 l' l0 Y' c1 l+ D P+ P3 C
" i5 E7 Z: ]9 |% z+ W6 C u6 u7 \
--diag_suppress L6305用于屏蔽L6503类型警告信息。- E* A; a2 E( U: a
" H& M8 A! \+ r6 g0 k, _- ] ?0 @% a特别注意,设置了分散加载后,此处的配置就不再起作用了:
1 J) m2 @5 L! V( n# y( {$ z! A1 U( R, p
9 v9 B: K) W! X' w# n! v7 Y2 [, `* m. [1 W% ]* B# Q
81.5 QSPI Flash的STM32CubeProg下载算法制作 i8 h+ a( L, I: G, N
下面将QSPI Flash算法制作过程中的几个关键点为大家做个说明。
* N% x% N0 _4 A% T6 W1 [- o0 A# F7 X, w8 `( o
81.5.1 第1步,制作前重要提示
2 R) B7 W M: |( }1 J这两点非常重要:/ H5 m5 f. U3 \1 o, {1 `$ \/ V$ v0 N
) K/ ]& G6 }: ?( t
程序里面不要开启任何中断,全部查询方式。
: o7 T; n5 p) m2 w0 ^' O* w6 W% _ HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。
8 x* q$ \& |6 B9 ?7 `: _81.5.2 第2步,准备一个工程模板
4 o4 |4 R' {( h8 V 推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。1 ], c$ g" Q8 I! W
3 o, v/ M. |* z& R" ?# p
0 G0 u( h+ S1 [0 S2 c. {: m+ q. `+ i- l
81.5.3 第3步,修改HAL库4 n/ V) |' u+ ?$ B% ?7 V2 i
这一步比较重要,主要修改了以下三个文件:
& |0 _' ^' x G# Z' d ?
# j! |% C" c A8 m+ @: B% l* ~% a& f! w% M5 _1 P6 B9 a
, |+ k# F: s; K( _主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。 Q2 h' V" W1 f" t. U1 Q2 A
( m! i9 `" P+ C" H' i- X
81.5.4 第4步,时钟初始化
3 \6 X$ I, T: U! b4 o, K1 n; P我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:
( y, K% B; U0 h0 \: `' {$ ~6 V2 ]) S9 ?3 V) ?
- /*1 _+ A5 L( {; F7 A! i5 O6 v
- *********************************************************************************************************
E9 W5 l/ v- t; Q) M& I( w - * 函 数 名: HAL_InitTick
8 {( h7 C- X+ l4 c - * 功能说明: 重定向,不使用
8 c- y& o# [2 n, H% j- _& y - * 形 参: TickPriority+ A" j* G# L8 {$ }
- * 返 回 值: 无
" G+ H0 M6 d5 q8 j4 f1 r - *********************************************************************************************************
; L% \2 p& U) R9 e0 U% W - */
9 d ~ \; `2 n2 n% W8 n8 Y; J& o - HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)! X8 ]. L4 }8 W: U' U% c
- {
9 |" _9 v U, K/ E v - return HAL_OK;9 z+ Q o% r5 ]! ?* s1 @% s2 O+ t
- }
复制代码 + H- a; E$ S( k: d' N
然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:( S5 u5 w# B4 F$ N, O; I' R2 r4 ]
( p. q1 B, F8 d M( ]; v
- #if !defined (HSE_VALUE)
# `9 E: n2 g# f& i3 D$ ` - #define HSE_VALUE ((uint32_t)25000000) /*!< Value of the External oscillator in Hz *// ~" C! g: r% G+ T) j
- #endif /* HSE_VALUE */
复制代码 $ T9 m- M7 O( D+ c
最后修改PLL:
7 v- |4 _8 x6 k0 G9 L |1 K2 p$ {, w5 H! Y( v8 Y
- /*
- P A& T) w. G9 R; C - *********************************************************************************************************
( }: I: ^: e/ Y( I" c0 l# [ - * 函 数 名: SystemClock_Config
) H, D) ~ o ^& f - * 功能说明: 初始化系统时钟
- q0 N7 A# k5 ~ - * System Clock source = PLL (HSE)
7 n% n- e6 j, Q1 E( P. z$ P - * SYSCLK(Hz) = 400000000 (CPU Clock)$ s/ D$ `+ q, r9 g' f( ]
- * HCLK(Hz) = 200000000 (AXI and AHBs Clock)( t1 j% l: r0 r, u) m
- * AHB Prescaler = 27 Z7 r0 M l% f- o! a7 z7 T5 e0 K
- * D1 APB3 Prescaler = 2 (APB3 Clock 100MHz)' {: |4 o: B4 I/ G
- * D2 APB1 Prescaler = 2 (APB1 Clock 100MHz)! c6 I, ]* _. A/ w! `0 r( D
- * D2 APB2 Prescaler = 2 (APB2 Clock 100MHz): v) f: S m2 f9 ]% q/ ?8 p. e
- * D3 APB4 Prescaler = 2 (APB4 Clock 100MHz)
( l& J! l' t1 M% N) ^/ P - * HSE Frequency(Hz) = 25000000- |" d( a( |, d5 `: Q$ |+ _
- * PLL_M = 5! s6 H, E6 I9 G$ \2 Y
- * PLL_N = 1608 F: \9 L. U2 q8 V% t
- * PLL_P = 22 z0 N& Y9 u9 K: n L7 ^
- * PLL_Q = 4
3 K/ k+ |! {3 D0 h5 ` - * PLL_R = 2+ K. b/ b! K1 B& D8 U, v1 T
- * VDD(V) = 3.3
( B d9 e* ^) H7 @8 ` - * Flash Latency(WS) = 47 P! N0 ^2 W! W9 f q
- * 形 参: 无6 D5 b7 Y2 I; q. S, s8 H" J
- * 返 回 值: 1 表示失败,0 表示成功
. M9 [) |% U+ x* W& { - *********************************************************************************************************
, O( R j7 [0 }: t" E4 P- j2 \6 Q9 ? - */( u8 C. N6 M: ]5 n0 |
- int SystemClock_Config(void)
9 c* [! }3 g# r8 f6 r - {
# y7 l" ~& G0 S* \6 M( V - RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
$ _' l m/ C6 V& P8 } - RCC_OscInitTypeDef RCC_OscInitStruct = {0};
7 _7 a* F2 w& O; ` - HAL_StatusTypeDef ret = HAL_OK;# E( `5 b0 d6 l4 U
- + ~' y" @$ S- u4 W/ i
- /* 锁住SCU(Supply configuration update) */7 o' ?( `4 Y, _+ x' E# r
- MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
, ]1 {, z% \# u. D - y: [" h. e, F: O. ]6 q, ?
- /*
复制代码
7 z& h- E7 y0 _( k 1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
& Z! Z. W9 P5 e 详情看参考手册的Table 12的表格。
2 O, N# j0 a5 L# }. M 2、这里选择使用VOS1,电压范围1.15V - 1.26V。
: e' w- j0 v. b - */. e P4 }" X$ I9 z
- __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
4 n& h3 A S( ^/ r' ^% w
& g: {; J1 t7 x% R. x. ?- while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}( y7 [6 _5 }% y" t
- % ~1 B9 k7 A& k! X0 s
- /* 使能HSE,并选择HSE作为PLL时钟源 */
. Q6 m6 K+ S; H - RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;, {5 U; h! {) T) S- [" F+ ?
- RCC_OscInitStruct.HSEState = RCC_HSE_ON;
" U. q2 H, z: ]: K - RCC_OscInitStruct.HSIState = RCC_HSI_OFF;& P# g; F- z: \* c; D
- RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
; J/ J% k2 b# ` - RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
+ }7 D5 D+ g5 R" B; b+ r% W - RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
8 U( [" x: i1 w' V) j5 u4 G
$ R8 ?; ]: d7 ?7 a& W- RCC_OscInitStruct.PLL.PLLM = 5;
# f, B2 t% `# x: |: n. k# m3 d' l - RCC_OscInitStruct.PLL.PLLN = 160;
5 ~4 ^" Y$ C i - RCC_OscInitStruct.PLL.PLLP = 2;, `! t4 c: _$ H5 Q% Z
- RCC_OscInitStruct.PLL.PLLR = 2;
2 l+ l: Z/ o. \4 i - RCC_OscInitStruct.PLL.PLLQ = 4;
9 o% f& J; R( J, X! i+ H2 y - 2 l* F5 w% H* o# X
- RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;' B% @, x) J0 a3 t0 ^& S# w
- RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;
0 O/ W3 j" ?* X, p1 d. s5 x2 `) q - ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
6 Q, ]( [+ _3 R$ ` - if(ret != HAL_OK)& n7 b3 e) y+ U3 E
- {
- E* C, e" ]3 O- ^2 _+ m# s - return 1; . ]8 w% r1 P1 w x2 e( a
- }+ \! @$ P2 n2 m% F
1 l' ~3 O* F6 F: D3 s8 H- /*
& ^4 k6 S/ ^# `9 ~. D3 V - 选择PLL的输出作为系统时钟 G0 b2 w Y+ h' E9 {% s
- 配置RCC_CLOCKTYPE_SYSCLK系统时钟
( j J/ M7 U2 h c - 配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线4 k0 w" [% x0 D# m
- 配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线' m$ \: Q1 h2 H0 k/ s
- 配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线2 N) l1 ?% ]5 T3 z6 @( h1 c" o# Q1 W
- 配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线- ?8 s4 b* B6 t0 k9 ]
- 配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线 + \; v' R& y; O/ B, o
- */
: O+ a4 B1 h9 b; [) `$ }7 r - RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \. ?& z+ u" o J* h4 C7 V
- RCC_CLOCKTYPE_PCLK2 | RCC_CLOCKTYPE_D3PCLK1);7 u. t' N! M0 C7 z* | \# k* W
e6 N4 c9 ?2 w1 I9 S4 R- RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;6 z4 U* J+ y l1 |, A
- RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
9 h3 B$ | t$ ]% `; U" a - RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
) k8 {/ x/ ~ ~. e - RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2; / N7 _, u0 A }! p/ q
- RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
s& J c) [5 r - RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
8 \: f5 v$ C4 ?8 p - RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; , i% L0 ]% B( z+ ?( s9 Y1 c
5 r9 s: n6 t* n6 G, Q6 C$ g# |! h- /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
9 B' Z8 ]0 B. |6 Z- c7 | - ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);# s$ G4 ^1 R3 P" v$ `
- if(ret != HAL_OK)
4 o% b- p* [/ u* l( x - {
: ]" f# ~& `" F* Z% G4 s/ L2 a' U - return 1;! H, `. u4 w) ]7 b$ Q) ~9 d4 Q* ~) ]
- }/ B5 L/ f2 }( m1 d7 z5 | R5 B: N
`" Q$ `: o" O# e$ M+ K* d$ N- /*
$ \% q0 i3 B, z6 r+ |+ [; Z: N5 _. n - 使用IO的高速模式,要使能IO补偿,即调用下面三个函数
1 _* W: N! G) T. j+ J - (1)使能CSI clock$ S" b" U* K1 {3 U5 H. u% }5 j
- (2)使能SYSCFG clock
9 F/ l9 |5 u3 j y6 T' H& { - (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
. v. M6 D; y5 b1 n3 r) ~ - */4 |- d( L g- X/ z; i2 A4 @3 J. }
- __HAL_RCC_CSI_ENABLE() ;
?. Q2 s3 L7 t, d% C" S$ P# ?' ?
' w. p0 E: l" `: _, ]; ?+ j* C1 Z. H- __HAL_RCC_SYSCFG_CLK_ENABLE() ;# ^" b" ?2 [9 b5 H1 H
- ; e, _* O _- a/ ?8 Y
- HAL_EnableCompensationCell();
( y. D5 r. Y& M6 W
: @ t+ A: i) W5 d# O/ v+ S% j- __HAL_RCC_D2SRAM1_CLK_ENABLE();- ]1 m9 A+ J: v# ~2 \+ T
- __HAL_RCC_D2SRAM2_CLK_ENABLE();
8 J) T2 Q7 A8 w% M3 h( i8 A+ u - __HAL_RCC_D2SRAM3_CLK_ENABLE();
- ^. S! Y+ ^) I: d+ u, A z* C - ' u2 G* r4 W" _' z' T! d: U$ t
- return 0;: _: y% x$ o9 S# Y. f
- }
复制代码
' y6 S0 N7 }( s- o; F! _81.5.5 第5步,配置文件Dev_Inf.c的实现
! c0 ]: Q; B* h4 k- n3 a1 n. L$ c配置如下:& p5 U0 O- {& }! t2 x& G% p( Q7 ~+ @
) m# l* l& y# L6 \4 A- #if defined (__ICCARM__)
' i9 J- _, [: E6 K3 H/ }: g# K1 w - __root struct StorageInfo const StorageInfo = {
6 C8 u9 B! A; C5 ?* \( Q - #else# x; Z1 T* w. a+ X: H6 j: S
- struct StorageInfo const StorageInfo = {
+ r6 J/ {( w0 f1 z - #endif
( j; z+ S2 U, [' E - "ARMFLY_STM32H7x_QSPI_W25Q256", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */
0 k7 z2 ^1 i# u ^8 c5 h - NOR_FLASH, /* 设备类型 */( G! @! T% b# u5 ?' H
- 0x90000000, /* Flash起始地址 */
5 I3 U0 M, e" m2 q4 B - 32 * 1024 * 1024, /* Flash大小,32MB */
& r, H: j5 P9 W9 ?$ ^ - 4*1024, /* 编程页大小 */* S/ }; S( B2 K' q; Z
- 0xFF, /* 擦除后的数值 */
& M- P7 c7 J4 E* D - 512 , 64 * 1024, /* 块个数和块大小 */7 s/ }( W1 A* N
- 0x00000000, 0x00000000,
6 @' y- X9 I" e" } - };
复制代码 , H; M' ?" G1 }6 X3 E! a- [
注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H7x_QSPI_W25Q256会反馈到这个地方:- v* s: h: d0 ?+ ^$ S
, x5 [3 m8 d% x6 i5 A) G
( V) L g1 Q' {9 k5 b& X
" Y4 c2 \; ?. b& v7 X81.5.6 第6步,编程文件Loader_Src.c的实现
! l: q) K5 I7 o2 U" o7 g; Z下面将变成文件中实现的几个函数为大家做个说明:1 o% ?: X- Y) D
1 }% C; y' O v( h- M7 U 初始化函数Init7 w3 @% C- s3 h- m
- /*
8 C) A! t3 B4 f8 ]- m% i - ********************************************************************************************************** a% R9 B9 l5 [; q$ Z
- * 函 数 名: Init
. [' V7 y9 L6 W - * 功能说明: Flash编程初始化" J5 h \+ {- p: e
- * 形 参: 无
; O x3 _8 k0 F5 L/ x( y% ^ - * 返 回 值: 0 表示失败, 1表示成功
4 ?* |6 n' S7 B$ t: { - *********************************************************************************************************0 q7 X A( A9 a, K J
- */& _3 e" g' ]/ P" a9 a
- int Init(void)/ m Q( u; w, x9 `
- { 2 ]; u, T% G. o, X! @1 o9 \
- int result = 1;9 @% Y G4 X9 v5 s1 D8 Y+ W1 t
1 F' J8 d8 W: y! p- S0 e% W4 a- /* 系统初始化 */& ?2 ?! M t2 G5 P8 j) v" o
- SystemInit(); ; O2 _, R2 I" ^% l
- 3 [6 C! F. P- X3 ]( C; X& A
- /* 时钟初始化 */! J# x7 t# x# x! b7 Q/ _
- result = SystemClock_Config();
' ]5 p6 c- q& W" E5 I/ M! i - if (result == 1)
5 x" @- G8 Y! W - {
( w: Y8 C7 Q4 p' V! \ - return 0;
5 e. H2 j( n0 J6 ?- F - }
7 a, N6 _# R0 {% w f' e - 5 ^ r+ h9 p' i/ u2 I# b% D
- /* W25Q256初始化 */3 ?" z% [. P) U9 J. G
- result = bsp_InitQSPI_W25Q256();
" v% y1 {, B+ Z. e u - if (result == 1)% [9 }4 O, i+ y
- {! C+ w5 S; r& A
- return 0;; K7 B: o8 M5 p5 a, U
- }
9 {' T3 c) t3 J& N, O- ~
0 ~6 p1 e) K" U- /* 内存映射 */
+ z- B ]7 H: v2 W; {' \ - result = QSPI_MemoryMapped(); 1 V4 ?/ h# M; M
- if (result == 1)
8 `' B& u" w% q" w6 i8 J - {1 O [+ y# H2 s
- return 0;+ x" B+ H; i# V3 b
- }1 ]- B l+ w% r$ p. {1 D' T \4 I
- + A/ ~. ]) l" x$ t
- return 1;
+ A& A# {/ w: i7 z" y) Q0 E - }
复制代码 ! _0 [0 v% V( i
初始化完毕后将其设置为内存映射模式。
. t7 [1 L" T/ w- t* H% ?$ S
; Z8 D% d% @$ S: z 整个芯片擦除函数MassErase8 J y& ^3 X) x- ?, t' e" ~% M
整个芯片的擦除实现如下:
, r5 K* a! \& \; q1 W7 z$ I/ U8 w g; z" t: S" z8 B, y
- /*
0 f3 g" p2 N. S( S) {* I - *********************************************************************************************************
/ t. Z. @( ?' [' x9 H0 f. q - * 函 数 名: MassErase0 p$ b) }3 a' j7 ^, p" E+ w
- * 功能说明: 整个芯片擦除' W3 ~$ T8 n) l, ~" e
- * 形 参: 无
% R0 u+ b( \- n4 W+ a+ x x8 k - * 返 回 值: 1 表示成功,0表示失败; R) s- u W" b! P( X
- *********************************************************************************************************
2 ], w' h# b1 Z, J; n! } - */3 s- `6 L4 i( z% Z% | X$ X
- int MassErase(void)
. s. R+ T0 d0 c$ | - {
. n/ n" s, `& E4 A - int result = 1;5 N( ^! j- O8 ~% }; r1 O- M9 W6 x; y7 l4 c
- + C1 }) U3 _+ x( v! Q# N
- /* W25Q256初始化 */
2 N6 ]; I/ o+ a. Y' Q - result = bsp_InitQSPI_W25Q256();# P3 F& |4 Y# ^* p5 K$ m8 l6 z
- if (result == 1)
% y! c2 ?, G9 Y6 |. T - {7 k' B% E- |4 ~# R' }
- return 0;
) D7 o- T3 o: J$ p: { - }$ O: g. `) \2 |* o1 p( `5 Z
9 L% R2 l/ r) M- result = QSPI_EraseChip(); ; @& O* O6 z+ K7 W' }% _
- if (result == 1)
3 l- i) S5 W+ P$ g - {: s6 o6 \1 P l. o, g
- return 0;7 w ]' s" o* J2 F; ]
- } 7 f: ] }) l5 ?9 d7 `0 Y: w; P
5 S& c: s: L* ?. M4 t7 i- /* 内存映射 */
0 t+ H \) w6 `) W% n. a d1 [ - result = QSPI_MemoryMapped();
5 i3 C- @( I4 f - if (result == 1)6 x# M) n! ^: w+ C: j5 o
- {& r2 Z$ S. r# G5 c" i8 M H! |
- return 0;* v) c7 u7 K9 [# _
- }0 x; G% @; w+ m+ X+ K. c- t- L
- D+ R8 E& Q- i: T- return result; ! {, B( b! z& ^2 _
- }
复制代码 2 m0 e( f$ Z2 s5 Y1 H$ Y! C1 p' _
扇区擦除函数SectorErase
5 [1 I8 W' j$ e扇区擦除实现:4 \( P% z3 N8 I7 }8 |; a
5 F+ {4 S; N" I, F8 x. F1 k7 Y& U- /*4 ~1 C/ d, B' k2 P1 H
- *********************************************************************************************************- u9 B1 V; u3 t: W
- * 函 数 名: SectorErase. n6 M. ]3 s" O% s: V/ L
- * 功能说明: EraseStartAddress 擦除起始地址
' X( K* D1 s, l' k& v - * EraseEndAddress 擦除结束地址
% y1 S' J; X/ l6 J - * 形 参: adr 擦除地址' [/ L% Y7 q) ~
- * 返 回 值: 1 表示成功,0表示失败
% K; E! [1 m: c - *********************************************************************************************************
7 o$ F4 t, f. `2 P" ] - */! h- N4 V& G) f+ t
- int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)
t8 o* T0 u0 W, X2 t) P - {7 O# U& }' n: B% d1 Z
- uint32_t BlockAddr, result;
/ w/ J* C. z5 C2 d - 2 ^' M0 r: j! l
- EraseStartAddress -= QSPI_FLASH_MEM_ADDR;2 P% X" H. C2 `4 X
- EraseEndAddress -= QSPI_FLASH_MEM_ADDR;
: l/ o" y5 C' ] - EraseStartAddress = EraseStartAddress - EraseStartAddress % 0x10000; /* 64KB首地址 */
9 T0 Y( w. d, s; p
. x. {8 D$ f8 C9 L9 w8 X- /* W25Q256初始化 */- q7 b7 E; Z I" p
- result = bsp_InitQSPI_W25Q256();% s9 H7 h$ ~- \! s
- if (result == 1)
6 b' U. W8 T+ a' ?- d8 \ - {- _- F5 x5 Z t
- return 0; 3 n% O3 m% a3 ^5 }/ C9 y3 _
- }3 \" R: z8 ~: x% C2 r
2 q: C) p/ f- u7 T3 h. ~+ k$ B- while (EraseEndAddress >= EraseStartAddress); Z, L0 f5 R, C$ \
- {
! Z" {# u( L6 c/ \' w) | - /* 防止超出256MB空间 */3 r4 A1 l# x6 r6 t
- BlockAddr = EraseStartAddress & 0x0FFFFFFF;, f; J7 U# n- ]6 x2 n) Z
7 i# g& q2 x" @- result = QSPI_EraseSector(BlockAddr);
% w" N6 {) l m0 U7 ? - if (result == 1)4 Y. R) c; @$ T; D' _$ N4 r
- {* `0 O5 R/ q0 n% W# C
- QSPI_MemoryMapped();
4 [$ c7 Z: x% B* N0 L/ b - return 0; : K$ U% r. N3 L, T2 s
- }3 z0 q& {. K) P
- ! t. U! p3 M- ^" A4 J( Z
- EraseStartAddress += 0x10000;
( n( \8 C# Q( [4 R2 f2 s' ]. A: A - }& M" F# Q9 ?6 A+ k
7 U4 H5 X3 \7 ]9 P* z- /* 内存映射 */
9 N6 d1 \: i' p1 r' ^ - result = QSPI_MemoryMapped();
; `" V& i" y ^! X7 q- r) V( q - if (result == 1)
& t- [+ Q0 Q5 ^2 ~9 F6 i - {; O) ]2 y, {4 r- `: w( L
- return 0;) A; Z Q: l; o6 J5 f
- }2 S, {7 [& J/ Z
* x, K7 Z* N3 M; [- return 1;
% a! f+ c* A6 B/ b - }
复制代码
7 `2 v; c _0 O/ S* A, s这里要注意两点:
; b0 ?4 y, l% _8 S5 u" r6 ~0 O' D0 z' I
(1) 程序里面的操作EraseStartAddress-= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。
$ m: n( ?& _2 w9 P; ~: w3 I! {( O3 I" e! [6 j: d
(2) 这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的64KB为扇区进行擦除。
3 J% m' p/ I- E9 H% r& f3 h
' t' K& M; \1 n. f1 t 页编程函数Write
I! X5 ?) p ~7 p9 O' y页编程函数实现如下:2 F5 T% Y1 y/ z8 u: E
" D; Z3 N6 Z8 q, T- int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)7 z$ ^, ^. U* A2 r0 ^6 B
- {
! `, ?3 {- k8 c: N" g' w - int result = 0;
% [ B) r+ p. f - uint32_t end_addr, current_size, current_addr;
8 {5 u8 R9 t3 E7 p - 1 g, f J1 w1 P7 i5 Q- i% l( Q
- + r0 {& D. m5 d4 g
- if (Address < QSPI_FLASH_MEM_ADDR || Address >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)
+ {9 E7 _* s) ? - {! G" e! }, J( Z
- return 0;& g( x( {0 u3 U' C7 B* G9 C5 w
- }
( s7 ]9 K8 `. b! b# `) p# u7 u2 d- x - & o" c5 Z; d5 i, r/ G, Z ?
- Address -= QSPI_FLASH_MEM_ADDR;
. m+ ]7 h. X, w H% ]
& o& O! ~+ i' P- /* 计算写入地址和页末的字节大小 */
. ?( J/ N7 B% U7 `' j7 j - current_size = QSPI_PAGE_SIZE - (Address % QSPI_PAGE_SIZE);
, u- D9 t% A- K8 B - . x% I, T& M0 R6 S1 w$ p& _1 h
- /* 检测页剩余空间是否够用 */
9 i! D- D/ D" ~- n1 l8 { - if (current_size > Size)
* C0 [' x1 S2 a3 Y - {
" d7 J8 s' w. x. r2 l8 ? - current_size = Size;# ~1 S; a$ C! T; J. { t
- }
3 @3 O' b3 X- J1 @8 H
3 V1 \+ o P& S- /* W25Q256初始化 */
3 R5 ? |- G/ C: \, Y, c! M4 b - result = bsp_InitQSPI_W25Q256();
+ y# s6 x+ N+ D - if (result == 1)9 e9 D ^" N0 R/ O& t) I) g
- {
, }" v; h+ }4 R+ e' D3 E - return 0;
# @2 B! L' V2 K! H - }
4 f# w. {8 Q' A) R9 s$ N, T
9 p# e& n7 R. w! Y+ M) K- current_addr = Address;! f: j/ K3 @% x: U/ f8 `- [
- end_addr = Address + Size;
/ w1 T. w9 N4 T, k$ _% @' K( q
+ S# h3 L6 T8 k: O, F- do{
* T0 O) B" ]' a! g: A - if (QSPI_WriteBuffer(buffer, current_addr, current_size) == 1)$ C H9 v2 I2 ]1 h5 }
- {6 P5 K9 w4 X) F" `. `5 b& W
- QSPI_MemoryMapped(); & H1 p; ^+ Y# c( I! e1 y" P
- return 0;
, ~7 N; e* S# [0 Z7 { - }
( D p8 D1 v% i( q0 N& f - 9 i- q; ~& l" s- k9 z
- /* 更新地址 */
9 b. O5 d9 p) g g - current_addr += current_size;
. o! z9 h1 E! a- i' f. @ - buffer += current_size;
$ j4 A/ }) ]( Q3 a - current_size = ((current_addr + QSPI_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : QSPI_PAGE_SIZE;4 G8 N2 E( T% O8 ?
- ) T/ c2 x4 y* C( _6 a
- }while(current_addr < end_addr);
9 H" X- Q e5 J1 M, i6 }
6 k% |# S/ v% ^* }, z- /* 内存映射 */
- e- Z, X9 r* ~ {. J1 x% W; U. Y- V - result = QSPI_MemoryMapped(); 8 P4 z# z7 C; u3 n
- if (result == 1)
: Q( B3 t& R7 r# O# i - {
" r5 H6 b& w% \: V - return 0;- Y9 o, B! @3 F' j
- }/ x8 w3 y/ b+ F! n) V; Z4 G
- & @5 e6 `3 {. {
- return (1); ) [) A( k% P! F
- }
复制代码
2 { f2 L1 S N/ Q d这里注意两点:
1 V1 x/ N; `. K+ E$ Y" I# M5 l: \- D q, L& O3 D8 j. i' J. G. F
(1) W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,所以此程序要做处理。0 `, C6 X$ _! O! c
; q4 n( a9 m/ @2 R; S(2) 程序里面的操作Address-= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。
/ D5 {: j7 d$ }7 F- ~$ u5 P+ t6 {# F6 ^' P
读取和校验函数
: M& U" M0 O3 ~. i' P2 V+ @我们程序中未做读取和校验函数。
0 ]. J& |' b/ S$ a% t6 }/ n& _; r0 m" ^1 s1 B$ B
(1) 如果程序中未做读取函数,那么STM32CubeProg会以总线方式进行读取,这也是为什么每个函数执行完毕都设置为内存映射模式的原因。
* K- U: U- E: j) M/ w/ S* k& R1 E; ^5 N& O0 S8 u9 d
(2) 如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。
0 I- i9 ^% m! ?9 d0 D* W/ ~
h: {( `" Y6 j8 N) H8 T' @81.5.7 第7步,修改QSPI Flash驱动文件(引脚,命令等)- Y9 M/ H9 o b6 }
最后一步就是QSPI Flash(W25Q256)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_qspi_w25q256.c(做了条件编译,包含了H7-TOOL和STM32-V7板子):
0 c7 Y% ]+ A/ H) r' [
7 i3 U& }) S" `7 i, e- /*
7 K9 K; W! w9 w9 l. P f( ^ - STM32-V7开发板接线4 N' T+ j) ~& ]( i, Y. {' d
- 2 N! [9 Z) h" v) N8 |. p K! v
- PG6/QUADSPI_BK1_NCS AF10& _* T( h- x4 V6 \% W0 Q8 |
- PF10/QUADSPI_CLK AF9; X! H6 v. `$ c$ Q
- PF8/QUADSPI_BK1_IO0 AF10
: i4 }1 }4 e' r; P% R" P. ]3 n - PF9/QUADSPI_BK1_IO1 AF10
$ m% d8 z( Y- L% Z& | - PF7/QUADSPI_BK1_IO2 AF9- G; o! N+ j! f3 b) L
- PF6/QUADSPI_BK1_IO3 AF9, l2 k& s' C( H
- ) a! n6 @0 l/ y& P
- W25Q256JV有512块,每块有16个扇区,每个扇区Sector有16页,每页有256字节,共计32MB6 r5 A+ G( R6 z# `8 w
- ( I% q8 g1 h: ]6 q5 _! g4 J
- H7-TOOL开发板接线
: I: j! D7 G8 n4 B. N - / d b& B w( c7 x6 i3 K
- PG6/QUADSPI_BK1_NCS AF10
. X) p7 H2 N g: g - PB2/QUADSPI_CLK AF96 t0 v! v1 J/ |, }3 A: J
- PD11/QUADSPI_BK1_IO0 AF105 {1 U# e* [! o2 h
- PD12/QUADSPI_BK1_IO1 AF10
, Q6 A; @1 K( u- _0 } - PF7/QUADSPI_BK1_IO2 AF9
0 v4 T3 T6 t% w9 C6 ? - PD13/QUADSPI_BK1_IO3 AF9
6 W7 G2 A8 m+ E - */
2 ^/ h- _' _8 V( a& ~ - " V: H X; v- h2 j
- /* QSPI引脚和时钟相关配置宏定义 */
: `9 C Z* l2 m% V& N2 { - #if 0
H$ [1 V4 u9 c7 N& c4 U - #define QSPI_CLK_ENABLE() __HAL_RCC_QSPI_CLK_ENABLE()+ G, d9 U, F8 F; d% R
- #define QSPI_CLK_DISABLE() __HAL_RCC_QSPI_CLK_DISABLE()
u& j4 s; j1 F, s: T - #define QSPI_CS_GPIO_CLK_ENABLE() __HAL_RCC_GPIOG_CLK_ENABLE()
) o& N, F2 z. V3 t2 C0 n" Q! c$ P - #define QSPI_CLK_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
2 G8 e7 y" Y2 L9 t. m - #define QSPI_BK1_D0_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE()0 a2 n1 m$ R2 \2 r- j# }# Y: e
- #define QSPI_BK1_D1_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE()
- G; ?$ M" ` Y. A) B1 }: s! { - #define QSPI_BK1_D2_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE()
0 c* D8 |: F/ V6 Q; y# e - #define QSPI_BK1_D3_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE() {* f' z. G" \
+ U# ~1 [1 B/ ]% }" `0 r: r- #define QSPI_MDMA_CLK_ENABLE() __HAL_RCC_MDMA_CLK_ENABLE()
/ G8 [6 l# r* c6 X7 U6 _; O6 x - #define QSPI_FORCE_RESET() __HAL_RCC_QSPI_FORCE_RESET()
, h. D0 X- t0 [3 v0 O - #define QSPI_RELEASE_RESET() __HAL_RCC_QSPI_RELEASE_RESET()
0 p9 f0 G% S# d+ p( ^; i - ( m' m" \4 ~6 j2 p; P+ g
- #define QSPI_CS_PIN GPIO_PIN_6
4 N, L3 ~; A2 B7 V - #define QSPI_CS_GPIO_PORT GPIOG
9 S5 L: q8 l; g, ]$ w( e# D' O i; W - #define QSPI_CS_GPIO_AF GPIO_AF10_QUADSPI6 A/ R5 S8 c' ^- C$ A0 M6 w
- " l9 g) B9 }: i3 l3 Y/ `- V4 ?+ c
- #define QSPI_CLK_PIN GPIO_PIN_22 i# J8 ?. Y4 m1 }7 ~& `
- #define QSPI_CLK_GPIO_PORT GPIOB
. l5 P( G) z0 y; f - #define QSPI_CLK_GPIO_AF GPIO_AF9_QUADSPI
& K" A$ j3 W6 o - d. n0 m1 W! T
- #define QSPI_BK1_D0_PIN GPIO_PIN_11' Y0 u r9 \5 |1 x) K" `
- #define QSPI_BK1_D0_GPIO_PORT GPIOD
8 E- M; f$ L; K5 s# A; D. H - #define QSPI_BK1_D0_GPIO_AF GPIO_AF9_QUADSPI" d' r6 t% |- w0 v; l9 W
- . ]: m( w# Z& [# O5 \$ |
- #define QSPI_BK1_D1_PIN GPIO_PIN_12
. |+ M6 p g3 z; r( n - #define QSPI_BK1_D1_GPIO_PORT GPIOD& T9 H! P) b% [4 p _
- #define QSPI_BK1_D1_GPIO_AF GPIO_AF9_QUADSPI, ]! L* R4 d4 A4 P( F
- : z/ a- T# C) f- o* m! i7 m: r3 b
- #define QSPI_BK1_D2_PIN GPIO_PIN_7' u9 ]2 S5 S9 x: J
- #define QSPI_BK1_D2_GPIO_PORT GPIOF8 u! `) ~# _: Z5 _) g7 r! r% ?. O0 I
- #define QSPI_BK1_D2_GPIO_AF GPIO_AF9_QUADSPI: X( f$ W6 p' W
- 2 A# g6 R6 A8 u( z2 j2 P
- #define QSPI_BK1_D3_PIN GPIO_PIN_131 P2 O5 r. Y. X6 H' c7 \
- #define QSPI_BK1_D3_GPIO_PORT GPIOD
/ O% ~$ K# W3 c4 G - #define QSPI_BK1_D3_GPIO_AF GPIO_AF9_QUADSPI- ?9 P$ N9 J2 x3 d
- #else
, o1 [% Q4 Z) ~5 k - #define QSPI_CLK_ENABLE() __HAL_RCC_QSPI_CLK_ENABLE(). X( i0 O$ ~! B' v" {2 Y, Q# }+ K6 K
- #define QSPI_CLK_DISABLE() __HAL_RCC_QSPI_CLK_DISABLE()
" n0 Z( |6 g' H9 v - #define QSPI_CS_GPIO_CLK_ENABLE() __HAL_RCC_GPIOG_CLK_ENABLE()
3 E2 l9 L, h5 Y; F& ?9 ~ - #define QSPI_CLK_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE()( |& k/ r! l3 B: j+ {* F3 b
- #define QSPI_BK1_D0_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE()' M! f* z6 F# u( I5 d: f/ V
- #define QSPI_BK1_D1_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE()
3 A, p+ Z% t" G! e+ u: j - #define QSPI_BK1_D2_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE()2 [6 p( K6 b& r. L+ @7 W, `# `
- #define QSPI_BK1_D3_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE()# f6 x3 `7 D5 \1 o: {' E& {' Q
0 c P% G: L: D. Q) G T, C- #define QSPI_MDMA_CLK_ENABLE() __HAL_RCC_MDMA_CLK_ENABLE()
% K& N/ X8 a! { - #define QSPI_FORCE_RESET() __HAL_RCC_QSPI_FORCE_RESET()8 {8 c0 k& a P2 d) y% O% W3 e8 P
- #define QSPI_RELEASE_RESET() __HAL_RCC_QSPI_RELEASE_RESET()
3 f4 b# `- a' c; M4 k- }& W# s
9 \4 S+ w/ @5 W- #define QSPI_CS_PIN GPIO_PIN_6
4 q" o8 E* A. S; D - #define QSPI_CS_GPIO_PORT GPIOG
; r, D. u9 i; M# ~7 j/ h" | - #define QSPI_CS_GPIO_AF GPIO_AF10_QUADSPI
' H9 y6 b: @$ K: v* ^: e) x. _
- o5 O7 @/ f. D _- #define QSPI_CLK_PIN GPIO_PIN_10
& E: D1 R5 L9 |" D# U - #define QSPI_CLK_GPIO_PORT GPIOF2 ]! }/ K' f5 y. k6 _
- #define QSPI_CLK_GPIO_AF GPIO_AF9_QUADSPI3 p$ x" F' q: h
1 j" N7 \; H9 r0 a; _- #define QSPI_BK1_D0_PIN GPIO_PIN_85 E' e( I2 p; w3 T' m
- #define QSPI_BK1_D0_GPIO_PORT GPIOF
3 B( ]! C& }+ {! g+ \ - #define QSPI_BK1_D0_GPIO_AF GPIO_AF10_QUADSPI% r/ }; F, t6 I* [. R X
- 4 r. {. u1 O/ d$ F2 L& I
- #define QSPI_BK1_D1_PIN GPIO_PIN_9& D( [6 t9 e$ L8 t2 Q* l9 F0 G+ F
- #define QSPI_BK1_D1_GPIO_PORT GPIOF$ t% ?; D1 v& |! x
- #define QSPI_BK1_D1_GPIO_AF GPIO_AF10_QUADSPI' O* M& o2 |2 o* e7 W+ f5 S
9 P8 g6 }* F* l1 W- #define QSPI_BK1_D2_PIN GPIO_PIN_7
8 Z2 C5 g; Q [2 F7 H) \' ] - #define QSPI_BK1_D2_GPIO_PORT GPIOF. o8 G! F9 w: l2 j: D' W3 A5 v
- #define QSPI_BK1_D2_GPIO_AF GPIO_AF9_QUADSPI$ w3 l; ^6 }1 p; C% `4 o/ }
8 a, W# [5 z2 {* \- R M% @- #define QSPI_BK1_D3_PIN GPIO_PIN_63 D/ l* h5 x3 P$ }3 u
- #define QSPI_BK1_D3_GPIO_PORT GPIOF
, ^: r% y0 f1 }+ _, j - #define QSPI_BK1_D3_GPIO_AF GPIO_AF9_QUADSPI& X: a. g# q* w( k6 p
- #endif
复制代码
/ A/ t1 U" `# [9 _" B( E9 t硬件设置了之后,剩下就是QSPI Flash相关的几个配置,在文件bsp_qspi_w25q256.h:
- o. S, F/ B/ P6 w
/ Z* \% P( S" o7 B* f3 Q主要是下面这几个:# Y5 @" m8 o. R( T6 q# d& F
: z: Z3 e+ o, }2 u3 z
- #define QSPI_FLASH_MEM_ADDR 0x90000000" d$ q g/ _ C& g! t5 @
- 8 V$ l6 U7 N. N" V, F. L0 x
- /* W25Q256JV基本信息 */
* M/ M$ [: O8 a1 k - #define QSPI_FLASH_SIZE 25 /* Flash大小,2^25 = 32MB*/: A7 W% c( f* G
- #define QSPI_SECTOR_SIZE (4 * 1024) /* 扇区大小,4KB */
& c# K0 [: x$ g9 c6 R7 R9 U - #define QSPI_PAGE_SIZE 256 /* 页大小,256字节 */
) G% p. `2 R4 w# |0 F* ^1 K) s - #define QSPI_END_ADDR (1 << QSPI_FLASH_SIZE) /* 末尾地址 */
3 R6 L _; X W4 u' E( Y - #define QSPI_FLASH_SIZES 32 * 1024 * 1024 /* Flash大小,2^25 = 32MB*/7 B9 {$ \" F' x3 a. k h$ E
4 Q- t* t- a1 k/ I- /* W25Q256JV相关命令 */
- B2 p+ Y# N& ? - #define WRITE_ENABLE_CMD 0x06 /* 写使能指令 */1 t% V. P& f* K# z( W& l2 S
- #define READ_ID_CMD2 0x9F /* 读取ID命令 */
3 ~# ^/ g4 ]/ S2 H2 U( P - #define READ_STATUS_REG_CMD 0x05 /* 读取状态命令 */
: }$ G+ f3 d* s6 N& F K( S - #define SUBSECTOR_ERASE_4_BYTE_ADDR_CMD 0x21 /* 32bit地址扇区擦除指令, 4KB */6 @5 P" \5 ]$ @) H& M: `
- #define QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD 0x34 /* 32bit地址的4线快速写入命令 */4 l- W8 _: l& Z7 |# G) J
- #define QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD 0xEC /* 32bit地址的4线快速读取命令 */
8 e+ I. p" v0 G - & x$ q' x8 N1 X- N7 H
- #define BLOCK_ERASE_64K_4_BYTE_ADDR_CMD 0xDC /* 4字节地址,64K扇区 */. C' _9 Y3 x3 e
! s; O- b$ m, r5 e3 s3 d2 r0 S- #define BULK_ERASE_CMD 0xC7 /* 整片擦除 */
复制代码 3 N6 ]- s9 ?% _0 {+ j8 _' m% q
81.6 QSPI Flash的STM32CubeProg下载算法使用方法
1 A5 q7 ?1 b: e0 c/ i编译本章教程配套的例子,生成的算法文件位于此路径下:( C& \5 C; a; O
' ~3 A5 X2 _$ E) m
: y# u# _! y' S* J
* c' ~5 N D/ U& W- R1 V81.6.1 下载算法存放位置7 i; ]' u9 D7 j( `" y
生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:* n7 G5 V4 ?, Q/ D7 D& M
! x4 b- v- f7 K% v\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
5 K' p; A+ b# \2 X) ?0 s' i q4 m# d
" Q2 A1 e8 H' F
. m4 m* U6 `) W" W( k81.6.2 STM32CubeProg下载配置# r- q4 c' Y9 C( e
我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。
2 P5 A6 f# Z7 p6 _: @0 x# |6 w9 Q. q; o3 }" x3 w- }) I
: R& \0 f4 |9 F+ R0 M
5 Z- _" _& R* p1 W6 H& y* X点击Connect后效果如下:
' h# i1 A2 \* ]0 M( i6 I X4 s! N1 M, e' I9 P
$ \* {9 `: y5 V; @
, Y+ t1 g/ ] i3 s8 w在这里选择我们制作的下载算法:/ O! \/ p5 W7 {! W; ^8 W9 C5 P
- c! X- R, Q. g. _
# w* _. |0 [" ~; z
8 N9 C X7 t) K3 a8 z* q+ i3 y任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming! N4 M) T. m& T
- \% }+ k3 E4 `1 Z. |3 ^6 O& U. m/ j# a* R& S
6 K, J+ M/ h5 f5 [+ I& X# U
下载完成后的效果如下:( e% @& M6 p$ q6 s$ W
7 p# u0 N1 L+ M/ T1 c4 @0 g2 X
1 f. C7 t, s/ p$ [: Q3 o
( B+ J$ a5 H. u2 a81.6.3 验证算法文件是否可以正常使用
# ]/ i8 d; p% h- j1 D0 @0 T8 X为了验证算法文件是否可以正常使用,大家可以运行本教程第82章或者83章配套的例子。也可以使用STM32CubeProg直接读取:. @; t# Q" B; s- h6 u
! C' \. a5 o9 o2 a1 C
+ O$ F. u* w" o' M. H& F0 D) n$ p# k' z. Z
81.7 实验例程说明
! I/ @1 D& { q9 R, K3 W+ E本章配套例子:V7-061_QSPI Flash的STM32CubeProg下载算法制作。
* D- S2 F) e2 O1 ?+ ^! y. U
# k: V7 `9 C" X编译本章教程配套的例子,生成的算法文件位于此路径下:
. m X0 N( q6 c: T4 h: s5 W5 j5 x
, D4 p1 |" ^* a5 R: J" s! A3 C1 U& r8 j+ c# t& ]) f$ O
. s2 {$ X. V4 o B: v
81.8 总结2 o9 h+ w' n4 Z$ ?9 n
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。. o |; @: w$ b) C- y
& v1 `8 o9 E3 U
9 `" h2 P0 b+ u5 A) i0 n( B4 S4 [3 m( T
|