你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32H7的SPI 总线应用之SPI Flash的STM32CubeProg下载算法制作

[复制链接]
STMCU小助手 发布时间:2021-12-19 15:00
85.1 初学者重要提示
0 L1 F7 r2 b) k6 P% @6 m: f% F( Z  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。. [+ {. s5 ]  c5 j5 h2 ?& a
  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样% W3 q( F+ Y. e/ m$ Q
  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。
" @4 p, ^- B% r: {7 [  a85.2 STM32CubeProg简介
2 j1 ]+ Q) }  q4 w9 a2 G" \$ VSTM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。
( E6 D% ~$ ]  s% E3 R, C- [& k6 I1 q, ~  T# C( D- f3 p
d80578d217eb2a83520c6f3d797218f4.png
) V5 H) S; u) F
) Q, `! c$ m& n: a* r
85.3 STM32CubeProg下载算法基础知识
' o2 Q9 S; I% h) o; r$ D8 d: d: Q
STM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。
; s% X& S. a: K+ H9 W; M5 B- g) l  e. H+ s0 z: s
85.3.1 程序能够通过下载算法下载到芯片的核心思想7 x: l& ~/ M% d) o
认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。- O2 f: m+ A* {  f
* }. E: K8 R% f& e0 o( f
85.3.2 算法程序中擦除操作执行流程: D2 x6 M/ ?1 N" _) a5 @9 M% {
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。
" A$ w- k7 s& e& k1 }
7 G7 e. p  r# B擦除操作大致流程:
' z; i4 b" i' q: t1 Q* K' H+ p" D
% X! |, c# A0 v
d75670256363bb50203bed0bbe4f9fd2.png
8 G% H8 H3 u7 O7 H: c

% a# S: n9 n% v1 f  加载算法到芯片RAM。: J2 \% \9 z, e4 N: C0 f! [$ d  x1 C
  执行初始化函数Init。7 W6 r& w- y  V/ @! t. ?8 C
  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。
5 d: o6 L* ?: `5 U: p7 ^4 H* J  执行Uinit函数。
+ S6 m* M2 ~7 h5 M; v  操作完毕。5 \, a" q7 ^; o% y" q9 q

