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