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