Part1前言 当我们要下载编译好的镜像到Flash时,首先要做的一步就是选择合适的Flash下载算法,而这个算法本身就是一个FLM文件: * M, b, j: i) l) L
! |) O. C# D2 ]1 f& m
代码既可以下载到内部flash,也可以下载到外部flash,或者一部分下载到内部,一部分下载到外部。 ) v, D! w& m$ A6 y6 s
Part2一、将代码中的图片资源下载到外部flash在UI设计中往往需要大量的图片和字体,图片和字体资源在代码中以静态数组的形式存在,这些大数组在内部flash中一般存放不下,所以需要把这些占用资源比较大的数组放在外部flash中,然后通过QSPI地址映射的方式访问,或者通过SPI将flash中的资源分批读取到RAM缓存中使用。 - 通过MDK打开分散加载文件,配置“ExtFlashSection”段:# W# i( n& o7 m& Y x
- ; *************************************************************
+ L" w( {# u, \8 ` - ; *** Scatter-Loading Description File generated by uVision ***
; {7 c: [0 @. D7 Q; b - ; *************************************************************4 {4 Q$ {8 W0 l5 D
- # f, i; C* g8 Z& [4 E
- LR_IROM1 0x08000000 0x00020000 { ; load region size_region
N$ {/ b. {5 o7 r - ER_IROM1 0x08000000 0x00020000 { ; load address = execution address! D! J% {6 A" p g! I$ H' x+ C
- *.o (RESET, +First)
7 d8 k* ]. @( o, v; `: b, b - *(InRoot$Sections)
% X/ F/ N/ l: P' @5 P( P - .ANY (+RO)5 @2 f9 ?5 |3 g! R
- .ANY (+XO)4 w2 z/ y! y8 d7 Y# M0 ?& k
- }9 ]/ j5 u* a6 u0 g
- RW_IRAM1 0x20000000 0x00020000 { ; RW data
. A2 }/ w+ O" A5 E" b" b4 j# i/ P - .ANY (+RW +ZI)
# m8 T% b- B6 r - }
7 ?! l: m, x" U8 K - RW_IRAM2 0x24000000 0x00080000 {
9 W5 k0 {- W' A& G0 ^6 T - .ANY (+RW +ZI)
$ x2 g& L; y9 ?2 L - }9 h8 \$ V7 s! S( N
- }
9 h* b3 l# u* u0 }" U0 ^+ d! R9 Q; O8 d
! Q$ V* u$ [% Q! O- LR_EROM1 0x90000000 0x01000000 { ; load region size_region3 Z6 c9 ]+ i+ D: Y8 X& }
- ER_EROM1 0x90000000 0x01000000 { ; load address = execution address
+ T. p0 B# J d+ H) k, ]# y1 J5 v( d - *.o (ExtFlashSection)
$ l" Y$ M4 ]% u1 B$ \: D" P r - *.o (FontFlashSection): q' |" ?$ ~6 C! g; _' a
- *.o (TextFlashSection)3 o8 Y9 C5 N4 t2 D
- }
E& T4 r8 ?9 {, D1 N4 k3 h - }
复制代码
& K2 r& D% l" [9 B8 u/ T
6 K: B& ^! ] u添加LR_EROM1 段,起始地址为0x90000000 ,大小为0x01000000 。
" X! j( x" ?: V5 g) |; z' d2.在代码中将图片资源分配到ExtFlashSection段 - #define LOCATION_ATTRIBUTE(name) __attribute__((section(name))) __attribute__((aligned(4)))
" C+ B; N& f2 z! \( F5 O/ `6 ~. ]/ O6 J
0 v& B- s+ d9 x3 K" U- KEEP extern const unsigned char image_watch_seconds[] LOCATION_ATTRIBUTE("ExtFlashSection") = // 4x202 ARGB8888 pixels.
; i( ?7 W$ W1 a+ i+ N) J( U! s9 [" L# q - {
2 `0 j8 v6 U V9 W - 0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0x00,) a' M8 [8 W7 R
- 0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0xff,
6 W6 r4 a/ C7 W5 Z* E" D8 f - 0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0x00,
- w% F& E/ S j8 V - 0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0x00,
& b2 S- q- b$ v, R3 n7 U0 ^ - 0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0xff,
! v$ q2 I- a# k; h1 ~' A3 L; {6 a9 l. Q - 0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0xff,8 I3 h& U# s& U) @+ E
- 0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0x00,
( b b2 m. W% b( m4 ^ @ - 0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0x00,/ ~3 e( f( i7 J7 D. P- V* N- ^
- 0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0xff,
8 J+ r. U( R( J. w3 h$ T, t, c9 z3 L - 0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0x00,# e: U+ x/ b7 a( N* k w
- 0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0xff,
+ d( `$ y/ x0 K' i5 a. ? - 0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0xff,
G) L- t0 @* N& W0 p+ m6 E1 e9 ? - 0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0x00,, [/ R( A. o- I( z2 i% s7 R. S5 z
- 0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0x00,
- i2 [' z3 O3 J4 H, o - 0xf8, 0xfc, 0xf8, 0x003 I, R& T; l/ O8 q/ I, ^
- };
复制代码 - I# W2 v- B7 A- ?$ K
% N3 F3 d6 Q% h' g3 r
3.编译代码! d4 W0 p0 ^( [" x3 B. ]
, e; B2 b; C+ H' D8 A% f, x w0 r
1 o5 D, f) a! X5 O0 O9 `) l
$ I9 L9 n& d. a; u& q
! C9 o6 ?1 H! B/ H, e3 A, R/ h
# a* o# t& J4 Y5 v& k5 r查看map文件,image_watch_seconds这个数组已经被分配到了0X90138690这个地址了,这个地址正是LR_EROM1 所在的区间。
( c: W! D% G' |3 L, y Part3二、MDK下载算法原理1程序能够通过下载算法下载到芯片的原理通过MDK创建一批与地址信息无关的函数,实现的功能主要有初始化,擦除,编程,读取,校验等,然后MDK调试下载阶段,会将算法文件加载到芯片的内部RAM里面(加载地址可以通过MDK设置),然后MDK通过与这个算法文件的交互,实现程序下载,调试阶段数据读取等操作。 4 } N0 I1 E0 }+ z: [* n6 R# d0 G5 z
2算法程序中擦除操作执行流程4 S; c# J3 W2 f) Q z f
+ p% }' K0 o: e" d
6 ?/ c$ f) D: F: p
1 {2 C7 y/ W" G& n
% f6 o' I$ G3 b
+ H8 V( M" N, W+ z算法程序中擦除操作执行流程- 加载算法到芯片RAM。
- 执行初始化函数Init。
- 执行擦除操作,根据用户的MDK配置,这里可以选择整个芯片擦除或者扇区擦除。
- 执行Uinit函数。
- 操作完毕。$ G, S) Y6 J1 i5 A1 z# C
" G4 ` t/ q0 e/ C% p
. r# ] ]+ b- r# B$ Q3 q- g3制作FLM文件步骤- 将ARM:CMSIS Pack文件夹(通常是C:\Keil\ARM\Pack\ARM\CMSIS\ version \Device_Template_Flash)中的工程复制到一个新文件夹中,取消文件夹的只读属性,重命名项目文件NewDevice.uvprojx以表示新的flash 设备名称,例如MyDevice.uvprojx。
- 打开工程,从工具栏中,使用下拉选择目标来选择处理器架构。
- 打开对话框Project - Options for Target - Output并更改Name of Executable字段的内容以表示设备,例如MyDevice。
- 调整文件FlashPrg中的编程算法。
- 调整文件FlashDev中的设备参数。
- 使用Project - Build Target生成新的 Flash 编程算法。
6 |9 `2 C% @# [0 V( t8 O
以上步骤是利用官方的工程模板修改代码,这种方式网上已有很多教程(推荐使用这种方法),不再重复介绍,接下来介绍一种不使用模板工程制作的方法,目的是为了了解其实现原理。
* c, {% n3 l8 w( w! Y; U2 N) y Part4三、使用STM32CubeMX新建工程6 j5 l. S1 c' U8 U8 [- j& r
4新建工程硬件平台: RT-Thread官方ART-PI H750开发版 软件: STM32CubeMX,MDK 选择MCU型号(STM32H750XBH6)
) W# d/ k) i4 J# @* C' U; I6 Q- y% o' \0 }4 L- ` }: I q; y4 U# Q: Q3 a0 w
! S% ?( q6 F1 [" d8 I6 s* ^2 J
7 p# u8 f6 B) R* Q
选择MCU型号
s$ Y/ k8 G7 e2 D& u- W! |
/ m, j% y* H6 w2 \) J' p2 b8 k
' e5 W+ Z8 y8 l, o$ [! o# B配置SPI
: j% F( a& J! Y) W' a3 k
! C2 [& X! O2 }0 f) z
' G6 b; \5 o) }0 W. A8 I9 }
+ `3 p1 W2 R' ~+ ~9 {) w& N, j1 q3 x: {1 }& b
+ n9 z" I! z3 W& t2 `
2 k7 {6 [- b! O
& _1 ^/ u- w, t8 ?/ [% x配置UART! ~8 w1 A" v1 t
6 w/ a+ h# V z& Z) k$ C: c' q+ n
& g/ t* \) ]5 D6 T3 t; v$ a
" ?1 G$ [! p" j2 @
2 F2 B7 f; l# g* a2 B0 n; S
`7 f$ }# b) [( B% V- U6 P8 U" U- z配置时钟树
2 _: B: t1 u' R/ ?# v s! ^$ ]$ K4 x [( I
9 ^' |# `* W4 x* t& E' N$ e* ^. R
4 p) m# ^* U+ \7 x1 Z% F
0 ]' j: ?# g( g/ \. ^( o! f9 \
* L4 ?/ ^( Q( T x: D% o _: U) X" w% w& @8 l0 E
6 ^: c+ b* ~. I( R* S& q设置调试接口
. u% H: t! I7 E# l
- P: ], V/ m1 [( L
! _( z3 r9 y# [$ K6 w# t3 H* |
6 T0 {. M+ Z8 ^, o P4 J
' v6 P: r$ z$ X, v+ W S
2 w/ c. f2 K# h
设置工程并生成工程
, K+ W8 O, Q9 p2 O8 `. r0 Z# o2 P+ g9 o' g
) x4 N7 u! m- f6 ^
! a3 j7 B; J7 [& n; y1 T
) a/ Y, c5 {3 o* v/ b6 s, E% S* t6 f# w
52. 移植SFUD串行 Flash 通用驱动库. T( A" M5 V0 Q; L& ]0 T8 m
SFUD 是什么SFUD是一款开源的串行 SPI Flash 通用驱动库。由于现有市面的串行 Flash 种类居多,各个 Flash 的规格及命令存在差异, SFUD 就是为了解决这些 Flash 的差异现状而设计,让我们的产品能够支持不同品牌及规格的 Flash,提高了涉及到 Flash 功能的软件的可重用性及可扩展性,同时也可以规避 Flash 缺货或停产给产品所带来的风险。 - 主要特点:支持 SPI/QSPI 接口、面向对象(同时支持多个 Flash 对象)、可灵活裁剪、扩展性强、支持 4 字节地址
- 资源占用8 W# j3 y% O& e$ O
- 标准占用:RAM:0.2KB ROM:5.5KB
- 最小占用:RAM:0.1KB ROM:3.6KB' s- a& w1 \+ c0 W
- 设计思路:" R" t5 y' ?( }* b( d' U6 h, A2 m* Q
- 什么是 SFDP :它是 JEDEC (固态技术协会)制定的串行 Flash 功能的参数表标准,最新版 V1.6B (点击这里查看)。该标准规定了,每个 Flash 中会存在一个参数表,该表中会存放 Flash 容量、写粒度、擦除命令、地址模式等 Flash 规格参数。目前,除了部分厂家旧款 Flash 型号会不支持该标准,其他绝大多数新出厂的 Flash 均已支持 SFDP 标准。所以该库在初始化时会优先读取 SFDP 表参数。
- 不支持 SFDP 怎么办 :如果该 Flash 不支持 SFDP 标准,SFUD 会查询配置文件 ( /sfud/inc/sfud_flash_def.h ) 中提供的 Flash 参数信息表 中是否支持该款 Flash。如果不支持,则可以在配置文件中添加该款 Flash 的参数信息。获取到了 Flash 的规格参数后,就可以实现对 Flash 的全部操作。
# o* J- e6 w! M0 e% q- L) q
( g1 t$ l) ?( x# [: U4 T- K+ N
8 w0 {$ E& S( |9 h, D移植SFUD将下载到sfud源代码放置在工程目录中
4 u, I0 }8 K; y c+ O& `+ w+ {) D. ]
% |* x" A9 R% E9 R9 m4 o将sfud添加到工程目录: 0 [( o( |3 D' x
" p) o3 c$ V( o9 Y! N7 @6 o
修改sfud_port.c文件: - #include <string.h>5 s3 W0 L7 t2 x8 M% ^- {% \
- #include <sfud.h>
5 l. U+ e% Z: a% X - #include <stdarg.h>6 ~+ t& w. z, g6 W$ }
- #include "gpio.h"6 r! c8 U# b, p
- #include "spi.h"
+ p I/ l }( q$ n
! M( \ k3 y" v" ^) R3 ~- o8 v6 s- typedef struct {; W- H: ]: N, B. N# [" s$ f3 g8 w
- SPI_HandleTypeDef *spix;3 L: o2 V& L9 M; G! M4 }6 ]
- GPIO_TypeDef *cs_gpiox;
; e6 v3 o0 k1 X- Z+ y - uint16_t cs_gpio_pin;
' M6 X& ~; r4 G - } spi_user_data, *spi_user_data_t;
3 P1 E% I+ n. k2 ? - 3 \- B9 U0 h8 ?$ ]9 K, c+ s; I/ K1 I
- static spi_user_data spi1;) p" z7 R5 F* f f' m
- static char log_buf[256];
) y' P1 g9 M7 T - void sfud_log_debug(const char *file, const long line, const char *format, ...);
4 x, ~. Q; x; i; a5 j/ C) K - extern int rt_vsnprintf(char *buf, int size, const char *fmt, va_list args);$ H7 F# |& E( @7 X8 x7 D
- extern int rt_kprintf(const char *fmt, ...);
/ `- g3 b3 Y$ L7 M2 O7 T - static void spi_lock(const sfud_spi *spi)! I- P$ {/ ]! p- b4 k/ B2 R
- {
% H1 @5 B* l( P. Q4 `1 K% B, L - 9 E/ m- o8 R5 o
- }
: {) x5 k9 U: L0 _ H* b9 v
) l1 t' G2 l7 Q7 S- static void spi_unlock(const sfud_spi *spi)
/ K7 f( _& {$ O# T" i* r - {; U: l8 ~3 Z: d2 T5 e% x) N' o# Z: ~
- U4 R4 N9 t' U- i+ Z" B
- }6 R$ C6 T; c! z2 X1 n1 N
- /* about 100 microsecond delay */
/ v* h5 a" a3 T3 j e) s - static void delay_100us(void) {) u ?% g; f" s
- uint32_t delay = 2000;7 D" E" q" a7 ~5 ~* Q2 C
- while(delay--);
5 u# ~, t0 B4 k" i' T b- ] - }' a: ]& J6 @$ U; x* G
- /**6 b. o" ?. p& u/ w% |0 D
- * SPI write data then read data- W3 O/ |- [0 p' B
- */
! ~: d% h: D0 @9 d - static sfud_err spi_write_read(const sfud_spi *spi, const uint8_t *write_buf, size_t write_size, uint8_t *read_buf,
' I2 K& k4 t2 h; C" L" b" W: f - size_t read_size)
1 L1 a. u! g7 `" K" A4 ~6 `% l - {: c6 r3 Z/ x5 P/ t( B
- sfud_err result = SFUD_SUCCESS;
) q! |$ s( p. Y2 P( c! D. y5 K - /**
# g$ Q6 W6 b3 B9 [ - * add your spi write and read code& ~1 r6 b! Z2 v e8 ~
- */* D' s% _. N5 R# R. a& K( _
- spi_user_data_t spi_dev = (spi_user_data_t) spi->user_data;
% M$ U0 [& u1 j" p9 R5 W - % D% T% Z- f) g# [/ f+ ~0 l
- HAL_GPIO_WritePin(spi_dev->cs_gpiox, spi_dev->cs_gpio_pin,GPIO_PIN_RESET);
+ C# o) N' G0 x: i - if (write_size) {, q8 i% D5 q, H6 m$ a* N
- HAL_SPI_Transmit(spi_dev->spix, (uint8_t *)write_buf,write_size,1);2 b! t3 _% p# F) J9 D) e
- }
0 e; t7 Y4 t' z% s, E, a9 V - if (read_size) {5 W8 G" y! K- |2 v* R, r
- HAL_SPI_Receive(spi_dev->spix, read_buf,read_size,1);
2 x( A$ w5 D: s, i# } - }
* Q/ X+ K& M4 M o, Z - exit:: R& |) K0 j6 c2 j. Q! X
- HAL_GPIO_WritePin(spi_dev->cs_gpiox, spi_dev->cs_gpio_pin,GPIO_PIN_SET);' Y/ n) x4 Z- V5 R9 R8 z
- return result;# l) t7 Y4 g) @% \+ d5 X4 P
- }
6 B3 z8 |2 M6 u+ ?5 Q - # d2 A! o( L6 k% |7 q; ?' j
- sfud_err sfud_spi_port_init(sfud_flash *flash)
* I5 I4 l l( X - {
! J7 ?( q' D% E. r+ Q/ [ - sfud_err result = SFUD_SUCCESS;
3 ~/ V; `5 U# C2 f9 W( B - , n5 k: a! Y. u4 T1 d/ W3 Q
- switch (flash->index) {
! P4 Y' h5 Z) A. J - case SFUD_W25Q128_DEVICE_INDEX: {
7 ~4 \! B8 @- D! p4 z - spi1.spix = &hspi1;
3 C* ?2 B* b, o% X - spi1.cs_gpiox = GPIOA;
: u9 U2 S5 z( k0 A - spi1.cs_gpio_pin = GPIO_PIN_4;6 |2 q# k1 U( @. d8 o4 d3 u
- /* 同步 Flash 移植所需的接口及数据 */
( q% r# D* H6 U% k4 G' R - flash->spi.wr = spi_write_read;3 w1 T1 }, v/ p! _
- flash->spi.lock = spi_lock;
% t. m* |2 P2 e0 {# x q - flash->spi.unlock = spi_unlock;5 M3 G5 D& P9 V
- flash->spi.user_data = &spi1;
- s0 @" r; j, a5 m3 z9 Y - /* about 100 microsecond delay */" |2 |- X# G& R5 Q+ a
- flash->retry.delay = delay_100us;
" m0 l; p4 K$ G4 @. r! ~ - /* adout 60 seconds timeout */1 C( W9 t1 O0 W& Y! _- c/ [' B. m2 Y
- flash->retry.times = 60 * 10000;
1 y" u$ g6 y3 K6 Y# S. J# S5 r - , U7 q2 `- i+ c, ?1 e! j" F {# ?
- break;4 C% F7 J3 w* E3 {" X+ f8 i. p; ^
- }
% I/ ]9 p, c& W! W: \, e& t" i - }+ q: P+ u- t& a+ O
- ' v1 s: N9 c/ J
- return result;
* B8 b0 n) q- J0 F4 ^$ H( { - }& w9 Y0 z3 r8 L3 ]5 _! @; v
3 u. H8 ^5 x) O; L1 W4 j0 f% Y- void sfud_log_debug(const char *file, const long line, const char *format, ...) {3 p) X+ A1 J/ Z; v
- va_list args;/ s n0 e( V) v% I! ^) A H" T
- + }5 f# h+ N; i
- /* args point to the first variable parameter */* {0 o: C3 d& l; R6 }6 C8 V9 ~4 n' _
- va_start(args, format);
/ K9 Q* O2 R* V2 o - rt_kprintf("[SFUD](%s:%ld) ", file, line);
% [- H! E2 R' @ - /* must use vprintf to print */
6 h# B2 J3 y6 F* F. Z - rt_vsnprintf(log_buf, sizeof(log_buf), format, args);
) D0 o, Y$ ^$ D0 ~# R2 b4 _ - rt_kprintf("%s\r\n", log_buf);8 o. t) w7 H5 L" O2 D7 f# \
- va_end(args);# ]0 @5 T+ K' j) e
- }
1 W) {8 A2 `8 F. h0 }) p& x: O
) {! E8 L% n3 o( y6 {- void sfud_log_info(const char *format, ...) {
7 x( n, j. N# _* B) r - va_list args;
6 K! o( ?6 H$ w; D' M6 K - 6 F( P, Z# t9 K0 L% y
- /* args point to the first variable parameter */
; A8 `; o k+ h0 f% x - va_start(args, format);
# I: J4 L3 h6 k) n3 ~( `8 W - rt_kprintf("[SFUD]");
+ m) O' b7 p5 p! n; t - /* must use vprintf to print */ ]5 j* [* i/ o1 @7 k e
- rt_vsnprintf(log_buf, sizeof(log_buf), format, args);
* ^3 W3 D% u( ? - rt_kprintf("%s\r\n", log_buf);
3 b d* [) m4 P: K) Z& ]: w" ~; T. V - va_end(args);8 @7 ^# |3 n0 g* ~" F
- }
7 M8 ?7 k$ T6 j0 E: r
复制代码
P; y- u; A0 P6 O
# H# h* o% }8 g, ~0 Y测试SFUD在main.c中添加测试代码: - /* USER CODE END Header */
0 v; P, U+ F$ V1 _1 F - /* Includes ------------------------------------------------------------------*/
, A& S2 n' B' G0 W4 c1 v - #include "main.h"
( k; \1 V( [5 {4 `% d - #include "spi.h"' s; M+ R2 Z9 y
- #include "usart.h"
* o# `- L: Q9 ]2 W- A6 k - #include "gpio.h") d( R6 d3 F% l' |" o" d! Y# y
- . f8 y" C3 i) z6 ~. r
- /* Private function prototypes -----------------------------------------------*/
2 }1 ~) c" ~5 |4 k - void SystemClock_Config(void);/ G" \, j& ]$ I4 T8 T: ]8 o
- static void MPU_Config(void);
* Z4 q( A r7 m! I+ o - /* USER CODE BEGIN PFP *// n" q' ]& k! _3 j% t+ a* o% f
- extern int rt_kprintf(const char *fmt, ...);. G8 b' z7 l: d3 K
- #include "sfud.h"
" K# z7 ]) e9 A$ h - /* USER CODE END PFP */3 k8 F4 m! J0 b% T; s+ }
- * A$ Z2 h, |; H4 Y. T( U, E
- /* Private user code ---------------------------------------------------------*/4 k V r; Y& C8 k" h
- /* USER CODE BEGIN 0 */9 s- N1 t" }0 r+ I
- #define SFUD_DEMO_TEST_BUFFER_SIZE 1024
8 X" h( m0 n5 z8 j4 u: ?
" B1 X8 g+ }1 o4 _! L# |3 T- static uint8_t sfud_demo_test_buf[SFUD_DEMO_TEST_BUFFER_SIZE];
: y) F. s- x/ J3 d/ ?) o7 w - /**
/ ?, c# {( v" B' d+ ` - * SFUD demo for the first flash device test.3 t+ W, m7 R$ F3 l2 _
- *
5 @. k1 A' \# X* v - * @param addr flash start address% u- N( _9 T( p
- * @param size test flash size
/ Y {& J) m* W5 v( A, u9 H - * @param size test flash data buffer
! }/ s, t* T4 z1 @/ w! H2 @7 u - */
: `$ O8 d I' b) m; W, L$ C - static void sfud_demo(uint32_t addr, size_t size, uint8_t *data) {: X1 H4 Q7 h* |
- sfud_err result = SFUD_SUCCESS;% _* |- C1 C+ W5 l
- const sfud_flash *flash = sfud_get_device_table() + 0;6 @9 O7 V9 y3 q' a
- size_t i;
! z4 H9 L# E7 N" n! I - /* prepare write data */
5 k* L, e4 w4 A& P+ T - for (i = 0; i < size; i++) {1 {/ L& s! X& e0 b0 X$ b( E
- data[i] = i;
l% S5 y! F5 i1 C8 R& t3 { - }6 Y/ j; L g( Q% q" D
- /* erase test */# j2 u1 c; C) x/ [5 N' k; s( q
- result = sfud_erase(flash, addr, size);
( {6 C# c+ s: N" `( I- Y; l y - if (result == SFUD_SUCCESS) {: c, t9 b4 N! c
- rt_kprintf("Erase the %s flash data finish. Start from 0x%08X, size is %d.\r\n", flash->name, addr,
& x7 d* p0 I' V+ J: u) V$ a - size);+ V# s2 Z# Z" [
- } else {3 P5 P/ d7 O: W/ g; A
- rt_kprintf("Erase the %s flash data failed.\r\n", flash->name);
+ C6 q0 Y% g! v( N* Q) e' {. { - return;5 q8 Y* G9 `! I6 j' q
- }% ^$ B& R2 u8 z" a( D, m4 D1 s
- /* write test */5 v% O( L# W) Z7 v- v- D
- result = sfud_write(flash, addr, size, data);
0 j0 g( T% |5 z7 k1 T - if (result == SFUD_SUCCESS) {
( F2 n5 M8 ]8 f+ [1 Q* p% C/ [ - rt_kprintf("Write the %s flash data finish. Start from 0x%08X, size is %d.\r\n", flash->name, addr,5 F6 `. A3 {- K' n/ p# C
- size);5 n3 z8 @+ ]- D6 P) c! L
- } else {
! X: d! O9 s7 H+ M1 _1 ]$ y- K - rt_kprintf("Write the %s flash data failed.\r\n", flash->name);$ k" F( |! q# X! N4 `! m3 c" f
- return;
9 H) g7 l- _6 T4 s' j$ k1 j - }* Y! `& J) x! x
- /* read test */
" }3 j" @) U. c Z) M4 K A# i - result = sfud_read(flash, addr, size, data);" Y6 k8 ? W/ r7 T+ z: l# y
- if (result == SFUD_SUCCESS) {3 V9 _7 ^2 s% w( N5 X& l
- rt_kprintf("Read the %s flash data success. Start from 0x%08X, size is %d. The data is:\r\n", flash->name, addr,
6 e+ M0 d) s# J; i9 x5 L - size);
, @: ^; [( S6 l. D& O& H - rt_kprintf("Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\r\n");% b8 D: ?# ~" E; L+ S5 n
- for (i = 0; i < size; i++) {
3 N' t! l+ U5 z x& U+ y - if (i % 16 == 0) {
. F3 o' x& S( P# R7 e - rt_kprintf("[%08X] ", addr + i);7 Z/ B- [) w8 K3 Q% R
- }; f% }9 j5 n( Z4 X
- rt_kprintf("%02X ", data[i]);
/ a2 l. N: T3 a2 D - if (((i + 1) % 16 == 0) || i == size - 1) {
O* W( N/ p; [5 K: d' d" X7 } - rt_kprintf("\r\n");
9 k. R- }! R+ z6 N) h - }3 S4 R& h& X* ^6 {3 y) f/ A8 N
- }: L9 d4 c5 ~$ b) |3 I
- rt_kprintf("\r\n");: m2 P1 J. g! t* S
- } else {) B, X0 z; W. w% C0 y
- rt_kprintf("Read the %s flash data failed.\r\n", flash->name);3 S/ P2 O0 R! L
- }, y/ K Q& W. Y4 I4 K7 c. |) J' O
- /* data check */& Q- ?7 [4 r: F7 Q/ q. }
- for (i = 0; i < size; i++) {1 _( |/ f: ?, h0 g$ Q
- if (data[i] != i % 256) {
% ^( @" ]+ {& P) l- q3 m. j9 I# @ - rt_kprintf("Read and check write data has an error. Write the %s flash data failed.\r\n", flash->name);+ }$ e; {2 a" o2 C
- break;6 @! H3 @3 p4 H A- C2 N
- }8 i% ?& w7 }! F
- }
% M. h' y9 r, w& F7 X; U - if (i == size) {
# G( j* g* |# V6 M - rt_kprintf("The %s flash test is success.\r\n", flash->name);2 f! u6 a" }! ~) e: C
- }
& w5 b% Q' y4 h8 ] h* x1 B( C - }0 k$ l4 z+ ] ~! x @! q7 S+ ?0 [
- C0 Z! a1 X. H7 f- R- /* USER CODE END 0 */; T* v4 D/ [3 @; W6 m' @7 e# F
- 4 E% x Z2 D+ m/ @
- int main(void): e, @6 C6 }2 g2 ]% \0 B1 H
- {# B6 d" X6 o( t# w; b
- 8 J* S' D( C' Q5 W a
- /* MPU Configuration--------------------------------------------------------*/
! z- H% }8 a- E5 i7 u+ C - MPU_Config();/ t! Q$ |/ n1 A
" k. {( e7 D4 u1 T( J- /* MCU Configuration--------------------------------------------------------*/
! ~( E1 K+ s/ v& F/ m/ J$ e9 | - # e5 }' D7 t& |' ~. B
- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
0 s# r- w* G) e8 ^$ o* Q; x - HAL_Init();
; b. ]' R) v7 b- A - / Y. p8 R# W4 W; v2 [5 j B( f
- SystemClock_Config();
1 b7 d* J2 S/ a
k- n3 \. p: T" X% E5 W- MX_GPIO_Init();6 L: }; x1 n( V8 D8 m, |
- MX_SPI1_Init();- M2 ~% v5 H" |2 q! K' M+ k
- MX_UART4_Init();4 M4 \- ~ b4 y0 v( Z9 R- \
- /* USER CODE BEGIN 2 */, W" R& B ]; @( h, g/ T, l$ l& [/ m
- if (sfud_init() == SFUD_SUCCESS) {7 x8 s4 R( l6 l6 o- R' e A
- sfud_demo(0, sizeof(sfud_demo_test_buf), sfud_demo_test_buf);5 j6 e5 C) M& ^
- }
, ^% {. u5 R$ D4 _$ x9 V/ I: B. { - . P" W7 Y# t& D, R" o5 A
- while (1): S& r) d& o A' r
- {; ^6 d5 D" V: V
- 8 [( R& a q5 i- }, l( U J; g
- }
4 N! _ V* h1 d( { C/ | - ! l9 w; b) f; S& b+ c7 y6 Q
- }
& M# O4 J, O: v% S1 R - 5 h Q# G; P- L. S$ N; c- m6 X
- #endif /* USE_FULL_ASSERT */
复制代码运行如下:
# E6 q" _$ B3 w6 j; G& [
$ V- ?7 E" w( y( A1 I: b8 y$ O 63. 制作下载算法
/ ^, E, R$ O$ U, r0 n0 C重新生成不带main函数的工程
8 r6 F6 l" n4 N$ N7 C( s& @
: I5 e0 J0 {5 M; a2 v
, O$ {4 W* s1 @2 {
8 B0 J/ A. o' |0 q7 C6 n添加修改编程算法文件FlashPrg.c模板工程里面提供了FlashOS.h和FlashPrg.c ,复制到此工程中,然后对FlashPrg.c 代码进行填充。 - #include "FlashOS.H". [9 W7 {' B; z( k
- #include "sfud.h"
Y3 ?3 L& B: X5 u+ v - #include "gpio.h"6 C6 p! ~: `# B" I7 D9 I
- #include "usart.h"
, x3 l4 @. i: }4 @6 n* @5 E' D; P: D - #include "spi.h"
1 Q1 m8 h1 _9 Z+ a* o1 r
, m4 w# }, j8 s5 w3 p- static uint32_t base_adr;7 f3 p0 r" r6 Y- W/ h! Q _
- . m8 G& E) t* I# i
- /*; i- \6 k0 A' X# l
- * Initialize Flash Programming Functions
# Q' {3 _- F% e: b - * Parameter: adr: Device Base Address
) n. z3 f( g$ Y' T* G& H7 u - * clk: Clock Frequency (Hz)/ m: A1 E7 Z- ^/ ^4 c( `+ @. n' [
- * fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify)
5 f, z8 {( y# F( y, K9 x - * Return Value: 0 - OK, 1 - Failed
) b/ E6 \8 R9 k* K( [; @4 s - */
% E( g$ k5 j7 D1 m/ E% d - 8 Z# [$ L0 |" V* b
- #if defined FLASH_MEM || defined FLASH_OTP1 H$ J* H3 L, m
- int Init (unsigned long adr, unsigned long clk, unsigned long fnc)
5 V: q3 d7 w x; @ t3 t* V - {9 J' Q0 |. X& X Z3 l
- MX_GPIO_Init();8 W U% D* J: v, D
- MX_UART4_Init();
/ V+ {& z9 H% g4 |' u: x! L - MX_SPI1_Init();5 e7 K9 D6 g0 I0 ?3 Y
- base_adr = adr;
9 {. w& a0 W) E2 F1 Z - if(sfud_init() == SFUD_SUCCESS) {
# r% c5 n9 H% e! H+ o8 q- C! t - return 0;
2 S- y7 u& `( X3 m - } else {
9 O' o% t, k7 W9 F( [, w* P( n - return 1;
7 ^4 V+ V7 }# b( v - }+ R9 x% D+ v# Q) x" E/ C
- }2 R# \; h* x( E3 J
- #endif
9 M' _% p6 w! f7 ] ]$ g - " `' G- s" u2 h1 n, M
- + G5 ]- j; e: }1 x
- /*# L* `" j3 ?5 P& q
- * De-Initialize Flash Programming Functions
8 \3 U" f8 X. q* o: f - * Parameter: fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify)
5 p2 l+ a, Z# C$ w9 ?, i8 C - * Return Value: 0 - OK, 1 - Failed
( m8 p! p- f2 y' r+ |+ J - */% m9 H8 Z: F1 X1 q$ B1 v& ^- W
- 9 I/ D. U! M' V
- #if defined FLASH_MEM || defined FLASH_OTP
8 g9 ^ J$ l5 d: s! k! \ - int UnInit (unsigned long fnc)
* M( G. d1 G h9 @# E. r - {
: t& Q* a J/ g/ D0 ]' v' E - & g% Z6 Y. y' f4 o$ ^
- return (0);
* J2 c4 X" J$ ]# Y' T* r - }
* } R; W6 d- U - #endif$ X( m9 G9 Y' q9 \- X+ [7 {
- v5 d, `3 k( h9 o0 ?- /*
- L& t% @# X" t$ H9 o - * Erase complete Flash Memory: d8 |. ]) p# U2 n9 Q: f' t
- * Return Value: 0 - OK, 1 - Failed8 i g# ]2 v' h" h% }/ k
- */
. B2 I7 ~" \. P8 d$ I# n
( M# N- l# J- K1 \1 i7 ^9 K5 n- int EraseChip (void)# t# Q$ M$ u# J
- {$ T* h7 {% ]2 v& ?
- int result = 0;
- I4 d& p- _# w$ J" Y7 ~ - const sfud_flash *flash = sfud_get_device_table();
0 M6 W* z4 L& p2 T V. y1 A - /* Add your Code */" H- t1 w/ S2 _9 S3 F3 b! O4 b
- result = sfud_erase (flash, 0, flash->chip.capacity);2 u7 [- u1 v0 l5 w- w/ p5 T9 U
- + O, A, o8 i7 p* C
- if (result == SFUD_SUCCESS)
6 Q6 R6 z( ~2 E8 l3 _0 i$ @2 [9 q4 \ - return 0;
& a2 x/ U, z* d/ h - else
9 J5 `- Z- F, b) \9 _& ^ - return result; // Finished without Errors8 V1 U1 O# V- T4 P! h
- }
" J" a. ?5 P# X+ A9 C, n - 6 J$ B# U/ p- P' x# R
- /*4 k: Y5 U3 Z& U5 L
- * Erase Sector in Flash Memory3 Z% @$ l! x2 x
- * Parameter: adr: Sector Address' ]0 g$ A2 |! e/ X+ |2 [
- * Return Value: 0 - OK, 1 - Failed
/ o. t3 @. @/ Y. u( C$ P - */
, R f* M% m" ~0 ~ - 1 X6 J* |% T* [
- #ifdef FLASH_MEM
$ w( `- R* _! z; j - int EraseSector (unsigned long adr)
3 H% J8 R9 j3 l' |4 q0 ? - {. R! \0 ~. X' R4 ?2 i
- int result = 0;0 n; ]! s* n! R9 t% r1 ]
- uint32_t block_start;
, H' i U/ I ?" o0 V9 I - const sfud_flash *flash;
- U5 M) A, b2 j) G - flash = sfud_get_device_table();- c9 W0 r. x: |; ?9 H: Y% q
- block_start = adr - base_adr;/ b& B0 b9 n" b
- result = sfud_erase (flash, block_start, 4096);
$ }4 ^2 g. g: ?$ \4 ^+ Y: X
; g# c+ o5 u# ~2 J6 V- if (result == SFUD_SUCCESS)$ K* j) e6 D. W, H+ s; D; A
- return 0;) L% k' b3 ^1 k9 I5 M
- else
: M) }. J% }% h - return result;# _; {) }1 r. W/ c' ]/ L
- }
& p' Z& g1 r! n7 Q5 m# x8 i( r - #endif2 a4 e& |5 E5 R& X
, o4 ^3 J5 n5 K7 w' q
6 [! u" y0 T( d' r5 x3 H: j8 q- /*
3 R* _1 R# k1 P - * Program Page in Flash Memory a& d K0 |* ~3 `& I9 G$ `7 ]
- * Parameter: adr: Page Start Address
2 B: _- O& B3 v# E7 S9 V& b - * sz: Page Size
) t( I2 n0 y O) D7 m - * buf: Page Data
8 ]8 W- ]( i5 D. ^9 Z' E2 n# _ - * Return Value: 0 - OK, 1 - Failed
0 v7 U3 B* _9 @ z - */2 D7 r0 K9 B9 s' O8 |; D% [- U
- , X$ S$ [; v/ X, j0 |3 T
- #if defined FLASH_MEM || defined FLASH_OTP! _; j0 M9 p( k: S0 T$ @3 [, l
- int ProgramPage (unsigned long block_start, unsigned long size, unsigned char *buffer)* N" A: B f9 B! |
- { h2 Z5 | Y( }/ e+ X6 o
- const sfud_flash *flash = sfud_get_device_table() + 0;
" Z5 ?2 R1 g& s9 ]( F - uint32_t start_addr = block_start - base_adr;
2 A( r9 o. y/ L; f - 5 f, e- t$ v/ B8 R" W0 H
- if(sfud_write(flash, start_addr, size, buffer) == SFUD_SUCCESS)$ @) t( j* `+ ^ I
- return 0;! N( i1 ]" S9 }0 Z6 [8 |
- else. Z# @0 B4 ?! Z. Z8 d" Y
- return 1;
! @1 }( m; `' S2 U' D) s$ y8 D - }
+ N9 h4 {- d# G4 X' ^
+ y5 E% X8 g- N% {; h, u& R
R* d* [$ \8 }7 T. X- @/ q) Z, ?- #define PAGE_SIZE 4096. L! I9 F) e0 Z1 ~8 b: Y
- uint8_t aux_buf[PAGE_SIZE];6 A" {% H3 C& K+ V
- unsigned long Verify (unsigned long adr, unsigned long sz, unsigned char *buf)
- [6 ]# t4 |/ e* D+ G - {
G' \ c$ p4 w V6 w! G9 d; \ - int i;% K+ o4 @0 f1 y0 L# U
- const sfud_flash *flash = sfud_get_device_table();6 P( K% V! R$ {
- sfud_read(flash, adr - base_adr, sz, aux_buf);- p; {" _6 s# L, O
3 V! p; o: R* f# S- for (i = 0; i < PAGE_SIZE; i++) {0 \4 f p* D3 u7 g& m( l
- if (aux_buf[i] != buf[i])
9 F, J5 {2 Y5 B& w - return (adr + i); // Verification Failed (return address)
# ]3 Z! ~1 i( d6 R - }
8 B! ?" Y) @# H - " h' |) ?8 ^ |$ a* H! n+ S
- return (adr + sz); // Done successfully: Q& C/ s' M6 \+ B. I6 f
- }7 |- q: T5 V, X3 ]. R' I" U; M
- 7 Q T+ [0 u1 Z* g
- #endif
复制代码
4 ` Y m. N& q
' P, p4 F+ {( x7 M在工程中定义FLASH_MEM宏
8 O l) N- G6 A, T
3 ]7 R0 R& s4 {8 b$ K, d 添加修改配置文件FlashDev.c模板工程里面提供了FlashDev.c ,复制到此工程中,然后对代码进行修改。 - #include "FlashOS.H" # y8 f7 Y* ~3 N* i# }8 ?, G4 U6 u
! y7 P7 [2 b' i- #ifdef FLASH_MEM
1 |( L0 ^: O, T6 [ - struct FlashDevice const FlashDevice = {
, S! s! ^" ?% N2 D - FLASH_DRV_VERS, // Driver Version, do not modify!
0 N+ l, P7 N6 F' J - "STM32H750-ARTPI", // Device Name 9 B6 Z$ {4 a1 `% k
- EXTSPI, // Device Type
: h: k! N* T5 Z4 H* x } - 0x90000000, // Device Start Address8 D5 |" x7 @) x0 E
- 0x08000000, // Device Size in Bytes (128MB); c/ c0 j6 O6 @
- 0x00001000, // Programming Page Size 4096 Bytes* s3 `8 O) Q% w8 x7 M
- 0x00, // Reserved, must be 0) c, W" D" z) i3 b
- 0xFF, // Initial Content of Erased Memory
9 }9 O+ p" m( ]2 Y3 t5 i3 _6 v - 10000, // Program Page Timeout 100 mSec
# X+ T0 @+ A8 N4 }/ k - 6000, // Erase Sector Timeout 6000 mSec
3 Q4 z6 d- ~' C9 a
8 V6 c4 _+ T9 i# ^ a4 F- // Specify Size and Address of Sectors- }( x' j, U8 |% l0 q
- 0x1000, 0x000000, // Sector Size 4kB
2 X$ d& d$ N3 e \9 f& ` - SECTOR_END5 w1 ^; r! k) G, z' M* @2 P) b
- };2 d5 S$ s* d- ~+ t
- #endif // FLASH_MEM
复制代码
3 q/ W/ J1 ]9 l% E2 J特别注意:"STM32H750-ARTPI"就是MDK的Option选项里面会识别出这个名字。0x90000000是MDK分散加载文件中定义的外部flash起始地址。 & k% ^. i8 {" l* o0 A- s7 d* M" M
地址无关代码实现C和汇编的配置勾选上: ; w$ k \$ |8 j: N! s
; ^: q3 ~! r4 Z2 ]% z4 ZROPI地址无关实现 如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免用户不得不将代码加载到内存中的特定位置。这对于以下例程特别有用: (1)加载以响应运行事件。 (2)在不同情况下使用其他例程的不同组合加载到内存中。 (3)在执行期间映射到不同的地址。
, Y2 g, k$ n& r. c) V1 V( V2 c
RWPI数据无关实现使用Read-Write position independence同理,表示的可读可写数据段。使用RWPI编译代码,解决RW段即全局变量的加载。首先编译的时候会为每一个全局变量生成一个相对于r9寄存器的偏移量,这个偏移量会在.text段中。 在加载elf阶段,将RW段加载到RAM当中之后,需要将r9寄存器指向此片内存的基地址,然后接下来就可以跳转到加载的elf的代码中去执行,就可以实现全局变量的加载了。 综上所述,勾选ROPI和RWPI选项,可以实现elf文件的动态加载,还遗留的一个小问题是elf模块如何调用系统函数,这与此文无关,留在以后再讲。 $ E0 n* w1 J7 n6 M; e- E1 B
特别注意: - 由于模块中不含中断向量表,所以程序中不要开启任何中断。
- startup_stm32h750xx.s不再需要参与编译
9 j+ I' n1 f1 @7 q) T& @$ z 1 K6 e/ K/ T) N3 q# a1 x# M) ]
/ ]1 P& j8 e( g4 Z; D! l
2 s/ J8 X' @5 Q' k3 ?+ R4 T% s3 |
9 q1 b) A0 {5 P/ R- L
7 l8 s, l& Z. j/ n修改分散加载文件复制一份新的分散加载文件到工程目录中,然后修改成如下代码 $ A3 g3 L6 f2 l5 X! K
# L8 l K) ?3 `" u/ t, s Z3 Z
--diag_suppress L6305用于屏蔽没有入口地址的警告信息。 - ; Linker Control File (scatter-loading)
% }4 I% H- D+ V/ A( C0 ]5 U4 L - ;
$ ~0 k3 z* r- O1 e
# I. ^5 b6 X l' h3 Q3 i- PRG 0 PI ; Programming Functions
3 X' n6 k5 g2 U+ C# O4 t - {1 L% `% U8 y% O" B7 w* I
- PrgCode +0 ; Code
, q7 n, X( \9 R0 }* O$ ^; V& s - {
/ U8 Z3 O& a9 S0 W4 f. g - * (+RO)
5 X/ ~9 { H( e" i" ` - }9 F3 u" e$ |7 x4 d. K7 [
- PrgData +0 ; Data
7 a. E/ O6 m+ Y6 f; _, t - {
/ J, S/ F1 Z5 y3 n' a - * (+RW,+ZI)
$ g" L9 s7 Z) F z5 y - }
1 k% L. I/ F. r - }
' {4 f+ d. r9 H - 3 c' Z9 o7 W. X: L4 a# i+ W9 d
- DSCR +0 ; Device Description2 a1 E- U0 e9 K" [+ Z) U" `
- {3 n. m; Y$ U8 l! u3 t) n% E& K% K
- DevDscr +0$ q2 f1 j% e) ^; h# Y# P$ A4 F+ H$ |# @
- {$ M! G/ q- _: @. l% J! e
- FlashDev.o
, H3 g2 o3 q# |0 U/ q# k - }5 a, i+ A. y: u R. ~
- }
复制代码 3 m. d$ K. g- d# p7 e
将程序可执行文件axf修改为flm格式通过这个cmd.exe /C copy "!L" "..\@L.FLM"命令可以将生成的axf可执行文件修改为flm。
# j& o$ S+ J1 V2 ^
% F& R% n& k- o
将生成的flm文件拷贝到...\Keil_v5\ARM\Flash目录,即可被MDK识别到。 - t% |8 Z/ u' W* ~3 s+ y
完整版请查看:附件
& x' Q6 T& k/ I# J4 K, b) h5 V
% N: _" K+ k+ i, d" O, a5 i |
可以分享附件吗?