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