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