根据ST的参考手册描述,h750的片内flash只有128kb,在实际的项目应用中,128kb的容量有点太小了,有时候光光移植好lwip+freertos以后,flash的容量就所剩无几了,所幸可以使用QSPI FLASH来存放程序代码,可以把程序的部分或者整个程序都存放在QSPI FLASH中运行。这里介绍两种不同的方法。
" D8 Q* [$ B$ ~7 K; |
* S. L5 f9 `: b2 R+ r- u5 l一、通过分散加载文件的方式
: a# c5 e, p8 a/ ?$ Q$ I
# M5 f0 K1 l8 C% r* i9 E 本文中使用的是正点原子北极星板子的例程,跑马灯实验。例程比较简单,主要是看分散加载文件,在最后有一个LR_m_qspiflash加载域,这个就是将代码存放到QSPI FLASH的。分散加载文件的具体细节就不在这边讨论了。
! B/ ^! k# h: `) v) F+ e0 V0 O$ n+ g# D: X0 x' C
当然,光光靠配置分散加载文件,是无法把程序下载到板子上的,因为有一部分RO是要下载到片外的flash的,因此这边还需要使用特殊的片外flash算法。在正点的资料有有提供这一算法。通过添加算法的方式,这时应该能够将程序下载到板子上了,但是,这个时候程序不一定能够跑起来,还有一个重要的步骤,那就是初始化QSPI,并设置成内存映射模式。正点的例程中是将初始化操作放在了时钟初始化函数中。至此,程序就能够正常运行了。
9 K- p Q" n1 g0 B& m$ R) C3 C( h; |# Q- o
- #! armcc -E
7 }' u9 n6 b0 W+ G$ v' { - //
- u+ U' N7 W: \ - //STM32H750分散加载文件(.scf文件)
; _* J# r$ U# C* f' [* q4 R1 B, f5 g - //ALIENTEK STM32开发板 " u. M9 c3 H1 G1 e
- //正点原子@ALIENTEK) `3 L1 A5 W* f: N- |
- //技术论坛:<a href="http://www.openedv.com" target="_blank">www.openedv.com</a>! a9 l3 i' l$ G; K9 f- x9 Y
- //创建日期:2019/4/21
4 A# J7 q) ]. p - //版本:V1.0
& K& ]7 N) h n- c' }. I - //版权所有,盗版必究。
, D! Z) H# e+ \( X1 d2 I; u$ \ - //Copyright(C) 广州市星翼电子科技有限公司 2014-2024) ]% o" [" I) a
- //All rights reserved, w0 J1 T: y! W. G2 Y
- //********************************************************************************
) C0 o6 u- C- ?9 k" K# D' @ - //修改说明( B. e9 e# e9 s8 i, ?" X
- //无
1 N# j1 y7 g: U* y - //6 } d, \4 _' B' h* _
; ~% Y: `3 b8 g- 2 N( D% h1 V% n
- #define m_stmflash_start 0X08000000 //m_stmflash(STM32内部FLASH)域起始地址
( @# U% l2 a/ n% S - #define m_stmflash_size 0X20000 //m_stmflash(STM32内部FLASH)大小,H750是128KB
+ `& u: q7 D8 E, o3 u
7 @1 m! K5 V. ?- o- #define m_qspiflash_start 0X90000000 //m_qspiflash(外扩QSPI FLASH)域起始地址4 e2 [8 D; f- F, X" T2 ^% F
- #define m_qspiflash_size 0X800000 //m_qspiflash(外扩QSPI FLASH)大小,W25Q64是8MB D2 L; r% L& f- i4 f1 x4 b
2 `* p k* I K% D' |- #define m_stmsram_start 0X24000000 //m_stmsram(STM32内部RAM)域起始地址,定义在D1,AXI SRAM4 F# X. }: v8 G! r9 m/ @' p+ R! W+ L, ^
- #define m_stmsram_size 0X80000 //m_stmsram(STM32内部RAM)大小,AXI SRAM共512KB
A& k/ S! c/ ?0 n# u - 7 g$ e. z" R' u! k4 P: C; S
{3 G' L& D- _3 l* j$ O: W- F r$ S$ i
9 Y6 e, t( B/ C$ `. l- LR_m_stmflash m_stmflash_start m_stmflash_size { //LR_m_stmflash加载域 o" I/ ^5 p& v
- ER_m_stmflash m_stmflash_start m_stmflash_size { //ER_m_stmfalsh运行域,起始地址为:m_stmflash_start,大小为:m_stmflash_size % p. v' L1 U- N9 \
- *.o (RESET, +First) //优先(+FIRST)将RESET(中断向量表)段放这个域,实际上就是把中断向量表拷贝到m_stmflash_start
$ ~- [. X* S3 R9 D8 X" s - //RESET是一个段名,表示中断向量表(在.s文件定义);+FIRST表示时第一个要加载的.
, i+ _' |: c6 M4 v3 \ - * (InRoot$Sections) //将所有的库段(C/C++标准库)放在root region.如__main.o,__scatter*.o等
1 v# r# k3 \. M- d7 s- w - * (Veneer$Code)
. v& c x0 r% J$ L | - libinit.o1 \" [* V, \; r
- libinit2.o" ?" G* N( J# F- J' Y
- libshutdown.o! y- s0 K1 n4 l: {, e
- libshutdown2.o
. q. f" T& Q! k$ q0 V - __rtentry.o
$ `) i! r7 r; U$ l7 N - __rtentry2.o* x+ ^) y- a2 q( R( s
- __rtentry4.o
. q( I9 T |/ E1 |( [ - rtexit.o
; p& C z- L3 h2 T2 d( U. B - rtexit2.o & z# Y6 B' b% k ^
-
# D; v/ q7 {- t h' m/ d - use_no_semi_2.o2 y6 G; ^' X' M1 K6 Z# j
- heapauxi.o
! R" ?' z% Q: S3 A3 e: K3 l - use_no_semi.o
- k0 Y+ w- _7 C - sys_stackheap_outer.o
2 X6 v- u9 ~! T& v. }# W - exit.o
8 G' }- P2 F6 j$ g$ b - libspace.o& }# I j; J% m* m
- fpinit.o
3 ?1 U! u4 R9 h- M) w$ D! O - lludivv7m.o
1 m. _ A$ u& a* J3 j) S3 \1 F - startup_stm32h750xx.o
7 K9 Q: k- C8 ^+ l* G* D3 Z" n -
/ J" [1 A/ z( [7 A" A6 n8 u - rt_locale_intlibspace.o 0 f1 A3 a0 c' Z1 w$ ?0 ?1 a$ ?
- lc_numeric_c.o
8 W" y" D" L5 t - lc_ctype_c.o/ X. l& I5 [* k! Q3 G
- $ \% V! h8 Y3 b* m
- startup_stm32h750xx.o/ X+ _! }( S" c5 j% Y( A
- system_stm32h7xx.o
8 c( L4 P2 |/ b - stm32h7xx_hal.o( w) @0 g b) I8 c9 C) |# \, S
- stm32h7xx_hal_cortex.o
0 q, {7 `) d. y: B) | - stm32h7xx_hal_rcc.o
7 B8 J7 n% I+ g. ?( V$ F - stm32h7xx_hal_gpio.o# C# P4 a( R- |" a- \; S7 Q
- stm32h7xx_hal_msp.o" x1 ^+ a6 E8 X& o
- & O2 L Y# S3 V3 A0 h3 m; K& k
- main.o 2 ^& D! p1 c9 u- \6 i7 V
- sys.o
' x5 s0 E# l4 i' Z. Q; b - usart.o4 {$ B+ u! W; R
- delay.o
1 u1 [* `. F& m% ^ o/ U' g - } 7 O' {( h7 a0 N' z# c
- RW_m_stmsram m_stmsram_start m_stmsram_size { //RW_m_stmsram运行域,起始地址为:m_stmsram_start,大小为:m_stmsram_size.
# d9 R( l8 d: T5 `8 ^$ W1 s& d: f* T - .ANY (+RW +ZI) //将所有用到的RAM都放在这个区域
. m% o6 ^' M/ J( r" y0 p5 O% \ - }( Z" B% |! E: I' ^! k E
- }
7 q6 l! ?2 @ E8 G* `& u7 G; P - 5 `* z3 V, G+ N
- LR_m_qspiflash m_qspiflash_start m_qspiflash_size { //LR_m_qspiflash加载域
) ]7 e& b( C% V$ Z: C: _( E; C1 j - ER_m_qspiflash m_qspiflash_start m_qspiflash_size { //ER_m_qspiflash加载域,起始地址为:m_qspiflash_start,大小为:m_qspiflash_size 8 G3 Z2 |0 u& j5 ^) n! G' i7 V$ b( R
- .ANY (+RO) //将只读数据(+RO)放这个域,任意分配.相当于程序就是存放在这个域的., E9 Q7 _& P& r. i7 R
- }
& M( d( M! E' a. ^# c& } - }
复制代码
: h/ g8 y6 P" @( B以上分散加载文件,由正点原子编写,为了方便大家使用,不用频繁修改.sct 文件,特意
/ b x! V& G+ f% X+ B, J5 e/ ]3 X5 z. f/ Y' H- b% n" v4 s
将.ANY ROM 区域放在外部 SPI FLASH,这样大家在新增.c 参与编译时,默认就是存放在外部
2 T( q0 {( x8 G3 Y5 X5 `( g) T1 Y/ I% _& O5 Y( ?
SPI FLASH 的,这样使用起来就更方便。
8 [5 \/ R# U0 J& ?" J* f4 U! m' [% U( y/ _- E
注意:. I6 `, E+ x/ U k* z' \* j" P0 D
2 G5 c- q5 t' p+ m, Q* {
1, 如果你新增的代码,对速度有要求,可以将其对应的.o 添加到内部 FLASH,即放在:' G3 u' k- U: a# H8 B5 C2 ^
) i; @8 h8 O# ~6 s, B; [3 ?* HER_m_stmflash 运行域。9 I' [9 Y# ]8 {4 [! }/ [
) j9 M1 P! w; L5 \$ U2, 如果添加新代码后,程序无法正常运行(通常表现为黑屏/不启动),可以尝试将新增 m& z: i9 }. D# ]
@1 i4 z1 G% [6 q( }/ }3 L的.o 放到 ER_m_stmflash 运行域后(重新编译)再尝试。如果还不行,可以尝试将所' a7 C8 |( H+ r( o
# O* {& Q. s% i, D/ J有代码都放到 ER_m_stmflash 运行域后再尝试。
" q+ l2 C4 O* }4 G; n7 E: I- \* Y2 t8 g
9 o2 ^: _3 v! o# K
& `/ X' J$ u3 d二、通过boot程序跳转到qspi flash执行用户程序8 `, F) m% k$ n. d* ~
4 N3 l, `* n" l 第二种方式就是通过在片内flash中存放boot程序,然后通过boot程序跳转到用户程序,用户程序就放在qspi flash中。
4 H7 ]/ T" m9 x1 k3 q" t" b2 N4 ^3 I; k; s+ ~
boot程序主要的工作就是初始化qspi,并设置成内存映射模式;禁用中断,重新配置中断向量表地址,然后进行跳转。这里分散加载文件直接使用系统自动生成的就可以了。程序起始地址0x08000000。
2 u0 u0 Q( U& r7 T& J) |! ]9 }( p8 K2 { T% M# d
用户程序需要做的操作就是把程序起始地址改成0x90000000,然后添加下载算法,因为整个程序都是存放在qspi flash上的。下载完成后,用户程序就可以通过boot程序跳转执行了。
# z: A* B2 K7 U/ s% \) I- ]: T9 Y3 l/ d( R
: t0 P5 h0 @/ M4 T" ~' Q( G9 J9 S2 A( x& C2 M: q H
0 q/ k+ P2 X0 t3 d4 K
+ C; j" y9 {! k- _5 d6 F! ]3 f
注意,不管是第一种还是第二种方法,都是将qspi flash给设置成了内存映射模式,而此模式下qspi flash是只读的,所以这个时候就没办法用来作为存储器来存储数据了。不过网上也有人说内存映射模式下也是可以写数据的,至于真假,还没验证过。+ @1 x% C5 l+ I: }% h, \9 f8 ^
/ J& G- Y- l( H" G5 v. h3 K
( p1 A& @- J' Q6 o- b( D |