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

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

[复制链接]
STMCU小助手 发布时间:2021-11-4 23:27
81.1 初学者重要提示+ P& w0 Y6 k8 r
  QSPI Flash的相关知识点可以看第78章和79章。
* w. Q* E; ^# N  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。# I$ V7 K* }& Q7 {  N3 m1 j
  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样  m* R8 B* @8 i. a# x
  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。
! F9 t9 w1 v+ w4 p$ w81.2 STM32CubeProg简介# `/ r$ _2 E  w8 r0 `' z
STM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。. u- \, H) s4 f/ B% Q2 S
- k5 c9 S( F) ~
1bbabf500126f58ff1439b6bc36014be.png
3 Z- |( l- }' `" j& @" A& A# Y! V
* b: P# v% J' h$ D  I4 ^! |, w
81.3 STM32CubeProg下载算法基础知识
' v* h/ `# ]! D- [" N$ k  BSTM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。
2 j, s1 y5 o: |! v3 z
% p& A( ?1 g( U* j% x  Y9 h81.3.1 程序能够通过下载算法下载到芯片的核心思想! a3 y' x0 L, P8 C1 m, T; V# w$ I
认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。3 _" x8 K$ l& U. S
: \; W, |$ x/ e% Q7 n  N: B# l5 ?
81.3.2 算法程序中擦除操作执行流程
: |! Y$ u! d) A+ s3 {7 K5 P注:下面是MDK的算法执行流程,STM32CubeProg是类似的。
2 b5 H3 k" `3 p8 n" V$ _8 ~  y. c# J. H/ ~0 H' J$ `
擦除操作大致流程:
7 E6 t% i/ l9 W3 ?
0 |  P) u  |: Y3 s) K* ?9 z8 C3 a
161f92ebd9b1dbf9febba815fa091b8b.png