6 p$ b, V4 b6 t: k85.3.3 算法程序中编程操作执行流程- q5 Q; y* v5 W
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
( o' z# m6 g; Z/ u+ _3 S) h
! g2 L# i* _+ B编程操作大致流程:; P: ?0 S" W8 W' ~3 J# q
1 m( |- n* m2 _' V# R
4fa96e9e33d700081f2cdbd312afb31e.png

' `5 o3 x3 q- n' r
# N- G, U: @# r, x  @4 d/ w  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。, k7 u2 H& G$ K9 ]8 K7 m8 C
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:. a# m& ]) V) y8 Q, c; Y" ]( {2 Y
  加载算法到RAM。
- Q9 f& X/ d' |& ^4 p3 m8 j  执行Init函数。( ]# @# c5 i( ^/ L5 e! D& b  y6 C
  加载用户到RAM缓冲。  X9 C1 I% U5 P* N2 G- M1 _
  执行Program Page页编程函数。, f5 F* @( s6 `7 ^& w! U2 o
  执行Uninit函数。& }/ g$ A3 M' |8 t: I
  操作完毕。/ X9 _0 e* t: v( h* ?' C2 `0 q

0 |1 S9 U& L/ u( e1 S- s85.3.4 算法程序中校验操作执行流程" M+ @# S/ o9 C6 E. \0 t7 E5 W4 ?1 h7 U
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。: X$ M7 J" M" L- b' x! V9 U; H+ C

$ n7 k4 C1 e. U( U& A; w校验操作大致流程:
5 _9 c2 c6 {* E# p( W
$ H% n- S. a! p8 V6 v* f6 G3 @! @% v
7deeae4b650bd82c457492af14fc26b7.png
0 L+ X+ a6 u' U* L$ B  Y3 k- }1 v$ y
0 u" d" C) D8 b" e1 y
  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。3 }. v: k% d1 F. U. v# b: D' {, t9 l9 v
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:, S3 R' L1 e% |( l; c  l+ k2 t$ E
加载算法到RAM。, @: y" S; |: S+ ]: S
执行Init函数。
' ?- i/ U) {9 k0 a6 r. f' { 查看校验算法是否存在$ y8 |6 W5 S: U& ?
  如果有,加载应用程序到RAM并执行校验。( p3 s; Q& I% w$ I9 P
  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。
1 h% z. T. }' j  执行Uninit函数。
4 {+ B' _: \6 Z# P$ a' \  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。% ~" f4 ~* @4 x) g+ Y4 l8 K
  执行RecoverySupportStop,回复支持停止。
+ U! X! L( p& h; G0 V) s# m$ F; ~  执行DebugCoreStop,调试内核停止。: s0 t: f! [$ |
  运行应用:
; f4 F- R  v3 E) a: e5 @5 Y" |9 p  执行失败
7 H- |! j1 @2 `" |- i3 P1 i' N  执行成功,再执行硬件复位。
  ^7 ]( s' L* ~/ P* V' y  操作完毕,停止调试端口。; v% Z& d' U; k. w$ `/ v

9 T6 O9 i; z* ^: Z# m85.4 创建STM32CubeProg下载算法通用流程
5 Q+ C# X9 d8 A; N/ @3 w1 y( X下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。# ]' v  E. E8 u1 a* v* f8 N( q% g
9 m( W: k3 E% f9 g# e  l
85.4.1 第1步,使用STM32CubeProg提供好的程序模板( y, F1 F3 w/ a" t9 V
位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
3 c3 u0 a, M! y6 [9 q& F! Y# H) H+ w4 I; M
97f385e3b62a055bac9b5c0214b877e5.png

, R. M5 j8 N7 Q+ i; b
! ?6 D! \- u6 ^# G; T- r( B8 ]4 W6 S# H以M25P64为例(注,其余步骤就以这个为例子进行说明):
. v7 c8 Y; m- z6 d* `* k, H- T# e" y2 ?7 t, v
43b298f996937fe35e89697fc6479cb9.png
; I9 Q' }1 @. O# h
4 y; K7 K7 R: W- u8 Z4 m3 N
85.4.2 第2步,修改工程名
3 `% o, ~7 [% q) E6 j/ l4 G) O! pSTM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。6 f' X7 x& \/ N- M1 B

+ `4 K1 M+ _2 |% P85.4.3 第3步,修改使用的器件
+ H+ y: s1 J8 u9 F( _4 ^- o
在MDK的Option选项里面设置使用的器件。+ b) c6 k* X" ?, M
5 o6 I) j$ a/ N! }4 O8 S
36026b9a6d60249610d0246eb725b246.png

) [" n# b+ g$ {: B# q* x  f8 w1 _
# k# R8 G- b# J. K# }! j  c" _2 ?85.4.4 第4步,修改输出算法文件的名字
* G7 ?; j7 K  z/ W这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。: R6 E5 P  W$ z+ ~" y' w2 p
1 W, H: k) |/ U: b, I9 m
52c72b3993f28a366a22eac56f8fd043.png
6 h0 o( U9 Q+ G

2 {* f! ^- G, o' a& @  {注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:
( \4 z5 d) W3 d3 _, O
. L3 K2 f9 ~  S/ P9 m% f
7b48153027a44de33926c2da008716a0.png
4 ~) F; z) ?/ j) K5 y2 j

% P1 ~8 B1 ~& E+ L85.4.5 第5步,修改使用的库文件和头文件路径, R, _: R5 o, i- T& Z( w
根据大家使用的主控芯片,添加相应的库文件和头文件路径。
0 _! Z* F8 \2 u8 f1 `; F3 g' w# m2 l% a: S! `/ M
85.4.6 第5步,修改编程算法文件Loader_Src.c9 j; M% d! R( w" @. v
模板工程里面提供的是M25P64,部分代码如下:
0 d6 j& x" y1 E# G" E" \" S- @+ y, m9 T
  1. /**
    9 j( m, k5 P) E
  2.   * Description :: D  R! k& q" {$ g# L
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the
    ' P. F& H( x, V. N: I% n* O/ W
  4.   * device and initilize the FSMC with the chosen configuration
    / {) L( G) @9 m3 X! Y
  5.   * Inputs    :
    % V. H- J1 x3 F
  6.   *      None
    9 d3 l0 |6 M  e5 l2 x/ B' x
  7.   * outputs   :9 j/ v4 z) @9 M
  8.   *      R0             : "1"             : Operation succeeded
    8 o- h! S8 N! Z9 P2 K
  9.   *               "0"             : Operation failure& N8 _  Q  a) D, l
  10.   * Note: Mandatory for all types of device
    0 Q- x& _3 U! ]2 Q" g, l9 v
  11.   */. o( o0 n5 s+ Q. V" `0 y
  12. int Init (void)
    " t* y1 a0 {0 l# v& u2 o
  13. {  1 N2 O/ B* K2 R
  14.   SystemInit();
    % }; |. M! |) q" q
  15.   sFLASH_Init();$ C1 ]- h& g/ V% c- a- {
  16.   return 1;! q; k3 I1 f3 [2 U3 U4 N
  17. }
    1 g5 r6 [8 I. U. m& E

  18. 1 R. k, G$ Y. j" S% Y' X

  19. - g) z. h; K( T* _0 w# T
  20. /**% k7 N9 ]8 `+ g8 S- ~
  21.   * Description :7 {: W2 j3 J* p9 N" J+ x5 T+ E+ I
  22.   * Read data from the device
      ?/ e/ s9 Y$ ]2 P: I) O
  23.   * Inputs    :
    " E& t# O# Z9 K* k5 j( S4 M; [9 n: X/ W
  24.   *      Address       : Write location) v* F4 Z" Q$ R2 {
  25.   *      Size          : Length in bytes  $ U$ E2 h; W8 w5 e9 O. S' H
  26.   *      buffer        : Address where to get the data to write
    , [8 d9 }) u  E$ z! O: w
  27.   * outputs   :
    # j2 U8 \* d/ o+ j# T) O# q
  28.   *      R0             : "1"             : Operation succeeded) X) |' a" c4 E3 e5 g. e4 X1 H
  29.   *               "0"             : Operation failure
    6 p) ~% F5 z5 M4 K( O
  30.   * Note: Mandatory for all types except SRAM and PSRAM   
    " M, B' E0 L) A) l
  31.   */
    6 T, o$ b9 }/ n$ T1 K9 v; k
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)
    & Z& O1 g) g9 B* u
  33. { ' W- o) u/ a. _8 ^! w, R0 ]
  34.   sFLASH_ReadBuffer(buffer, Address, Size);
    7 Y* R- W8 z  B6 G4 q( \
  35.   return 1;0 g" J* M! L2 {* O4 G
  36. }
    " [+ F7 @. t/ T: Q; W5 Q  g

  37. * Q+ g+ E5 m  G% u

  38. & ^( n# |& ^3 Q' F( K/ K* A
  39. /**
    3 o$ F' j3 Q% y4 W
  40.   * Description :
    & w5 h  F  v% r# P3 z
  41.   * Write data from the device
    / Z$ V: ]3 m- g/ t
  42.   * Inputs    :
    * C0 `7 ~8 x2 {# G+ Q0 G
  43.   *      Address       : Write location! r0 Y% T2 |3 E  M7 O$ m
  44.   *      Size          : Length in bytes  
    * C* T" {, k  \! o5 P0 j
  45.   *      buffer        : Address where to get the data to write' @- Q  R6 X* n. ~$ z
  46.   * outputs   :/ e. o1 M' I/ X9 U
  47.   *      R0           : "1"             : Operation succeeded
    1 g. \3 K" m! o% m! ~4 O
  48.   *                     "0"             : Operation failure
    ! y: B2 y& z3 B- f
  49.   * Note: Mandatory for all types except SRAM and PSRAM    / N9 D6 {4 L! f, L2 h- t
  50.   */9 r+ T: \5 |2 q6 W1 `, N3 q
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)
    , `, l# M# W$ v: h/ Q9 B7 }! \/ h, x
  52. {
    2 Q' T% |+ }2 ^' s: \
  53.   sFLASH_WriteBuffer(buffer, Address, Size);
    " k7 _: B3 _1 m& O0 N8 U% ~
  54.   return 1;* ]2 |2 A8 w. f" `& ?( A
  55. }
      l7 l- _4 m2 Q3 h' @+ [9 w2 i! W

  56. : j; P: p- T' o
  57. $ _8 @$ `3 Z) M5 j! `) v9 p- a; y
  58. /**
    9 d" n" t$ h' W' |8 N8 H. j
  59.   * Description :
    $ M. z# |& m1 F3 k. U, k6 h2 j' {
  60.   * Erase a full sector in the device2 A: i2 P/ i5 z$ L; H0 \  v5 s
  61.   * Inputs    :. s7 e+ b( V0 ?# J& k
  62.   *     None; C$ O! w% ]$ `; g* n* f7 h
  63.   * outputs   :
    % v9 L$ A$ N! S; ~8 M% A) R
  64.   *     R0             : "1" : Operation succeeded
    " s' q/ X) t; q* O7 B0 p
  65.   *              "0" : Operation failure
    $ P  t6 g' w: s* i+ k
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH9 c3 V0 W( O7 k2 e1 d
  67.   */1 b' `+ E4 W4 ~4 f% k1 s5 K8 J$ V
  68. int MassErase (void)
    5 O6 ^+ \& ^/ D( i( L0 z" H! n
  69. {  4 V1 }. B; e2 m, K6 F
  70.   sFLASH_EraseBulk();9 C4 e) I5 a# W7 W$ o) q* I
  71.   return 1;   
    # r9 s; B, R1 C
  72. }
复制代码
% j% n. l* Y6 h& e! n* S
85.4.7 第6步,修改配置文件Dev_Inf.c3 X3 h9 A% B. D% w/ _
模板工程里面提供简单的配置说明:
- v, O/ {) B' e6 J" a7 k0 A1 h% T! g+ I7 `
  1. #if defined (__ICCARM__)
    + n' ]& f) t3 _5 n0 m: y% {
  2. __root struct StorageInfo const StorageInfo  =  {& S/ t5 [+ `% }; b: Q6 `& m
  3. #else+ u+ i. T/ |; E2 `; F' G# e
  4. struct StorageInfo const StorageInfo  =  {/ M4 P4 r" X2 N. y" \. @% T- b
  5. #endif
    0 o" k7 n, t7 C4 O. V: k! \3 G
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number
    4 A+ Y! B9 k: I$ H
  7.    SPI_FLASH,                       // Device Type
    4 O7 }( v3 L" F
  8.    0x00000000,                     // Device Start Address* |, D. H: `  `8 p1 q4 b9 t
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)5 Q4 l1 @/ u& ]% ~( w7 D
  10.    0x00000100,                      // Programming Page Size 16Bytes
    2 h. H, m* P- v8 l5 G
  11.    0xFF,                            // Initial Content of Erased Memory
    ' c6 _: Y6 X+ ]) H2 I5 Q# T
  12. // Specify Size and Address of Sectors (view example below)
    0 C" h* @7 c6 w) S
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes
    $ E3 E3 L+ _8 P7 c( K
  14.    0x00000000, 0x00000000,
    # \/ q0 V4 [2 E% _
  15. };
复制代码

- Z% z* [9 a* l# R7 l注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。- F* m$ O% D5 x/ E/ Y

0 f! N; f! P3 x0 O3 r9 _% D85.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。

" z( \% }) w, S, tC和汇编的配置都勾选上:6 G# r9 @, B4 C( h
8 M( C# ?3 E6 r& |
afddd6e839292aa2b94b43e79544d630.png
2 S8 q& _+ d/ l9 Y3 I% s
" W6 h  N$ W8 `0 I+ s9 P: Z
汇编:, D& w* W4 j( P& \1 N. {

! s% [0 ?: [9 f: T
f639ae5b0d3c2e0e0190ec509fbc8a4d.png

6 y: I6 B; Y2 O0 ]  y/ s
% @  U+ W. t5 r( o$ g( d& A2 P如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:
  z' v) C5 K) h6 U
5 c7 R' b. g% l1 b(1)加载以响应运行事件。
! I' L6 K3 K& g7 b: {
$ N/ Z5 a" D0 w; r0 q(2)在不同情况下使用其他例程的不同组合加载到内存中。
# k0 X* h, v, |" @7 R6 W/ J& ^
& A6 X6 h# q* e6 _9 D7 t(3)在执行期间映射到不同的地址。
6 k+ u& n) x. l$ a- }8 |. T7 y. `/ N4 ^) a
使用Read-Write position independence同理,表示的可读可写数据段。
' l7 U; ~! Y) X2 r  n8 u/ V: W6 N$ `$ a5 l5 M* V2 V" i
85.4.9 第8步,将程序可执行文件axf修改为stldr格式
; I1 c; k$ A& k通过下面的命令就可以将生成的axf可执行文件修改为stldr。4 i7 w4 m. ]( o3 z, _: ^% n

; y( _5 v! e/ @. A# i% A0 f
c683673d2481d8dce75b6fa01d624600.png
5 v( T/ T+ E8 ~- D+ G* k
" n) Q3 U3 A$ Z( A
85.4.10   第9步,分散加载设置
. \' l6 i: y( c$ w我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。& B5 v6 h) }% m2 i+ q0 G

  h" a1 W1 I; ^2 ?# R
b96835a7506516319002e6b2e8db9371.png

, H. ]$ Z. z2 r  U! v8 }  l
3 t6 h2 K0 Q6 v9 A: }分散加载文件中的内容如下:
0 `/ O0 ^% b* T( ?1 L3 |6 \# Z' z5 X  I: F2 A; D" T
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions
    1 O6 n' G" x" X% j7 V! e3 {
  2. {
    , F2 f$ d% m* ]9 N
  3.   PrgCode +0           ; Code3 H' c7 n- `+ G$ Q  L; l0 w1 i' b
  4.   {
    0 \# j4 q% `/ T: {9 j
  5.     * (+RO)
    ! d, p) j: |2 j5 I! L- P2 x# A
  6.   }; m# \; O# m, P. G5 k2 M" z
  7.   PrgData +0           ; Data" w8 E, O; x9 \  \0 r
  8.   {7 }8 C7 }, j6 O3 O( Q- ?
  9.     * (+RW,+ZI)' t: L) m% ?& ~. R) |
  10.   }; z& E1 S: o) Z
  11. }
    & {/ s" H/ ~0 c: q9 g

  12. * ?2 U- {9 I3 I& c
  13. DEVICE_INFO +0               ; Device Info  Z+ G9 Q- N+ N' w) y: B8 S' e* Q
  14. {
    5 x6 h* _7 k, W, H  b
  15.   DevInfo +0           ; Info structure$ e' j2 `; i6 y: r. I
  16.   {
    5 k, g& ]7 }9 n5 c& C1 _
  17.     dev_inf.o
    $ d4 n& \7 S8 l0 E% r: v3 a
  18.   }
    $ Q& l" R( Q* b0 i% D8 X& C1 S
  19. }
复制代码

- }* N5 u5 f* I8 c) x- D# E1 ~. y这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。: U/ e8 p* w+ r" [8 c. H

* p. T! }/ N0 [/ l8 i' O; u! m' _--diag_suppress L6305用于屏蔽L6503类型警告信息。
( I9 W! }, o( r% Y, T  \2 X
& t( I" D& ~* V. x0 M& Y特别注意,设置了分散加载后,此处的配置就不再起作用了:* z1 Q6 c8 K- g
0 H  G3 }- M/ i# L' r
641395c374a3af02f1a92d3ff9ee4199.png

1 e) M/ R' m! k% ~7 p# [. J* J; H8 I/ [
85.5 SPI Flash的STM32CubeProg下载算法制作8 X; B, i4 q& \& e3 Q
下面将SPI Flash算法制作过程中的几个关键点为大家做个说明。
; h. r& u9 ]8 }6 p8 e% q% Y, _. s) R9 C4 ~, u0 P3 g
85.5.1 第1步,制作前重要提示
9 ?1 \9 ]% z) r! O4 K. f
这两点非常重要:6 b( R2 ~+ M% s# l: o
; M  m3 f* z/ B
  程序里面不要开启任何中断,全部查询方式。
' ]7 [1 u, X1 u" A2 w" w  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。) W4 C7 y3 k3 O; q

; Q* `7 _6 Q4 |85.5.2 第2步,准备一个工程模板
4 I6 r$ g- e6 F% N, J9 h 推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
# A; Z; _6 Q' q* f$ v* F: I% U5 S
. p' x8 v# s5 _/ R  Z! f
80ad5e3abdd6a993c8621230401a1313.png

* C% _0 }4 B6 ?3 M
$ x7 P" S$ Y- x; ~; a# A85.5.3 第3步,修改HAL库
/ e1 m" l/ x1 ~& y1 g这一步比较重要,主要修改了以下三个文件:
( |- C9 p4 b& |" B7 U' ]: p
+ k  Y5 a) f  ~9 W' A1 A+ ~/ k
33bf89f25487547d4008f43b7cf10e03.png
8 \! m, C" J# I$ x7 f

# G0 V- j  q, R8 z, v6 f主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。8 [' ], }4 o  k# B( {0 S

% x4 O" c% m0 N: j85.5.4 第4步,时钟初始化
4 E0 g; O  c2 f, N* d' [我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:. N. X2 y0 v% k4 x3 m
0 [# a- G' e" ^+ _/ e) X1 ~( ^
  1. /*0 t+ L; o$ l6 ?" C- j9 I  K, U
  2. *********************************************************************************************************
    - o$ m+ V5 A9 o5 w4 W$ ~
  3. *    函 数 名: HAL_InitTick
    : Q9 l: @1 N7 S, ~
  4. *    功能说明: 重定向,不使用
    3 @( g' `  o4 ?7 A4 L2 s3 O
  5. *    形    参: TickPriority
    : ]- @0 L3 W! k9 }+ t) Q% N
  6. *    返 回 值: 无/ G  Z2 }7 e- s+ K9 B  F
  7. *********************************************************************************************************
    $ r; G& ]: s% j4 w8 h4 `3 G
  8. */+ R( d7 S2 R9 ?
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)8 h1 y! `& k+ I: v& |& |1 X
  10. {' I! t; t6 y+ o
  11.     return HAL_OK;
      d1 u, d5 _- L: c* t9 R; \
  12. }
