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