根据ST的参考手册描述,h750的片内flash只有128kb,在实际的项目应用中,128kb的容量有点太小了,有时候光光移植好lwip+freertos以后,flash的容量就所剩无几了,所幸可以使用QSPI FLASH来存放程序代码,可以把程序的部分或者整个程序都存放在QSPI FLASH中运行。这里介绍两种不同的方法。
& j2 n' \9 q, Y/ `" G: A8 Z0 a/ E8 {1 ?* P: [ P
一、通过分散加载文件的方式* V" |; ]# _6 S( Z9 M
! a+ D3 _ [, n
本文中使用的是正点原子北极星板子的例程,跑马灯实验。例程比较简单,主要是看分散加载文件,在最后有一个LR_m_qspiflash加载域,这个就是将代码存放到QSPI FLASH的。分散加载文件的具体细节就不在这边讨论了。
2 R" B4 y- B( X. |1 X# t* D F' n) { M2 U5 {, z2 o
当然,光光靠配置分散加载文件,是无法把程序下载到板子上的,因为有一部分RO是要下载到片外的flash的,因此这边还需要使用特殊的片外flash算法。在正点的资料有有提供这一算法。通过添加算法的方式,这时应该能够将程序下载到板子上了,但是,这个时候程序不一定能够跑起来,还有一个重要的步骤,那就是初始化QSPI,并设置成内存映射模式。正点的例程中是将初始化操作放在了时钟初始化函数中。至此,程序就能够正常运行了。
5 w' W' O: y8 x- L/ y2 ]
- z4 X! n# ^ I8 @4 R& C& t1 ^- #! armcc -E . y g0 _- [) F! w
- // 4 ^/ ]6 Q) V6 [4 C1 h
- //STM32H750分散加载文件(.scf文件)
5 }6 ~( _" {& m/ g - //ALIENTEK STM32开发板
$ ?; `) @: ? N! L - //正点原子@ALIENTEK4 ?0 U1 C6 j ~' X
- //技术论坛:<a href="http://www.openedv.com" target="_blank">www.openedv.com</a>7 X" Z% ^) _& Z, A! j# m3 O2 P
- //创建日期:2019/4/21
6 g# e% N3 A; d - //版本:V1.0
$ v8 r( x9 [9 K: Y - //版权所有,盗版必究。
% D" u7 E1 U! u! q - //Copyright(C) 广州市星翼电子科技有限公司 2014-2024
+ E1 f7 t& G; @1 l( f% e8 n - //All rights reserved
6 s5 ~: ^2 O W5 | - //********************************************************************************9 M9 X7 g/ }) y$ W; j% `/ E; U) U- x
- //修改说明
' P- [% c3 A, X' Q& n - //无 }% G+ }* {* U
- //8 G" O( _3 K" x
- * J3 P; [1 E9 l$ Q3 H- t' U
- , V' P- r: S. N5 |: \. B
- #define m_stmflash_start 0X08000000 //m_stmflash(STM32内部FLASH)域起始地址
& j1 [5 u$ N( O+ l - #define m_stmflash_size 0X20000 //m_stmflash(STM32内部FLASH)大小,H750是128KB
P9 A5 ^% c; l; @# v* x8 L1 `
8 X8 o, t, D; a2 X K% p- #define m_qspiflash_start 0X90000000 //m_qspiflash(外扩QSPI FLASH)域起始地址; c, q9 \& ^3 _8 c0 N, U& x
- #define m_qspiflash_size 0X800000 //m_qspiflash(外扩QSPI FLASH)大小,W25Q64是8MB
) @* F8 j3 D z' X' G$ X3 w - 2 `2 p \, `, [: ~+ u1 P7 _
- #define m_stmsram_start 0X24000000 //m_stmsram(STM32内部RAM)域起始地址,定义在D1,AXI SRAM6 f# m) B# ]6 d4 t7 v; c! C/ y
- #define m_stmsram_size 0X80000 //m_stmsram(STM32内部RAM)大小,AXI SRAM共512KB
' r7 U5 a: S% d& M+ O - : X/ D- p1 d7 H4 ^
1 c+ C, {* H: n: C' L4 v
/ K8 v! b; \8 m; z) \- LR_m_stmflash m_stmflash_start m_stmflash_size { //LR_m_stmflash加载域
- s" v/ \( H& c6 Y; I. V7 m/ G+ @ - ER_m_stmflash m_stmflash_start m_stmflash_size { //ER_m_stmfalsh运行域,起始地址为:m_stmflash_start,大小为:m_stmflash_size
. p. c; ~( ?$ I5 i; W3 G - *.o (RESET, +First) //优先(+FIRST)将RESET(中断向量表)段放这个域,实际上就是把中断向量表拷贝到m_stmflash_start
8 a" W) k6 t- q# h - //RESET是一个段名,表示中断向量表(在.s文件定义);+FIRST表示时第一个要加载的.
; d( E/ g @5 V N/ ~5 d' u - * (InRoot$Sections) //将所有的库段(C/C++标准库)放在root region.如__main.o,__scatter*.o等
. e# |6 H0 Y4 z$ B& b - * (Veneer$Code)
2 z: R' c$ \) k I$ `+ l% \. Z - libinit.o4 z6 |1 g3 C5 \7 K- j' {3 C3 [9 e
- libinit2.o
! y* s G& b& J* @5 X# \6 _ - libshutdown.o. `7 f: D' k- N
- libshutdown2.o
J! ?: j( a/ k% z5 X6 x7 r - __rtentry.o
; R4 |' o% L1 _# y9 \) M- Z - __rtentry2.o+ |7 t& V4 @: j/ ^ i/ d% i
- __rtentry4.o) \8 t" b) s. `( t
- rtexit.o: z! R! k- a: h. V5 b! _. c
- rtexit2.o 4 W! c; q2 r. f2 W; m
- 9 u' N) B* {& p7 ?, Y1 Q8 e( _* `
- use_no_semi_2.o
/ e5 o0 y. y& O8 a - heapauxi.o3 {! q7 p' r( A- n
- use_no_semi.o
. I8 J' g& w# D - sys_stackheap_outer.o
2 H; ]" M2 c- N$ K* L4 N - exit.o
# [. k9 m) w& s" V9 s, d% x0 d: y - libspace.o; h2 Z; z p4 _ J% `% L" i' P" c* y
- fpinit.o7 M3 Q: ]+ a4 l7 ~% Z( l
- lludivv7m.o6 {* P$ F5 b( h! ^ }4 s/ y8 Y
- startup_stm32h750xx.o6 s9 s) h& o5 g( I
-
8 _% k) Q7 w0 m6 O& `7 e6 T% e - rt_locale_intlibspace.o 9 u3 f; `8 c7 Y. u4 {/ P
- lc_numeric_c.o 8 W0 f4 L; g4 G( m9 f& e- h
- lc_ctype_c.o
8 }; t: O2 ]9 ^0 M% {% H - ; b* y/ I i- Y! O2 {! }3 N
- startup_stm32h750xx.o, V8 x% ~' H+ {, {; o+ |6 d" K
- system_stm32h7xx.o' F8 ^, B! {1 C) [
- stm32h7xx_hal.o: ^/ j+ j9 C1 f& n
- stm32h7xx_hal_cortex.o4 v6 N/ ?; C3 j9 B! z) @
- stm32h7xx_hal_rcc.o2 @9 M: ` [. ~* @
- stm32h7xx_hal_gpio.o3 l( E* d4 l4 |2 p9 }8 {* `$ l
- stm32h7xx_hal_msp.o+ E! z3 M5 V; f4 w# u
-
4 x, Y E' A |, ^* U8 Q - main.o
: h% G' ] b6 [! s - sys.o : M6 g1 t- x! w1 M2 F, _
- usart.o5 w t% I+ G. ]- @. d) N5 s
- delay.o
9 p7 z3 h# @4 j' T& W6 q - } $ X8 o }+ h$ U3 R! y
- RW_m_stmsram m_stmsram_start m_stmsram_size { //RW_m_stmsram运行域,起始地址为:m_stmsram_start,大小为:m_stmsram_size.
& t; i, T2 d3 {3 r8 ? - .ANY (+RW +ZI) //将所有用到的RAM都放在这个区域1 t" Y/ Y. ?2 @' ?9 n, Z7 Z. }
- }
2 @6 v) u5 ~5 V - }
1 r8 w6 j" Q* e* A
- Y3 H* g2 a6 t( k" a2 |- LR_m_qspiflash m_qspiflash_start m_qspiflash_size { //LR_m_qspiflash加载域
# R! M& D( u3 b+ Z+ D9 q - ER_m_qspiflash m_qspiflash_start m_qspiflash_size { //ER_m_qspiflash加载域,起始地址为:m_qspiflash_start,大小为:m_qspiflash_size
. i# x' k0 ~% r - .ANY (+RO) //将只读数据(+RO)放这个域,任意分配.相当于程序就是存放在这个域的.
( e1 k: Z* F8 ^ - }
- j- P& K v/ V: ^ - }
复制代码 1 Y$ y0 O2 x/ }8 k" z/ G
以上分散加载文件,由正点原子编写,为了方便大家使用,不用频繁修改.sct 文件,特意
" k/ L. f; ]" Y: q6 u
# k3 a" o2 Z* c4 T, @/ q将.ANY ROM 区域放在外部 SPI FLASH,这样大家在新增.c 参与编译时,默认就是存放在外部
+ ?7 Y& l, M# s. r4 I* @+ `' W: H/ U
SPI FLASH 的,这样使用起来就更方便。
+ L# h( v! P6 f
3 o1 R* L9 s# a; q! F注意:3 Q* _8 p1 T2 D* ]/ `" O1 Q
3 [4 b1 f- a* Z& S& p; [
1, 如果你新增的代码,对速度有要求,可以将其对应的.o 添加到内部 FLASH,即放在:
5 k9 d# [1 c0 Z$ y8 v
0 H- j, ~7 B+ `" mER_m_stmflash 运行域。- w! U6 c, `* _" }/ K! |6 j% D" T& y
4 K `6 Y. m7 q9 M- @' f
2, 如果添加新代码后,程序无法正常运行(通常表现为黑屏/不启动),可以尝试将新增
+ [: s8 A: K s8 {; w- |5 H+ d3 K: k1 I6 {- Z# b
的.o 放到 ER_m_stmflash 运行域后(重新编译)再尝试。如果还不行,可以尝试将所
8 w! T1 x7 J: ^; ?1 g" U* J; Q, n& l; S
有代码都放到 ER_m_stmflash 运行域后再尝试。; d2 p% ^7 h* a2 }% E+ |
5 E& n+ \( n1 k& }
4 R7 \4 J' R0 f' e( b% x; y1 C
( P4 |/ y) _8 g$ d0 `8 q8 u
二、通过boot程序跳转到qspi flash执行用户程序
7 A8 v4 h$ @0 C8 B! J) D% _4 f# w m2 O
第二种方式就是通过在片内flash中存放boot程序,然后通过boot程序跳转到用户程序,用户程序就放在qspi flash中。, H( {4 `) [; o5 E7 E9 y8 f& f' _
( E$ A3 n3 C/ V4 h) x& ]4 mboot程序主要的工作就是初始化qspi,并设置成内存映射模式;禁用中断,重新配置中断向量表地址,然后进行跳转。这里分散加载文件直接使用系统自动生成的就可以了。程序起始地址0x08000000。# f w/ }# ~/ A+ }2 @: J
5 n- W- K0 s2 u8 V7 C- I" e u用户程序需要做的操作就是把程序起始地址改成0x90000000,然后添加下载算法,因为整个程序都是存放在qspi flash上的。下载完成后,用户程序就可以通过boot程序跳转执行了。3 Y! [% P3 V2 F) l4 J! X
. L) n0 D; J! x- g4 i8 c W
1 x2 I- Q6 J$ a( ^6 v' V& H T. t& E/ m+ h) C
; N0 R& t. b i' J- V6 {- D& m( \4 x; K$ i; F
注意,不管是第一种还是第二种方法,都是将qspi flash给设置成了内存映射模式,而此模式下qspi flash是只读的,所以这个时候就没办法用来作为存储器来存储数据了。不过网上也有人说内存映射模式下也是可以写数据的,至于真假,还没验证过。
7 x3 g( y( f9 a1 t0 v( u& B
4 k2 j9 l/ [: s6 I( `1 i7 ~( K9 w9 S4 T; A; j: |* n/ l
|