复制代码
! p) F/ w% Y. W( a
然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:3 U- `9 u1 A! g1 |* h; d

5 v: A$ {' H; K" `0 z0 E: y
  1. #if !defined  (HSE_VALUE)
    ) |: y6 ~) n9 ~; F2 H, L
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */7 A) O( m# {3 O! p
  3. #endif /* HSE_VALUE */
复制代码

: p& d- u& a: [- Y7 G$ ]: O8 Y" p4 k最后修改PLL:
' L2 n/ q% T% A
. c$ U, X( v1 O( E/ D+ m2 f
  1. /*% l2 @' Q, b5 j% u8 b
  2. *********************************************************************************************************! F$ o4 G3 h9 k; h% k& N
  3. *    函 数 名: SystemClock_Config
    - p7 D7 r0 l: l
  4. *    功能说明: 初始化系统时钟
    9 K! i* q: o5 U3 l/ ~; {$ p8 l: R
  5. *                System Clock source            = PLL (HSE)1 e$ V3 W' j9 m$ B: _$ _
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)
      v+ X( [* R% n# J* s6 r1 O/ J
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)( n% I; m& }2 S8 c8 A) L$ J; U% d7 k
  8. *                AHB Prescaler                  = 2
    ; M/ q: B. \; ^% x1 p- c
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)+ _8 R" d/ b" |' d
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)/ X5 [- r# E* e# \) f
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)
    - c6 v& b8 ]5 \( f: S( w7 ]# T
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)$ g5 y8 k! W* W0 |8 S
  13. *                HSE Frequency(Hz)              = 25000000
    - H" Q. a( G  Z3 E, r
  14. *               PLL_M                          = 5% |9 q( E1 ~9 h  c; ^
  15. *                PLL_N                          = 160, C9 `5 a$ K2 ]* v& [
  16. *                PLL_P                          = 2
    ( H. X: C( f4 b( F9 @
  17. *                PLL_Q                          = 4, D& V5 [" w4 a  |# f  s4 H
  18. *                PLL_R                          = 2
    4 c) L: c4 A* ~8 R/ U. n+ I
  19. *                VDD(V)                         = 3.3
    + E1 t% y+ s7 l# l' {3 I. q
  20. *                Flash Latency(WS)              = 4, f5 k8 t: c  f9 \2 w' i
  21. *    形    参: 无
    # K2 {9 ]2 Y* v) k3 W- A6 u
  22. *    返 回 值: 1 表示失败,0 表示成功
    " d% L, G6 l  t1 \: ]( |: B
  23. *********************************************************************************************************0 i* w4 @/ x0 {# r7 s! U& b
  24. */( v8 G4 D( u! ^+ K& ~4 E
  25. int SystemClock_Config(void)
    7 g+ N! `4 s4 @7 w
  26. {9 ?  D) e* M9 I  W/ W. y- O2 y
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};& K- F% W! \8 B
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};0 F' F) @. }# C9 b% A4 W
  29.     HAL_StatusTypeDef ret = HAL_OK;
    " A1 q% c4 m% C- S3 y

  30. 4 t4 X" ?; u' z2 W; ?
  31.     /* 锁住SCU(Supply configuration update) */
    / [& L: ^5 G- B) h' d
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);/ ]4 M; x6 D0 X3 r+ e. F+ H

  33. % m, z" n. g& g) i5 g
  34.     /* ( j) H" F( j& y4 H
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
    5 m9 Y' y+ Z, i6 ~3 I, ~; X
  36.          详情看参考手册的Table 12的表格。" w* k/ O6 l9 T' s) M# B
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。5 k1 l8 Q8 i( |/ P5 n: p
  38.     */
      b( M) P' h* b$ s. y: \
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
    ' v1 e# c9 ]. P& s& ^- ?2 j
  40. : Z( ?0 s$ A5 l+ T- e% z1 U; a
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
    # m" @( `8 ~' \; s: {5 x; l

  42. 9 f, t) T1 ]8 \' z- F. j- y
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    ; E! Z2 y4 T% |5 e& r
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    ) s3 W- M4 G" N! Q' b
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    / v3 P/ P3 S% N; f
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;. w& x3 P6 _- l) a4 E
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
    . Z5 b! k2 k/ A* V
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    % a8 Y5 N. x& @" J, E2 ]
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    8 A- B. `) z( g. T3 Z6 F8 z
  50. 0 U" F" M3 `- H8 b) Y
  51.     RCC_OscInitStruct.PLL.PLLM = 5;- [. f! ]8 @* x1 b& W
  52.     RCC_OscInitStruct.PLL.PLLN = 160;) M) m( G$ Y. ?- o" o
  53.     RCC_OscInitStruct.PLL.PLLP = 2;! I2 j- `: S3 h4 \
  54.     RCC_OscInitStruct.PLL.PLLR = 2;0 B" [1 [) j$ Q
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        ) E2 d4 q9 D. Y1 y; r# T! D

  56. 2 u# }) g; F0 D$ J9 e
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    # J9 Y0 M# h/ C- A. k) t
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;   
    . g6 t6 c1 T: E7 k
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);  q, {7 V+ w, u
  60.     if(ret != HAL_OK)
    7 P9 U7 ^- Q* e7 k
  61.     {0 h* e" A) ~9 d; D+ ?9 R0 v
  62.         return 1;        
    - j+ {) `' D% H, j( f7 z' n
  63.     }
    : J' L6 S8 e5 I
  64. / n  p# d) q$ x. R4 p
  65.     /*
    4 k$ P5 m9 w; O1 _. c" D+ i* v
  66.        选择PLL的输出作为系统时钟
    % }/ J' a* o; T6 ?
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟
    / x4 U  k- H5 T/ w
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
    . b; U& b1 I/ X. o3 k3 b8 ?/ I
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线6 C- X& ^% F, V$ }5 j2 d
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
    # G4 \7 f* U: Q6 ?5 I. p* d4 {( y4 H, ]
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线) M2 B: H, ]# l
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     * |) U! _; n. e5 q8 B/ z: `
  73.     */
    - L" ^1 B, \3 X* ?
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \8 `+ W( _$ Z% Q+ p( i
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);
    # T, G$ \2 c* N4 H' N
  76. ) K' ^/ F8 m& H% z6 c  ]! k$ {' m5 t3 |
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    4 N# `: r% I1 J5 k- m
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    / e; A, M/ S/ \0 g4 }0 {! [; k, y
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;4 r6 @: n1 L6 K8 F- x
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  1 q$ J' S+ k, k( _& Y% W$ t
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
    ) P- P; t4 s4 ^+ S/ P5 Z) B
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; 0 f9 O$ U: Z; j2 ^- `3 i
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;   W; Q% F8 T  r9 J3 l9 L: }

  84. 5 r# T0 J4 I; z, [8 F+ z& r
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */, n; C: K! \4 v0 t5 \
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);  E* a# p" N& ^5 M
  87.     if(ret != HAL_OK)9 }9 r/ J1 O- o1 H* I
  88.     {( v: Y6 v( }- Y  S9 l
  89.         return 1;
    * o# W" k# z, b  a# F* x* i
  90.     }9 `, G3 d" L  ], l
  91.   N7 @6 G# A, A* F# t6 g, i. t9 J
  92.     /*. f9 E, [' h% [8 B% e
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数 " j7 t. K" g  ~# L1 B1 V: z
  94.       (1)使能CSI clock
    . h4 Y& M% r& L& F/ D/ F% |, \
  95.       (2)使能SYSCFG clock  ~2 ]7 b) M* Z3 i& m2 j
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
    # b/ {/ C% m9 V  Y: ^. ~% e+ U
  97.     */5 R7 i* M6 ~; Q7 j
  98.     __HAL_RCC_CSI_ENABLE() ;" ~9 v0 l) ^5 W  i. |7 r2 {+ c

  99. ! j) B2 X. N5 K* i% F- K( M. m, y
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;
    5 @8 n3 v7 [! A% `

  101. : i0 g& C( X0 e; ^% i
  102.     HAL_EnableCompensationCell();
    ; I  R2 V; _2 s' @- E; b7 k

  103. / _2 q& R, j8 t: V- d4 |
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();
    3 w) p3 O* F9 q% H* O
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();: f) |0 W  F6 w  `
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();# j# a- j0 q6 R8 L
  107. * E9 v$ K5 J* C8 @* {
  108.     return 0;. E6 }% {8 [" Y2 E) ~& l, {
  109. }
复制代码
* Z" c4 }. C; u/ v) Y0 S0 j# T/ ]' m
85.5.5 第5步,配置文件Dev_Inf.c的实现) D( {3 T* P' b2 \" W
配置如下:/ l! r4 I" a1 J$ t0 ?0 L
) W; v0 @: V8 h8 q
  1. #if defined (__ICCARM__)( l, x; `9 A  r; X4 T: \
  2. __root struct StorageInfo const StorageInfo  =  {4 k& W2 o' y8 z# |" I6 r7 Z
  3. #else) Z) E# E5 [$ f6 G- v7 g
  4. struct StorageInfo const StorageInfo =  {0 y8 A& Q* d  }: N; Q
  5. #endif5 m% w' X0 o8 H+ U
  6.     "ARMFLY_STM32H743_SPI_W25Q64", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */
    ) J2 u- N/ R1 r: r
  7.     NOR_FLASH,                      /* 设备类型 */+ C# _/ G" o4 N" P  b% c8 x/ V
  8.     0xC0000000,                     /* Flash起始地址 */  l8 J" _* C: Z! |9 X
  9.     8 * 1024 * 1024,                /* Flash大小,8MB */
    ) R$ i0 T2 i) T) q5 I0 v3 x
  10.     4096,                           /* 编程页大小 */
    0 _* n9 l2 T% x; T. ?# N
  11.     0xFF,                           /* 擦除后的数值 */( ^0 z! D" l2 v
  12.     2048 , 4 * 1024,                /* 块个数和块大小 */
    $ w0 |. a! R  ~7 ?; a
  13.     0x00000000, 0x00000000,8 _+ \) I  x$ W9 n0 c
  14. };
