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

【经验分享】STM32H7的QSPI总线应用之QSPI Flash执行程序(BOOT+APP方式)

[复制链接]
STMCU小助手 发布时间:2021-12-21 23:21
82.1 初学者重要提示+ s+ Z: A3 r& Z# t: c7 ]) M% i
  操作本章配套例子前务必先将QSPI Flash的下载算法放到MDK安装目录。2 P( [( K' c4 S: @; M$ @
  QSPI Flash执行程序的BOOT和APP作用:( j7 O; c5 M3 ?6 o1 F+ {. l9 s2 F
  BOOT程序需要下载到内部Flash。主要用于跳转到外部QSPI Flash执行程序。那么问题来了,我们可不可以不使用BOOT,上电就直接执行QSPI Flash的程序?不行,因为QSPI Flash不像内部Flash,无需初始化,上电就可以使用,而QSPI Flash不行。
3 _+ M8 u. ^6 h3 z  APP应用程序要下载到QSPI Flash里面。APP应用程序可以在QSPI Flash里面执行主要是因为W25Q256支持XIP(Execution In Place),并且STM32H7的QSPI Flash支持内存映射。
; d4 p0 M' s" V5 D3 L82.2 下载算法存放位置) X6 i/ b' G  k
编译例子:V7-060_QSPI Flash的MDK下载算法制作,生成的算法文件位于此路径下:! x5 Z: A4 U1 Q5 P! q5 {% Q

% @) K* c! g  q; U9 G5 h
618b073cf66c2944fcdb610aef0e67ea.png

+ L: H- i% J$ W, b6 ]+ A. M- C( {
生成算法文件后,需要大家将其存到到MDK安装目录,有两个位置可以存放,任选其一,推荐第2种:  y4 d3 p1 s' a5 E4 F- i4 P: f
: ^0 \: @# a/ ^
  第1种:存放到MDK的STM32H7软包安装目录里面:\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软包版本不同,数值2.6.0不同)。  t! ^' U8 D% y, k- [: G
  第2种:MDK的安装目录 \ARM\Flash里面。% G! X8 a! }+ d* n( z  N
; b/ m6 F% O. \7 q3 \  a
b192c51eb80b27959d7c06b80a142d6e.png
" u$ t: _3 D6 O  C8 Z2 Y

/ s1 @- X+ r: r6 t+ O  j1 x$ h82.3 QSPI Flash的Bootloader说明
7 L3 H$ {* U4 R7 I6 R& m
Bootloader的实现比较简单,需要大家将其下载到内部Flash。程序实现上主要注意以下两点即可。
; Z* l7 ?; G  O) X6 o- S4 [7 a: l
# M3 R$ ]6 s9 P% ?' `82.3.1 初始化QSPI Flash并设置内存映射模式
- e! R+ h0 j3 {3 l4 w( T: [: G. r在bsp.c文件中初始QSPI Flash并设置为内存映射模式。6 P. l2 n) a6 `+ }: I1 x8 r
  V# G0 [0 u7 E
  1. /*: v/ F4 s& }4 `; f9 d) j. T
  2. *********************************************************************************************************6 n, _" G" O) z. r% [
  3. *    函 数 名: bsp_Init
    ; M0 ]8 b6 X" M; \, V! F4 a3 P4 {
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
    4 `9 R+ M' y. F2 W; K. F
  5. *    形    参:无" \  Q# ^& {; O0 ]  p3 X, Q
  6. *    返 回 值: 无& w( {; p* x4 D; [) Y( S3 ^/ x
  7. *********************************************************************************************************9 g6 y( s2 n2 J, }" K# L8 X! R4 f# p
  8. */% W; |5 F1 x( d" U
  9. void bsp_Init(void)
    5 u' q3 s8 Z$ }" w
  10. {
    & ]. `- j6 N$ l
  11.     /* 配置MPU */
    - R% G4 X! C: w" d" L5 A" S: F
  12.     MPU_Config();0 B) h. G$ X, u3 v# X' \
  13. , ^( P9 Z- r! [+ G
  14.     /* 使能L1 Cache */- P2 v5 T! Q3 v6 p) f
  15.     CPU_CACHE_Enable();
    3 s" J+ M* Z8 I2 ~- }5 l

  16. 4 O7 L& e2 e! T9 _8 M' {: E& i* D
  17.     /* " Q& x! _% A  ]. w
  18.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
    * a: M2 X) a5 x, g$ V
  19.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。" E, e5 [* T$ X* x* X" G
  20.        - 设置NVIV优先级分组为4。
    9 m7 p: `( V5 k2 f& H
  21.      */' R4 A/ Z9 C' f4 S. [
  22.     HAL_Init();
    0 ?* y' p0 _+ |; `
  23. ) k' ^3 g) E: s) U+ K; G
  24.     /*
      k+ k0 K' v) D  M% a0 T
  25.        配置系统时钟到400MHz
    , e, v/ O( J% ~. _, P" E
  26.        - 切换使用HSE。
    & B) c: u5 M: D" H& M' F& W
  27.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
    % F$ n0 ?- X( g' f# B' s6 u
  28.     */2 m0 N) ?3 v0 D, g2 n7 g. A
  29.     SystemClock_Config();
    $ M* A' ^: b! K$ Q

  30. 8 y* ~- R) y$ {" `
  31.     /*
    5 N6 y+ P7 G, A7 W) b# X  ~/ W
  32.        Event Recorder:
    9 S" [  M( i+ W0 g' p
  33.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
    , r% U7 N6 R2 X+ q% F
  34.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章
    5 N; J/ K% v& r1 M/ q: ]9 _  Q( H; v% ]
  35.     */    ' B. `+ x4 T' o
  36. #if Enable_EventRecorder == 1  : V% H, _( l2 R
  37.     /* 初始化EventRecorder并开启 */6 l( B! z5 e/ R  S
  38.     EventRecorderInitialize(EventRecordAll, 1U);
    5 r. H, g' w# m6 y" B# X; |( \8 C
  39.     EventRecorderStart();" @8 ~( k5 K+ s& O8 h4 W! `
  40. #endif
    : h# p+ s4 F" w: a2 ]
  41. 6 i, ~) M, E( [2 J; b2 g
  42.     /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */; {' \# E0 a. N1 x2 G3 f5 L0 Z
  43.     bsp_InitQSPI_W25Q256();  /* 配置SPI总线 */   
    3 O4 g( ~- l4 D: ~, U! k1 g" a" N
  44.      QSPI_MemoryMapped();) l8 D6 W6 K4 A; w7 G! ^
  45. }
复制代码
9 e. E8 q* h# b8 q& j
82.3.2 程序跳转的实现
# u4 ~9 `+ s! w0 V9 K% m- L从内部Flash跳转到外部QSPI Flash的实现代码如下
9 S3 Q* L2 B8 q& j3 L( g/ a; h* p0 Y- p* f
  1. 1.    /*
    # u) g! J8 r+ P1 l9 V% s
  2. 2.    ******************************************************************************************************5 i/ Z3 O4 Z6 i) }& k5 A
  3. 3.    *    函 数 名: JumpToApp
    0 g" E, j- C9 Y" j* w
  4. 4.    *    功能说明: 跳转到应用JumpToApp5 Y5 ?- D3 G* {1 P+ f7 c
  5. 5.    *    形    参: 无% h% y7 f; F  r3 o
  6. 6.    *    返 回 值: 无
    / z4 C) R: @# z$ d! z, U: A
  7. 7.    ******************************************************************************************************
      h9 T( p: [1 o7 H' U* h5 f
  8. 8.    */
    8 C  _/ D8 ]6 j9 @
  9. 9.    static void JumpToApp(void)2 R: ~5 m& X- @: j3 U
  10. 10.    {
    % X- E7 W" C3 H1 |5 K
  11. 11.        uint32_t i=0;
    ! j! q9 b; m4 \% T. @# }
  12. 12.        void (*AppJump)(void);         /* 声明一个函数指针 */% F; F# i6 L) c* r# ?& `5 V  j3 R
  13. 13.        __IO uint32_t AppAddr = 0x90000000;  /* APP 地址 */' T+ F: y5 r- \( S  f! L
  14. 14.        
    * w! i8 d. i' z5 b  w) j& I
  15. 15.   
    2 B2 @% a" k7 }! ~7 J1 N
  16. 16.        /* 关闭全局中断 */
    7 s3 Z: o; I$ [  M" M# ~2 i2 O% Z
  17. 17.        DISABLE_INT();
    9 _8 \1 L  i" h( G
  18. 18.          n, H$ O5 ^' [1 [, z
  19. 19.        /* 设置所有时钟到默认状态,使用HSI时钟 *// h: t. L) z1 i1 M$ i: h
  20. 20.        HAL_RCC_DeInit();
    0 D- O; ?/ H$ ]
  21. 21.        
    4 C+ a7 t1 ?/ m5 t, z- U% c0 }
  22. 22.        /* 关闭滴答定时器,复位到默认值 */
    , l' F  e( E. |( a# r
  23. 23.        SysTick->CTRL = 0;
    9 O( M, s1 k6 t) f  U0 ?% B$ B
  24. 24.        SysTick->LOAD = 0;
    & z8 a. p2 k$ g- X  p1 f
  25. 25.        SysTick->VAL = 0;' ?8 M, W2 B8 m: ^
  26. 26.   
    , l5 {9 {$ @, i- Y2 ^% t
  27. 27.        /* 关闭所有中断,清除所有中断挂起标志 */! B; h7 y; b* Q0 J) k9 Q2 o
  28. 28.        for (i = 0; i < 8; i++)
    / {+ `/ A2 I' x0 x: L- Z8 ]' h
  29. 29.        {
    % i* c- b: f% {: t, q' R' y2 i
  30. 30.            NVIC->ICER<i>=</i>0xFFFFFFFF;
    ' E6 _* t, h2 G& j. I
  31. 31.            NVIC->ICPR=0xFFFFFFFF;7 f; `1 V  ~" m( p2 G
  32. 32.        }   
    & P- H; T  c3 E0 v
  33. 33.   
    % l% w) s# d) C& H5 K
  34. 34.        /* 使能全局中断 */
    . O' {8 A7 D% x4 @1 m- y! `
  35. 35.        ENABLE_INT();: g$ G  m2 c0 i
  36. 36.   
    1 {; o$ I1 n% o
  37. 37.        /* 跳转到应用程序,首地址是MSP,地址+4是复位中断服务程序地址 */
    ( J# g) S$ u' A, F8 O$ Q. e
  38. 38.        AppJump = (void (*)(void)) (*((uint32_t *) (AppAddr + 4)));
    # T& k7 a6 a7 h# V/ v- r" ]
  39. 39.   
    ! z1 l& z' x0 A6 ~9 _
  40. 40.        /* 设置主堆栈指针 */! c0 q1 W7 v" h
  41. 41.        __set_MSP(*(uint32_t *)AppAddr);! k$ i: O6 @9 r
  42. 42.        
    ! {: Z7 T( w$ {8 j8 s8 T& O
  43. 43.        /* 在RTOS工程,这条语句很重要,设置为特权级模式,使用MSP指针 */! q$ K; p  y% Y  w$ H6 o
  44. 44.        __set_CONTROL(0);6 Q% f* t4 Y) j2 E: q1 ]
  45. 45.   
    4 @/ C, y0 X, ~* y4 U
  46. 46.        /* 跳转到系统BootLoader */1 v9 P& @) t0 S% ]
  47. 47.        AppJump(); . N. v& Q; M# d/ j: k
  48. 48.    8 f+ g! d& L5 \4 L5 @! j
  49. 49.        /* 跳转成功的话,不会执行到这里,用户可以在这里添加代码 */' j3 q0 v  E6 E; m$ W
  50. 50.        while (1)
    6 }9 @* h# s5 U1 Z6 p
  51. 51.        {
    2 a* o  q& N1 W2 {! t
  52. 52.   
    ( _5 p3 I! ]/ K% \. ?. k
  53. 53.        }. e+ J. E2 d1 w" T; x/ g
  54. 54.    }
复制代码

6 [- f/ z5 j8 E3 c这里把程序设计中的几个关键地方做个说明:
$ T% e) b' [8 m0 Q$ O$ v9 r4 D4 w
" T# c* `6 i, t. Q% V  第12行,声明一个函数指针。
( h7 k8 n- D$ \) Z, d" D  第13行,QSPI Flash地址在0x90000000。* h0 ^/ M" f  k1 o& g: P
  第20行,此函数比较省事,可以方便的设置H7所有时钟到复位值,内部时钟使用HSI。
9 A0 o3 Z  h1 ~9 ^, _5 i% Z  第23到25行,设置滴答定时器到复位值。
' [3 i: M* k: {0 ?  第28到32行,清除所有中断挂起标志并关闭中断,这里是直接通过一个for循环设置了NVIC所有配置位,共8组。: g% P% t: u. ^: _2 P$ b+ l

( a$ o# g1 M& B  ]# ]  U& k1 d- ?$ v
bfe6b8c0ea19b535e2fb06c6618577f6.png

5 R* {2 [* V; q, L5 s3 ^- V9 n( n" `6 I  ~) w! l9 L# H. ^) v
  第38行,将系统bootLoader的中断复位服务程序的入口地址赋给第12行声明的函数,用户执行这个函数时,就会直接跳转过去。
( e; g9 A  L# v5 \- ^3 T  第41行,设置主堆栈指针位置,即QSPI Flash应用程序首地址存储的就是栈地址。; r5 R( \! r* A4 y% r; a. ~* A
  第44行,这个设置在RTOS应用程序中比较重要,因为基于Cortex-M内核的RTOS任务堆栈基本都是使用线程堆栈指针PSP。但系统bootLoader使用的是主堆栈指针MSP,所以务必要设置下,同时让M内核工作于特权级。此寄存器的作用如下:
4 f6 Z- W0 s& O9 X! c/ f
5 d8 J' j: @. i" f& w: Z/ X
b00b09a901596bcedb07c31790d8d3d9.png

. h- @) ]* P; }: k' s5 I+ ]
, t) w& _' S: f& Y, i  第47行,跳转到系统bootLoader。$ B( B1 e/ Q; n0 l1 v1 A5 B
- {* y! f$ ^) h
82.4 QSPI Flash的APP应用程序说明
6 H5 t, E' v9 V! W& wAPP应用程序是由第53章配套例子:V7-033_LCD的汉字小字库和全字库制作 简单修改而来,主要修改如下两个地方:
9 {+ J- ?; d9 `" L$ o$ m3 c5 s( \$ o3 I. m
82.4.1 设置Flash地址& T- n; M# @) {: z  x
设置Flash地址为QSPI Flash首地址0x90000000:
6 N% b' t1 L5 E, P) z+ ~* }; i2 p2 n( Q
75afb2342189857e8fa086fc0af08462.png

  w* L. C# C: O) n" l+ k7 p8 ]5 M5 v# U- `/ z8 A
82.4.2 设置中断向量表
+ s+ J8 p7 [  F9 n/ Z: W9 ]9 B在main函数最开始就设置中断向量表到QSPI Flash首地址0x90000000,也可以按照本教程第28章的说明,将中断向量表设置到DTCM里面。$ H4 `4 O$ h1 G3 q% _( R; o# f
5 @7 y$ i7 W/ L) u. z! X" Q+ S
  1. int main(void)! ?! p6 l9 y; W7 A
  2. {0 ]1 q2 p; K$ P" Y/ R
  3.     uint16_t ucBright;           /* 背光亮度(0-255) */& C8 f- o2 p3 @2 \/ z& {
  4.     uint8_t ucKeyCode;        /* 按键代码 */
    5 V" |7 R2 ?* w+ z+ |
  5.     uint8_t ucStatus;        /* 主程序状态字 */
    1 s/ R& n3 E6 s" w2 F
  6.     uint8_t fRefresh;        /* 刷屏请求标志,1表示需要刷新 */6 i8 y4 A! p. \4 e5 s
  7. + e' a- M$ J6 @: V7 |
  8.     SCB->VTOR = 0x90000000; /* 设置中断向量表地址 */6 W/ v* G' j3 D6 H5 ^" o  m
  9. & M+ |  H5 K* N
  10.     bsp_Init();    /* 硬件初始化 */
    : d+ w3 g; L2 G% W: a2 p, a6 O+ b$ T' V# _
  11.     PrintfLogo();    /* 打印例程名称和版本等信息 */
    . p/ h* z, }+ |& Z- S
  12.     PrintfHelp();    /* 打印操作提示 */" K: z$ P8 f+ s& _' I( x5 P  }1 x+ Z

  13. , t% h) U# B) N6 [
  14. 省略未写
    3 u- z1 |6 d0 O
  15.   ~  d& o! B" U0 y, F" L& C7 Q3 T
  16. }
复制代码
0 N* i& }: u- S! R
82.5 QSPI Flash的APP应用程序调试下载配置
3 ~& m' S! K& T2 h3 v3 |( W将下面两个地方配置后,就可以像使用内部Flash一样使用QSPI Flash进行调试了。/ v& U  x, t) \$ d. [& `

: h  P4 d+ K8 B: j7 ]82.5.1 下载配置. n: X/ M, q' Y( q
注意这里一定要够大,否则会提示算法文件无法加载:- c9 _+ q$ L  ?& f/ K% i4 l

" l( }* N! D! f
96d039e1657c72042027eb7e46ffc33a.png
2 D! A! e8 ^/ p) \4 _5 b7 p6 e( g

* l& s3 J) H+ x* r: V4 r) r我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。推荐使用AXI SRAM(地址0x24000000),因为这块RAM空间足够大。4 @& X' V" E& h
. l8 C) Y4 I# A2 K& Z- V' r# ], N
如果要下载程序到QSPI Flash里面,需要做如下配置:' {4 p5 h- z9 S8 ]* D
, p( q4 @+ Z9 M8 X. T
5c95b0f51dbecf9eac259ddcf6b3da55.png
: r% z9 h! q, d( F: f

$ @( V9 v+ d. L# k1 a& _0 `82.5.2 调试配置
6 q( H/ m5 [7 o- r: @- f! p注意这里一定要够大,否则会提示算法文件无法加载:# Y- |8 Q9 n+ Y2 z2 `

/ x/ l8 I, O' |
6b0758d3dd4db39b7189a03d59f0eca0.png
2 c3 T( \" I$ H
6 r9 M" n$ B  r' v
我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。
1 P  G$ H4 y% M' N) B2 I/ ^6 |6 L; p4 t5 v, p" P5 ~: y
如果要做调试下载,需要做如下配置:
9 M: w" m: P. H& a: I3 I, Y+ f# v$ p* w* M* F5 d* h2 [) H
927401ada0fef49a78ac8029e372a755.png

$ j' n' l2 l) q: m( @! l2 H2 V6 q' O1 N" A! d0 d
8 _. [$ ?4 h# }
82.5.3 程序调试效果: z, b% k7 X  ]5 e/ x
APP应用程序调试效果如下:4 c& _& y# q$ M" E+ n4 j: ~: ~1 n

* T, x* x( T& D& N0 H3 k, S
681a23ba44bd11addce0fa9aae797dbd.png
. X8 h4 @: X( v5 A/ P+ D2 m# _
# S0 D7 i# Q/ g3 C+ h. W3 e/ W. ~. N
82.6 实验例程说明

: M* W) |7 n) i) \/ p* }本章配套了两个例子:, `/ }. k: @# Y: D/ I

! {& ?% H( Z' W) _+ G8 t) b  V7-062_QSPI Flash运行程序(Bootloader)。8 ^- g; C5 \0 P" A8 R
  V7-063_QSPI Flash运行程序(用户APP)。
7 G" L* z6 W1 G3 x8 m8 h% P2 l3 @$ N  f' `$ j% m& z
* j: e1 ?/ ]) B+ P) `, F- l+ g
Bootloader例子需要大家先下载到内部Flash里面,然后按照本章第5小节的说明配置后,就可以像使用内部Flash一样调试下载QSPI Flash了。2 i+ a& ?0 c, x

  q6 w' |% z& E% X, j; o82.7 总结
7 Q0 ?8 v$ r0 g$ U% ?( @! o本章节就为大家讲解这么,为了熟练掌握,建议大家是操作练**。/ P9 M. u" S2 Y  r" p5 q0 o! C

% O. L4 p+ r% n6 C2 A; d) v% s$ |" J3 d4 r' L, q, ?6 h

8 r9 w3 \2 h& D2 l5 S" _6 R
赞 收藏 评论0 发布时间:2021-12-21 23:21

举报

0个回答

所属标签

相似技术帖

官网相关资源

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