" o1 _9 r& G* I+ @
6 z  L6 P/ \* t, _4 g" b: }  加载算法到芯片RAM。
: w3 a$ \  z3 ], q2 c  执行初始化函数Init。  c6 T1 d  B4 l) m! x( `/ j5 H
  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。
7 ?% `- _8 _- h  z; h6 y, u  X! o  执行Uinit函数。: L. A, v! ?$ c
  操作完毕。. P2 {1 v2 M4 Z( X% T" I, r+ ^
81.3.3 算法程序中编程操作执行流程$ n% I" u* Q5 L( A! ]' C
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
( b0 m# ~: I2 J! r8 o* C7 d/ q+ ]
( H3 P4 h0 Y3 |' T4 y9 ~编程操作大致流程:
+ W; Y7 K0 o' l: |$ @( C
( p7 M# e/ a( s, U4 U9 z
d80dfd755411aa0d0565f1c56f10e66a.png

. H+ B: N% |8 ^
* U7 |+ ?! Z- I9 M. I& U" `  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。
" N& U; w* f% n6 B0 u  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
" y" Q2 N7 U' f# i# G1 C, ]% S  加载算法到RAM。
$ {! z2 P" D# E* m8 L5 k' T  执行Init函数。( U  m: ?1 ]  H5 }. b5 q
  加载用户到RAM缓冲。' x  V- C) T8 l( e: u
  执行Program Page页编程函数。
( U; y" H5 c  P' I+ i  执行Uninit函数。
7 b/ [; H+ [# g  h  操作完毕。; U5 f& e9 C7 i
81.3.4 算法程序中校验操作执行流程
3 K) r( x3 t  J$ I注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。4 R7 b+ D/ i3 u! u2 U

* Z* s$ B4 J% t/ {校验操作大致流程:
6 [/ x% H  P8 B( v3 Q. W/ x( C, w0 ?$ }5 J, G. K$ t4 ]
f15191090ab169d48a203ea3617af9b4.png
+ o9 r' o6 M9 c. z
% b- }# v) v6 e7 R9 `5 y* v1 D8 a! a
  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。" M$ ^9 b- f* I3 T
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
. Z" r" w2 h8 s  e  加载算法到RAM。3 a, d: m, I0 u8 W7 M  k! k# Z
  执行Init函数。
2 Q$ E4 k8 c# T& F2 n2 n  j) @  查看校验算法是否存在
; R: ~0 H& m0 h  如果有,加载应用程序到RAM并执行校验。
: W3 R/ W7 Q. _* S( L  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。
) G# g6 X9 d$ d  执行Uninit函数。
2 V% O# o8 L. B7 G  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
# @; B! a; G" A  y! [, R  执行RecoverySupportStop,回复支持停止。
/ K4 P& j9 m$ w# S  执行DebugCoreStop,调试内核停止。
% d) |+ s9 K' ~( v1 a  运行应用:
* Q+ W7 ~: t* S6 B  执行失败
" i& i$ \, J% r6 Q" @# S' j5 \  执行成功,再执行硬件复位。/ _/ T" v; \* e5 k
  操作完毕,停止调试端口。
2 q( W% n/ `' K5 f2 v# c% L/ u81.4 创建STM32CubeProg下载算法通用流程0 ^% z* ^2 [6 z9 m0 Q8 I
下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。7 K0 t5 @1 U7 H  m, d6 `
! o& \/ U: x/ R$ [
81.4.1 第1步,使用STM32CubeProg提供好的程序模板
# J. P) y3 b4 D2 n位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
/ _: x5 i  U9 ^  t2 [1 u/ Z) |! Q, ]- U
4fbfb0047e29fe23083224607059d97f.png

' ]9 n1 o  ^% ]8 z1 b$ R) b: u
6 h1 }. ]# L* J4 v  H' w以M25P64为例(注,其余步骤就以这个为例子进行说明):3 x: n+ I2 ^& A$ U9 b& E7 }
- j% n- W# L$ n
d1b1a340fdc655c1030330a7dad2abba.png
3 {3 a8 T4 @) s" o7 H
' x3 ^# l7 }. L/ m
81.4.2 第2步,修改工程名
/ B- t2 s. k3 `6 q3 H2 dSTM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。$ p( n; I5 E. j/ m5 J# O' W1 w6 d
, X; h: T7 t! ]; h9 ?  A
81.4.3 第3步,修改使用的器件
2 U) W: A  l% U( O) Q1 r在MDK的Option选项里面设置使用的器件。& J4 I2 p5 [0 S& j$ \' B% h

$ u7 v: h+ W3 D1 V
092174ad976a8ce9c7fca533049e2dd0.png
& c) |6 l3 K& g; \; b, u% Q" Q

$ b6 A  L2 l4 G81.4.4 第4步,修改输出算法文件的名字% B/ h$ G2 O( i/ r
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。
5 U% ^# b6 T& R* K  U6 n) O7 h( A# I1 @- a& j( X
79c42251311dd3892fc7d6b8ddde68b8.png
) J5 a4 O1 k. u2 W/ j

) U$ L/ ~( {* D+ R: s注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:: F* h# ^6 X3 O/ [6 u# R" P
e2fe61fa7ea99b25b178341b7350558f.png

" s2 x. i- ~; y- u- v9 d' [
- }+ ]4 V7 Q9 i: t6 {' N" E0 U81.4.5 第5步,修改使用的库文件和头文件路径6 P/ H7 |* a- O4 o6 W: J, ?
根据大家使用的主控芯片,添加相应的库文件和头文件路径。
$ `* ~. Y8 i7 `# L# G1 U9 @/ |: _' B' m* {2 o3 }$ {0 d) Y8 D
81.4.6 第5步,修改编程算法文件Loader_Src.c
0 ~6 a! X! {. o6 Q模板工程里面提供的是M25P64,部分代码如下:1 U6 L! B4 N8 n, N; y

; G! k1 h7 X9 r8 g+ w
  1. /**4 c5 z/ t# |+ e4 W' r/ c# r* F
  2.   * Description :5 l2 v# y3 [  i
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the
    4 }6 Y% M8 `: y% }
  4.   * device and initilize the FSMC with the chosen configuration
    3 L6 f9 n4 R& M0 w9 m/ a; `3 Y- `; |% }
  5.   * Inputs    :
    ) E3 Y. W3 F! l
  6.   *      None
    ! u' s* t3 r% R/ V% P2 R9 h
  7.   * outputs   :0 R5 D0 C% t' l" D- A) X: m6 y' ~
  8.   *      R0             : "1"             : Operation succeeded
    + S: J1 s) n4 v% n
  9.   *               "0"             : Operation failure
    2 O6 a+ J6 u# H, `
  10.   * Note: Mandatory for all types of device 8 t! E3 R. w- f+ y8 E. w/ D! _2 y8 j' A
  11.   */
    : g, ]- X, i  Z$ g/ X
  12. int Init (void)
    3 _3 w. F  K& c- d
  13. {  
    4 N. A$ R  E) `/ n6 L- k% B
  14.   SystemInit();% P& _) a; y& O. m% r
  15.   sFLASH_Init();
    " G. L1 r: T& _& o6 z
  16.   return 1;9 I/ K! b; o5 N2 @* L
  17. }8 ~2 F& w9 l" L$ D& X+ k  v* J7 `2 G
  18. * X! H: }5 ~) b( \

  19. 7 [- {3 F/ z6 n. D% s
  20. /**
    : Q; L+ s( E0 P' b# D* h
  21.   * Description :
    0 F4 E# p) h8 q/ R3 M  }& x
  22.   * Read data from the device
      H) A( O8 X% }7 k5 g
  23.   * Inputs    :6 e' r, |+ a  u$ z7 q" E7 _# I
  24.   *      Address       : Write location, a" C3 f5 I. P
  25.   *      Size          : Length in bytes  1 Q: C  S1 i- ~1 Y
  26.   *      buffer        : Address where to get the data to write
    / S. _0 e8 A5 M6 h  n
  27.   * outputs   :% f  b& |- _7 r
  28.   *      R0             : "1"             : Operation succeeded5 z* _! N/ y; V5 Z7 ?
  29.   *               "0"             : Operation failure
    - ^' |# Z/ G4 u: Q) ~
  30.   * Note: Mandatory for all types except SRAM and PSRAM    8 R3 _+ Y( G' R2 [$ S
  31.   */( k* T: ^- y! F8 b
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)) B! H2 P- `) r$ c
  33. { 4 B: y6 E+ @( ?. b
  34.   sFLASH_ReadBuffer(buffer, Address, Size);* @7 r7 ^; q# T
  35.   return 1;
    ; u/ }9 M0 e& n
  36. }
    . z0 h4 p3 G! }+ Y/ E7 f6 ^
  37. 2 d  x" S% R" P1 V

  38.   M. f8 R9 q( ~5 g
  39. /**
    5 N3 ^# @6 d0 k! N
  40.   * Description :2 i& O  C; J9 O# ^
  41.   * Write data from the device
    1 N8 K8 T' T1 u) s
  42.   * Inputs    :
    . `+ u: ]; f) l# j( U0 l* b+ q
  43.   *      Address       : Write location; O* U' j* S# K: |3 X
  44.   *      Size          : Length in bytes  1 a) p' U4 D/ X3 a
  45.   *      buffer        : Address where to get the data to write
    / o6 F0 G" x* v$ _# v. x) B( a
  46.   * outputs   :2 L# G8 Q/ r6 E4 ?$ E; {
  47.   *      R0           : "1"             : Operation succeeded* G' u6 x& @! p. i0 }) [& e
  48.   *                     "0"             : Operation failure; Q, n0 D5 I& |0 e" W; D4 U5 ?
  49.   * Note: Mandatory for all types except SRAM and PSRAM    & z  P4 i. {' E. h3 L
  50.   */
    - S& k& J' t3 v) J8 \: i% R
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)
    4 `0 ~4 ^, F0 A  i/ D; T
  52. {0 Y& G' \! h7 K4 J/ X
  53.   sFLASH_WriteBuffer(buffer, Address, Size);
    " S  \1 @. t4 B( Z4 f  B; ~% s
  54.   return 1;8 K; D5 G* b( G% L$ }2 z" P
  55. }
    ! k& {- z% q' \" V
  56. - a  y4 Z2 D+ V- ]1 n
  57. ' K7 u, C, c5 r% ?8 p
  58. /**
    4 @. |4 ]. i5 o; O# s
  59.   * Description :
    / B; U! q( D  T
  60.   * Erase a full sector in the device+ N- ^, H' E' r: c' E# o: p
  61.   * Inputs    :
    6 ]! Q; `, V6 t: J6 S" O
  62.   *     None5 r' F% e* Y( u8 E9 {* \/ x* T
  63.   * outputs   :
    ; {9 K' {  a3 h) t/ Z( ]1 W' z
  64.   *     R0             : "1" : Operation succeeded
    3 ?! U7 v2 t0 O0 s  x# e
  65.   *              "0" : Operation failure
    8 J6 V) s3 @( h) h6 K2 x
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH+ \) Z9 M0 M9 A
  67.   */- c. H; A3 Y# S7 T5 q5 A* K1 _! |
  68. int MassErase (void)- i9 o5 x. q" e
  69. {  
    $ n( u9 _4 C. |/ h: O+ j
  70.   sFLASH_EraseBulk();
    0 l( s1 a7 Q8 r' b( O4 Y
  71.   return 1;   
    $ {0 S6 s4 T7 _: t
  72. }
复制代码

& D) r9 p/ `) i2 O  y, O81.4.7 第6步,修改配置文件Dev_Inf.c
' U4 `5 C6 E5 Q模板工程里面提供简单的配置说明:
5 K3 Q4 b. N. @! y2 m/ |) x$ u) x3 \3 l; |$ U
  1. #if defined (__ICCARM__)' Y6 o- Z- m+ w- L5 ^' D% S( j7 n+ I
  2. __root struct StorageInfo const StorageInfo  =  {
    * V  p6 k" j+ T: e+ f" N. ^  D
  3. #else
    ! D3 S5 ^! q# C
  4. struct StorageInfo const StorageInfo  =  {
    - e+ u5 K  }3 `, K
  5. #endif
    , e% X! R& t' r2 h+ u, v
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number
    $ E- z# T; e' m
  7.    SPI_FLASH,                       // Device Type
    $ o9 ^& ?' b: Q% Z# A5 x. S9 v
  8.    0x00000000,                     // Device Start Address
    % [* `% S- m8 u! \+ n( Y
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)9 T, `8 v6 e& T9 T
  10.    0x00000100,                      // Programming Page Size 16Bytes- X% T4 ^" R' @3 n6 x# U8 V/ @5 n+ ?
  11.    0xFF,                            // Initial Content of Erased Memory
    % Q* y- Z+ ~8 a' B% {9 T7 p2 w
  12. // Specify Size and Address of Sectors (view example below)
    2 X& \: @3 ^5 @2 n4 w* h7 Z
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes
    ! K/ j( L* }2 c; J9 I
  14.    0x00000000, 0x00000000,' ^7 V4 d9 C+ T6 @1 D$ y9 G
  15. };
复制代码

1 g! p  S/ D0 M6 m; |: \; S0 @注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。  g3 }) X$ J$ K/ R1 H9 m7 Q# Z

6 G$ ]! E5 t1 B, E! T" z
: Q9 `' ?. R/ b  v# i* a6 v3 y8 M& V1 w3 L! e
81.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。
* d+ Z  [  ^( u2 t8 C- v! UC和汇编的配置都勾选上:
; Q' _" A* H0 S3 i* N+ V  J+ j
( p6 z% l# @. F9 w5 b1 c% Q
8f2ba38db4c1f685f131e7f1f4e98551.png
+ y( A5 y7 `# q6 p, E8 n7 a
! e$ S4 f* F, y& G+ I4 D
汇编:8 H1 K! Z6 x! S; H1 s2 p6 o3 X  J

9 \$ R$ h) `4 `+ h  K' O
0d264153b167afa2303144bdd378d501.png

( E+ D$ d+ |, m( |- y3 `( m2 ~2 L3 q, b4 O  \# ]( C3 {
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:
4 ?" {9 ^% K/ \# B) A/ ~/ [
: O2 n  I% M3 z* X2 R& g(1)加载以响应运行事件。
, `8 u, b( \% A! v5 S( w" t3 ?; `' }3 Z
(2)在不同情况下使用其他例程的不同组合加载到内存中。7 Q9 @# f% k3 Y
# c0 R9 k, l* m5 U4 G
(3)在执行期间映射到不同的地址。- I4 E) ^' c, U3 \7 c1 K8 L
/ t( E4 c4 R4 H. W; N8 o; Z
使用Read-Write position independence同理,表示的可读可写数据段。4 M- z3 L- N* T: r# s- U( x

- v' i0 `9 i1 U  V9 j. c* K8 d81.4.9 第8步,将程序可执行文件axf修改为stldr格式: R! ~8 [9 _) |" ]2 t
通过下面的命令就可以将生成的axf可执行文件修改为stldr。% X/ ?$ j4 b" Y, |. A/ z8 a7 @
! d: @, x2 D0 c- r8 o" K/ D. k2 l. G
f50b1594f3741e77b4fbd5523564d3af.png

; m. y2 ?3 R, u. D6 g' X2 y; P  @( ^$ h* c  Z" i  O( m8 U
81.4.10   第9步,分散加载设置
, ~$ T4 M+ M0 O& H) \8 K, E! K' o我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。" z7 n& `% E8 t9 X& `+ u* K4 x  ~
: I8 z; j  F4 i6 i4 s4 [7 D
8a01cb7c9be38519d4d0510a218447e2.png
9 {! I5 g1 y0 {' I( [

: O* r) X5 J$ S) G分散加载文件中的内容如下:: W1 Z7 O" \5 \& R8 O8 X

0 u8 U; U5 O+ f0 d3 B, y" ~
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions4 o: M8 {$ B. S# Z# d$ z
  2. {
    " r" P6 N' ^) \+ P, G* c) `
  3.   PrgCode +0           ; Code! n* Q$ B# @' h9 ~- `
  4.   {
    & C) F: B  @! w
  5.     * (+RO)' F4 b# u+ l! `) E; }
  6.   }
    8 e( J/ o  \, S( o" [
  7.   PrgData +0           ; Data
    # w2 `! ~$ T& R' L- v
  8.   {: v1 u4 K) o- @. ~2 n: O! ?" }
  9.     * (+RW,+ZI)
    . L! K7 t+ c+ c$ U4 v3 ]
  10.   }
    4 L( L0 p  }4 a# c
  11. }0 X. T* C- t% `) r3 s  n$ P
  12. 7 h# |/ ~: |0 f8 `
  13. DEVICE_INFO +0               ; Device Info
      Q4 Q) @' B% C7 G
  14. {# k8 g9 U' u+ B! ?" Z3 }
  15.   DevInfo +0           ; Info structure+ F  o7 V! o; m0 c7 i! b
  16.   {
    ! M* T- C* f$ {7 c: G" z. n
  17.     dev_inf.o, [" ]% U( h9 J2 T$ t" }+ e) F
  18.   }
    $ E+ i4 K. C: n! U- H; k' m, A# H
  19. }
复制代码

* G) r0 @  f2 d( d这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。
- _0 ?( I' k2 h5 F4 G' ^4 g0 x; ?- a  X, M+ O! r2 g; p/ `
--diag_suppress L6305用于屏蔽L6503类型警告信息。
$ h( d$ d8 J, j* `
& o* ]% ^+ M1 }  g1 T特别注意,设置了分散加载后,此处的配置就不再起作用了:2 e. N. Y9 H; Y

8 F+ ]; R$ y! f' D: ?7 o
13b022190bd5115ed249129fc3886a38.png
- d9 d+ i' m7 p* R
- [1 _$ l9 F7 P* V. b( M; E; h
81.5 QSPI Flash的STM32CubeProg下载算法制作
5 @$ t; L  [# q" `6 Q下面将QSPI Flash算法制作过程中的几个关键点为大家做个说明。
# e. W5 G1 d% V6 W% \
3 J2 P- r& y; t1 w) `4 m0 f. Y  C81.5.1 第1步,制作前重要提示
$ [& O2 k& A  p% N$ X2 q( T这两点非常重要:' c( I6 _8 p/ f6 ]6 B( i+ u9 T

$ |/ l/ ?) {# H2 |. U- i& P6 c  程序里面不要开启任何中断,全部查询方式。  ~' P, P6 v0 e8 J: K; D
  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。# U5 @1 I# L. o1 K' O  I+ l  b
81.5.2 第2步,准备一个工程模板# A  n* c8 U' f9 `* Q- l: h
推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。: `6 ?: o  S) J5 p+ `: \( r9 T- d
& ^4 p- C1 o& r1 H
f46a8d2276d0ffb7beee251cdb0809a4.png
! M3 \# X- D% D$ L
0 e3 `* h  R6 f/ b
81.5.3 第3步,修改HAL库& V8 s* c0 \# f
这一步比较重要,主要修改了以下三个文件:
# o; K  X$ v+ \- H+ E9 Q1 N4 A" P. X3 a7 f: P( G. g  |
1efb78256b9465df406e3e9e95ae30e5.png
# y' a7 i& x: i4 M! G: h( i7 f

, n7 X2 M3 V/ O( ]5 }8 h. l主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。
) j# x/ R5 o) z' N& Z9 N5 c$ i- d0 \* \5 R; [
81.5.4 第4步,时钟初始化2 u. @9 P' I6 U7 ?
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:
0 K; X) r% l* u9 Y" t
" ?( l; w" U# U9 r8 P
  1. /*
    1 N9 j# Y4 o4 t$ F5 w
  2. *********************************************************************************************************
    $ ~+ u) `% `) {. h( |" \2 _
  3. *    函 数 名: HAL_InitTick& d+ ]9 W- n2 L
  4. *    功能说明: 重定向,不使用, V1 P. _- N2 L: d
  5. *    形    参: TickPriority  n# M5 @  Z+ C  }' b
  6. *    返 回 值: 无
    - e1 x( a1 I  H9 Z  C8 |+ z
  7. *********************************************************************************************************0 V4 w- S/ E" D- T; z
  8. */
    4 o( L3 ]: o; _) A8 Z- H  C! c
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    , Y1 ~( f6 ~* T" J- s) s% ?5 b
  10. {
    8 O6 g" i; e% X- s
  11.     return HAL_OK;1 U# ?1 J$ ^7 n2 f! m) ], Z
  12. }
复制代码
7 m3 T/ d* u) W1 G( b3 K5 p' b
然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:3 N  ~% m( D  i
6 V2 c% f, M8 o% F, I' P( r
  1. #if !defined  (HSE_VALUE) , y9 N) e; r: \, o
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */* F1 B7 U. _( {0 [0 }0 r
  3. #endif /* HSE_VALUE */
复制代码

  C1 s$ j0 e# I; F& y最后修改PLL:
  Y! w0 d9 y9 i0 D1 A2 c) }' P, z$ J5 F
  1. /*) d7 C/ V$ {% x$ L
  2. *********************************************************************************************************
    1 A4 H# t/ b7 R- J5 M
  3. *    函 数 名: SystemClock_Config! ^8 k4 j3 M: s8 q3 Z
  4. *    功能说明: 初始化系统时钟
    2 w6 ?$ S1 j8 ^2 R9 Q
  5. *                System Clock source            = PLL (HSE)+ M  j7 G) j, p/ W
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)9 |% Z3 W# N: U# @" v
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
    3 \" ^6 |2 t/ T4 _. p8 y
  8. *                AHB Prescaler                  = 2! [; P5 |, ]: _' X5 Z
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)% ^9 v0 A: \: _" |: C
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)
    / Q% j$ a, J( r  u6 f
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)
    2 E$ K( K5 s2 b2 h' N
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)
    ) Z9 H9 y* B# r* a5 T, B
  13. *                HSE Frequency(Hz)              = 25000000
    $ i9 K: d% ^* A, Q+ q4 E# V
  14. *               PLL_M                          = 5$ c: Y1 `9 C+ n1 _8 B, F$ _+ N; u
  15. *                PLL_N                          = 160
    ! C1 b- m' i# u- x
  16. *                PLL_P                          = 2
    8 T& q- h6 d, V9 m
  17. *                PLL_Q                          = 4$ Q6 S6 L1 v6 ^1 Q$ l
  18. *                PLL_R                          = 2
    * ]& ^6 A  P9 H' y1 J! n% C
  19. *                VDD(V)                         = 3.3
    1 r; p! \. l8 p: F/ P% T3 L
  20. *                Flash Latency(WS)              = 4
    / l; `( D" w% l" \  o6 n8 N8 S6 W
  21. *    形    参: 无
    " h/ J) L8 p& V9 x2 _
  22. *    返 回 值: 1 表示失败,0 表示成功
    ) M3 u0 ~" G& ^) X
  23. *********************************************************************************************************
    % t3 [. U; a. c% w+ a
  24. */
    9 f! W; a" U5 S
  25. int SystemClock_Config(void)
    " E6 G+ y  ^. ]
  26. {
    6 }; {9 y, x1 l( M% {
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    & M# L( B$ A" _+ }7 A. a3 V
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    1 E; c3 c& h  b% }5 i  |6 N
  29.     HAL_StatusTypeDef ret = HAL_OK;7 i) L: s0 B6 A' F; t

  30. 7 c% q4 X1 n4 Z3 Z7 g
  31.     /* 锁住SCU(Supply configuration update) */$ n4 e% |! s( A; o
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
    . \, d. f% G0 q! j' p
  33. / s( P, z9 `8 ^4 o4 a1 }" y
  34.     /*
复制代码

+ o, a6 Z* ~* S      1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
' F' q( E, |- t9 J. E) c         详情看参考手册的Table 12的表格。
6 v; ~& w* F  ~6 p' l! @      2、这里选择使用VOS1,电压范围1.15V - 1.26V。% i- ?' ?" |( M
   
  1. */7 N' M& d' ^9 ?  F+ L
  2.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
    9 ^# ], E. C5 O2 D$ ~
  3. - i8 z  F2 X4 ^* m
  4.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}+ n. F3 N  O# V# r! C/ ]  m, H
  5. * r; N& }( u' I8 Q4 B! s3 [) U
  6.     /* 使能HSE,并选择HSE作为PLL时钟源 */, J" a% B  g" c
  7.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    6 h3 `7 [5 R$ D$ _% k2 S
  8.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;1 w+ g* q: l$ Q$ j% z( R: R/ i! z* o
  9.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;  x4 w( l  n  }. m1 X3 Q( K% R
  10.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
    3 U* ?% h) ^& v' m2 r# H
  11.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;3 h; M) ^$ n! t6 u7 }* I5 H( `+ G9 _
  12.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    ( R- F' a# K& Y/ ~6 P4 l- j

  13. # ]1 i9 a1 g6 |( o# V
  14.     RCC_OscInitStruct.PLL.PLLM = 5;
    . c) n" y0 G3 e4 a) @5 u
  15.     RCC_OscInitStruct.PLL.PLLN = 160;
    / b; q8 J! }9 e8 {% i" {4 M& i
  16.     RCC_OscInitStruct.PLL.PLLP = 2;
    : E& Z- z, k, J! w1 x+ ]; o/ i/ s
  17.     RCC_OscInitStruct.PLL.PLLR = 2;6 m! X4 @' n; e0 A! ^8 _" ~$ F$ S% J
  18.     RCC_OscInitStruct.PLL.PLLQ = 4;        
    ' N! O6 q5 N+ d. u. F
  19. 7 h% [. h# w  u7 x& T% z
  20.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;  h( A' F" S, x: t# B, |) A& N7 N8 y
  21.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;    ) i8 O/ S: {. E) K5 Q6 b% R* h
  22.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);# ^6 g9 \# T$ ^6 T
  23.     if(ret != HAL_OK)+ L4 G7 v& }0 x2 V$ `
  24.     {
    " g" V3 N6 `) e2 S8 q- N0 ^) S9 Q
  25.         return 1;        ; T9 Z+ S0 R: {; A8 e4 r
  26.     }% m9 i+ \! c% \6 P' ^( ^( X

  27. , F! l* ~' ^* e# ]" H/ \# E0 L- p' F
  28.     /*
    0 T% V8 {, R+ ~3 G0 v3 l/ J
  29.        选择PLL的输出作为系统时钟8 i- B8 e. C, {4 B' y
  30.        配置RCC_CLOCKTYPE_SYSCLK系统时钟
    " D; G6 O; C+ @; R
  31.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
    ! {. h- e$ Q% g
  32.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线% N) i) E& l: L- t, {
  33.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
      I$ f& O7 ^6 q! j; U
  34.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
    ; \/ ^1 T# @5 }0 _: l  m( x
  35.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     ! a3 _+ ]/ ^5 K4 D, v2 z5 T
  36.     */$ E# Z; f5 W  O2 ?! q
  37.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
    6 O! w- i3 P% D5 B2 [
  38.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);  {, ?8 G/ i6 Y
  39. ! F% k6 @. ^3 o. P( D; B
  40.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    : i* R! s3 k1 [, J& {
  41.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    ( |0 Q, \8 P9 M1 q9 p, P5 W& ~% z
  42.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;' q& {( Z$ k' t8 |# Y: [0 A3 w0 u
  43.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  , }5 `) [! c7 R! ~5 b
  44.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; - n; X3 i2 r5 a7 e, _: Q+ {7 N2 y
  45.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; # z$ ]& V, x' h4 f/ `, m$ k
  46.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; 4 s5 r5 }5 p7 Z( M. Z' g4 a, P5 L7 q

  47. & j3 T) S0 U: D- d. O
  48.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
    6 X4 M( n6 h2 A
  49.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
    0 H' O$ M# T% G2 C8 K* P
  50.     if(ret != HAL_OK)
    7 l) M, Z: o* H( d  T
  51.     {4 c5 Z3 P8 j! k- z* h; z& s9 Q
  52.         return 1;, G0 h. p' O9 I  ^4 |- w
  53.     }: s- Z) B6 F7 l) @
  54. / V" n6 ~5 t' B1 j" d
  55.     /*
    3 p+ w# Y9 j- \9 h& s
  56.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数
    8 h1 I) c- q8 {
  57.       (1)使能CSI clock
    ' ?) y; D4 t4 @- z/ s4 Y
  58.       (2)使能SYSCFG clock* [( U( ~/ q9 Z" l8 c
  59.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
    + G/ h( u  A( z* |
  60.     */
    , m" d5 v- V% R7 q4 V
  61.     __HAL_RCC_CSI_ENABLE() ;# ]6 r. }9 g* A% B* h" S
  62. 0 r% ?1 E! t3 e# z7 ^2 Q" K- w
  63.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;6 O1 m& N& F  r. J7 [1 P

  64. 4 n7 u) o! C3 G' u/ `; a
  65.     HAL_EnableCompensationCell();+ V& x' [0 q/ D5 v; r% F1 q; b

  66. ' W2 l* p2 U% q2 _. Q& ]  s' [- o9 s
  67.     __HAL_RCC_D2SRAM1_CLK_ENABLE();. P+ [8 V9 L' R8 Z
  68.     __HAL_RCC_D2SRAM2_CLK_ENABLE();" t6 U. V- P8 D$ C- O
  69.     __HAL_RCC_D2SRAM3_CLK_ENABLE();; E7 J5 D1 p. W( _
  70. ! u, d7 S: y3 S) r9 L4 ?$ L7 z
  71.     return 0;' L# f3 P$ n1 K# e% X+ |) @1 v
  72. }
复制代码
1 }6 \- j. z! o% S& I& E  k% A
81.5.5 第5步,配置文件Dev_Inf.c的实现
0 Z) S+ b3 I( B" u4 |$ H* p' @, V配置如下:) i; T0 R8 r5 H# \

* I% \! Q* w1 Y4 D0 {' F
  1. #if defined (__ICCARM__)
    ) g0 k+ Z" R! m) J, q# r7 g
  2. __root struct StorageInfo const StorageInfo  =  {
    - Z$ [1 F* x5 _/ B
  3. #else
    & R$ n  \! B) W1 |1 x. H3 o
  4. struct StorageInfo const StorageInfo =  {
    ( g: C9 D* a. O6 c# S& J' ^! j
  5. #endif' Q0 x: x3 b8 \- o. l+ Y, g
  6.     "ARMFLY_STM32H7x_QSPI_W25Q256", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */
    4 l; Y3 Y8 \' X7 ~& v9 C( d
  7.     NOR_FLASH,                      /* 设备类型 */# a! @( p. x! Y% j  ^3 z4 b% ?5 m
  8.     0x90000000,                     /* Flash起始地址 */
    3 k- o1 B, f4 P7 M8 X
  9.     32 * 1024 * 1024,               /* Flash大小,32MB */
    # E1 Z- b' W+ B1 q  U+ ]; b* K
  10.     4*1024,                         /* 编程页大小 */4 Z3 w1 K; m1 q6 ~6 L, x, _/ t
  11.     0xFF,                           /* 擦除后的数值 */9 p0 |" W. z7 |
  12.     512 , 64 * 1024,                /* 块个数和块大小 */
    : S2 w* l+ v6 U# ^* E  ?5 N
  13.     0x00000000, 0x00000000,- o2 q- u0 f6 ~2 @; O% t& Y1 i' p9 t
  14. };
复制代码

5 N0 b$ c8 q5 a: m" p9 }/ r注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H7x_QSPI_W25Q256会反馈到这个地方:
7 g6 k: D6 A" m, B  G( }7 t# ]+ C) D
bc495adf8d970e6e1fb6822b88b94e2f.png

/ `4 P1 b, Z( j- [& b3 f
) Y+ y, Z8 M; g' W9 |  Z/ U0 l, L; b81.5.6 第6步,编程文件Loader_Src.c的实现
6 D* [: P9 m1 p下面将变成文件中实现的几个函数为大家做个说明:. j8 T* z9 |; Y) q4 S5 @% O
! B. @- u8 A7 y' `$ U
  初始化函数Init
- h9 C# J9 s0 t; |; K
  1. /*+ ~% O# r8 d/ n1 _$ {& z+ z, |4 b
  2. *********************************************************************************************************( ~" d! l9 H( w! l8 K
  3. *    函 数 名: Init
    - o4 q' i" t$ b& E$ I# f
  4. *    功能说明: Flash编程初始化
    7 {, V, |+ F4 ~! B
  5. *    形    参: 无8 |0 m- v' g2 Q% G; E  D% [" f  _. ?
  6. *    返 回 值: 0 表示失败, 1表示成功; W4 J6 [) [( I& J! F4 R8 T
  7. *********************************************************************************************************. Y8 w- ]1 k, [9 U
  8. */  }7 Z# ]! a5 q8 T) [9 i) O
  9. int Init(void)4 j6 O7 |$ J# L) a  `8 v6 |& Z
  10. {   
    $ G; B( o7 V: b9 \% t
  11.     int result = 1;
    2 o! q5 T+ z  L. J; r. u6 ]

  12. ; r- U1 m4 C- v' {9 C+ I
  13.     /* 系统初始化 */
    + y- X/ b$ P2 Z7 _# r! I
  14.     SystemInit();
    . m% z3 |0 L6 H0 y5 ]+ a, K7 z

  15. 5 C$ g' X- k& R! A/ D
  16.     /* 时钟初始化 */  O" B4 f5 B* a! W* q; z3 m
  17.     result = SystemClock_Config();
    ! e  F9 U, k( z4 w4 J( X
  18.     if (result == 1)5 {+ N% o* l; L' u# ~( e
  19.     {
    ! t$ `7 ]5 G+ `& C. y# X! P
  20.         return 0;        ( h' i' }# j/ k$ |8 z1 g6 p- O
  21.     }
    4 W. p6 r- {' U/ N6 j% _$ ~7 T

  22. + [$ h* d1 k. P! C4 G
  23.     /* W25Q256初始化 */. o4 R  b, C$ D" T
  24.     result = bsp_InitQSPI_W25Q256();
    * p* X, H2 r, K, l+ b* S( j7 E* \7 J
  25.     if (result == 1)) Z# R- k- X/ Z" x1 L
  26.     {6 M, u  O6 }6 o. h) T
  27.         return 0;
    9 E' S* R1 i5 i* L
  28.     }$ e4 H$ d  Q- _+ Z7 i

  29.   _- {6 i, \6 q0 Z4 `& N* Y+ F+ T
  30.     /* 内存映射 */   
    ( q5 J- o9 j2 {: H1 i
  31.     result = QSPI_MemoryMapped(); + i3 o% P& e5 Y4 _' A& r  m
  32.     if (result == 1)
    $ u, h' p( x/ L* Z" o8 H1 S6 I
  33.     {
    7 P% D3 D$ e4 x9 \7 u0 g
  34.         return 0;
    % g, d, E" A7 b: {
  35.     }
    # P) `+ l" S2 _, ?% ~4 r% G

  36. 5 w0 t- W8 L& j) \6 m, p
  37.     return 1;2 B* O: o" |5 f# a
  38. }
复制代码
6 j, R" F4 B: a# v- ]! \( a
初始化完毕后将其设置为内存映射模式。
  w* T* i( d9 e" J7 }
& c4 Z7 [5 L( h- X3 ?3 V/ P  整个芯片擦除函数MassErase
! y% p$ c, t& m/ n, \整个芯片的擦除实现如下:
$ g( A5 c! @5 \% ]* D2 k1 h, v; f  `' O; r" N. p
  1. /*
    2 w2 Z# l! h# G0 @$ j- C
  2. *********************************************************************************************************
    & {* z' Y: }( F- L' q+ t% [) [% f% {
  3. *    函 数 名: MassErase+ N* e* j2 c6 L4 x9 {, n
  4. *    功能说明: 整个芯片擦除8 C. r' m' ?  Z0 n4 |' u" k
  5. *    形    参: 无! n+ [) D1 T/ r6 A5 g
  6. *    返 回 值: 1 表示成功,0表示失败
    5 L: U" ]8 {9 Y4 U
  7. *********************************************************************************************************: |; |: g% [' K2 z# W
  8. */! Z1 }, F# R9 V- x
  9. int MassErase(void)( n- Z7 p/ N5 `5 g: W: R
  10. {
    / L" q" A8 n# O$ q7 _+ h1 B, [
  11.     int result = 1;, `# ]. U: h. w$ v0 u

  12. 6 `+ z! }: ]1 F6 t
  13.     /* W25Q256初始化 */
    3 o1 C+ H. c5 t+ E" h* }
  14.     result = bsp_InitQSPI_W25Q256();
    ' H% Z2 P, R- W1 G2 F8 c' p( ^
  15.     if (result == 1)
    % U  P$ [$ A( ]: @9 D5 u
  16.     {7 ^! t5 ~& y. r8 g! P5 s% y0 M4 v* L
  17.         return 0;2 L9 q$ \: t' }$ ~, V3 N' A
  18.     }! @6 ~: ~9 O5 f* h) d, D4 n3 A

  19. 1 N, X2 d+ c8 V6 _0 }) m$ |( |
  20.     result = QSPI_EraseChip();
    6 k0 j0 R5 E5 G2 q
  21.     if (result == 1)& E# o7 v; B% e1 u
  22.     {
    ( K2 [7 _# h. u) s! A' h: ^: _
  23.         return 0;  h1 g1 M" b$ U* Q' X! t' e' ?9 b/ y3 d
  24.     }   
    + r4 S2 g7 Y( U/ p6 k

  25. 5 m5 V2 i* [/ R1 F# B% D
  26.     /* 内存映射 */    ! L2 U3 u8 N! C" ?/ S/ E2 z# _
  27.     result = QSPI_MemoryMapped();
    " E% ]' M2 O- O! f4 v8 W
  28.     if (result == 1)0 A: e9 r7 ~/ g3 a
  29.     {& Y% j0 T( W( o: _* e
  30.         return 0;
    . ^9 o! }* Q0 t$ f! {7 f: ]/ H- s
  31.     }. n- E5 X  f; E) L- j3 W! `0 o
  32. # _- ~1 ^/ R" G# _
  33.     return result;         
    ) g8 T- b& i1 G3 g+ r" C
  34. }
复制代码
& H; X0 F9 x  K) h! k: u
  扇区擦除函数SectorErase
, }% {9 D# V7 a4 ^1 Q扇区擦除实现:+ c$ u; o( B- d9 d  M9 H. V

8 @' \+ x2 I1 K1 i3 a
  1. /*
    & Q9 H: O% {: ^" q8 \
  2. *********************************************************************************************************; U0 n3 M, m! Y2 s4 W& B
  3. *    函 数 名: SectorErase
    , }; ~( X- V2 C, R! c6 h- S+ k
  4. *    功能说明: EraseStartAddress 擦除起始地址
    ( D& {3 H4 E% p
  5. *             EraseEndAddress   擦除结束地址) Y- l" }4 K0 Q" h, w( a
  6. *    形    参: adr 擦除地址1 |+ V) ]1 l5 R
  7. *    返 回 值: 1 表示成功,0表示失败
    6 ]: D8 q: r( j; x7 e+ f
  8. *********************************************************************************************************
    8 m5 D8 p* ^5 j
  9. */
    / V4 }8 H9 s% d" X2 r; X
  10. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress). ]" T" s. M% N: |8 t
  11. {
    7 P4 S  O' V/ b6 L' E7 K2 A
  12.     uint32_t BlockAddr, result;$ F  Q9 Z2 z/ z( E  y% a- m! z  O7 e

  13. , o9 P7 u  O! Z# Q
  14.     EraseStartAddress -= QSPI_FLASH_MEM_ADDR;
    . ?  C/ S- m7 g8 i
  15.     EraseEndAddress -= QSPI_FLASH_MEM_ADDR;6 N$ R: w9 R) ^5 Z
  16.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x10000; /* 64KB首地址 */( t* a( u' {! ]

  17. & v3 z. [( t5 Q& t
  18.     /* W25Q256初始化 */
    # \& m8 U$ \3 f
  19.     result = bsp_InitQSPI_W25Q256();
    / Z( g8 a( b6 @: i  g2 v/ K
  20.     if (result == 1)
    ; ]6 i* d9 o  ]4 ^; `! p5 [
  21.     {
    7 @3 B/ G/ ~1 s/ ~
  22.         return 0;        % P$ l+ u5 F/ c2 ?4 d% y
  23.     }
    ; Y" N- l% Q; i2 T

  24. ; ], h2 q+ Z" b1 T9 H: M; V7 `) U
  25.     while (EraseEndAddress >= EraseStartAddress)
    5 _- g7 A, w; o
  26.     {1 k$ b5 `/ M2 Z" x0 \0 v. h7 R" W
  27.         /* 防止超出256MB空间 */
    ' `  I" W3 a; L2 L" P! u
  28.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;4 a; N" r" [6 e& r* C, e7 U( H

  29. 2 Q* k' V4 I' a1 r( _4 U' k, i0 K2 ]
  30.         result = QSPI_EraseSector(BlockAddr);    & _+ f: v4 t% Q+ X' c/ i3 f6 x
  31.         if (result == 1)
    % T& j+ v4 }2 ]2 e& l1 Q% P
  32.         {
    - G' J, f% ~7 U& n: D& }, C- Z
  33.             QSPI_MemoryMapped(); 7 A( `. n- c5 [( v9 W' U
  34.             return 0;        ( d; q! c3 U: n4 }; I/ @
  35.         }
    ' }% ~7 y8 I) S* Y, u, [
  36. 7 |. V- _7 `7 a! h) q8 Z; u
  37.         EraseStartAddress += 0x10000;
    ) T7 D' t( S# Q
  38.     }
    6 _! O5 F4 y3 Y6 y' {6 h/ o  Z

  39.   g/ ^' g. p9 |* A
  40.     /* 内存映射 */   
    * n. k7 b; k, J  W7 ]  f3 v* J
  41.     result = QSPI_MemoryMapped(); 0 o2 K/ [0 C" S" C* _/ G6 a% W
  42.     if (result == 1)
    # u  {5 `+ G* z& v
  43.     {  A/ c2 x: ~$ ~
  44.         return 0;
    0 D! r+ ?! }2 A( f. }4 C' T
  45.     }* [$ i9 Z5 T2 H: L. u9 w- B1 i

  46. 4 Q9 G  T4 Y# G2 X& O- s- b; R
  47.     return 1;
    ) P8 Z) m' W: X, [/ I2 }
  48. }
复制代码
0 }- v% R9 V2 ^0 t) [! N5 I1 [
这里要注意两点:
. w* V) K: C# E( z! _) g
" m/ E8 F: b/ n% ^/ \3 O( {/ u+ t7 M(1)     程序里面的操作EraseStartAddress-= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。
' o% N( C' I0 b7 @) b# i
6 }; U, A5 b, V) e$ `  h- O3 F(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的64KB为扇区进行擦除。. x3 X+ u6 \$ Y( B8 g
2 G/ t# l3 V+ [- I
  页编程函数Write, w( X: Z5 D5 I. O
页编程函数实现如下:
. f! U% F' D. a4 y; W+ |: ]/ o+ ?; H$ h( b! o
  1. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)
    - L% J2 \9 \- o% f, x
  2. { , l5 }1 ~! `2 {/ c. }& {
  3.     int result = 0;! t' f% S3 s* V+ l$ P; q
  4.     uint32_t end_addr, current_size, current_addr;
      ]; K  }8 i  `* o% p2 c/ r# b: H

  5. 4 ~* n2 w) R6 X% X8 d) J
  6. 7 p' e& @: m" [0 u" u. y6 d
  7.     if (Address < QSPI_FLASH_MEM_ADDR || Address >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)' w9 j$ Z5 A- g2 I" k
  8.     {) B+ `* K' ~( T/ I
  9.         return 0;
    - v- l* W6 U( N4 s
  10.     }
    7 b2 ^! \& W7 x" q2 s1 X

  11. 0 |+ X' }: W5 M
  12.     Address -= QSPI_FLASH_MEM_ADDR;. s0 ^- W( N9 D5 _3 Q) H" I

  13. ! Q3 @: d% X1 \2 x' {! Y
  14.     /* 计算写入地址和页末的字节大小 */  F- y) m& k: Z; C
  15.     current_size = QSPI_PAGE_SIZE - (Address % QSPI_PAGE_SIZE);! m; O( C, ?1 [' `" s& w, ]  i! y( i

  16. 1 ^. u% x! ~# z: J
  17.     /* 检测页剩余空间是否够用 */8 R; e" w1 z- f( ~! e0 W) S. f4 c' P
  18.     if (current_size > Size)
    % i, l7 w" j$ e9 ~
  19.     {* f4 h$ X' q. [" j4 p5 y
  20.         current_size = Size;
    - G; \+ L" T6 c3 v9 y+ W
  21.     }/ v" R. p5 H: T+ |/ [
  22. " r$ S5 [+ I9 O8 n
  23.     /* W25Q256初始化 */
    % l9 x6 e* a) q; r
  24.     result = bsp_InitQSPI_W25Q256();3 N4 C) }- g( Y- F1 n. ~' L2 G
  25.     if (result == 1)
    - g) Y1 H) x+ T
  26.     {
    % U% U4 E: T) z9 p0 Y* Q
  27.         return 0;
      W* {6 n/ o7 ~! `1 r/ w. A( A
  28.     }0 k1 P0 y1 X6 E# g% j7 y! K7 u
  29. $ m. X6 P% m; g- ^7 u* W6 r
  30.     current_addr = Address;
    ' R7 b/ s' ?! @; M7 S
  31.     end_addr = Address + Size;
    . \" ^8 u3 T; D4 {5 R$ _" g$ ^

  32. * a& {$ `% B6 y) T( c9 m, [& [
  33.     do{; _1 w4 ~( r1 u) W+ p
  34.         if (QSPI_WriteBuffer(buffer, current_addr, current_size) == 1)) D- v) H1 w* W0 A3 q3 v
  35.         {
    + S" ]! g- i- p6 E( v
  36.             QSPI_MemoryMapped();  
    , I2 W. c. M- S, n- D
  37.             return 0;  
    ; d' B/ q! w+ W! z9 U
  38.         }
    2 ?8 t5 v( {" _3 P

  39. . ?' h$ t# x  [* ~% ~0 b; B* c- j
  40.         /* 更新地址 */
    ) \! y, i7 t2 X' s1 x
  41.         current_addr += current_size;: [3 {. y: e; d7 l$ j7 W% B
  42.         buffer += current_size;
    ! \+ C' t" I7 F1 K% k
  43.         current_size = ((current_addr + QSPI_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : QSPI_PAGE_SIZE;
    , B! J' l5 |7 f7 p2 v- O0 A
  44.   N0 P# p, {! e, i; \1 B) ?
  45.     }while(current_addr < end_addr);
      E, q% W# b% m

  46. 8 X" T& V$ [! {* H+ C
  47.     /* 内存映射 */    # x* s9 R% }4 Q1 _2 C0 n' C! j
  48.     result = QSPI_MemoryMapped(); # d' w' ^8 p  x; k) n& y
  49.     if (result == 1)
    + ^6 ^! R0 _. L& D- o
  50.     {9 i5 o. O- L) H4 O1 O7 k
  51.         return 0;$ b8 I, G" M' W+ M" d7 g
  52.     }# c; I1 I1 u. X: T
  53. 0 \: J: i# ?2 p- W  \- p
  54.     return (1);  
    6 Z  I6 ~6 K4 s5 n8 y4 g# ~7 o0 r  }
  55. }
复制代码

; F: h6 G( K" ]/ O+ H这里注意两点:
$ m" x7 t2 Y- u2 r  `7 j4 @4 B/ w4 ~  e5 |" [+ P
(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,所以此程序要做处理。/ U7 W1 [. b' _, f" I6 j5 x! G* Q
' L! C( B6 D: \/ E
(2)     程序里面的操作Address-= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。
! x- S( u/ z$ l( H; D4 c- z; _7 K: q. ^1 d+ }
  读取和校验函数
; M$ F- e1 C) ]9 o5 P* q我们程序中未做读取和校验函数。
( }: I8 d  `; p$ O
  ]( _+ }' K# h" j( X* _" U6 T7 H# Q(1)     如果程序中未做读取函数,那么STM32CubeProg会以总线方式进行读取,这也是为什么每个函数执行完毕都设置为内存映射模式的原因。8 z) y5 p7 h3 a- w( ?- q# k- B7 s

. T. p0 M+ q- N+ \) j  f% s2 |(2)     如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。
4 ?" `2 R  }/ V4 w% |" B" x
! i, Z* y, a0 b8 b+ l2 `2 \81.5.7 第7步,修改QSPI Flash驱动文件(引脚,命令等)
, Y% n6 ]8 p$ i5 ^; _3 K+ Z7 }最后一步就是QSPI Flash(W25Q256)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_qspi_w25q256.c(做了条件编译,包含了H7-TOOL和STM32-V7板子):
. }& t2 X6 e7 V
" F# m/ o) l6 g
  1. /*
    % }& j. f7 p: V2 A$ k4 w6 a
  2.     STM32-V7开发板接线8 T% \# Y3 n  i& ?; s2 p

  3. : z- r4 f! Y: {4 O9 q  j
  4.     PG6/QUADSPI_BK1_NCS     AF107 h. z% M* t2 b8 n# z( G- r
  5.     PF10/QUADSPI_CLK        AF9
      L& c) r2 G/ E9 X& d( V% B6 `
  6.     PF8/QUADSPI_BK1_IO0     AF104 L! v7 r# F2 `$ P8 H
  7.     PF9/QUADSPI_BK1_IO1     AF10- j: t. z5 T! o6 ?- S. K0 n6 Q2 b
  8.     PF7/QUADSPI_BK1_IO2     AF9
    3 s+ G' \- N9 u9 q1 ~
  9.     PF6/QUADSPI_BK1_IO3     AF9" C' c! O# g4 q; M- M5 ]) @
  10. 9 E3 O  u- j$ z0 c/ x3 S
  11.     W25Q256JV有512块,每块有16个扇区,每个扇区Sector有16页,每页有256字节,共计32MB, P) M! s0 _  w2 |
  12. ! [1 h- N* P7 {, o8 |
  13.     H7-TOOL开发板接线* i' }7 u0 f4 Q/ _

  14. ; ^/ C3 v. }+ Z* F, G' s
  15.     PG6/QUADSPI_BK1_NCS     AF10/ ]5 K/ E2 U6 e% J+ z' ^
  16.     PB2/QUADSPI_CLK         AF9! Y0 H. v5 f& h
  17.     PD11/QUADSPI_BK1_IO0    AF109 I- ^" c2 x7 S1 n
  18.     PD12/QUADSPI_BK1_IO1    AF10) y, c1 Z. Y! J5 q! L1 L- a  i
  19.     PF7/QUADSPI_BK1_IO2     AF9
    5 A9 w0 t& n' W% I
  20.     PD13/QUADSPI_BK1_IO3    AF9+ T& ^0 S( |/ _* c4 Y" `& h
  21. */
    ; i4 N. z" J& a, y7 m
  22. ' o; V  o) ~. I: }/ l3 q! O
  23. /* QSPI引脚和时钟相关配置宏定义 */
    + H+ v0 I8 M3 n
  24. #if 0. h/ {' Y! J2 x* f9 _
  25. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()! x& S) F% ~; F2 K
  26. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()2 \3 C- O% _* N. U8 q* k9 c' P
  27. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()
    5 T7 c. `2 @  c
  28. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOB_CLK_ENABLE()
    ! R$ I* s( o9 q6 j3 @% t) b
  29. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    + W. n& l7 z4 W* O' R
  30. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    . o$ W' ^- _0 ]: q8 p' S
  31. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    $ G% k, w% p! O) D! N. w1 y' `
  32. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOD_CLK_ENABLE()
    2 s9 S/ R- U( T* ]" l) {, \

  33. % e6 z9 s/ J( G: x7 H
  34. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()
    % Z9 ~4 }9 f" b# `0 ~
  35. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()  _# P! u9 ~4 |3 J: p( J  G7 W9 y
  36. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()& F9 `! K+ K/ k9 c4 J
  37. ; H" |! V; Y6 b7 y6 z7 |
  38. #define QSPI_CS_PIN                     GPIO_PIN_6
    2 [, m! ^! G2 n4 Z  J0 i
  39. #define QSPI_CS_GPIO_PORT               GPIOG& n  n; l; X  Y9 L$ s
  40. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI6 e8 I  R7 T, ]+ B8 v1 b: @0 ^- J, y0 t

  41. - R+ Y6 V  ?, ~; r
  42. #define QSPI_CLK_PIN                    GPIO_PIN_2$ i, c# u, m) C" a
  43. #define QSPI_CLK_GPIO_PORT              GPIOB
    ( b7 H; w3 I. U- ]7 @
  44. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI# P3 _* H% O! O9 K
  45. / m# G; O* }- k/ ^
  46. #define QSPI_BK1_D0_PIN                 GPIO_PIN_111 }& p; Y  N  [0 ]2 {5 f
  47. #define QSPI_BK1_D0_GPIO_PORT           GPIOD: F" q' y% i% o/ I& }' N
  48. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF9_QUADSPI9 M, G4 P) l+ s1 r

  49. 9 G; a7 P( k) i) L3 @% n6 C. N
  50. #define QSPI_BK1_D1_PIN                 GPIO_PIN_12
    0 i- a( i4 a  H
  51. #define QSPI_BK1_D1_GPIO_PORT           GPIOD
    , n9 r' f; {1 h7 T  h$ ^! N
  52. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF9_QUADSPI
    / B. e! l& n# V1 k  R# D

  53. ) w/ B% [! X6 L5 o$ ]
  54. #define QSPI_BK1_D2_PIN                 GPIO_PIN_71 {. F6 ~2 A$ j0 J7 Z7 H
  55. #define QSPI_BK1_D2_GPIO_PORT           GPIOF
    ; l9 n7 w8 d1 a
  56. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI
    * v2 M. v* d, M, W. P# \; @. v! D% C! @
  57. 1 L& L4 D! T; b0 \1 G9 I% e7 Z
  58. #define QSPI_BK1_D3_PIN                 GPIO_PIN_13, w' n# O% v7 d
  59. #define QSPI_BK1_D3_GPIO_PORT           GPIOD; }7 J* U/ t3 v/ M. @0 v
  60. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI* S' c; U" Y7 |; y& C$ J
  61. #else5 L8 v& m4 _) J) c% a
  62. #define QSPI_CLK_ENABLE()               __HAL_RCC_QSPI_CLK_ENABLE()) R& f" O2 O! w5 a6 l: }' Y  Y
  63. #define QSPI_CLK_DISABLE()              __HAL_RCC_QSPI_CLK_DISABLE()
    " a* Z" c' l. n
  64. #define QSPI_CS_GPIO_CLK_ENABLE()       __HAL_RCC_GPIOG_CLK_ENABLE()* F/ V7 y4 q3 q* {* u& P# z- u2 K
  65. #define QSPI_CLK_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOF_CLK_ENABLE()2 H9 E2 a* y' p  j
  66. #define QSPI_BK1_D0_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    ) R8 Y6 w0 J" [# K4 Z% y
  67. #define QSPI_BK1_D1_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()
    % I. x4 C: a! f9 x$ J
  68. #define QSPI_BK1_D2_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()& p) Y6 O6 A+ v$ ^
  69. #define QSPI_BK1_D3_GPIO_CLK_ENABLE()   __HAL_RCC_GPIOF_CLK_ENABLE()$ B( E; `3 b" B& y4 k8 R" X7 G* w
  70. & G# F# s% @# g7 n
  71. #define QSPI_MDMA_CLK_ENABLE()          __HAL_RCC_MDMA_CLK_ENABLE()
    6 @( q6 R% ?! j% d& f7 {$ @8 T
  72. #define QSPI_FORCE_RESET()              __HAL_RCC_QSPI_FORCE_RESET()
    2 _8 d6 E3 ~, Y$ y6 j1 c4 f& x
  73. #define QSPI_RELEASE_RESET()            __HAL_RCC_QSPI_RELEASE_RESET()! o% ]. u  u( n  a3 j5 s
  74. - K: K0 r* @, x$ C3 E* t) t' Z
  75. #define QSPI_CS_PIN                     GPIO_PIN_66 T9 Y7 ], ^3 E) V1 w8 K7 s; v/ H
  76. #define QSPI_CS_GPIO_PORT               GPIOG
    2 R8 w7 V, k' o. ^; b
  77. #define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI
    8 |# J, _& }, ~$ q) E' r  g

  78. ( j& f" K4 ^! ]: n
  79. #define QSPI_CLK_PIN                    GPIO_PIN_10
    2 V/ J$ t0 c! s3 S7 M9 T
  80. #define QSPI_CLK_GPIO_PORT              GPIOF
    - U. t7 K# e6 X3 T: F. y' v
  81. #define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI
    " B% i' M7 ?6 N& Q& n: e
  82. 1 ^0 A9 b# ^$ f9 z2 X% U0 b
  83. #define QSPI_BK1_D0_PIN                 GPIO_PIN_88 n# ^) D7 R  ?( q2 k/ f* W
  84. #define QSPI_BK1_D0_GPIO_PORT           GPIOF9 s- j, u+ p  B) H+ [: q. s
  85. #define QSPI_BK1_D0_GPIO_AF             GPIO_AF10_QUADSPI  c0 V6 h1 y; L: H

  86. . C5 _; t/ u: K; Q# s: h
  87. #define QSPI_BK1_D1_PIN                 GPIO_PIN_9
    5 d# f' V8 [+ s0 x) a$ a( F
  88. #define QSPI_BK1_D1_GPIO_PORT           GPIOF/ o0 b" v2 r; T" y
  89. #define QSPI_BK1_D1_GPIO_AF             GPIO_AF10_QUADSPI/ `" w+ v7 Z7 p% l% L1 ?+ _

  90. - c% Z' Z' E1 O. E8 t2 c8 L0 P' j, S
  91. #define QSPI_BK1_D2_PIN                 GPIO_PIN_7
    6 K, v/ [7 w8 V+ k& r( i2 t2 i$ D
  92. #define QSPI_BK1_D2_GPIO_PORT           GPIOF
    ) d, g" i0 C0 q1 m; ^2 I+ n; h* F
  93. #define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI$ z) b- f% K6 Q7 i" k) k/ |
  94. 0 I" N- N9 O' X/ C1 v# {8 V3 K: \* R
  95. #define QSPI_BK1_D3_PIN                 GPIO_PIN_6: R# V, ]0 j% ]! z! ~/ h
  96. #define QSPI_BK1_D3_GPIO_PORT           GPIOF! O( G" h- Y. L* P7 U" X+ J! m
  97. #define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI
    ' ]% M6 `& o7 \* Y  F
  98. #endif
复制代码
9 Y# n  b5 W0 c: N& m
硬件设置了之后,剩下就是QSPI Flash相关的几个配置,在文件bsp_qspi_w25q256.h:( y! }' X/ j0 c- d5 @% e: \) {$ ~
, e" q% i1 o, n" a5 n/ |& u$ o
主要是下面这几个:& t- T% Z1 z# H4 ?2 E7 }1 o( F

. b& r# i6 I* l  D; G1 q* Q8 w% X2 ]6 k
  1. #define QSPI_FLASH_MEM_ADDR         0x90000000
    % u  @3 e( E. f$ A, d

  2. 4 P* q( O7 M* n0 B' F2 A' d
  3. /* W25Q256JV基本信息 */
      H, ~7 P1 I; m( e" y3 N; C% I
  4. #define QSPI_FLASH_SIZE     25                      /* Flash大小,2^25 = 32MB*/; R" @$ k3 h/ d- W$ k
  5. #define QSPI_SECTOR_SIZE    (4 * 1024)              /* 扇区大小,4KB */# ^7 ^4 `0 c3 P: H0 |: o. l3 p( H. w
  6. #define QSPI_PAGE_SIZE      256                     /* 页大小,256字节 */! Z, W! Z! ?# y& ~2 B; V- m5 W
  7. #define QSPI_END_ADDR       (1 << QSPI_FLASH_SIZE)  /* 末尾地址 */
    . Y. G% ?. f! U+ O' A( S
  8. #define QSPI_FLASH_SIZES    32 * 1024 * 1024         /* Flash大小,2^25 = 32MB*/
    4 |  O( F) S$ h- M4 d6 F- N+ B
  9. 6 ?/ w9 t7 v+ x: Z
  10. /* W25Q256JV相关命令 */
    / M. Q# q1 ]6 k9 G' _" a
  11. #define WRITE_ENABLE_CMD                        0x06    /* 写使能指令 */9 T& s8 B* I2 {' e
  12. #define READ_ID_CMD2                            0x9F    /* 读取ID命令 */: _; G; L; `8 v
  13. #define READ_STATUS_REG_CMD                     0x05    /* 读取状态命令 */
    ; Q( v& ^" E2 B0 N  L
  14. #define SUBSECTOR_ERASE_4_BYTE_ADDR_CMD         0x21    /* 32bit地址扇区擦除指令, 4KB */
    # [7 w& a! a) ?* F' I
  15. #define QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD       0x34    /* 32bit地址的4线快速写入命令 */
    * l4 Z( v0 y( [9 s1 S2 |1 S  b7 O
  16. #define QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD    0xEC    /* 32bit地址的4线快速读取命令 */
    * ]/ X# w) x8 d: l4 P# x

  17. " s+ I: D# O1 w! G2 b
  18. #define BLOCK_ERASE_64K_4_BYTE_ADDR_CMD         0xDC    /* 4字节地址,64K扇区 */% S4 H' E5 W1 P3 E4 U( A. @; _

  19. / v: {8 {, }/ X: p
  20. #define BULK_ERASE_CMD                          0xC7    /* 整片擦除 */
复制代码

' N1 [( S) X; U81.6 QSPI Flash的STM32CubeProg下载算法使用方法; X  |; R  ?- `
编译本章教程配套的例子,生成的算法文件位于此路径下:
; _5 f/ j2 p, u& Q7 k* c  T7 C* W5 Z7 }8 R! Z, B
d45f9274d8762b095790b3e6778b0a93.png
" [) I9 t  m+ a6 L5 {5 M( V. U! D5 @1 q
: V7 V9 C- r/ W2 M+ J; }
81.6.1 下载算法存放位置3 m& y7 l' b; `) @
生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:  O" Y& w5 d7 ^/ t$ a

7 }8 t/ i! _) j8 @. `) a2 E\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader9 S/ \  |. C$ U* b# J/ b

- L* R) _) X9 l
60cce5a15415e27e9d5b75b05028d1c0.png

* ^; f4 p' e) }5 g* N2 M. H+ }6 }; B. |$ H( R: e0 u0 h, o
81.6.2 STM32CubeProg下载配置' a& G- Z, ~6 k9 |% T
我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。
" z) D: W% u) @! m$ K
0 @  V4 X/ L: ?
9cc179641e765ea43cb38d268bf734df.png
# r3 r- E* G9 ~' k0 Q4 X
4 z4 m# w2 U9 }" t9 J6 V' h' q2 w' f; a
点击Connect后效果如下:5 z6 o' p  A, q6 X; |- m- M4 Y

" \+ W0 h) N. w9 A; X% A, F
b39ce8a738163430db2119bdd87e3fbb.png
+ W3 a% N1 C% g! N# _' H
: {( i+ V1 @" t. j; q1 M
在这里选择我们制作的下载算法:
& t3 x! {/ y5 \. G; O; `
" W* ^# l3 @2 U" [7 v' p3 ~
17f6e0f50a18ae818faaa1399daef12f.png

2 n$ x/ t3 i; s$ Y; `7 d
- m0 T( p1 X- |  @( P任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming- |. k* U0 D) W' V$ ]

/ R4 B& ?2 v# H% P# \5 C9 ~
43a0d90faf1d392f5a74eaa7777e4178.png
% ]/ x: @' o5 \- D% U2 `: k  }3 V

/ G( R& h8 y8 Q下载完成后的效果如下:  r1 c3 v% Y$ I

7 w, \$ z0 s5 o. U, u
a1d6662baa9e7be00f7e67e02b5c9b79.png

/ b. Q' ]- @  B3 T9 F" S
: {+ @" c8 e7 `% s: ~; m5 d) ?4 ^81.6.3 验证算法文件是否可以正常使用
) P0 o' k9 @+ N( l; G( ~为了验证算法文件是否可以正常使用,大家可以运行本教程第82章或者83章配套的例子。也可以使用STM32CubeProg直接读取:
( Y- ~3 ~) B' h& c% P+ V, k8 D  ~; ^% z
3f2eefeef3db68623cb8969974c40a20.png
" u, |8 m. T9 R6 @" d

* D# t0 Z' r: h  [& q0 g81.7 实验例程说明
8 I+ b7 L% Q$ {. z, d  Q本章配套例子:V7-061_QSPI Flash的STM32CubeProg下载算法制作。
- e, X  b: d9 @
$ Q+ d. Z% Y! R" S编译本章教程配套的例子,生成的算法文件位于此路径下:
4 c( S1 \3 p& b! q* B2 F, \0 Q  y) t8 V2 J5 O& n6 M& ~" ^7 c
b7028271136c8053d7300063ed4aec3a.png

! K8 X% r9 l6 `# P" u' \+ h" t2 p; p/ k! \4 j, E
81.8 总结7 x. V) @8 d4 Q  X) c2 l
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。
9 h; g8 d( r& i! F; Z3 \$ M9 t4 y
/ ]3 ?( {- P) U; w

- V& O5 M/ q8 W) s$ o
赞 收藏 评论0 发布时间:2021-11-4 23:27

举报

0个回答

所属标签

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