复制代码

) V$ i. h9 T4 o  N' f注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:1 y) h# F8 z6 G3 H# @# t
8 D( `; |4 @5 W) f2 [

- [$ J2 `# B& U6 \3 [/ E; }
- P1 U- w1 j  H85.5.6 第6步,编程文件Loader_Src.c的实现3 V+ }' J  n; o4 }' V% h
下面将变成文件中实现的几个函数为大家做个说明:/ h: A! E. y8 t8 Y8 x  t9 L0 _2 I

% m1 }6 x9 N8 v4 s5 ~  初始化函数Init" }: q+ r5 r/ F( q7 I2 z
  1. /*4 R6 ]6 p+ `6 M3 k* d/ L4 `0 Z
  2. *********************************************************************************************************9 p8 d! y. |( d# Y3 Z2 ^
  3. *    函 数 名: Init( ~7 _9 j( c$ n
  4. *    功能说明: Flash编程初始化
    ( B0 @- T/ {9 b( n
  5. *    形    参: 无
    9 C  E4 x9 X- R$ z: H+ x7 ?. L
  6. *    返 回 值: 0 表示失败, 1表示成功. k9 Q2 v8 v0 w6 B  j2 y/ n
  7. *********************************************************************************************************& G- B7 |% N: l/ E8 p# |3 z
  8. */
    , O+ X; V: M2 P/ B1 {9 g! H. i% K* Q* U/ ^
  9. int Init(void)* \3 ^8 J' U6 r' D
  10. {   
    . U3 f, Y/ {* F5 k6 _
  11.     int result = 0;$ h5 M! k1 i) q

  12. * H* X" j" y& ~7 `% g+ r
  13.     /* 系统初始化 */! v) T5 G0 ]1 ^. C; K; ^& I. J
  14.     SystemInit();
    - b. I7 K2 O5 @" b( o7 V$ |8 S
  15.   I  g9 P1 K0 Q6 C
  16.     /* 时钟初始化 */
    # A* P" l  l* F! Y1 q' \
  17.     result = SystemClock_Config();' x2 P. D3 N" R7 k: o% X
  18.     if (result == 1)% y- k: B: ]8 }, u5 s! _
  19.     {
    2 |% Y( ^& |& }- i: F( y5 y
  20.         return 0;- V, s+ h* N5 }5 c; o
  21.     }
    # [" c: D' V' B* D' a
  22. ; q% b, h) G5 u( _
  23.     /* SPI Flash初始化 */; t& }3 {( k+ W1 L% r! ^- T
  24.     bsp_InitSPIBus();
    8 L2 i( n. K) w% ~" t* ~# a
  25.     bsp_InitSFlash();2 U  {0 T) ~  T" J( K' u
  26. ( A9 l. o3 A3 W$ \" U0 g; U
  27.     return 1;
    9 D- o' V6 l" X" V6 d3 o: B
  28. }
复制代码

5 F2 w  }# @9 s  m5 S' d$ f3 E; H  整个芯片擦除函数MassErase- T' D3 Q. B. E2 z* s
整个芯片的擦除实现如下:
' Y, _; C4 n/ {# T: `5 m" W( G* s
2 K7 p- W1 m/ T( P( I! S  f. B
  1. /*
    * o* {& t# K  C, |
  2. *********************************************************************************************************- P# n  ^( h! I" @
  3. *    函 数 名: MassErase
    # N! Y8 X6 U4 B* b5 [& i( ?
  4. *    功能说明: 整个芯片擦除
    # E4 J- h6 J" \. i  |
  5. *    形    参: 无: G* G/ S% @4 t+ }
  6. *    返 回 值: 1 表示成功,0表示失败, C' S( d: n: i- H" T0 i
  7. *********************************************************************************************************4 I: R- Q# h$ g% d) u4 |  A3 p
  8. */  P5 G3 i* u7 s' h5 |  ^
  9. int MassErase(void)- q5 a  Q' Q0 j; q4 z
  10. {
    1 |' h1 s" ^  U
  11.     sf_EraseChip();
    : l7 l) z; p: ^1 U
  12.   ~! V7 ]( ~3 J
  13.     return 1;   
    / y9 l7 ?- x, e" A. W2 ^* ?$ s
  14. }
复制代码

) t& }- p, T) b3 v& W  扇区擦除函数SectorErase. }. R7 i) x5 `
  1. /** E: ~2 m1 K8 ~* Z; p: C
  2. *********************************************************************************************************
    / X7 }) \. Z1 x) d; |, L
  3. *    函 数 名: SectorErase! S& p! j6 y" t( N9 X
  4. *    功能说明: EraseStartAddress 擦除起始地址. w" t. S# ?$ P# s* a# Z3 f* C7 [/ Z
  5. *             EraseEndAddress   擦除结束地址
    4 [) l) H& Z& f
  6. *    形    参: adr 擦除地址
    # ?/ @8 g& ]5 W! e& S
  7. *    返 回 值: 1 表示成功,0表示失败# `9 x$ |4 f" y6 ~
  8. *********************************************************************************************************
    : w: R( }6 \/ f) _" ~2 ?! R
  9. */- r& z. j2 F5 O% F' B, R# F
  10. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)- }) W9 @/ t! p( E; Y& }$ W
  11. {
    / i7 f6 g0 `! \. r) X" B
  12.     uint32_t BlockAddr;
      c5 F9 p% m6 y; L2 n: ]

  13. ) n( E" }8 R1 _& j" {$ g
  14.     EraseStartAddress -= SPI_FLASH_MEM_ADDR;
    3 n7 E" n+ z# R5 X2 Q* D
  15.     EraseEndAddress -= SPI_FLASH_MEM_ADDR;8 f% q: H, N" ^
  16.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x1000; /* 4KB首地址 */
    ' O6 _& T; v+ v" }( v

  17. / A$ d% ]* l: O1 L/ m- D+ h& D
  18.     while (EraseEndAddress >= EraseStartAddress)+ p8 Z+ D, t/ {$ E
  19.     {, O2 l# z" A4 @3 b9 u( J4 @3 T  |" J8 _
  20.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;
    . n5 Y) y" F4 o* z5 T
  21. 6 F, r1 V; ?& m: P2 L! l
  22.         sf_EraseSector(BlockAddr);    5 r% `- z' O! I# L+ R4 W

  23. $ q. R1 b3 n. T$ l3 U! p# Q5 @
  24.         EraseStartAddress += 0x1000;
    3 ~% c- r! l# }" _' G4 B( n5 f
  25.     }
    ; x+ Z+ Y6 K$ q6 K5 f7 ~9 C4 x: F# `
  26. 8 U) G. l$ V! |. a" l9 F
  27.     return 1;
    5 @# c& _# u- r2 g9 c9 D
  28. }
