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

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

[复制链接]
STMCU小助手 发布时间:2021-11-6 23:35
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
d80578d217eb2a83520c6f3d797218f4.png

, }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
d75670256363bb50203bed0bbe4f9fd2.png
) 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
4fa96e9e33d700081f2cdbd312afb31e.png
; 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
7deeae4b650bd82c457492af14fc26b7.png

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
97f385e3b62a055bac9b5c0214b877e5.png
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! D
43b298f996937fe35e89697fc6479cb9.png
6 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
36026b9a6d60249610d0246eb725b246.png
+ 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
52c72b3993f28a366a22eac56f8fd043.png

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
7b48153027a44de33926c2da008716a0.png
* 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 ]
  1. /**8 r0 Q: @9 B3 b8 E6 Z8 K- c: B. k
  2.   * Description :  a5 C4 d! @- {& k
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the6 E' W/ h3 ]) \1 k1 ?8 C
  4.   * device and initilize the FSMC with the chosen configuration
    . v4 D8 n/ v& V, v7 h! @
  5.   * Inputs    :
    . S$ k* o0 _: f% ^. O2 Q7 M0 g
  6.   *      None
      Y# s5 T; Q, `3 K& F. v0 d" Y
  7.   * outputs   :4 ?/ a, [5 z' _) J5 R) I# {
  8.   *      R0             : "1"             : Operation succeeded4 T# Q+ Q; S0 H, O5 G% L% R1 Q: l* u( m
  9.   *               "0"             : Operation failure  x- x0 J- H! F7 b
  10.   * Note: Mandatory for all types of device
    - K; W% G" K# n6 c8 C$ n
  11.   */& n. t8 c0 E$ q
  12. int Init (void)5 n$ Q* h  W- K; ?7 D
  13. {  / q" x5 ^0 x# [6 E# Y
  14.   SystemInit();5 {' [$ K/ j! N1 H3 J0 k" y# }
  15.   sFLASH_Init();: t, b: T0 r- l4 n' g
  16.   return 1;! j9 d6 O* S3 Q4 W! F9 s. Y
  17. }
    ( ?8 O! m9 I( x9 f9 Q8 [) p: k

  18. 8 F9 J' m8 Z& {" h, e9 b6 R

  19. 8 I' ~3 q& D) Q1 Z! u8 d
  20. /**0 L; H+ \" D% {
  21.   * Description :) Y# P3 P4 B7 q: X2 U8 [* n
  22.   * Read data from the device 4 P% V8 x/ Z) j6 k2 B: l- |1 v
  23.   * Inputs    :
    4 y3 k2 g, V0 ^# V8 _
  24.   *      Address       : Write location
    9 p8 g8 y4 Y' j4 h; r
  25.   *      Size          : Length in bytes  . C2 z! o8 D& p( T, H% v# d& h
  26.   *      buffer        : Address where to get the data to write
    & I  k, c1 E+ F, w
  27.   * outputs   :& ]( ]2 }  x  \! r& Z' f# W, G1 [
  28.   *      R0             : "1"             : Operation succeeded
    # v. I2 z& y1 h6 b" O# q" b
  29.   *               "0"             : Operation failure. h" f4 }' M) _% e
  30.   * Note: Mandatory for all types except SRAM and PSRAM    * S4 x4 q7 |, M, G5 \7 A; A
  31.   */9 K( b" U- r) w/ f+ F6 ^$ }# B
  32. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)+ `, l4 X" i0 ]6 i8 C0 b
  33. { ) H) {2 K# A& k. Q- u5 I+ |; @+ T+ B
  34.   sFLASH_ReadBuffer(buffer, Address, Size);6 I  V9 q1 C' w9 C* p6 H5 \/ I* o
  35.   return 1;
    4 ^3 f* J( J' M% n7 k/ C
  36. } 4 B" j  [, C: f' f/ z
  37. 5 s* N8 |3 Z! K# u7 K

  38. , x( u# T! y+ y8 c: W
  39. /**
    7 s- C) E- P/ e' V9 V
  40.   * Description :
    " U  m' J+ U7 d/ Z: `
  41.   * Write data from the device
    * h$ x1 e$ A0 m
  42.   * Inputs    :6 `  r- S' e. T$ f1 v$ \7 S: j
  43.   *      Address       : Write location, L, {/ e3 k2 [4 d4 a* T* U
  44.   *      Size          : Length in bytes  & B; e( H" \. l& @
  45.   *      buffer        : Address where to get the data to write8 H+ Y$ O& ?: ?: a; b$ J! t
  46.   * outputs   :# Y# Q$ n6 s; F8 p! ~
  47.   *      R0           : "1"             : Operation succeeded5 o0 s) u  T  Z
  48.   *                     "0"             : Operation failure" j/ y, C5 T* j. `
  49.   * Note: Mandatory for all types except SRAM and PSRAM      U! r* t# C' F, O, q
  50.   */
    + R3 J  @  L! E$ x. m0 D
  51. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)
    # [4 E* L) Y4 A3 y5 W: I$ E" E
  52. {9 t8 I/ S( e6 O1 s
  53.   sFLASH_WriteBuffer(buffer, Address, Size);
    9 f+ c' ^% L7 I7 F# ^
  54.   return 1;0 R# Q+ c: S1 T7 U- @5 R
  55. }
    ; d7 I( f/ ~9 L/ }+ O

  56. , u5 S0 I8 j, b6 n: I0 {( }
  57. . W( O4 a* O1 l$ b5 T7 y
  58. /**' z+ `5 a0 I! n  _" E' a
  59.   * Description :3 A- ^. y$ j1 S& H6 p% `
  60.   * Erase a full sector in the device
    2 E# `" _/ D1 x, i; n8 B1 b
  61.   * Inputs    :# G8 R1 l4 e+ ?0 `6 V! T( {
  62.   *     None+ U+ Y5 X( j, j9 z% b
  63.   * outputs   :- E. i- O3 h# M: P/ @
  64.   *     R0             : "1" : Operation succeeded
    . ?" j! ^$ h' s4 G8 c7 _
  65.   *              "0" : Operation failure
    + y7 U- ]4 |4 ^" W1 N6 i! U! M
  66.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH
    ' `7 _  p# K# s
  67.   */
    . k0 N. m& @& Q7 r+ n
  68. int MassErase (void)
    + J$ h2 s9 O- V* I* F6 Q+ K8 X* h& O
  69. {  " F  q- Z* Z, G1 _: z
  70.   sFLASH_EraseBulk();
    ' L% T; K& ~* `) Z( u9 R, y
  71.   return 1;    , E6 q4 l; `; y8 x7 O* W' _% V
  72. }
复制代码

, \; 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
  1. #if defined (__ICCARM__)' P" \, [6 S; x
  2. __root struct StorageInfo const StorageInfo  =  {
    , |' b7 m: i$ q" O8 w$ M  @( _
  3. #else
    & u, ~0 F$ a! i3 m
  4. struct StorageInfo const StorageInfo  =  {
    ( Y- u* g8 c5 t% u8 \- A' B
  5. #endif
    # V* X3 }4 e5 u) {
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number
    / g: ?7 C1 C0 k* U: B) U: V
  7.    SPI_FLASH,                       // Device Type; S0 K6 p0 T, l6 U0 s: j
  8.    0x00000000,                     // Device Start Address! O/ S6 Z4 g; _2 I* m" o- W
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)
    % d) z4 M! X5 g2 x( g9 X
  10.    0x00000100,                      // Programming Page Size 16Bytes
    # F. a9 [. q) r% j
  11.    0xFF,                            // Initial Content of Erased Memory  ?# f* _3 @5 Z5 `5 m# ]& F
  12. // Specify Size and Address of Sectors (view example below)' G0 o. X, y, N  s9 v& B+ K9 ?
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes 2 A0 h& n: n" z) e0 u2 `! `
  14.    0x00000000, 0x00000000,
    ! T: J2 N) m- V4 l2 Q
  15. };
复制代码

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
afddd6e839292aa2b94b43e79544d630.png
+ @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$ c
f639ae5b0d3c2e0e0190ec509fbc8a4d.png
8 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
c683673d2481d8dce75b6fa01d624600.png

# 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( } b96835a7506516319002e6b2e8db9371.png
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
  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions
    $ l# u& G( _4 q' Q
  2. {
    1 }+ \" D, F0 _+ a( e0 F1 W
  3.   PrgCode +0           ; Code0 W5 P' d5 O3 x# ?* [/ L
  4.   {
    5 q& p, X5 T3 _
  5.     * (+RO)
    ! W: ]4 O, ?  i4 o$ `+ U; D) X
  6.   }
    ' x) }- x9 W9 |5 x9 k. M0 Z
  7.   PrgData +0           ; Data
    - L; i  f' ~! K# J
  8.   {) `0 X7 J6 H6 a
  9.     * (+RW,+ZI)# Y9 c" ?% S* Q- i) Z$ I. m
  10.   }
    0 B- ?* S, A! F4 @
  11. }" O$ {% A! R& w$ Z  ^  A

  12. 5 y$ f$ O' C( |! y- H( W6 a8 X
  13. DEVICE_INFO +0               ; Device Info
    ' n% S& [+ o6 ^: ?2 F6 L
  14. {, S# n1 I; E2 V2 {$ u" k' u
  15.   DevInfo +0           ; Info structure# \( K5 _4 m& Q2 g8 q8 n
  16.   {2 g- `% z3 h" p8 ~7 J
  17.     dev_inf.o8 y5 H! f: g% }1 _
  18.   }/ N) N  G+ e0 I5 k7 s. E
  19. }
复制代码

& {, 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
641395c374a3af02f1a92d3ff9ee4199.png
; 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
80ad5e3abdd6a993c8621230401a1313.png
! }! 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 `
33bf89f25487547d4008f43b7cf10e03.png
( }" ]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 [+ ]
  1. /*
    & Y1 {. Z2 k" R" g. W: a( F  U
  2. *********************************************************************************************************- [' F% U5 P  P) B
  3. *    函 数 名: HAL_InitTick6 l* o; G8 C$ V2 ]) I
  4. *    功能说明: 重定向,不使用
    / S# b1 v; _8 W# l
  5. *    形    参: TickPriority
    / q' Q1 G& k, j4 }$ R0 K+ F8 X1 A
  6. *    返 回 值: 无
    " b/ {4 C0 c5 y
  7. *********************************************************************************************************
    7 w# G. m8 n0 q- `
  8. */
    * v4 H4 P4 F5 N1 j. U( e8 `
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    ' r3 p% s; E5 V) l
  10. {
    " ^+ _' T. [5 y4 K5 b' Z) l
  11.     return HAL_OK;
    ' O6 Q" B4 H( o5 U) H/ b
  12. }
复制代码

$ 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
  1. #if !defined  (HSE_VALUE)
    2 r  g) @3 A( `, m! G
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
    # v) }5 r7 X) E+ |5 D9 d: U
  3. #endif /* HSE_VALUE */
复制代码
/ e. K4 n5 X; _; s2 b
最后修改PLL:
* @, X9 O: k7 i
- u' M- L# G; ]9 G9 u# j. W
  1. /*
    1 \) F7 a, \0 ^
  2. *********************************************************************************************************( z6 h. K0 C9 e
  3. *    函 数 名: SystemClock_Config
    ! f& E. b2 B% M# h- z- L( S, Q
  4. *    功能说明: 初始化系统时钟
    3 _- l' f- h! l& S- i& Q8 V4 B: P
  5. *                System Clock source            = PLL (HSE)
    / d1 y# m  k& T  ?5 e
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)9 F# r# \; [$ J% `6 }9 w
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)& s" A7 q: q  ?; R! n
  8. *                AHB Prescaler                  = 23 U) h; t/ n6 W4 t5 P: E
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)
    6 k, b. Z5 M* z, N, D; B# w
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)* u( ^! ^8 r' m: r+ e5 E* ^9 J5 \
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)
    % a9 R: S3 [3 E9 h) |
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)
    2 L8 Q6 J- X  P* c
  13. *                HSE Frequency(Hz)              = 25000000
    / F# c. e9 R& |+ @( x; v
  14. *               PLL_M                          = 52 v1 p% _3 M7 C, E9 Z: z! f
  15. *                PLL_N                          = 1608 X2 `6 B, x. }" a. g; u. \
  16. *                PLL_P                          = 21 m: n- W) O: c: e
  17. *                PLL_Q                          = 4$ u) |4 l. e7 A% R5 H% W8 ?
  18. *                PLL_R                          = 21 c  i+ z6 |; `( `% F0 {8 D) B' {
  19. *                VDD(V)                         = 3.3, s5 y: e4 r0 w" u% V
  20. *                Flash Latency(WS)              = 4
    4 p$ b8 E( ^7 \
  21. *    形    参: 无' H: v* U; O4 T* z& G6 a8 A6 e
  22. *    返 回 值: 1 表示失败,0 表示成功
      K' w* X! Y0 q+ B) `5 V2 |' _, c$ L
  23. *********************************************************************************************************
    + J2 W2 r% J2 `% g, o% w/ B1 x
  24. */
      @! ~' @( V6 C; b5 k5 v
  25. int SystemClock_Config(void)
    3 _6 ?4 f- W+ r, G2 Q/ G
  26. {
    2 ^. m2 n* I8 ^3 i# `0 o
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    ! @+ C7 b) Z4 ?( ^9 o
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};4 @. K2 i2 ?) H' v3 m
  29.     HAL_StatusTypeDef ret = HAL_OK;6 {- O% a3 m4 I; N; f
  30. / E" ~$ X- U2 B5 ?0 p
  31.     /* 锁住SCU(Supply configuration update) */8 v7 `6 ]6 L6 W" {. C1 X0 b8 ?
  32.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);' R, N( j5 P! \0 }

  33. ; e7 i& _% M( R2 E/ B: B
  34.     /*
    1 Z" S8 `: E- B# s1 ?/ R
  35.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,+ W7 m2 b, r& V% M9 |. J! |# M  S8 G
  36.          详情看参考手册的Table 12的表格。
    3 H2 B8 }% R6 ?; a7 R
  37.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。
    " \0 `% U% ^# r) R2 S
  38.     */
    , U2 f) H6 A, ~" H
  39.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
    / q* |' `/ r3 p$ q

  40. 8 c& u0 _1 b$ A  K& I) O- X' m
  41.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}$ ^5 @" V. L( D+ c7 q

  42. / n0 L3 j" \7 D: m
  43.     /* 使能HSE,并选择HSE作为PLL时钟源 */
    ; C9 @7 P  p* k( d* S
  44.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    5 Q/ N* _* B/ @! P" e8 k0 u
  45.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;6 ~2 e! f6 r! p* e4 \2 Y* U2 u
  46.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
    ' R9 K/ ^! R% p9 C
  47.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;$ s! Q) H* f3 l
  48.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    0 Y" D$ Y6 s- D' ^. R! F% H) H
  49.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;1 f% ]( x! V% j  ]
  50. - v; L: J" F$ w8 `
  51.     RCC_OscInitStruct.PLL.PLLM = 5;+ w' W" ]( C4 F5 t* a
  52.     RCC_OscInitStruct.PLL.PLLN = 160;
    + F% L8 G* }% @) i: B
  53.     RCC_OscInitStruct.PLL.PLLP = 2;
    ; Y1 a1 }7 T2 d( F4 r! f- g
  54.     RCC_OscInitStruct.PLL.PLLR = 2;
    & O/ u  C8 `4 I+ [8 ?5 t8 Z, z
  55.     RCC_OscInitStruct.PLL.PLLQ = 4;        
    ( _2 X' a0 b$ Q

  56. 6 U4 Y/ y& t# s
  57.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
    % I) h( J* ~6 H/ L. f
  58.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;    / `% x4 Z0 }; v6 _7 h- `  y+ @5 a
  59.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);" s! g$ U2 O, V5 j3 C  r' @
  60.     if(ret != HAL_OK)
    0 s, f0 E5 A1 R* W7 f
  61.     {
    5 N0 p* t5 J+ p8 e: Q4 C  B
  62.         return 1;        
    ( j& I- V( G& M* U, a: ]3 z
  63.     }
    , {+ x" F! Q9 n! E' t
  64.   p2 N  f8 E/ k6 Y9 W8 I
  65.     /*
    6 R1 ~' @0 ]  e+ w2 a: u
  66.        选择PLL的输出作为系统时钟
    / j5 {6 y8 M% i* _5 ^
  67.        配置RCC_CLOCKTYPE_SYSCLK系统时钟' Z- |9 I+ Q+ m- z! I+ S
  68.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
    ; f% }, q2 r: v# L' S) U
  69.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
    7 C8 B. [1 ?) @% u& g- J2 {
  70.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线$ U- {  H7 l  ~, @
  71.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
    + s; g% c2 N! b( p6 n- J# W
  72.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     
    8 ^3 S& u  j7 x6 l* T# \# B! z
  73.     */# f# S5 h8 B2 K3 M& M1 d, I9 z
  74.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \! i3 d% o+ ?9 W; @* N  `* q+ @8 \& a
  75.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);
    1 m" n& |  B/ a, U9 C% j7 ~
  76. . F4 G5 N" k6 b; y/ |( M
  77.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;0 E( C5 ?  Z5 }5 Y
  78.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
    $ ~2 U& P* R) A7 o  J9 x
  79.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
    ) L6 j% x4 L" @4 n8 H
  80.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  
    4 g* {" a+ w6 ~; b8 Q5 v
  81.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; - [$ {2 H# a) u6 a  l
  82.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; + B2 g3 H4 F; S7 G  I
  83.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
    : f+ {7 }: v! k0 B4 M% s

  84. 6 Z9 i1 U6 F9 K
  85.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
    * a  `' |+ S' f2 c$ L
  86.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
    % H; W" K( t  O* ]( y
  87.     if(ret != HAL_OK)3 F3 |7 t. N7 i- s
  88.     {# B3 {) d' i  g+ q% @) J
  89.         return 1;
    9 X9 \- f8 N1 L
  90.     }8 m: k6 I' {# Z. a
  91.   Z( o: N! @# E5 ?: b. M3 {* D8 U3 D2 _
  92.     /*
    % q6 B; B$ J, W$ h6 w+ A. m
  93.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数 3 D" a1 ]! z" u& S7 V
  94.       (1)使能CSI clock
    & M1 v# L9 l% e! }1 a: y
  95.       (2)使能SYSCFG clock" q7 L* Y4 B- `
  96.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
      g' U% C7 C7 K6 x" |7 U- |) \
  97.     */
    # t+ s( f4 q& {' h; [1 P
  98.     __HAL_RCC_CSI_ENABLE() ;
    ( }8 O1 F" r4 R( e, |6 E' b3 h* W

  99. " E) _% F# {4 p3 o
  100.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;
    & n6 }: J# m( O: ?  j# g+ |
  101. # b2 E9 ~7 U$ T% F, R5 A/ W
  102.     HAL_EnableCompensationCell();
    ' W' F3 D2 ~9 R" S/ c8 }. o; Z
  103. 8 z3 f$ c* t% T" W1 V0 w# N
  104.     __HAL_RCC_D2SRAM1_CLK_ENABLE();2 p. v! t3 D  `& ?( Y
  105.     __HAL_RCC_D2SRAM2_CLK_ENABLE();) G0 i. @- j9 [0 d
  106.     __HAL_RCC_D2SRAM3_CLK_ENABLE();
    7 P8 i* V% s5 Q. L* o% i

  107. 7 I+ Z- Q: R) p2 h+ b) y
  108.     return 0;0 M2 A& X- {- y. j1 _
  109. }
复制代码
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
  1. #if defined (__ICCARM__)
    ; O, Z3 b7 y2 G5 Z# V) J
  2. __root struct StorageInfo const StorageInfo  =  {
    3 y$ K; E# E% f6 L8 K
  3. #else  A7 _/ e! e% E, B5 Z- u8 r
  4. struct StorageInfo const StorageInfo =  {
    2 u; j4 z4 O/ x9 H# G2 i/ ?
  5. #endif% }: p* G. d9 v% I* K7 T7 h
  6.     "ARMFLY_STM32H743_SPI_W25Q64", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */4 B! n+ e& ^2 Z7 N5 m' u: K
  7.     NOR_FLASH,                      /* 设备类型 */3 Y6 |7 j, }; G  T
  8.     0xC0000000,                     /* Flash起始地址 */
    4 }) n8 N; l7 f+ T' ^  i( }
  9.     8 * 1024 * 1024,                /* Flash大小,8MB */
      d. s% b, H& R7 D, u% O1 \
  10.     4096,                           /* 编程页大小 */
    $ M. p  l. _3 U- P
  11.     0xFF,                           /* 擦除后的数值 */
    2 s/ s" l, R4 ]+ q& u7 k0 H
  12.     2048 , 4 * 1024,                /* 块个数和块大小 */
    2 G+ T+ h2 j0 _: r* U0 {9 b; f9 v
  13.     0x00000000, 0x00000000,
    6 X" P# _5 m! A( Z( g
  14. };
复制代码
; 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
51f220556177a36712977b8d140b627f.png
, 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
  1. /*& k+ y& F( l  u3 {$ D* m- e
  2. *********************************************************************************************************6 J6 J1 U% w0 {3 T' ]
  3. *    函 数 名: Init
    , [: e4 O( p+ m, z) q8 w9 V
  4. *    功能说明: Flash编程初始化% _+ {9 \6 [$ L" g; p  m
  5. *    形    参: 无( V4 u$ p0 ?2 Z8 `( E" r8 S* Q+ B
  6. *    返 回 值: 0 表示失败, 1表示成功- ?6 n6 t9 u3 i- E2 \5 W% n
  7. *********************************************************************************************************
    ; o( P  J: c, J+ _# v& j
  8. */
    & E5 U& [! _: Q5 J  R6 l
  9. int Init(void), f* b. I4 s! x$ ]8 [
  10. {   
    & H. N0 ?2 ~9 \) O6 t  X0 @
  11.     int result = 0;4 n, V1 T8 x6 M. o9 d

  12. 5 ?& M: U* J/ v' j: b$ o
  13.     /* 系统初始化 */
    ) k$ `) i6 ^* x" G; a
  14.     SystemInit();
      k) e3 k9 s- @. a  t
  15. ; |: ^  I& d( G; O( q
  16.     /* 时钟初始化 */9 t/ U8 S9 |( T+ i. E+ O; H" p
  17.     result = SystemClock_Config();7 a" Q; ?0 |$ K& r! E& O8 O
  18.     if (result == 1)
    / q  A+ H- x8 f' X5 a
  19.     {
    - c# }4 Y3 v3 S8 h
  20.         return 0;! W  j- Z" F# F5 P; s/ M- m2 y
  21.     }
      m' h- i: q7 q& Q

  22. " b9 m5 Z4 O9 w0 Z# M1 F! d
  23.     /* SPI Flash初始化 */5 A% A1 a# M5 ]& u' U7 |/ D
  24.     bsp_InitSPIBus();
    5 G* R7 e, {- G0 C* W5 D
  25.     bsp_InitSFlash();
    3 c# a1 n) }& n7 p' {4 I

  26. 8 K& j0 ~8 h* S3 i
  27.     return 1;# C! X: Y/ w; Z7 T. v
  28. }
复制代码
: 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
  1. /*: \( e1 W) c9 X% ~- H' s+ d2 Y2 _
  2. *********************************************************************************************************% a  v( l7 Y! ~, K
  3. *    函 数 名: MassErase* f/ x) H6 @# F% K* R
  4. *    功能说明: 整个芯片擦除; P5 m* B' N2 r3 u5 Y, t" @
  5. *    形    参: 无8 T3 b$ e+ P1 ]' L
  6. *    返 回 值: 1 表示成功,0表示失败
    : k+ ^5 `: @6 v+ r: Z- t
  7. *********************************************************************************************************. Q' I. f: I6 \- X( s" p  q
  8. */
    2 x" C3 [2 o6 U& _" v0 i
  9. int MassErase(void)5 `5 w; x8 N9 h. Z+ I8 q$ M, t
  10. {
    ) Y& T4 C7 ?0 G
  11.     sf_EraseChip();
    3 N' v6 Q% E# g$ L0 B
  12. 3 [5 S) p% h. H2 e& g, ?! `
  13.     return 1;   
    6 A+ k) H- Q, d! G  F
  14. }
    2 B4 c$ v; `% O
  15.   扇区擦除函数SectorErase
    / C. \+ C& {: W4 U, F
  16. /*$ q5 W1 T' S2 u! y8 X7 O2 G: T
  17. *********************************************************************************************************
    * R- I0 y9 F' K0 P
  18. *    函 数 名: SectorErase& o* o, u: a5 E
  19. *    功能说明: EraseStartAddress 擦除起始地址
    . Z& S* \( k. }  |% S
  20. *             EraseEndAddress   擦除结束地址" O- g3 G5 `7 o6 U
  21. *    形    参: adr 擦除地址- V+ v1 q7 T1 K% h- J. o
  22. *    返 回 值: 1 表示成功,0表示失败
    " n) a9 F1 F' R" H4 P9 G
  23. *********************************************************************************************************
    1 D( W' U" R- h
  24. */; Q- l# C2 V1 ~0 V& x
  25. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)" F* E+ v; n8 L% s; ]
  26. {
    & I& G& ]9 N) b0 \( _9 M
  27.     uint32_t BlockAddr;/ V% W/ h6 ~1 J' W/ R# b: [# j

  28. . X8 |) ~4 j0 x! X4 x
  29.     EraseStartAddress -= SPI_FLASH_MEM_ADDR;
    7 ]9 Z6 N. i6 t# T: O0 f
  30.     EraseEndAddress -= SPI_FLASH_MEM_ADDR;, u% l- R: Q9 b& M% X4 ]; Q" e
  31.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x1000; /* 4KB首地址 */
    3 u# k. o9 I$ H( F2 a3 H( H, p
  32. + C6 b5 r3 p+ G+ y. |9 f3 E8 T) p+ e
  33.     while (EraseEndAddress >= EraseStartAddress)9 v' o, ]. J' r3 {/ F
  34.     {% u" N' Y* R4 h' q. L! `7 p) ^
  35.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;
    , e" a) O9 R( n0 i9 U
  36. 3 v3 A! }0 W3 G5 A" K" c
  37.         sf_EraseSector(BlockAddr);    7 a% u) R. X% r$ ^, R7 Q, t
  38. , q3 g: A; L# i+ l2 q3 U
  39.         EraseStartAddress += 0x1000;
    3 T* c, U3 [- b( z
  40.     }
    & |8 q0 ]; N& k9 [
  41. * N5 s& P0 g6 I5 L$ K3 x/ W
  42.     return 1;
    ; B) e+ z5 M) T" i4 T; A
  43. }
