根据ST的参考手册描述,h750的片内flash只有128kb,在实际的项目应用中,128kb的容量有点太小了,有时候光光移植好lwip+freertos以后,flash的容量就所剩无几了,所幸可以使用QSPI FLASH来存放程序代码,可以把程序的部分或者整个程序都存放在QSPI FLASH中运行。这里介绍两种不同的方法。$ J! x6 I# O0 |+ W7 Q6 j* V. ?
4 k7 v* W y% h' @+ S
一、通过分散加载文件的方式% F/ K$ U# W% M% @" R. H; i; {" r
v! ]/ q& h/ E' \3 m' |1 G 本文中使用的是正点原子北极星板子的例程,跑马灯实验。例程比较简单,主要是看分散加载文件,在最后有一个LR_m_qspiflash加载域,这个就是将代码存放到QSPI FLASH的。分散加载文件的具体细节就不在这边讨论了。
7 k; m9 o) Y, f- J6 {
" z \# K7 g F5 | 当然,光光靠配置分散加载文件,是无法把程序下载到板子上的,因为有一部分RO是要下载到片外的flash的,因此这边还需要使用特殊的片外flash算法。在正点的资料有有提供这一算法。通过添加算法的方式,这时应该能够将程序下载到板子上了,但是,这个时候程序不一定能够跑起来,还有一个重要的步骤,那就是初始化QSPI,并设置成内存映射模式。正点的例程中是将初始化操作放在了时钟初始化函数中。至此,程序就能够正常运行了。
$ }2 ]* z$ f- G) t
* J4 e1 }( y4 B; A- #! armcc -E
, q3 {5 u' }$ n - // 6 B2 H2 ?; z% D7 Q. E
- //STM32H750分散加载文件(.scf文件)9 o8 T% I( v7 I* p
- //ALIENTEK STM32开发板 ! c$ l( t' Y( M! n+ a
- //正点原子@ALIENTEK4 G# f6 }- W6 `2 r2 y# i- U/ s
- //技术论坛:<a href="http://www.openedv.com" target="_blank">www.openedv.com</a>. x1 |1 e$ g* ~1 W
- //创建日期:2019/4/21$ ]) x5 a- W# D2 w, Z
- //版本:V1.0
3 J# ]) U$ b1 p! M8 z - //版权所有,盗版必究。
8 T9 o! i! s( ~; o6 ^" Q( [0 ? - //Copyright(C) 广州市星翼电子科技有限公司 2014-2024
( k7 V! ]; C( S. W: X' Q# Q" ` - //All rights reserved
1 s. Z( ~6 d6 }2 y& |3 j - //********************************************************************************. s' ]# ~, N( K/ f4 D- p" E0 R
- //修改说明
. ?! G L2 k2 r: j% `; p - //无
9 {! s, F. G9 S& V2 x6 h) W4 t - //2 [% T* N" u8 x4 I
& C5 D5 R, r* v y- O
; t5 A- a6 q# o$ x- #define m_stmflash_start 0X08000000 //m_stmflash(STM32内部FLASH)域起始地址
4 r# ?5 |+ b9 y0 J' T& ^ - #define m_stmflash_size 0X20000 //m_stmflash(STM32内部FLASH)大小,H750是128KB
; X9 S0 H6 t: }1 G. v0 Z
# T, h4 I- Y& i- #define m_qspiflash_start 0X90000000 //m_qspiflash(外扩QSPI FLASH)域起始地址
- U3 Y* x0 q" x& W1 I4 e - #define m_qspiflash_size 0X800000 //m_qspiflash(外扩QSPI FLASH)大小,W25Q64是8MB) \& }+ ?5 e6 H% \4 I7 L& A
- " _$ C( V9 {4 c% r. w
- #define m_stmsram_start 0X24000000 //m_stmsram(STM32内部RAM)域起始地址,定义在D1,AXI SRAM
; Z8 \5 {" T' D6 J - #define m_stmsram_size 0X80000 //m_stmsram(STM32内部RAM)大小,AXI SRAM共512KB
7 i( E( \* _& }5 B9 A L3 @' L - ' \- p9 A6 X. c7 R- @
8 W D9 z5 V% K9 r. ^# t* w% \
. R& l* E; S! P/ a3 L8 Z7 n- LR_m_stmflash m_stmflash_start m_stmflash_size { //LR_m_stmflash加载域4 w$ l+ C4 _4 N6 g' D; Y) n5 R( w
- ER_m_stmflash m_stmflash_start m_stmflash_size { //ER_m_stmfalsh运行域,起始地址为:m_stmflash_start,大小为:m_stmflash_size # r9 n; M5 m. N- R( h& r
- *.o (RESET, +First) //优先(+FIRST)将RESET(中断向量表)段放这个域,实际上就是把中断向量表拷贝到m_stmflash_start
9 `- m* b1 N( N3 x1 J' V* D - //RESET是一个段名,表示中断向量表(在.s文件定义);+FIRST表示时第一个要加载的.
8 k; x+ q- P" W+ A5 O, V; J$ ]) _0 \ - * (InRoot$Sections) //将所有的库段(C/C++标准库)放在root region.如__main.o,__scatter*.o等+ ~2 J% Y% D# n# a, Q
- * (Veneer$Code) 0 T# l3 b, g& U- o5 u* K* J
- libinit.o
& K- G6 P" i; n% F - libinit2.o
7 b9 O/ _: |' k" { - libshutdown.o
7 D9 a, I) @5 n e# K - libshutdown2.o% Q }; U+ K6 _4 e9 o% D
- __rtentry.o8 e) F/ M( A) {
- __rtentry2.o& \. e4 o, f- Q) k w8 n
- __rtentry4.o
+ x, }5 D/ }- D - rtexit.o
% P- x7 c0 ]# o2 K9 q$ k - rtexit2.o 0 M5 d; U* j6 D0 o# ?. ~4 Y$ ^
- 4 [( T! `: Q& g' P( X
- use_no_semi_2.o% x- e- W6 O/ ]$ w4 D
- heapauxi.o9 C6 V. L* r4 F' `: Q: ]7 O
- use_no_semi.o& }& B; X8 ?6 O/ n7 G
- sys_stackheap_outer.o$ A$ f ?' s$ p- Q: b, ^4 _
- exit.o9 z! `0 B/ Q. ?( w
- libspace.o$ X2 J2 l' D* U" W0 s
- fpinit.o. W" \8 C0 J! a' H
- lludivv7m.o/ ?, {* \# V# L: n
- startup_stm32h750xx.o
9 F6 h% X" G% J0 G! U4 ^+ c1 r - . X9 F+ c9 Q5 ~" y j
- rt_locale_intlibspace.o 1 B; n+ H" m k7 K; z9 e0 Y- Y
- lc_numeric_c.o 1 v' ?% ` N/ K: X" E( X5 t1 N
- lc_ctype_c.o" x9 B* g9 A2 C. \6 a. ^
- : z2 r0 d6 Q/ {( X( l: [, T
- startup_stm32h750xx.o
( u. n% K7 W1 x8 L! W' y: M2 J - system_stm32h7xx.o, ]( v1 O x: _' \& l/ g
- stm32h7xx_hal.o7 X, P$ P; z! l! ~: Z9 y
- stm32h7xx_hal_cortex.o# S. q+ d& Q" O
- stm32h7xx_hal_rcc.o
+ e* p: O0 `* H |/ n/ K3 B - stm32h7xx_hal_gpio.o. v0 H. A# M3 o0 j
- stm32h7xx_hal_msp.o. t: x8 l, ~( X7 l' f
-
/ w3 P5 h7 ]+ H7 [3 \$ w; y" ^ - main.o 6 a+ c( u6 s, C/ b
- sys.o
) a* q6 T- D6 k( g* c- s& W. W - usart.o
! P- R, l0 b* D$ J0 Q - delay.o' F x* ~6 P' Q& p
- } 2 A% J# Y# z) D0 l0 ?3 F
- RW_m_stmsram m_stmsram_start m_stmsram_size { //RW_m_stmsram运行域,起始地址为:m_stmsram_start,大小为:m_stmsram_size.
2 \" U/ ~' o% z6 |9 b: x8 ^' g7 K3 X1 z- m - .ANY (+RW +ZI) //将所有用到的RAM都放在这个区域
! _$ D8 y$ M: m! `; ~ - }; [* \/ r. S1 Z& t9 u' y
- }; b( H2 j+ P2 l6 p) K6 r4 Z
3 M; b6 O/ s1 u) p2 M- v$ ~- LR_m_qspiflash m_qspiflash_start m_qspiflash_size { //LR_m_qspiflash加载域, D. C' U! Y9 X& M# ?+ w4 E/ N
- ER_m_qspiflash m_qspiflash_start m_qspiflash_size { //ER_m_qspiflash加载域,起始地址为:m_qspiflash_start,大小为:m_qspiflash_size
5 S# R7 Y4 m: N9 b+ m& e5 C: G) C - .ANY (+RO) //将只读数据(+RO)放这个域,任意分配.相当于程序就是存放在这个域的.
* Q" P! G7 ^' v- ^ - }
# v# @( Z2 Y/ M7 g' d1 B - }
复制代码 5 R- F7 `6 S7 m+ X) T/ _# z. }
以上分散加载文件,由正点原子编写,为了方便大家使用,不用频繁修改.sct 文件,特意2 u7 M9 e2 ~* w2 c
7 s. i' }/ v/ q
将.ANY ROM 区域放在外部 SPI FLASH,这样大家在新增.c 参与编译时,默认就是存放在外部0 |$ ~9 B0 }' I# R1 I8 V
7 | A* t' j( n% }: O I/ gSPI FLASH 的,这样使用起来就更方便。
% C$ @- s4 c4 [ O) M& z3 \& c
8 V( Z* q3 [ I. E" a注意:
7 Q" v1 C/ D: l+ Y1 C3 j9 X) {' x @0 d3 h7 K+ d
1, 如果你新增的代码,对速度有要求,可以将其对应的.o 添加到内部 FLASH,即放在:8 `- `4 R! w- U ~0 h* J0 P
2 Q5 K( I, Y, k2 P5 X" {ER_m_stmflash 运行域。
6 e4 W( j* W1 G2 m* ~
`; `1 L& K4 B0 [9 Q1 j2, 如果添加新代码后,程序无法正常运行(通常表现为黑屏/不启动),可以尝试将新增# f }0 E5 U( ]5 W1 P( F
! j2 `, M4 Q0 Y, I的.o 放到 ER_m_stmflash 运行域后(重新编译)再尝试。如果还不行,可以尝试将所
, k g; \( t# P" l
: p7 j- v6 X; b& E6 w' y有代码都放到 ER_m_stmflash 运行域后再尝试。( B7 |$ |7 H6 R
7 F$ K6 M' T# q
7 J) z" B- N( Q$ L, Z. ^2 U
- ^( E, A$ X: ]7 N4 ~
二、通过boot程序跳转到qspi flash执行用户程序
' {& Y) l7 A9 l& `. F9 E# i& v$ u5 }- }0 ?+ z' }: g) p
第二种方式就是通过在片内flash中存放boot程序,然后通过boot程序跳转到用户程序,用户程序就放在qspi flash中。
0 t# Y3 q2 ~& x" z
+ s L/ S0 p' J* R) v2 e/ L uboot程序主要的工作就是初始化qspi,并设置成内存映射模式;禁用中断,重新配置中断向量表地址,然后进行跳转。这里分散加载文件直接使用系统自动生成的就可以了。程序起始地址0x08000000。
7 z! C2 \% I6 S% L+ i% k: O S4 [& U3 N$ L
用户程序需要做的操作就是把程序起始地址改成0x90000000,然后添加下载算法,因为整个程序都是存放在qspi flash上的。下载完成后,用户程序就可以通过boot程序跳转执行了。) n0 E3 C0 S `/ a6 _" p' J
8 ]) D( r- j" U5 i& a/ E/ L0 }& F0 k% W6 l; _* \$ S
& u) W1 Z! h. N0 `: K2 b/ d
7 t' f- @# M$ z$ c3 I$ `8 s3 @
& P3 ?+ {9 e5 M注意,不管是第一种还是第二种方法,都是将qspi flash给设置成了内存映射模式,而此模式下qspi flash是只读的,所以这个时候就没办法用来作为存储器来存储数据了。不过网上也有人说内存映射模式下也是可以写数据的,至于真假,还没验证过。
5 T% w9 [3 F; m; i M4 _6 Q7 `7 W' Q% Q5 V' h9 }
8 t% }3 e) u# c- @6 t1 i
|