复制代码
( a! ^9 d0 K+ @: |; {& S
这里要注意两点:$ I4 q0 Z/ C% ^% @+ _2 b/ ?
7 x, Z! ]" A5 w8 G  L9 [; |! p7 U
(1)     程序里面的操作EraseStartAddress-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。
! P8 a% \- E% T' q, m* B5 S
1 q! b) W1 ^* j# f  F; p(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。# |5 D" v2 I. `; e/ l

$ b; k5 `' J" r/ f2 `  页编程函数Write1 J& t; @, F; M1 ~9 r
页编程函数实现如下:
, R" J: v# S/ T, ?2 F" ~' |# f/ Y9 M$ Q* y8 Q- X  d
  1. /*3 A& c9 u" I2 d7 A# i
  2. *********************************************************************************************************
    ( @5 |  l7 y5 O- a3 J6 K1 W
  3. *    函 数 名: Write
    ) n( i+ b5 I$ W
  4. *    功能说明: 写数据到Device
    ; V) q1 k$ U  d0 q
  5. *    形    参: Address 写入地址
    $ S4 V( |$ v3 ^; c7 d( X
  6. *             Size   写入大小,单位字节
    0 H; d, T* Z/ S* S( k
  7. *             buffer 要写入的数据地址, U3 o* B# N+ i& T* I
  8. *    返 回 值: 1 表示成功,0表示失败- e) j9 j) G: J
  9. *********************************************************************************************************. g  x3 v8 l" n( g. [& t" d. T
  10. */
    0 Q# G6 j3 C- J, v: Z& v
  11. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)
    ! K% t' S+ T: ?* c, L. W) h0 d
  12. {  4 k4 V- {% d, e3 q2 F7 X
  13.     Address -= SPI_FLASH_MEM_ADDR;
    ) c/ t6 ~( U: m; _

  14. " h* ]3 M, K; o" G& K% j) \4 }
  15.     sf_WriteBuffer(buffer, Address, Size);
    7 U/ T1 @7 G# O- ~/ T6 D
  16. 2 w8 A0 _0 }+ u7 E6 R, u7 k
  17.     return 1;
    3 C* |' D# Z4 P( m3 X8 o7 J
  18. }
