82.1 初学者重要提示
8 D6 {0 j4 e' E1 ^0 }' _ 操作本章配套例子前务必先将QSPI Flash的下载算法放到MDK安装目录。
9 G5 y+ V8 y9 n. X) r6 _, e8 F QSPI Flash执行程序的BOOT和APP作用:3 I) g8 L2 Z: x4 G$ `7 u6 W
BOOT程序需要下载到内部Flash。主要用于跳转到外部QSPI Flash执行程序。那么问题来了,我们可不可以不使用BOOT,上电就直接执行QSPI Flash的程序?不行,因为QSPI Flash不像内部Flash,无需初始化,上电就可以使用,而QSPI Flash不行。( m3 X! F- _0 W2 T& W/ H- Y+ _# w
APP应用程序要下载到QSPI Flash里面。APP应用程序可以在QSPI Flash里面执行主要是因为W25Q256支持XIP(Execution In Place),并且STM32H7的QSPI Flash支持内存映射。; }# ]$ {: W7 s8 N) R4 R! H8 w
82.2 下载算法存放位置* s& Y4 s5 j' H6 W2 B- E3 Y
编译例子:V7-060_QSPI Flash的MDK下载算法制作,生成的算法文件位于此路径下:
$ o0 `# j" X& I* K/ p) J3 ?! U/ b' b: r. e* d. |
9 ~) _) W" h# Z$ a6 a
& @: v, Q' ^& F$ E" S; `生成算法文件后,需要大家将其存到到MDK安装目录,有两个位置可以存放,任选其一,推荐第2种:
/ M2 S3 R+ S$ p9 ~' [
! L: ]% I' |9 J9 n/ E 第1种:存放到MDK的STM32H7软包安装目录里面:\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软包版本不同,数值2.6.0不同)。; U3 |3 S" p) D* q/ ^ h
第2种:MDK的安装目录 \ARM\Flash里面。
- n( N, i0 m# ~+ Y1 l
N8 m. m0 h# a& c6 v9 c* W; W1 b" o7 C+ y, F8 z
! V1 \4 e- J3 Q4 {' X& g6 g3 b6 R5 N
82.3 QSPI Flash的Bootloader说明& C4 q$ K. D+ O) M" z1 E; V" Q
Bootloader的实现比较简单,需要大家将其下载到内部Flash。程序实现上主要注意以下两点即可。
0 y* ?# {5 q. n6 P
% P" x6 u/ G5 c0 W3 _( S82.3.1 初始化QSPI Flash并设置内存映射模式
1 F9 \3 {9 I0 L9 M% ]* ?; F在bsp.c文件中初始QSPI Flash并设置为内存映射模式。9 M1 B) w3 v) i: R* o, v9 S
- J2 }' `' t5 c) z& L: i1 v4 j- /*
; Y; N5 S. l7 `1 C - *********************************************************************************************************/ W% v1 E7 e. `+ `/ H- ~
- * 函 数 名: bsp_Init+ w+ ~6 h. q' o# e( r
- * 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
p' C. ~. ^, T# }8 P6 f - * 形 参:无
, X8 U2 t1 m3 B m - * 返 回 值: 无
5 B$ z1 M6 x- v* J0 ` - *********************************************************************************************************) O, w6 |" k6 q, S% i
- */
8 {/ Y3 I% b/ @( d& p2 ~! Z - void bsp_Init(void); E" h/ h9 u3 b. y/ ^2 f5 O2 d3 y
- {6 r+ K' t3 C, @0 u$ h8 }' D) [1 P8 f, t
- /* 配置MPU */
7 U: u) j, O+ S' C - MPU_Config();
9 H W q, O) x0 ` C9 |. I5 }
3 c9 N3 z1 B9 l9 o1 E7 B- /* 使能L1 Cache */
: G7 C( b, v2 X: L" [ - CPU_CACHE_Enable();
. h* x1 |) N; z) J$ O0 `6 w - + g0 E$ M7 S0 n/ k3 f
- /*
+ C- Q# b2 i* N+ c - STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:/ ]8 \$ V- b6 {9 q
- - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。' L6 F2 b7 [4 J7 O' g4 i7 D
- - 设置NVIV优先级分组为4。
, k+ b3 i4 |# @# K4 b+ o - */
- D# w& u# K$ F4 X9 O9 M - HAL_Init();
$ Q) R5 z; N M- N - ; f, c7 h# w- U% F, y
- /*
4 R8 k# Q& u, K5 `8 @6 } - 配置系统时钟到400MHz8 b; L; |& b4 }
- - 切换使用HSE。: G- W& @* L) ?' c7 G8 y# Z) K5 R
- - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
; B% Y* a" e9 C' Y - */9 e. l) M5 S% h" ?
- SystemClock_Config();
; X+ f/ W8 {8 }9 G2 ]- O- B/ I* m5 q4 Z - ; Q* [: u& T" O
- /* , S' d, H, R1 g; a* Z9 i0 z
- Event Recorder:7 c. b8 ]6 N2 ~' i. [
- - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。& V% \/ z0 I( [% _
- - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章
* v' Q" z+ ]2 l# o - */
$ I9 e! {7 y0 o/ O4 k - #if Enable_EventRecorder == 1
3 t. V: t+ V' g+ s: j - /* 初始化EventRecorder并开启 */( P3 k h- z9 `; L; X
- EventRecorderInitialize(EventRecordAll, 1U);7 @5 ]7 {, T: w5 }
- EventRecorderStart();% a0 f/ a2 j) U2 u
- #endif
4 u! t N- Y3 v. ^# R8 n" Q - % I% A W% [" k4 J
- /* 针对不同的应用程序,添加需要的底层驱动模块初始化函数 */0 s+ V4 ^2 g! E0 w; `
- bsp_InitQSPI_W25Q256(); /* 配置SPI总线 */ ( q4 @0 U- u+ O4 M# Z+ y5 a o b- w
- QSPI_MemoryMapped();, F4 Z5 p. E1 I' E
- }
复制代码
! o4 z* O9 e" n% `+ D3 d82.3.2 程序跳转的实现
8 ~/ l. F0 ^- D* W, H从内部Flash跳转到外部QSPI Flash的实现代码如下
' D2 F9 \2 U6 [* h' i, g' g# K
2 L7 H( o; i3 [6 v- 1. /*
+ Y- x+ q, m7 E, G - 2. ******************************************************************************************************: K& n% X! `$ V' b! ]6 x% k
- 3. * 函 数 名: JumpToApp: p- l1 p9 J7 P4 O- Z
- 4. * 功能说明: 跳转到应用JumpToApp
5 r" ]. u" d) Z, ~- ] - 5. * 形 参: 无% }7 c, [6 J! T$ t% M, z
- 6. * 返 回 值: 无: ?2 U' Z# H' z" d" H2 I) ]
- 7. ******************************************************************************************************
& l0 l4 U/ x5 o - 8. */6 d9 Y' r- l1 |) L4 K
- 9. static void JumpToApp(void)
; E$ E5 f2 L" I; n( A0 u4 B - 10. {
$ V! a8 ~7 \8 x7 G3 a" F; n - 11. uint32_t i=0;
" r9 M! K3 k0 A4 e - 12. void (*AppJump)(void); /* 声明一个函数指针 */
6 U3 u; `2 M8 j) @4 a; X - 13. __IO uint32_t AppAddr = 0x90000000; /* APP 地址 */. v$ J4 b6 @% ~& C9 S/ ?
- 14.
B* h) h! b9 M' r k: J* x - 15. 5 R: t6 Y, f, k" D7 _* o* f
- 16. /* 关闭全局中断 *// ]2 n t+ d* s5 G9 D; \0 w
- 17. DISABLE_INT(); - q2 g6 {: s/ E0 `; R
- 18.
$ |- m$ v9 F) B" y6 H; e - 19. /* 设置所有时钟到默认状态,使用HSI时钟 */
6 H( }2 c L( G3 D4 B - 20. HAL_RCC_DeInit();
* ?- h0 C6 e: x" M$ `$ E( Z - 21. ( }0 q( R/ ?4 D: @) ^% K0 g: x
- 22. /* 关闭滴答定时器,复位到默认值 */
% i) D5 S/ N+ U$ } - 23. SysTick->CTRL = 0;
# {8 k' U: O# N, C - 24. SysTick->LOAD = 0;
, l' W9 i$ [" S5 i( ` - 25. SysTick->VAL = 0;1 L- ?& M$ ?" \
- 26.
5 x ~ z' z& m( \ - 27. /* 关闭所有中断,清除所有中断挂起标志 */9 D7 I& p+ i$ T
- 28. for (i = 0; i < 8; i++)
}0 s7 ~8 C. A& I3 m" O - 29. {
- z, }% X. {: _1 y) S( f( f - 30. NVIC->ICER<i>=</i>0xFFFFFFFF;. Y( C& |+ h4 A( ^
- 31. NVIC->ICPR=0xFFFFFFFF;
* t4 {7 Z* h2 b% d ?8 _$ w - 32. } ; O/ t' ~' k# c, ?3 m6 L3 f- N7 @
- 33. $ t- U8 m0 F( R4 A
- 34. /* 使能全局中断 */
$ S; W5 D5 t0 Y, b8 H3 f5 d - 35. ENABLE_INT();
) e& V a$ u6 [. t - 36. : W$ `3 W; j2 [- x
- 37. /* 跳转到应用程序,首地址是MSP,地址+4是复位中断服务程序地址 */
+ P# \" c# C2 B# E. V% |5 {' B9 |: t( z - 38. AppJump = (void (*)(void)) (*((uint32_t *) (AppAddr + 4)));
. P* [% R: V4 K% d+ l# B& y! I - 39. , k( T& n, H/ d' Z
- 40. /* 设置主堆栈指针 */$ {' u% F0 S" G1 u+ w
- 41. __set_MSP(*(uint32_t *)AppAddr);
8 k! ~# s7 K. L* R - 42. 1 _9 E% Y, r& w% q2 |0 H
- 43. /* 在RTOS工程,这条语句很重要,设置为特权级模式,使用MSP指针 */. \+ F2 j; G9 S0 H
- 44. __set_CONTROL(0);
" n# E s; D1 R! ~3 q - 45. f8 K; x; d. [0 c g# _; F( K* B# t
- 46. /* 跳转到系统BootLoader */
1 r7 _2 o! e3 d' R+ u - 47. AppJump(); " b z4 c5 S- R/ o) K; R
- 48. - b9 V t$ V" I, @
- 49. /* 跳转成功的话,不会执行到这里,用户可以在这里添加代码 */, q0 }6 e3 v! ]/ U# p9 @
- 50. while (1)9 M$ \" q3 d6 |
- 51. {- ?+ f. I- l/ s4 t) q
- 52. , q+ J! [9 j' k5 L# [
- 53. }
5 r b" h0 q) G4 P, Y( L - 54. }
复制代码
! S6 i) _- ~$ A) g7 t9 Y# I; c这里把程序设计中的几个关键地方做个说明:4 w$ _2 l% `4 Y% e
t: w# E: W# T! t# v+ W' v& Q
第12行,声明一个函数指针。* k( _# z2 S2 T# J( L2 ^0 _7 I
第13行,QSPI Flash地址在0x90000000。
( h: W4 L2 Y. }+ k7 t 第20行,此函数比较省事,可以方便的设置H7所有时钟到复位值,内部时钟使用HSI。
" b0 m# m1 C9 d; z/ `4 s 第23到25行,设置滴答定时器到复位值。' z# F6 ~; Y: [ R
第28到32行,清除所有中断挂起标志并关闭中断,这里是直接通过一个for循环设置了NVIC所有配置位,共8组。
9 [/ O7 ]- D/ _8 K* m: j2 g
C5 |0 {2 W: D# R5 E
% M7 ?) E6 f5 L3 K. ]1 [8 i) \; a
第38行,将系统bootLoader的中断复位服务程序的入口地址赋给第12行声明的函数,用户执行这个函数时,就会直接跳转过去。/ i# w R1 C! T
第41行,设置主堆栈指针位置,即QSPI Flash应用程序首地址存储的就是栈地址。0 P% ?9 j# s5 f8 O& S; m' c/ u0 O( c
第44行,这个设置在RTOS应用程序中比较重要,因为基于Cortex-M内核的RTOS任务堆栈基本都是使用线程堆栈指针PSP。但系统bootLoader使用的是主堆栈指针MSP,所以务必要设置下,同时让M内核工作于特权级。此寄存器的作用如下:
+ f3 c; |. `* g) M. C: h5 E' ^9 E6 F/ F
: O# O( g0 G; R* c2 Y6 L5 f8 B/ \1 A h3 C
第47行,跳转到系统bootLoader。
: D" H; X9 w% |
) Y+ L9 J1 A: Y. ]82.4 QSPI Flash的APP应用程序说明
, C$ {& F) T3 f. C6 h! CAPP应用程序是由第53章配套例子:V7-033_LCD的汉字小字库和全字库制作 简单修改而来,主要修改如下两个地方:/ L a0 |+ ?) L) F
6 F! H5 t) E- ^) x8 A1 {
82.4.1 设置Flash地址
' b" ]& m; Q) K9 x7 N设置Flash地址为QSPI Flash首地址0x90000000:
- l) F* L2 H/ x& i. ] v
5 N+ D4 ^( W: e. B. Y" Z4 @2 Q2 ?3 {
; A7 l- I$ f5 D3 m8 ?7 F2 S. X3 {
82.4.2 设置中断向量表" m! G/ E3 Q4 y* P5 X# }: K
在main函数最开始就设置中断向量表到QSPI Flash首地址0x90000000,也可以按照本教程第28章的说明,将中断向量表设置到DTCM里面。
& G) E' T9 y6 Z) a2 s" p8 B" T* v4 O0 s
- int main(void)
) ?( `4 D2 o1 a! V/ U k! B - {) O' l' h( c/ g3 t
- uint16_t ucBright; /* 背光亮度(0-255) */* [) T- R8 m6 j6 ?! _4 c# w
- uint8_t ucKeyCode; /* 按键代码 */
/ Z# W1 T6 G, Y3 U - uint8_t ucStatus; /* 主程序状态字 *// A2 p6 o* v6 s8 T8 O9 a
- uint8_t fRefresh; /* 刷屏请求标志,1表示需要刷新 */4 n, n8 M3 ]5 g5 e3 f
* O8 H: I/ {' I Z! t/ H( D- SCB->VTOR = 0x90000000; /* 设置中断向量表地址 */ `2 w/ k- w/ i1 [4 |
- . r" G5 T9 ]' W& E% ~
- bsp_Init(); /* 硬件初始化 */" a8 i O0 a; `" `, m/ g. e
- PrintfLogo(); /* 打印例程名称和版本等信息 */
* v, v3 p' c1 I" C6 ~ - PrintfHelp(); /* 打印操作提示 */
) d& l7 q1 I; v& a
3 b3 K8 Z& l/ |' ~2 b% C9 E- 省略未写4 z4 l( L7 ]: @3 z1 d+ h7 i2 Q e
. g( \% D9 y# f& m1 b: `- }
复制代码 $ X" M7 B" C+ a" z( ]
82.5 QSPI Flash的APP应用程序调试下载配置# s- c% l- i; \
将下面两个地方配置后,就可以像使用内部Flash一样使用QSPI Flash进行调试了。& T, P$ M. Q+ |6 V) _9 d0 _
# e. i' ?$ u3 R& F& S; G82.5.1 下载配置
4 [! w: M8 c/ H! j- q. d, K6 N注意这里一定要够大,否则会提示算法文件无法加载:7 B' q, L" T! v( o# }2 q+ \- `- a
6 s5 U' ` i! g( @( D
9 M: d$ K; K0 }: o( W) ]- _9 q. ]$ w2 y1 j
我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。推荐使用AXI SRAM(地址0x24000000),因为这块RAM空间足够大。/ ^6 s" o: g: ^
- j3 D* m: |9 a5 v1 @/ j3 l" ` R
如果要下载程序到QSPI Flash里面,需要做如下配置:
! e: ]: p% U9 h; R p% [
6 s* ^% d( f6 m' ?$ k8 S1 m( D; s& e6 s @
/ W4 ~2 U, Z0 f+ }; }1 P7 ]
82.5.2 调试配置
4 w5 S4 a7 E0 b. J8 f# \" B+ [注意这里一定要够大,否则会提示算法文件无法加载:
+ S* L2 M' H" ^0 r7 Q2 H; s4 k$ ^
0 v4 z! ]) W% T% e& g0 o/ E7 a7 L5 i& W- N# _6 X+ b) A
& t' m9 h& z* J9 P5 n* K8 u我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。
1 t$ s) J# r. r) |5 R1 p P% D
: G' X) e7 b! Z如果要做调试下载,需要做如下配置:
- y) C" n/ `: J- G9 i+ r7 k; z h' G# N3 V( s# y! }; e
3 T9 N$ m) z, l- E1 Z% s @. H
( N& Y: Y5 m) H7 M7 G" g8 _
: n8 l% [; z( J, A) O2 c$ l% k3 m82.5.3 程序调试效果8 C0 W J9 t3 j5 [0 l) J/ S
APP应用程序调试效果如下:! N* a2 X6 Z2 P: ^- E! T- i2 @5 m
' d0 R i7 @- C3 Q& P0 a/ N
2 D8 Q% F9 y( j5 r, o1 h3 z& l7 X5 J' a1 q! _# f$ R7 d& N' e# S
82.6 实验例程说明( a" `# C: T% a1 c3 U
本章配套了两个例子:! o9 t2 v( z" I" C( E/ [
7 i% d: E" Z, T V7-062_QSPI Flash运行程序(Bootloader)。% K- F) |& Z! A5 H. n8 s3 R
V7-063_QSPI Flash运行程序(用户APP)。: f1 V. A8 j" k: T
/ A5 N* x# y" |3 \! C& w
$ Q+ K$ M O2 Q3 d% @. K. ~/ mBootloader例子需要大家先下载到内部Flash里面,然后按照本章第5小节的说明配置后,就可以像使用内部Flash一样调试下载QSPI Flash了。& R# i W+ S! }+ p& @8 x; I
% f( V9 m4 U" e* \, v, K: Q
82.7 总结
' Y6 e- e) V7 N. t4 @8 j% f- |& Z0 v本章节就为大家讲解这么,为了熟练掌握,建议大家是操作练**。, Q# ~- R6 W* o
# q# c6 t1 `0 x) R& y
5 b- N9 R d. }: B% h, g# m) `# t! b. ]% o7 I! F) Z0 N' z& ]1 y
|