根据ST的参考手册描述,h750的片内flash只有128kb,在实际的项目应用中,128kb的容量有点太小了,有时候光光移植好lwip+freertos以后,flash的容量就所剩无几了,所幸可以使用QSPI FLASH来存放程序代码,可以把程序的部分或者整个程序都存放在QSPI FLASH中运行。这里介绍两种不同的方法。5 j6 Z0 J2 k# w& Y& x& g$ a6 o
: p' q. O0 U% Y- N6 a3 J
一、通过分散加载文件的方式
# y" L W& }8 M/ R7 a& m. ]2 W9 ]) |- M. J" ~) e1 J+ Z7 r/ z8 s
本文中使用的是正点原子北极星板子的例程,跑马灯实验。例程比较简单,主要是看分散加载文件,在最后有一个LR_m_qspiflash加载域,这个就是将代码存放到QSPI FLASH的。分散加载文件的具体细节就不在这边讨论了。
" Y4 _4 F6 ]) D5 O1 u
1 W: N3 J" [' \% ?5 H 当然,光光靠配置分散加载文件,是无法把程序下载到板子上的,因为有一部分RO是要下载到片外的flash的,因此这边还需要使用特殊的片外flash算法。在正点的资料有有提供这一算法。通过添加算法的方式,这时应该能够将程序下载到板子上了,但是,这个时候程序不一定能够跑起来,还有一个重要的步骤,那就是初始化QSPI,并设置成内存映射模式。正点的例程中是将初始化操作放在了时钟初始化函数中。至此,程序就能够正常运行了。( V# N* |0 `: J- W# X
! _1 S% o% N- G0 x
- #! armcc -E
- a5 E: K( b. v: N5 ^ - //
7 X/ R: [- x5 B# b - //STM32H750分散加载文件(.scf文件) r/ ~" k9 R& c; Q B0 F8 g
- //ALIENTEK STM32开发板 ! o0 M0 G+ m8 l& B9 {
- //正点原子@ALIENTEK% P/ G5 E8 G& _1 h& f& i+ [
- //技术论坛:<a href="http://www.openedv.com" target="_blank">www.openedv.com</a>
" P. r _0 n6 [ - //创建日期:2019/4/21
+ Z' R" _$ w# e; i P4 ~ - //版本:V1.0: P$ ]9 F- \# s( ~' `: n
- //版权所有,盗版必究。
0 l8 b" \ o8 P - //Copyright(C) 广州市星翼电子科技有限公司 2014-2024
/ |' R- W6 T% |" \$ p - //All rights reserved
* z0 \( _8 y! _- A2 @, w - //********************************************************************************
6 n) y6 D2 {9 P. ~9 }7 K - //修改说明
, S# G& E# n9 W- T5 _ - //无, i( [# m( k: q" J
- //8 j4 A$ b3 l( Q# k3 ~* N
3 T n" o& ?& F
4 s- N9 n+ O; @6 k- #define m_stmflash_start 0X08000000 //m_stmflash(STM32内部FLASH)域起始地址9 u5 L2 }" S/ ?& k0 q, v# }5 E
- #define m_stmflash_size 0X20000 //m_stmflash(STM32内部FLASH)大小,H750是128KB' O3 S6 O7 ~) G6 G2 b
( H% e, {0 X* g- #define m_qspiflash_start 0X90000000 //m_qspiflash(外扩QSPI FLASH)域起始地址$ q2 x2 h4 B" @6 N d
- #define m_qspiflash_size 0X800000 //m_qspiflash(外扩QSPI FLASH)大小,W25Q64是8MB E/ J* f% p2 ?$ R: ^2 H
- L, o8 t* n5 z9 [% l2 O- #define m_stmsram_start 0X24000000 //m_stmsram(STM32内部RAM)域起始地址,定义在D1,AXI SRAM- X! L. ~& @/ C" ~ Q' @' l2 ~& T
- #define m_stmsram_size 0X80000 //m_stmsram(STM32内部RAM)大小,AXI SRAM共512KB! b3 p+ ~3 U! G4 s* [: N
- $ x& O" o3 ]- t( u# b
- 3 b5 F9 ^5 B# T# _" y
' J1 X) B9 j. a' g0 }; n3 Z' q- LR_m_stmflash m_stmflash_start m_stmflash_size { //LR_m_stmflash加载域9 Z D4 V r5 |# A, g$ v
- ER_m_stmflash m_stmflash_start m_stmflash_size { //ER_m_stmfalsh运行域,起始地址为:m_stmflash_start,大小为:m_stmflash_size
0 ?4 F% T3 b& N ^8 |2 b - *.o (RESET, +First) //优先(+FIRST)将RESET(中断向量表)段放这个域,实际上就是把中断向量表拷贝到m_stmflash_start2 p: V/ Z/ N' ?. a7 G
- //RESET是一个段名,表示中断向量表(在.s文件定义);+FIRST表示时第一个要加载的.
% X" ^2 O' m7 g9 ^ w - * (InRoot$Sections) //将所有的库段(C/C++标准库)放在root region.如__main.o,__scatter*.o等
" G: C# P- |0 p- L/ t9 c% m, g0 ~4 r2 I - * (Veneer$Code)
. p% z7 v, k0 W: \' |5 a. w - libinit.o
' M+ S* J( h6 k( ^5 Z" \7 {) e - libinit2.o2 `( r X7 ^! t3 q: H
- libshutdown.o
/ t0 F" u/ Y! C) g, f+ m - libshutdown2.o, y, [ c6 H; t+ w) L
- __rtentry.o. ]5 l% k0 R5 X0 w8 [
- __rtentry2.o
5 ~6 f+ B# d2 A - __rtentry4.o
t; {/ R0 H8 v% e - rtexit.o
; q. x6 t+ q/ h& T - rtexit2.o : c' L! m/ \) {% i
-
! R" g7 X' T4 Z* D" p7 j5 N+ I - use_no_semi_2.o
9 K* m7 g# b3 B6 `; C6 P - heapauxi.o, ], o* ~6 O: D8 V+ R6 |- `: \
- use_no_semi.o2 c6 a9 `. J6 _, x4 P/ d9 l. _
- sys_stackheap_outer.o" A- m2 [) E9 z- V. |8 D
- exit.o1 u+ b4 f4 K7 \- k. H
- libspace.o
( p8 k9 i s' E0 J2 r" j - fpinit.o
q& g" h$ A I, w! ] - lludivv7m.o" N- W* n1 K$ n0 u
- startup_stm32h750xx.o
, x9 ]! G$ h9 y Y) x$ A" q7 x -
$ i- Y2 I A: Z7 F0 n$ h: o1 I - rt_locale_intlibspace.o % L2 K0 R9 p- e. K4 [# W9 m( F; Z
- lc_numeric_c.o
$ w2 x7 E) p+ S2 O - lc_ctype_c.o" a3 Q1 x- a d9 U$ _
- Q$ Q/ E# ~% Y# Y1 u0 d9 ^6 D" o
- startup_stm32h750xx.o/ N$ H$ t8 r1 ^8 P4 H& ]% F
- system_stm32h7xx.o; | G; p3 C9 [% e! q% T$ \
- stm32h7xx_hal.o1 |7 J* V* z0 ` i1 h* {
- stm32h7xx_hal_cortex.o
/ c5 ~* M" p! S$ S, a - stm32h7xx_hal_rcc.o
5 y' r, t9 T. o, o4 }: F# f - stm32h7xx_hal_gpio.o2 V4 _ c+ {. ^7 |6 O0 Q T
- stm32h7xx_hal_msp.o
& ^: i4 } A8 [0 y& ^ - / ~. f, x; t8 ?1 d' Z
- main.o
6 F! ?+ T C% Z0 F - sys.o , x) t$ a8 o7 A" S' A
- usart.o
h3 B# d- V1 {' \* G+ R% q - delay.o9 G( j2 T: d' ]4 x/ x8 q
- } ) A; ]4 f+ _" s+ [: B# Y P
- RW_m_stmsram m_stmsram_start m_stmsram_size { //RW_m_stmsram运行域,起始地址为:m_stmsram_start,大小为:m_stmsram_size.$ U9 A3 _, s2 W( ]) W' N
- .ANY (+RW +ZI) //将所有用到的RAM都放在这个区域
c- s% S) Y$ t$ p" s- _ - }
, B' E6 V0 e+ U! u) |5 _ - }
0 `) `5 J* L5 Z7 L7 \# v - 7 V* ]5 R+ I9 m4 E) e6 B1 A d
- LR_m_qspiflash m_qspiflash_start m_qspiflash_size { //LR_m_qspiflash加载域
2 n4 a$ Q/ b6 l1 ? - ER_m_qspiflash m_qspiflash_start m_qspiflash_size { //ER_m_qspiflash加载域,起始地址为:m_qspiflash_start,大小为:m_qspiflash_size
: U8 z7 g$ b T - .ANY (+RO) //将只读数据(+RO)放这个域,任意分配.相当于程序就是存放在这个域的.
. V, M. S) c* w3 N% x8 G - } 4 c& X: F4 M1 x' `1 U
- }
复制代码
X5 ]/ H0 Y0 D+ r! S以上分散加载文件,由正点原子编写,为了方便大家使用,不用频繁修改.sct 文件,特意
0 o4 O: F/ ]) I7 `9 T0 v, K% `9 f' ?# t7 q" H7 o+ M$ v4 X( C
将.ANY ROM 区域放在外部 SPI FLASH,这样大家在新增.c 参与编译时,默认就是存放在外部
* n+ z9 h0 X% S ]' F4 z e; x( j4 k
. K( D* H u3 H+ k. gSPI FLASH 的,这样使用起来就更方便。( f, e! i3 V9 k9 B5 l5 m2 ?
; H# K- k) J+ g2 z( d: r! [
注意:
5 g4 q6 y/ {( b. c- \: @; w
9 c6 w4 ~3 `# E1, 如果你新增的代码,对速度有要求,可以将其对应的.o 添加到内部 FLASH,即放在:
* a. z% t' m6 x% Q+ D$ S0 j% N# D% P
ER_m_stmflash 运行域。: |( [% e% h+ i$ E# z1 `
! k0 t$ N/ {+ W0 M2, 如果添加新代码后,程序无法正常运行(通常表现为黑屏/不启动),可以尝试将新增( u/ Y: K8 ]3 U6 v( ]! E
! `4 ]/ p* o5 c
的.o 放到 ER_m_stmflash 运行域后(重新编译)再尝试。如果还不行,可以尝试将所5 U4 T/ E% g U: a+ D! I. i
6 {* d0 Z5 U, D0 |2 W
有代码都放到 ER_m_stmflash 运行域后再尝试。
' x' {, \5 E3 J; V% H/ R* k# d7 z
% K" G3 }8 E A2 V8 a3 r
' e5 z& i( u/ o/ ^. g" i' {6 o4 E' M* M二、通过boot程序跳转到qspi flash执行用户程序
+ j8 A- @! U2 N; D2 k C' i
% f G% H1 l8 a2 u3 m 第二种方式就是通过在片内flash中存放boot程序,然后通过boot程序跳转到用户程序,用户程序就放在qspi flash中。" m6 g% b' B" z) ?' f) T0 ~' I
% ^! l U0 O4 H6 [ g4 H0 E; Wboot程序主要的工作就是初始化qspi,并设置成内存映射模式;禁用中断,重新配置中断向量表地址,然后进行跳转。这里分散加载文件直接使用系统自动生成的就可以了。程序起始地址0x08000000。
* D, e$ \% Z, S7 }0 x9 L2 N
, `" S& F; ^! O6 c8 V; i用户程序需要做的操作就是把程序起始地址改成0x90000000,然后添加下载算法,因为整个程序都是存放在qspi flash上的。下载完成后,用户程序就可以通过boot程序跳转执行了。
+ r: J0 k7 L) A8 J" Y# [ ~% ~. l( u! t6 j3 \& U: J
. l+ B1 Y. E7 B, P
1 Z: a# M; s. n* R2 g7 C4 X+ g+ `$ b3 L4 O
* Z2 F3 H+ z& l( Z9 |5 Z
注意,不管是第一种还是第二种方法,都是将qspi flash给设置成了内存映射模式,而此模式下qspi flash是只读的,所以这个时候就没办法用来作为存储器来存储数据了。不过网上也有人说内存映射模式下也是可以写数据的,至于真假,还没验证过。/ q$ y7 m9 ?& T& x0 c4 L
; v8 @( n- u& h& O' n/ F2 x1 ]0 e& U. p( e( N9 m0 S2 t9 O
|