复制代码
7 d6 A5 `2 Y+ W3 P$ M1 |! t0 S
这里注意两点:
- O, Z' o. ]% |4 S) |6 J6 g. i
! d( G: C2 }2 B  ?9 J  H1 l(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。$ W( T2 P' B0 l9 p% T1 }* \8 l

$ X3 t, F* h4 d; B( e(2)     程序里面的操作Address-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。$ ?% }% g) M/ G
, ^  K- i8 T2 l9 d
  读取函数
+ A! G. |' [1 V4 {# p4 z
  1. /*" f8 e, b# @) m# |2 \4 W( r
  2. *********************************************************************************************************
    : |" O. B7 E4 |
  3. *    函 数 名: Read
    # Z& Z9 e1 `; q# I: U% I  i
  4. *    功能说明: 从SPI Flash读取数据
    ' F) q: y* p9 S+ \  o$ u. f. T& X
  5. *    形    参: Address 读取地址
    , y2 G; a; Z( E* c" @& Y: J# Z
  6. *             Size   读取大小,单位字节4 L, `0 a5 @4 c7 ]# q  p$ f
  7. *             buffer 读取存放的数据缓冲
    & r; H/ @. M) D: X4 l1 @0 ]
  8. *    返 回 值: 1 表示成功,0表示失败/ _0 u$ D' b$ V% X4 q2 u
  9. *********************************************************************************************************
    , ]3 [1 u" C* D; W, X
  10. */
    2 F2 v' G/ W/ |; q9 k* v
  11. int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer)
    " J; a1 |# V$ {  k
  12. { * G; O1 n- Q' z; p5 E+ W4 [6 d/ s
  13.     Address -= SPI_FLASH_MEM_ADDR;
    8 w- Z& F" s& O* n  z: F8 x
  14.     sf_ReadBuffer(Buffer, Address, Size);
    7 _- h( h3 m" I0 i

  15. 3 O) V/ `0 o: T- n
  16.     return 1;
    . h, T+ Y( G( E3 R2 Q
  17. }
复制代码

+ b  E' d4 l: E/ k' q  读取和校验函数3 y5 U% A4 p, j  c
我们程序中未做校验函数。如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。3 F1 n! k1 J, g# q) G# E% o

/ X* n2 f. p9 r85.5.7 第7步,修改SPI Flash驱动文件(引脚,命令等)
0 Q% }! {- i6 O$ x最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:
7 s2 r" ]" |  R* O. L+ X# ^7 d# \. R- j3 u
  1. /*/ \( r# \9 i2 B1 f
  2. *********************************************************************************************************
    - a1 I+ x$ N* ]
  3. *                                时钟,引脚,DMA,中断等宏定义
    2 p0 r3 ^! N% \9 H( |" M+ n! \2 w
  4. *********************************************************************************************************
    5 w* ~7 e9 o7 s
  5. */- n' w8 a( k7 d4 M/ P3 E& l
  6. #define SPIx                            SPI1
    / T  i9 G5 H# J$ _, Y
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()
    . U3 t+ x  _( F; z( b  s* d+ Q% v
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()1 D& l' O8 d# x. z) o6 e
  9. ) i) ?' M) E: X2 b  ]
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()
    5 ]7 M1 G9 t( g! Y" ]' |
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()
    5 o( w; m$ f5 Z* ]% [3 R# O

  12. 1 [3 u5 P8 W& s: m; @$ B
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
      \2 [/ R) n$ A2 d+ `+ v
  14. #define SPIx_SCK_GPIO                    GPIOB4 o8 u* [* A( b" ^3 f% v
  15. #define SPIx_SCK_PIN                    GPIO_PIN_32 I4 K4 b; H- b9 w/ q
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI1
    8 @- Z! m% X- p' Q+ D2 L9 p

  17. 7 P- Z7 |  I7 x6 O6 o$ u
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
      c$ N. V) A" T
  19. #define SPIx_MISO_GPIO                    GPIOB
    + g) R; z- C- R9 _* |
  20. #define SPIx_MISO_PIN                     GPIO_PIN_4: ~+ P( b. ^. O1 j9 n* t
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI1( Y' u1 S1 E  i9 C
  22. % o: o8 U& f. k4 q3 g( n- F$ c' l
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()* q& J0 |0 ?: _6 z
  24. #define SPIx_MOSI_GPIO                    GPIOB( D0 }3 S8 d! M: m
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_59 O7 Q1 q& X0 ~/ B  O) V
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1
复制代码
( ~4 @: D, q5 x" S. K
硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:
2 e( h; e, ]% y2 g$ |* a# u* l
. O; Q  [, k4 T4 m* J+ F" G7 y主要是下面这几个:; j2 z: Q+ K. @3 n( O8 F

6 |" O6 q9 V2 ~
  1. /* 串行Flash的片选GPIO端口, PD13  */, R) z) _0 z- a' T
  2. #define SF_CS_CLK_ENABLE()             __HAL_RCC_GPIOD_CLK_ENABLE()" F7 d  s) A& n
  3. #define SF_CS_GPIO                    GPIOD1 }7 ~; I+ n8 C2 u: V
  4. #define SF_CS_PIN                    GPIO_PIN_136 ]* K  y- p9 E; i7 C8 |& c

  5. 7 n6 ?3 G* j) ^- H% e/ `% W0 s, ^
  6. #define SF_CS_0()                    SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U) : }% j- t: \% ]- c& ^3 u
  7. #define SF_CS_1()                    SF_CS_GPIO->BSRR = SF_CS_PIN, \+ H* j4 T# j3 C3 f2 y" e  j7 W
  8. " M. V, Z% Z: ^# o9 }
  9. #define CMD_AAI       0xAD      /* AAI 连续编程指令(FOR SST25VF016B) */( R9 ~6 Z1 ]+ V
  10. #define CMD_DISWR      0x04        /* 禁止写, 退出AAI状态 */
    & d' I; Y# E* _2 G! ~2 {2 l
  11. #define CMD_EWRSR      0x50        /* 允许写状态寄存器的命令 */$ k. I4 `2 O5 E! r
  12. #define CMD_WRSR      0x01      /* 写状态寄存器命令 */8 P2 r. r  u+ Z0 k/ a3 A
  13. #define CMD_WREN      0x06        /* 写使能命令 */2 Z1 A* J# B- q2 C8 R: y7 `
  14. #define CMD_READ      0x03      /* 读数据区命令 */
    $ |& a/ e+ I0 P3 \5 c
  15. #define CMD_RDSR      0x05        /* 读状态寄存器命令 */. q- j& @& l, B6 a
  16. #define CMD_RDID      0x9F        /* 读器件ID命令 *// @8 f7 @- g! G+ c) |
  17. #define CMD_SE        0x20        /* 擦除扇区命令 */% {7 b, \; X% ?
  18. #define CMD_BE        0xC7        /* 批量擦除命令 */
    8 b* x1 s3 L& G9 \' x/ d* k
  19. #define DUMMY_BYTE    0xA5        /* 哑命令,可以为任意值,用于读操作 */
    8 X3 h/ Z' |. G' e  S
  20. " V/ s* x5 r6 J# F  H* t9 T
  21. #define WIP_FLAG      0x01        /* 状态寄存器中的正在编程标志(WIP) */
复制代码
% M5 w$ ~: w) q: _- [6 e
85.6 QSPI Flash的STM32CubeProg下载算法使用方法2 s  M- N) s* o; \% v, y$ h" _; W: y
编译本章教程配套的例子,生成的算法文件位于此路径下:  a4 G: X% t5 o2 e3 R
( r% H% G$ Q; i) ]2 ~6 A8 p  F
5ec6d402a7646232a55f4834cd00ac0c.png

! L+ r( N; V: @; |7 b- `; R. X6 Z$ I
85.6.1 下载算法存放位置- D. K  }: [; }
生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:2 T. D( s  @* {' `& Y+ u
2 B# }6 U) ]$ l: O' ~0 k$ ]" p
\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader% ?4 r; m$ E3 F/ A+ T7 L* M
5 T1 U6 Q: w: I: j
2609073778af5845a869911ab36624f4.png
" e1 ]% z4 D& x# L9 M

- W; Q$ G( _4 s7 S* A  B+ m85.6.2 STM32CubeProg下载配置
4 j" B& c7 d2 D1 {我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。# ?; H5 Z9 @4 Q1 k2 c

# v! Y( {" Z. A
d4e256633eafdbf4bcd8cce6e3b5909f.png

7 J. O" Y1 f0 O& l4 ]8 i0 E/ V: z( P: X; A* ]3 d% X5 h
点击Connect后效果如下:8 U6 m* Y$ Y+ R1 V* n
" F: U2 {& ~7 J; j
8 _: Q. G# f0 o
/ K1 T& |* b4 v3 c% N, T" a/ N
在这里选择我们制作的下载算法:
- ^' ~6 c/ z* ~. K; t( n" P/ s
: M  ?& O( C. P' y% P
d4fa28f64be2de98e04f3188eb2fe09e.png

! ]$ R! Q6 G8 F2 f6 j( U# D  w) h1 f1 R7 r
任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming
5 |; S  K. L! d: Q, l2 `/ |: ~/ \% W% n& l  H# Q
842518a7cc1dc569598d372132b2ee58.png
3 \! \+ t- `. p9 [4 p* J  f& L

# k% ^% k+ }# w7 I" A. B$ `下载完成后的效果如下:
0 B; }7 E: d& {, y9 V( S" s* Y6 h
9 t7 p( S4 {; f, j, }" i3 L2 M
68a92f267829ca77f125db982d6c0479.png

; n1 Y% z8 L! C- |7 O. p% V! w+ P- j  ^. V6 Z; d' d! x8 G
85.6.3 验证算法文件是否可以正常使用% [& ~4 N, `2 }6 P
为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。也可以使用STM32CubeProg直接读取:* F$ }$ o! H7 J+ @/ ^% r

; ~, I' }  z$ H& Z- @( H/ ~
566d2fba421c710da70e5656c6934466.png
4 x3 [* ~3 v" D8 t7 o0 ]0 G

1 x1 K) I4 v3 q8 O2 u85.7 实验例程说明

1 j, A2 M* r" v( {" Y本章配套例子:V7-066_SPI Flash的STM32CubeProg下载算法制作。! \) u) `% d+ W# Q5 _& ]- M
! s$ B; F6 n- r
编译本章教程配套的例子,生成的算法文件位于此路径下:
2 @( T# V/ N5 B2 A: K# g$ U4 X/ P9 V6 a2 d
6108d1ff7e349f2aa268ee31804c57ca.png

* n) ]- m0 J% Q' m2 `' V2 j! K
" N9 _0 E, u' \8 q3 B0 q! @, o# ^85.8 总结, F; E7 S- y; y3 E2 h. ]/ Z
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。# {" {% L- u/ |% d+ H1 f' T# P
1 l$ K3 Y! r6 \
7 m. O4 i$ x/ b/ g7 J3 s

* C9 F! }! w& a
27a88335ceb606bd021c3ebc65d88e21.png
51f220556177a36712977b8d140b627f.png
收藏 评论0 发布时间:2021-12-19 15:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版