根据ST的参考手册描述,h750的片内flash只有128kb,在实际的项目应用中,128kb的容量有点太小了,有时候光光移植好lwip+freertos以后,flash的容量就所剩无几了,所幸可以使用QSPI FLASH来存放程序代码,可以把程序的部分或者整个程序都存放在QSPI FLASH中运行。这里介绍两种不同的方法。
* m* A- e7 {1 J: n# i4 Z& I, J, q
! {, B! b, V( v# D一、通过分散加载文件的方式
" T @* u1 m5 ]( o9 S9 P
, s# y' T" n4 T* Z$ g- _" R8 Q* _ 本文中使用的是正点原子北极星板子的例程,跑马灯实验。例程比较简单,主要是看分散加载文件,在最后有一个LR_m_qspiflash加载域,这个就是将代码存放到QSPI FLASH的。分散加载文件的具体细节就不在这边讨论了。 q% N1 U1 m( l: P" t
$ F! }3 C9 r/ l! c4 _ 当然,光光靠配置分散加载文件,是无法把程序下载到板子上的,因为有一部分RO是要下载到片外的flash的,因此这边还需要使用特殊的片外flash算法。在正点的资料有有提供这一算法。通过添加算法的方式,这时应该能够将程序下载到板子上了,但是,这个时候程序不一定能够跑起来,还有一个重要的步骤,那就是初始化QSPI,并设置成内存映射模式。正点的例程中是将初始化操作放在了时钟初始化函数中。至此,程序就能够正常运行了。, B* N( T& [# h0 m% p
: E# I3 u( @3 d# z- #! armcc -E & }7 l8 q0 v( y
- // ! ^; r6 z5 K4 C) b8 X3 j6 K
- //STM32H750分散加载文件(.scf文件)
5 K o1 x- T8 a - //ALIENTEK STM32开发板
, Y( B$ C: N: y- `! K - //正点原子@ALIENTEK
& V2 A. L# G8 a: l {3 s - //技术论坛:<a href="http://www.openedv.com" target="_blank">www.openedv.com</a>
6 U$ q: y0 U A% I - //创建日期:2019/4/21
, g+ f9 f8 T3 D - //版本:V1.0% k2 U% q! q+ k6 u
- //版权所有,盗版必究。
n6 F3 w& G! N3 p$ K& t! \! M# l - //Copyright(C) 广州市星翼电子科技有限公司 2014-2024
2 Q2 ?) P9 B( L/ ~& @7 P - //All rights reserved3 C% F+ u1 a+ Q+ x) v
- //********************************************************************************9 ?. e6 \. @+ y- t2 F
- //修改说明
4 @. b5 e. Y F' ] - //无
: M1 x" {# @ {/ b' v" i - //4 o, G i7 k; C2 n- Y" k" E/ ^
- , R0 I* W+ R+ Q3 h! ~1 t
- $ F6 s) {+ L3 ?! l7 G( X
- #define m_stmflash_start 0X08000000 //m_stmflash(STM32内部FLASH)域起始地址( C0 m3 R$ {7 A1 a5 @! U
- #define m_stmflash_size 0X20000 //m_stmflash(STM32内部FLASH)大小,H750是128KB( v0 _! J2 ~: G( _! P
- 0 J5 E; Z& U* r
- #define m_qspiflash_start 0X90000000 //m_qspiflash(外扩QSPI FLASH)域起始地址
' |8 H9 u) e0 R: d - #define m_qspiflash_size 0X800000 //m_qspiflash(外扩QSPI FLASH)大小,W25Q64是8MB, C' J m6 v' `
- ( w4 Q# n/ Z F) A7 {8 G
- #define m_stmsram_start 0X24000000 //m_stmsram(STM32内部RAM)域起始地址,定义在D1,AXI SRAM# w; y3 o3 x6 E( R
- #define m_stmsram_size 0X80000 //m_stmsram(STM32内部RAM)大小,AXI SRAM共512KB
! t) }2 L" N p! T8 F - 8 c& F5 b: _) E6 K6 b; j3 ^* g* P2 c
- / ^2 r. J ?8 U' K/ y$ m
- 9 [! G# D- m+ M3 Y* s
- LR_m_stmflash m_stmflash_start m_stmflash_size { //LR_m_stmflash加载域
6 A# ~7 Y0 x5 _7 y3 j - ER_m_stmflash m_stmflash_start m_stmflash_size { //ER_m_stmfalsh运行域,起始地址为:m_stmflash_start,大小为:m_stmflash_size ! G% o0 X7 G u: ~
- *.o (RESET, +First) //优先(+FIRST)将RESET(中断向量表)段放这个域,实际上就是把中断向量表拷贝到m_stmflash_start" \. i' S P2 u
- //RESET是一个段名,表示中断向量表(在.s文件定义);+FIRST表示时第一个要加载的.
- W4 ~- O$ H9 m9 w - * (InRoot$Sections) //将所有的库段(C/C++标准库)放在root region.如__main.o,__scatter*.o等
h+ Q8 Z" e6 |' V/ ] - * (Veneer$Code)
( w# V; y" j$ Q% E - libinit.o
+ \" ~: ]) s+ u4 z" e - libinit2.o
; t/ ~/ Y; y x' d% D- z - libshutdown.o
5 R! B1 o& X5 [% z, x) P - libshutdown2.o$ |1 {4 N6 b2 i4 Y! s3 p: E
- __rtentry.o
8 c+ X1 D1 }8 A# O9 _6 t# @ - __rtentry2.o% Y9 \ S; }5 U. o" B z
- __rtentry4.o, K9 @* J; R+ S5 M& |
- rtexit.o; _" [! t& q6 U) K2 r5 e
- rtexit2.o # @- s" U; N2 O3 C4 i2 S
- # l1 C% u6 m* a8 T& h) E7 ]2 }
- use_no_semi_2.o. [, f' m$ R* t; X* K9 |
- heapauxi.o6 u" C( W/ [( ~- ~( I# l- @
- use_no_semi.o
( _6 c- z; x) R0 W# ^+ S - sys_stackheap_outer.o
/ y$ J% R! {, V; w7 k ]7 V - exit.o8 r x$ H5 s! K. w8 s
- libspace.o. T( A* ]. E( t4 S; z
- fpinit.o
4 q& w7 N6 \) z' P' Z/ _( G - lludivv7m.o* J4 ]! E! x% M* K+ B3 V5 Y# u
- startup_stm32h750xx.o
9 m6 }+ n) X6 M: j - 4 g7 ?5 r3 |) k% G0 A+ ^6 I
- rt_locale_intlibspace.o
3 e, o% M# W3 k y - lc_numeric_c.o + V" z, R) c. |* m
- lc_ctype_c.o& Y" R% H8 n5 T: A: |! m
3 @( I3 [+ A5 h" C6 `4 m- startup_stm32h750xx.o
' E" u) J! }' C% g, T8 e - system_stm32h7xx.o& X/ v! u7 Y- P/ a: b
- stm32h7xx_hal.o
4 d, K& k% }/ N0 F: |2 k: w( ^6 K4 O - stm32h7xx_hal_cortex.o8 m& E/ R6 E! b4 w0 q9 N
- stm32h7xx_hal_rcc.o' a& N0 a5 `$ ?3 c3 w
- stm32h7xx_hal_gpio.o
6 I# @# z9 I( W. u - stm32h7xx_hal_msp.o
6 B, c# t& | i - 7 s# h& l1 ^( D! s- ?! e
- main.o % ^1 a( E% M% t' H4 S0 S9 X' p
- sys.o
5 Q& K! F; d8 k - usart.o
& R: J9 z: P2 s A9 \: K. t. T: L - delay.o& U7 K) w+ P; n, X
- }
( H! z' b t8 ^ - RW_m_stmsram m_stmsram_start m_stmsram_size { //RW_m_stmsram运行域,起始地址为:m_stmsram_start,大小为:m_stmsram_size.- j5 n5 g6 \- e% u% N2 l1 v
- .ANY (+RW +ZI) //将所有用到的RAM都放在这个区域& N; j4 Q- a: {" _6 `7 A' P
- }0 k$ Z e$ d3 b- X
- }9 }& y# `. G- Q
5 f3 E* V( }) H' @+ M- LR_m_qspiflash m_qspiflash_start m_qspiflash_size { //LR_m_qspiflash加载域1 \- x' n7 z T
- ER_m_qspiflash m_qspiflash_start m_qspiflash_size { //ER_m_qspiflash加载域,起始地址为:m_qspiflash_start,大小为:m_qspiflash_size 6 ~2 S! O* n. @: V# Y W
- .ANY (+RO) //将只读数据(+RO)放这个域,任意分配.相当于程序就是存放在这个域的.
6 {" G' Y5 X! w q2 [: J! v - }
4 `& \, i1 K6 p' g# t - }
复制代码
# B% ]1 \7 s! u L# H% t5 I以上分散加载文件,由正点原子编写,为了方便大家使用,不用频繁修改.sct 文件,特意
) I# r% G1 E# G8 C! s. f' I2 O- z' S, k/ v. R
将.ANY ROM 区域放在外部 SPI FLASH,这样大家在新增.c 参与编译时,默认就是存放在外部
& \9 |* F, u& [) @; S) M1 y/ q8 K- F/ w" u9 z
SPI FLASH 的,这样使用起来就更方便。
- K% `$ ]% `0 Q( `% l' d9 X
+ R) x# w) a# U) M2 [% y5 a注意:
; B) z1 J7 O# Z( e9 r& ~8 k/ L7 j; u/ H* W0 e) ^/ g. Q
1, 如果你新增的代码,对速度有要求,可以将其对应的.o 添加到内部 FLASH,即放在:
3 U" J* v2 ~7 b; J9 ~8 H8 a% y6 \# w9 i! R. c+ J5 j: E, X
ER_m_stmflash 运行域。$ C) p1 ^! p# J2 M/ v t$ U2 F* ~
" X& _1 J# l& ^8 v# w [6 o3 g. H2, 如果添加新代码后,程序无法正常运行(通常表现为黑屏/不启动),可以尝试将新增
- I& H, P/ K+ p: M7 }+ S$ @4 M, ~3 ~: E' a
的.o 放到 ER_m_stmflash 运行域后(重新编译)再尝试。如果还不行,可以尝试将所
. p) \5 e7 {4 @$ f4 s" ^4 ~2 a9 N" G7 T
有代码都放到 ER_m_stmflash 运行域后再尝试。6 i( l) m3 W n6 c4 u
1 D6 [( c) Z0 j( ~+ m* M
9 t: c9 K" L" v4 V" }7 r: w- ?5 H. u
$ l% E7 C9 a# p8 ]二、通过boot程序跳转到qspi flash执行用户程序
! y9 G% [5 R2 S. k2 j. D6 [& b+ g2 E/ t+ ?/ u0 ~8 C3 J
第二种方式就是通过在片内flash中存放boot程序,然后通过boot程序跳转到用户程序,用户程序就放在qspi flash中。1 t$ o3 t( K/ p
& E; F& |7 H* x2 y" C* ~6 _boot程序主要的工作就是初始化qspi,并设置成内存映射模式;禁用中断,重新配置中断向量表地址,然后进行跳转。这里分散加载文件直接使用系统自动生成的就可以了。程序起始地址0x08000000。
1 z7 u( k3 m' v0 x1 Z3 {9 Y+ Y8 d
" F6 I* S* A3 b: _( E: T用户程序需要做的操作就是把程序起始地址改成0x90000000,然后添加下载算法,因为整个程序都是存放在qspi flash上的。下载完成后,用户程序就可以通过boot程序跳转执行了。
% u( u3 b+ A1 w: b7 z: U7 R5 L! Y- P" @ `! c' H
) K' |0 D7 f7 |; d& X4 b( S8 Y# J% e
2 V1 ?$ @0 B" c
* u) f. j5 U; l3 S! h7 E, J; [3 b; v3 A8 K- b
注意,不管是第一种还是第二种方法,都是将qspi flash给设置成了内存映射模式,而此模式下qspi flash是只读的,所以这个时候就没办法用来作为存储器来存储数据了。不过网上也有人说内存映射模式下也是可以写数据的,至于真假,还没验证过。
/ B& T% I- E' _6 b. G
& {% q( }3 G1 _0 ~: G& W8 Y. A
1 u+ Z; M- G, Z* I9 p% u1 I) C |