根据ST的参考手册描述,h750的片内flash只有128kb,在实际的项目应用中,128kb的容量有点太小了,有时候光光移植好lwip+freertos以后,flash的容量就所剩无几了,所幸可以使用QSPI FLASH来存放程序代码,可以把程序的部分或者整个程序都存放在QSPI FLASH中运行。这里介绍两种不同的方法。
, ~- m Q: p9 w1 @- V
. b( T, K1 S3 S一、通过分散加载文件的方式
2 S9 [2 R! v4 j9 W) C {9 n
/ r: Y+ e+ {' u7 K% q% B4 g 本文中使用的是正点原子北极星板子的例程,跑马灯实验。例程比较简单,主要是看分散加载文件,在最后有一个LR_m_qspiflash加载域,这个就是将代码存放到QSPI FLASH的。分散加载文件的具体细节就不在这边讨论了。# N% O7 F. P# M* @) U ^
5 F3 J$ ~9 R- q( ?
当然,光光靠配置分散加载文件,是无法把程序下载到板子上的,因为有一部分RO是要下载到片外的flash的,因此这边还需要使用特殊的片外flash算法。在正点的资料有有提供这一算法。通过添加算法的方式,这时应该能够将程序下载到板子上了,但是,这个时候程序不一定能够跑起来,还有一个重要的步骤,那就是初始化QSPI,并设置成内存映射模式。正点的例程中是将初始化操作放在了时钟初始化函数中。至此,程序就能够正常运行了。# M7 E' R/ X/ ]" f' U2 d7 T& y
9 s f3 z% ?" S* }& w- #! armcc -E ! A8 H1 U7 d' K9 T; L
- //
4 R9 g. U) C \ - //STM32H750分散加载文件(.scf文件)$ ]6 L9 O0 f% a
- //ALIENTEK STM32开发板 % \4 Z' F$ L G6 Q# P
- //正点原子@ALIENTEK0 X% r/ ?) L$ F2 t1 `8 ^
- //技术论坛:<a href="http://www.openedv.com" target="_blank">www.openedv.com</a>+ D3 }# [5 K1 D m7 t
- //创建日期:2019/4/21
- X3 i7 p% i) X1 r5 N! j9 O9 |# Q - //版本:V1.0
! D/ M# H3 H$ S n4 l( Y, |% U6 @ - //版权所有,盗版必究。& n4 s, ^# h$ N, j Y
- //Copyright(C) 广州市星翼电子科技有限公司 2014-2024* L0 ?0 L- e9 s2 C7 \! P
- //All rights reserved
: |: A( C( V* [* L - //********************************************************************************3 }5 n7 u3 a3 @0 V$ u/ P; @6 m! ^9 f
- //修改说明! l# k L7 _" D0 U( m
- //无
8 X1 n/ L e- i+ C6 \& V% l - //
( f+ h. B l9 I2 i' W
2 q W/ p1 O4 S f/ \- # c2 f9 u8 d# O, J& W1 A+ F8 P
- #define m_stmflash_start 0X08000000 //m_stmflash(STM32内部FLASH)域起始地址9 c% u! C, A: E% u4 x
- #define m_stmflash_size 0X20000 //m_stmflash(STM32内部FLASH)大小,H750是128KB
$ ~" D1 ?5 t ~& t! q - ( N) w4 Y( L6 O: [1 q$ f
- #define m_qspiflash_start 0X90000000 //m_qspiflash(外扩QSPI FLASH)域起始地址
2 \' e$ W4 b5 E6 c" n3 N# `9 n - #define m_qspiflash_size 0X800000 //m_qspiflash(外扩QSPI FLASH)大小,W25Q64是8MB
2 t b d0 e3 z# C- e& \ - 1 h/ n2 {$ q# b8 D: G6 k9 o& p
- #define m_stmsram_start 0X24000000 //m_stmsram(STM32内部RAM)域起始地址,定义在D1,AXI SRAM
; t- Z9 i* l/ g; O8 O- k - #define m_stmsram_size 0X80000 //m_stmsram(STM32内部RAM)大小,AXI SRAM共512KB
7 B1 `1 g* X$ \# q6 {, a - * Q$ ~; ]) j2 B! o% O. V' G
, F$ Y! V! e) b) Z
- o0 w( g2 b0 F+ o/ e# P- LR_m_stmflash m_stmflash_start m_stmflash_size { //LR_m_stmflash加载域
5 Z- ]6 ^1 G& V/ f5 e3 P% e - ER_m_stmflash m_stmflash_start m_stmflash_size { //ER_m_stmfalsh运行域,起始地址为:m_stmflash_start,大小为:m_stmflash_size
5 F- |3 ]6 _( ~; l/ ]( Q% S - *.o (RESET, +First) //优先(+FIRST)将RESET(中断向量表)段放这个域,实际上就是把中断向量表拷贝到m_stmflash_start
8 n9 ^7 S3 J, E/ t0 t7 U - //RESET是一个段名,表示中断向量表(在.s文件定义);+FIRST表示时第一个要加载的.
& q3 B3 s! C% U0 F - * (InRoot$Sections) //将所有的库段(C/C++标准库)放在root region.如__main.o,__scatter*.o等2 c- V- E a! s7 Y' A
- * (Veneer$Code)
2 v# r2 a# d, H+ F3 Y" V$ I" u - libinit.o
! c. L# r, P8 c1 k/ I5 [4 f - libinit2.o% b" w* [/ I* g2 T7 o
- libshutdown.o: r0 o4 _% g2 h. _
- libshutdown2.o
6 z/ g) _+ w3 H. c a3 Y8 t - __rtentry.o8 E/ {3 ^0 V- A, h
- __rtentry2.o
[4 _; ^6 @2 P - __rtentry4.o2 Z' l y5 G5 }7 t) |
- rtexit.o; a P' x, o9 ~) W- R' U. C8 Y
- rtexit2.o
$ s- `% Y" ?: w3 B -
2 r8 i6 G+ ^6 z- e- ` - use_no_semi_2.o/ |1 v# Q' J( W- F
- heapauxi.o
! c- L, f% `# N: _ - use_no_semi.o
) V/ g2 H; i) m, c& H. D - sys_stackheap_outer.o
" W$ q* j+ C9 \. q6 _7 P, ]# a - exit.o
2 w6 h# h: _2 `8 j - libspace.o/ F1 E- s0 L) H9 T
- fpinit.o
- Q# X- g: J" Z) D7 _* u - lludivv7m.o" G- E v, P# P7 A0 v3 m
- startup_stm32h750xx.o4 q8 n4 I" ]% V3 F
- , P" L7 _3 x% D6 V
- rt_locale_intlibspace.o ) M# z( w. i+ }/ G0 y& `- o% t/ C
- lc_numeric_c.o 8 L' y0 |% n: M6 x
- lc_ctype_c.o3 [( {: X2 g$ ~, ^6 B) k+ B2 j
- 8 C _3 L N( t% q# e5 f
- startup_stm32h750xx.o7 `0 E9 ^# `& j$ K! l2 u# W2 w$ j" i
- system_stm32h7xx.o
3 n9 d3 u! U- i# j - stm32h7xx_hal.o* ]2 ~& s i, ]. e- T
- stm32h7xx_hal_cortex.o2 T" M/ R1 Y/ f6 g: ]
- stm32h7xx_hal_rcc.o
0 r$ m" ?( l. d; V; L - stm32h7xx_hal_gpio.o
% B$ z5 j2 c. S4 m" @& _ - stm32h7xx_hal_msp.o! |4 l4 d9 Y5 o7 q" ~# w+ v
- 6 F$ ?) K+ N9 S) K) |/ O
- main.o 9 ^) U) u8 O) w& \2 C3 _' A
- sys.o
$ ]8 s5 P$ s- I9 x% a* E3 _+ W - usart.o
5 P% R4 ?, f7 f h3 G! V( B- y. q- X - delay.o9 h9 b! F6 o5 R9 V( v
- }
4 n; W8 |" F# Y - RW_m_stmsram m_stmsram_start m_stmsram_size { //RW_m_stmsram运行域,起始地址为:m_stmsram_start,大小为:m_stmsram_size.; e/ w6 _& W$ n6 a r
- .ANY (+RW +ZI) //将所有用到的RAM都放在这个区域
* q4 U p5 d, V* ~6 M4 T4 | - }
% d; [) w" Z9 E" D - }! V, x4 R l6 x0 t
- / w; N5 F1 N7 {3 t" T k
- LR_m_qspiflash m_qspiflash_start m_qspiflash_size { //LR_m_qspiflash加载域5 H0 t9 d2 m, ?0 m- \5 r) M& g" r) L2 }
- ER_m_qspiflash m_qspiflash_start m_qspiflash_size { //ER_m_qspiflash加载域,起始地址为:m_qspiflash_start,大小为:m_qspiflash_size ' \( x7 X% l/ h) R# R% D1 t4 V- y
- .ANY (+RO) //将只读数据(+RO)放这个域,任意分配.相当于程序就是存放在这个域的.
$ R2 T+ ], d2 R' |; K; ~ - } 4 f+ Y! q3 d6 H. c4 T6 m* w( h2 u
- }
复制代码 - v% v A1 ~6 ~5 B$ }9 w
以上分散加载文件,由正点原子编写,为了方便大家使用,不用频繁修改.sct 文件,特意3 r( H) H" \- y- t9 V+ J
' s0 O2 Y- l# h6 c+ O! F
将.ANY ROM 区域放在外部 SPI FLASH,这样大家在新增.c 参与编译时,默认就是存放在外部- c- |1 d, F- x
* n7 x7 B* V6 y# i5 y( uSPI FLASH 的,这样使用起来就更方便。: R: _: i F8 a# t+ } j
% |( e1 g3 e! f# u8 c! T; y注意:5 p% p! }1 m6 j0 y, s. x
9 {7 j' ~/ W! N' }
1, 如果你新增的代码,对速度有要求,可以将其对应的.o 添加到内部 FLASH,即放在:% \/ {+ i& _" q n& [7 v$ @
3 q+ l0 ?. v: K3 }
ER_m_stmflash 运行域。7 k B" ~( |. {1 P( A4 F
, x- c( { q8 w4 N. O& ?1 M4 m
2, 如果添加新代码后,程序无法正常运行(通常表现为黑屏/不启动),可以尝试将新增+ H/ e# ^3 z2 k/ F$ t' x
, d+ H1 O2 Q7 e& l. c5 M$ X的.o 放到 ER_m_stmflash 运行域后(重新编译)再尝试。如果还不行,可以尝试将所0 p- @) y, E$ P& i/ O i
5 L) s+ e) Y8 g+ f. W/ Q5 l有代码都放到 ER_m_stmflash 运行域后再尝试。
2 t/ S& O, |2 K4 Y+ N) w, ]9 Z4 a
1 L9 d5 f. U/ V; L, B% w- F
- r3 w1 }7 O9 a! D r, \2 ^ b. S* o- i二、通过boot程序跳转到qspi flash执行用户程序8 j3 X% O8 @9 Y+ b3 G: d
: ? [9 ~5 h) G* H
第二种方式就是通过在片内flash中存放boot程序,然后通过boot程序跳转到用户程序,用户程序就放在qspi flash中。
; t. o' i* k. p4 `; u+ k
4 o5 y5 V$ r+ R5 ^; \! pboot程序主要的工作就是初始化qspi,并设置成内存映射模式;禁用中断,重新配置中断向量表地址,然后进行跳转。这里分散加载文件直接使用系统自动生成的就可以了。程序起始地址0x08000000。
5 V- \" @) m0 z- N$ H8 J+ K" K8 ^* [+ U9 I, U
用户程序需要做的操作就是把程序起始地址改成0x90000000,然后添加下载算法,因为整个程序都是存放在qspi flash上的。下载完成后,用户程序就可以通过boot程序跳转执行了。
0 H6 R8 i; R7 D1 e5 U# Z7 i9 |$ p$ d$ r/ }
* q* A8 Z3 d. B. V' H# X) t
: f2 l0 O: ~; i- [6 N# w" T9 t( R/ ~$ m& l; r5 O
- v* _" ?# ~1 I7 D注意,不管是第一种还是第二种方法,都是将qspi flash给设置成了内存映射模式,而此模式下qspi flash是只读的,所以这个时候就没办法用来作为存储器来存储数据了。不过网上也有人说内存映射模式下也是可以写数据的,至于真假,还没验证过。) u+ B0 ~1 C$ \! D1 @. `- i
1 k" j A2 s9 _. _6 T
9 P0 t! |* J, Q/ o |