复制代码
+ 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
  1. /*
    # s' j6 \1 |* t
  2. *********************************************************************************************************
    . o% l2 I. d  r2 `- D2 o
  3. *    函 数 名: Write
    1 Z8 x, r% T9 ]
  4. *    功能说明: 写数据到Device2 ]& B$ k! M* I0 E
  5. *    形    参: Address 写入地址
    $ V9 K; @/ ~6 q% K# X
  6. *             Size   写入大小,单位字节) h2 `6 j6 I+ n) k) R7 `
  7. *             buffer 要写入的数据地址
    1 [! Z" f: e( x$ {  b1 ]" ]( E
  8. *    返 回 值: 1 表示成功,0表示失败! X6 g9 y$ u$ S8 h" Q
  9. *********************************************************************************************************
    % n+ I7 X: n, J. W* X9 }9 [+ u! z
  10. */
    - w8 t* I# Q1 ^: ?, o! ]4 D
  11. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer): U: Z$ X2 h( v; W( L; t5 z
  12. {  
    / L5 i6 E- b' i* F5 k( w# k
  13.     Address -= SPI_FLASH_MEM_ADDR;
    # ^- T! y+ p6 Q: F: ~
  14. & d0 G4 Y. U5 `4 g8 c* \/ [
  15.     sf_WriteBuffer(buffer, Address, Size);
    - i) F+ l8 x& c) P1 @6 }

  16. ( _1 {5 z: ]0 Y9 E
  17.     return 1;
    . M: l. c6 ~. b, E: |
  18. }
复制代码

" 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
  1. /*
    2 x( d% @8 X  L" W7 D
  2. *********************************************************************************************************
    1 b% q+ r' m& o. V! \
  3. *    函 数 名: Read) T4 C0 h8 k1 O9 R' z  i- ^; _
  4. *    功能说明: 从SPI Flash读取数据7 {- U/ \# [8 c
  5. *    形    参: Address 读取地址
    , V. ?9 n5 R2 m! y% O# x
  6. *             Size   读取大小,单位字节
    $ m" z1 E) [9 h, e
  7. *             buffer 读取存放的数据缓冲
    9 u. x0 F4 k  ?$ ]4 @
  8. *    返 回 值: 1 表示成功,0表示失败
    1 X- V* z. N' b" ?1 A$ t
  9. *********************************************************************************************************) \% m: i* Y, l
  10. */8 w7 Q) ?) t3 u5 ]; P- u9 r
  11. int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer)7 {0 o$ x' U) e; [5 U
  12. {
    . k' Q" w2 n, H
  13.     Address -= SPI_FLASH_MEM_ADDR;$ H8 X4 _1 o5 u4 K  T/ A& Y
  14.     sf_ReadBuffer(Buffer, Address, Size);
    , p; z1 ^" r+ e% d. ?
  15. 4 T& R. I; i$ O' ~  x
  16.     return 1;$ W) R8 q( y( Y( D9 D
  17. }
复制代码

, 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
  1. /*
    + ], l3 t# Z6 P% @2 R0 b8 X7 A
  2. *********************************************************************************************************) `; B* O7 @5 o3 S- O: F
  3. *                                时钟,引脚,DMA,中断等宏定义0 j* O: |! n: _: ]% K2 X
  4. *********************************************************************************************************
    % G; {, ?/ b9 B" e3 s7 B
  5. */( h8 Y' z' Y: W  A) S& F3 e
  6. #define SPIx                            SPI1
    7 \- [" E  `' y/ I+ \6 j; X0 ?' w1 L
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()
    ' D2 R' `6 h6 `) S' \  Y
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()
    7 y, _0 t" H3 ^2 p4 D
  9. 8 h/ z* o1 J5 t! j8 V( G0 T+ J
  10. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()- p* d9 L! k( y+ X
  11. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()
    ; I& L' ?" Q) Z9 o* `/ @
  12. 1 D1 g& a+ ]- a# q/ U) x
  13. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()8 D6 m7 H- s# Q# @, L+ H
  14. #define SPIx_SCK_GPIO                    GPIOB
    . ~2 A* T9 P0 e- G& |
  15. #define SPIx_SCK_PIN                    GPIO_PIN_38 G% Q" A: F: g0 G1 _3 F! q
  16. #define SPIx_SCK_AF                        GPIO_AF5_SPI1; F" K$ s3 S  p! U, u& U* Q

  17. 8 ~6 I) s- z  b# A( h; \: J) t4 R) N
  18. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()3 u* j7 d+ n1 ~8 f2 H
  19. #define SPIx_MISO_GPIO                    GPIOB
    " g, p: F. n- d7 t/ l- T! K
  20. #define SPIx_MISO_PIN                     GPIO_PIN_4
    ; W3 w2 e2 I4 @' @" p- F4 ?) M
  21. #define SPIx_MISO_AF                    GPIO_AF5_SPI1
    ' g9 T" M" g2 g! z

  22. : s5 n* e+ j" J) a" P0 j
  23. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    " I, W: u$ m& m
  24. #define SPIx_MOSI_GPIO                    GPIOB5 [. R+ S* d" p( Z
  25. #define SPIx_MOSI_PIN                     GPIO_PIN_5' A. O/ T4 ?8 M# o2 q" M
  26. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1
    % s; }! ~+ x1 W/ F8 b) ~
  27. 硬件设置了之后,剩下就是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
  1. /* 串行Flash的片选GPIO端口, PD13  */
    ! M: {8 u0 P, z- E4 r  z0 e! ~# j* h
  2. #define SF_CS_CLK_ENABLE()             __HAL_RCC_GPIOD_CLK_ENABLE()" [% t5 R# p1 ~  m
  3. #define SF_CS_GPIO                    GPIOD  [. c: b6 `4 R5 }
  4. #define SF_CS_PIN                    GPIO_PIN_13
    : i( p1 m; C0 U$ h

  5.   \* x& h" m$ n( u* |2 q
  6. #define SF_CS_0()                    SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U)
    5 ]' s/ Q9 N* H' S. m
  7. #define SF_CS_1()                    SF_CS_GPIO->BSRR = SF_CS_PIN
    & G, F. w: z  h/ Y
  8. " m7 w: d# w) ~! ]
  9. #define CMD_AAI       0xAD      /* AAI 连续编程指令(FOR SST25VF016B) */9 {$ K7 f4 Z# r  `2 v
  10. #define CMD_DISWR      0x04        /* 禁止写, 退出AAI状态 */% ~2 U: i2 W) ?7 \! `+ T7 E
  11. #define CMD_EWRSR      0x50        /* 允许写状态寄存器的命令 */
    # @. @" Q& B- `( I
  12. #define CMD_WRSR      0x01      /* 写状态寄存器命令 */
    + {$ ^( q/ [' d' u
  13. #define CMD_WREN      0x06        /* 写使能命令 */
      f1 X0 y2 ~) C8 ?5 W" h9 v. d
  14. #define CMD_READ      0x03      /* 读数据区命令 */
    % \' E& e! e- L: @- q3 p: f
  15. #define CMD_RDSR      0x05        /* 读状态寄存器命令 */9 x9 [# X" o2 g, I5 I# a
  16. #define CMD_RDID      0x9F        /* 读器件ID命令 */2 Y0 ]# Q  a5 x0 k* _/ W' A3 w9 J7 G
  17. #define CMD_SE        0x20        /* 擦除扇区命令 */
    3 [* o& v3 m2 S) I
  18. #define CMD_BE        0xC7        /* 批量擦除命令 */& ^1 X2 n1 O$ s! g2 T9 T
  19. #define DUMMY_BYTE    0xA5        /* 哑命令,可以为任意值,用于读操作 */% `) D1 z7 Z& U
  20. 5 r) O; E' z$ V' Q
  21. #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
5ec6d402a7646232a55f4834cd00ac0c.png

) 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
2609073778af5845a869911ab36624f4.png
% 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
27a88335ceb606bd021c3ebc65d88e21.png

) [. 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
d4e256633eafdbf4bcd8cce6e3b5909f.png

. 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
d4fa28f64be2de98e04f3188eb2fe09e.png

- 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* k
842518a7cc1dc569598d372132b2ee58.png
1 @( 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
68a92f267829ca77f125db982d6c0479.png
& 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+ ^
566d2fba421c710da70e5656c6934466.png
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
6108d1ff7e349f2aa268ee31804c57ca.png
+ @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( ^
收藏 评论0 发布时间:2021-11-6 23:35

举报

0个回答

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版