根据ST的参考手册描述,h750的片内flash只有128kb,在实际的项目应用中,128kb的容量有点太小了,有时候光光移植好lwip+freertos以后,flash的容量就所剩无几了,所幸可以使用QSPI FLASH来存放程序代码,可以把程序的部分或者整个程序都存放在QSPI FLASH中运行。这里介绍两种不同的方法。
; i; L2 }# X( N' M$ D
o4 U6 m% ~7 p7 B2 I一、通过分散加载文件的方式
3 ^* V$ @) Q6 Z v
: H+ ]+ k9 |+ Z' M2 l( z6 c 本文中使用的是正点原子北极星板子的例程,跑马灯实验。例程比较简单,主要是看分散加载文件,在最后有一个LR_m_qspiflash加载域,这个就是将代码存放到QSPI FLASH的。分散加载文件的具体细节就不在这边讨论了。
7 R! C1 ~8 Z" @3 G2 D$ H# D$ ], ]# h
当然,光光靠配置分散加载文件,是无法把程序下载到板子上的,因为有一部分RO是要下载到片外的flash的,因此这边还需要使用特殊的片外flash算法。在正点的资料有有提供这一算法。通过添加算法的方式,这时应该能够将程序下载到板子上了,但是,这个时候程序不一定能够跑起来,还有一个重要的步骤,那就是初始化QSPI,并设置成内存映射模式。正点的例程中是将初始化操作放在了时钟初始化函数中。至此,程序就能够正常运行了。0 u: j. z8 s% k) `3 B
: H1 b: x* O+ i8 T3 U- #! armcc -E $ E- P5 e& q6 J' O2 o7 X# m
- // ' x; {0 C* _( N; d
- //STM32H750分散加载文件(.scf文件): N1 s) ^1 ]) Q0 r: q# }% f
- //ALIENTEK STM32开发板 , p6 E; q+ i5 F6 ]
- //正点原子@ALIENTEK
@0 o3 Q4 `& V( E2 ?* Z; K - //技术论坛:<a href="http://www.openedv.com" target="_blank">www.openedv.com</a>0 W! Q! p8 }6 f* y' y6 \+ O! ?+ j
- //创建日期:2019/4/21
6 F( L- z! D5 P D+ g7 V" `1 f, F - //版本:V1.0' F' E, v+ T- a& A/ v+ q
- //版权所有,盗版必究。
, r/ e7 \' `' M8 U3 H5 e - //Copyright(C) 广州市星翼电子科技有限公司 2014-2024
$ G a+ O) Z2 W: ^& S* L - //All rights reserved
3 w3 f$ l, J2 U# L" _% D' | - //********************************************************************************
0 |* d3 j3 B4 A2 T5 @6 c - //修改说明9 {; U4 k* ^& L- V2 c) N a b; N s& P
- //无, T+ Z" `' Q8 q+ y4 |
- //& s8 ~% t5 f" G/ X T4 ^: R
- 1 l$ R9 F1 G# H7 Z
' U4 |; j8 i! ?7 K; Q- #define m_stmflash_start 0X08000000 //m_stmflash(STM32内部FLASH)域起始地址
4 `, T, F% h7 g0 s: E - #define m_stmflash_size 0X20000 //m_stmflash(STM32内部FLASH)大小,H750是128KB/ C! M2 S7 v% A
( s9 ]( m) P7 o- ?- F8 e' q$ w( w1 p- #define m_qspiflash_start 0X90000000 //m_qspiflash(外扩QSPI FLASH)域起始地址4 r ^1 r' M, t) K# Y
- #define m_qspiflash_size 0X800000 //m_qspiflash(外扩QSPI FLASH)大小,W25Q64是8MB3 Z* x: [0 ?5 _, N" i3 F
- 1 a" O+ w: O5 `
- #define m_stmsram_start 0X24000000 //m_stmsram(STM32内部RAM)域起始地址,定义在D1,AXI SRAM. B0 A I- K! X. c. [
- #define m_stmsram_size 0X80000 //m_stmsram(STM32内部RAM)大小,AXI SRAM共512KB3 ]/ s3 ?( g" B5 T w+ T! t
- 2 h+ h7 A$ a! [
6 W* j* J* u0 k. k& H, v7 }- 9 x! l0 X& a: h. f( f* K* ~/ F" X
- LR_m_stmflash m_stmflash_start m_stmflash_size { //LR_m_stmflash加载域2 w! g' K+ E" X- ?* a6 S7 }
- ER_m_stmflash m_stmflash_start m_stmflash_size { //ER_m_stmfalsh运行域,起始地址为:m_stmflash_start,大小为:m_stmflash_size 9 C' f1 o- L5 H6 G
- *.o (RESET, +First) //优先(+FIRST)将RESET(中断向量表)段放这个域,实际上就是把中断向量表拷贝到m_stmflash_start
# ?' G! s2 r$ w+ F - //RESET是一个段名,表示中断向量表(在.s文件定义);+FIRST表示时第一个要加载的.; e2 J- D0 X, K% \
- * (InRoot$Sections) //将所有的库段(C/C++标准库)放在root region.如__main.o,__scatter*.o等
- Y8 i" s# b4 ~/ v, C8 `+ I! V - * (Veneer$Code)
! r; X. f c0 I- o. Q5 a) a6 q3 L+ w - libinit.o2 _# ~: M( ]& R
- libinit2.o
9 p- `( z0 y. r' h; w, j/ @ - libshutdown.o2 \4 ^& L" B7 H+ x8 i
- libshutdown2.o
& j! n" y& f3 R/ P" t - __rtentry.o
8 o4 f" H/ o7 x r( E - __rtentry2.o
+ X; s# U% C7 P - __rtentry4.o
, @, C" E S) _$ d/ g - rtexit.o) I- \( B" B+ A- M/ m9 v
- rtexit2.o 6 a; N6 g% X% C( Y2 } R! B
- " B' X$ G9 `0 w5 R9 S9 y
- use_no_semi_2.o
- f2 z$ T: B9 v: y' i5 \& r ^ - heapauxi.o
- t7 z0 Y, ^: ]+ V! r8 d - use_no_semi.o
/ N, C `6 \+ s g0 E3 f - sys_stackheap_outer.o
' v! J) a8 w0 X: u6 K7 q: J- w - exit.o
$ E4 D5 U( X( F+ Z - libspace.o
C1 y$ W2 l: A; |9 ~! f! L8 r2 A - fpinit.o
; v: A, k+ ~3 _7 S7 v& W! } - lludivv7m.o& a' J; \; ~* c# z; d Q% u" I0 Q
- startup_stm32h750xx.o0 d; e# e+ ?* ?2 Y% Y9 {- u
- 2 f4 Z- n) O# Y
- rt_locale_intlibspace.o
! D. Z$ d' q" H- V Z1 p4 H - lc_numeric_c.o
: l% D9 v3 z& [% S+ [9 ] - lc_ctype_c.o
3 ?! T$ f1 O3 L5 K+ N
' [4 z2 N& M4 E0 ^/ j- startup_stm32h750xx.o7 ]5 d c. M0 s2 @8 p# |6 l% L) g
- system_stm32h7xx.o
& x+ w; S( G. W$ | - stm32h7xx_hal.o# u T/ e7 |4 S/ g) P
- stm32h7xx_hal_cortex.o* {/ \- u+ U/ d7 H6 z) M% r
- stm32h7xx_hal_rcc.o
; u0 I. j/ N, W) ]7 |2 ]) @ - stm32h7xx_hal_gpio.o
7 _. L8 v1 l/ R( w - stm32h7xx_hal_msp.o( t v4 b! H/ e1 W3 H0 J' l$ ?
- 2 K. R+ S4 N: F( H9 f1 o
- main.o + F6 S6 N/ Y# f% Z6 _: Z( E( H
- sys.o
) ^9 r; Q. j: P) F - usart.o
5 G- ^) b4 W# p- g3 w - delay.o
; n) B C9 K6 l: m - } ( Q1 V. o& j9 q( g
- RW_m_stmsram m_stmsram_start m_stmsram_size { //RW_m_stmsram运行域,起始地址为:m_stmsram_start,大小为:m_stmsram_size.. S1 e9 |! O/ c x
- .ANY (+RW +ZI) //将所有用到的RAM都放在这个区域( o; ^7 A- J) I$ g2 s# R
- }
7 A; q/ X. F, N% n, W - }) }2 k. a9 E9 @
- 8 x' J: J" N" L# \4 W
- LR_m_qspiflash m_qspiflash_start m_qspiflash_size { //LR_m_qspiflash加载域9 O/ G8 Z" q7 z1 m
- ER_m_qspiflash m_qspiflash_start m_qspiflash_size { //ER_m_qspiflash加载域,起始地址为:m_qspiflash_start,大小为:m_qspiflash_size . P. c9 U1 D' u9 r
- .ANY (+RO) //将只读数据(+RO)放这个域,任意分配.相当于程序就是存放在这个域的.
' Q' {" {9 p/ J6 M - } " y" J }4 I: _& i5 D5 W. S
- }
复制代码 3 D+ A' K& S- b6 {' a5 n0 _' L
以上分散加载文件,由正点原子编写,为了方便大家使用,不用频繁修改.sct 文件,特意9 W" _. T8 m7 U$ ~
: S. x4 V' p3 ~4 c7 x
将.ANY ROM 区域放在外部 SPI FLASH,这样大家在新增.c 参与编译时,默认就是存放在外部1 C" @! ~, ^& W5 ~) H. Y
9 q, @5 }+ v. ]# e; S) d# X$ l
SPI FLASH 的,这样使用起来就更方便。1 N0 W3 y7 @) o8 { T
4 Y) w, W0 _9 k# G( j) {注意:
8 h& _, x# Y1 y! X9 Y3 X! @& L) `, a( [) e5 {) W0 O* g2 _& R! S( W3 @" l
1, 如果你新增的代码,对速度有要求,可以将其对应的.o 添加到内部 FLASH,即放在:
% S3 V2 C- {- ?
$ `% [) `& j3 B z+ s& s4 PER_m_stmflash 运行域。
+ i* c3 @/ S* t' _+ g; w6 J% d3 V. s+ k) i j
2, 如果添加新代码后,程序无法正常运行(通常表现为黑屏/不启动),可以尝试将新增* R8 F8 [( M; }1 E8 l) c
7 D3 w6 j/ M; @) C4 G3 X
的.o 放到 ER_m_stmflash 运行域后(重新编译)再尝试。如果还不行,可以尝试将所# y* r6 v: T1 E4 F% y' W+ H9 V+ e/ J
) U) M- m' X6 y- a: g" @( p有代码都放到 ER_m_stmflash 运行域后再尝试。
) D6 c5 j& e1 c8 {. g
1 [4 _# n, j) W% E6 g8 \$ q
9 r# W$ l3 h& Z; O& }/ m4 m1 f) \7 z: p7 l$ K! ~. q
二、通过boot程序跳转到qspi flash执行用户程序# j/ t" d/ ?! f, [$ d# H+ I
% c) J ^& ]+ W5 i0 U2 o 第二种方式就是通过在片内flash中存放boot程序,然后通过boot程序跳转到用户程序,用户程序就放在qspi flash中。/ y. l' R, h% A
8 T4 X* E( ~6 ]
boot程序主要的工作就是初始化qspi,并设置成内存映射模式;禁用中断,重新配置中断向量表地址,然后进行跳转。这里分散加载文件直接使用系统自动生成的就可以了。程序起始地址0x08000000。7 x6 d4 y! D' r8 t: ^
/ n: u9 n: Y; W! c. ~
用户程序需要做的操作就是把程序起始地址改成0x90000000,然后添加下载算法,因为整个程序都是存放在qspi flash上的。下载完成后,用户程序就可以通过boot程序跳转执行了。
1 y* m/ [+ h1 V! C7 ]1 R/ }
4 M" b$ x: ~8 b ]7 E
; F. k I' l! u( A% T! e+ {( r+ |5 s& I4 J* e( _
$ y0 ^' T7 }, m7 F3 |. z9 i/ P
! i" v S# m) ?4 ^- {% T
注意,不管是第一种还是第二种方法,都是将qspi flash给设置成了内存映射模式,而此模式下qspi flash是只读的,所以这个时候就没办法用来作为存储器来存储数据了。不过网上也有人说内存映射模式下也是可以写数据的,至于真假,还没验证过。* c$ N* U( T& H' P# K! J; w
: h3 ?1 H, I$ u; H6 y [
% p, e& W0 K% S/ H; b- Z
|