文章目录
- 前言
- 一、将代码中的图片资源下载到外部flash
-
- 二、MDK下载算法原理
- 1. 程序能够通过下载算法下载到芯片的原理
- 2. 算法程序中擦除操作执行流程
- 3. 制作FLM文件步骤
- 三、使用STM32CubeMX新建工程
- 1. 新建工程
- 选择MCU型号(STM32H750XBH6)
- 配置SPI
- 配置UART
- 配置时钟树
- 设置调试接口
- 设置工程并生成工程
- 2. 移植SFUD串行 Flash 通用驱动库
-
- 3. 生成FLM文件
- 重新生成不带main函数的工程
- 添加修改编程算法文件FlashPrg.c
- 添加修改配置文件FlashDev.c
- 地址无关代码实现
- 修改分散加载文件
- 将程序可执行文件axf修改为flm格式
前言
当我们要下载编译好的镜像到Flash时,首先要做的一步就是选择合适的Flash下载算法,而这个算法本身就是一个FLM文件:
代码既可以下载到内部flash,也可以下载到外部flash,或者一部分下载到内部,一部分下载到外部。
一、将代码中的图片资源下载到外部flash
在UI设计中往往需要大量的图片和字体,图片和字体资源在代码中以静态数组的形式存在,这些大数组在内部flash中一般存放不下,所以需要把这些占用资源比较大的数组放在外部flash中,然后通过QSPI地址映射的方式访问,或者通过SPI将flash中的资源分批读取到RAM缓存中使用。
1. 修改分散加载文件
- 通过MDK打开分散加载文件,配置“ExtFlashSection”段:
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x08000000 0x00020000 { ; load region size_region
ER_IROM1 0x08000000 0x00020000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
.ANY (+XO)
}
RW_IRAM1 0x20000000 0x00020000 { ; RW data
.ANY (+RW +ZI)
}
RW_IRAM2 0x24000000 0x00080000 {
.ANY (+RW +ZI)
}
}
LR_EROM1 0x90000000 0x01000000 { ; load region size_region
ER_EROM1 0x90000000 0x01000000 { ; load address = execution address
*.o (ExtFlashSection)
*.o (FontFlashSection)
*.o (TextFlashSection)
}
}
添加LR_EROM1 段,起始地址为0x90000000 ,大小为0x01000000 。
- 在代码中将图片资源分配到ExtFlashSection段
#define LOCATION_ATTRIBUTE(name) __attribute__((section(name))) __attribute__((aligned(4)))
KEEP extern const unsigned char image_watch_seconds[] LOCATION_ATTRIBUTE("ExtFlashSection") =
{
0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0x00,
0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0xff,
0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0x00,
0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0x00,
0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0xff,
0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0xff,
0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0x00,
0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0x00,
0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0xff,
0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0x00,
0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0xff,
0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0xff,
0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0xff, 0xf8, 0xfc, 0xf8, 0x00,
0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0x00, 0xf8, 0xfc, 0xf8, 0x00,
0xf8, 0xfc, 0xf8, 0x00
};
- 编译代码
查看map文件,image_watch_seconds这个数组已经被分配到了0X90138690这个地址了,这个地址正是LR_EROM1 所在的区间。
二、MDK下载算法原理
1. 程序能够通过下载算法下载到芯片的原理
通过MDK创建一批与地址信息无关的函数,实现的功能主要有初始化,擦除,编程,读取,校验等,然后MDK调试下载阶段,会将算法文件加载到芯片的内部RAM里面(加载地址可以通过MDK设置),然后MDK通过与这个算法文件的交互,实现程序下载,调试阶段数据读取等操作。
2. 算法程序中擦除操作执行流程
- 加载算法到芯片RAM。
- 执行初始化函数Init。
- 执行擦除操作,根据用户的MDK配置,这里可以选择整个芯片擦除或者扇区擦除。
- 执行Uinit函数。
- 操作完毕。
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 编程算法。
以上步骤是利用官方的工程模板修改代码,这种方式网上已有很多教程(推荐使用这种方法),不再重复介绍,接下来介绍一种不使用模板工程制作的方法,目的是为了了解其实现原理。
三、使用STM32CubeMX新建工程
1. 新建工程
硬件平台: RT-Thread官方ART-PI H750开发版
软件: STM32CubeMX,MDK
选择MCU型号(STM32H750XBH6)
配置SPI
配置UART
配置时钟树
设置调试接口
设置工程并生成工程
2. 移植SFUD串行 Flash 通用驱动库
SFUD 是什么?
SFUD 是一款开源的串行 SPI Flash 通用驱动库。由于现有市面的串行 Flash 种类居多,各个 Flash 的规格及命令存在差异, SFUD 就是为了解决这些 Flash 的差异现状而设计,让我们的产品能够支持不同品牌及规格的 Flash,提高了涉及到 Flash 功能的软件的可重用性及可扩展性,同时也可以规避 Flash 缺货或停产给产品所带来的风险。
- 主要特点:支持 SPI/QSPI 接口、面向对象(同时支持多个 Flash 对象)、可灵活裁剪、扩展性强、支持 4 字节地址
- 资源占用
- 标准占用:RAM:0.2KB ROM:5.5KB
- 最小占用:RAM:0.1KB ROM:3.6KB
- 设计思路:
- 什么是 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 的全部操作。
移植SFUD
将下载到sfud源代码放置在工程目录中
将sfud添加到工程目录:
添加串口打印文件:
为了测试方便,添加一个串口打印的文件my_printf.c
#include <stdarg.h>
#include <stdint.h>
#include "usart.h"
#define RT_PRINTF_PRECISION
#define RT_CONSOLEBUF_SIZE 512
#define _ISDIGIT(c) ((unsigned)((c) - '0') < 10)
#define ZEROPAD (1 << 0)
#define SIGN (1 << 1)
#define PLUS (1 << 2)
#define SPACE (1 << 3)
#define LEFT (1 << 4)
#define SPECIAL (1 << 5)
#define LARGE (1 << 6)
#ifdef RT_PRINTF_LONGLONG
inline int divide(long long *n, int base)
#else
inline int divide(long *n, int base)
#endif
{
int res;
if (base == 10) {
#ifdef RT_PRINTF_LONGLONG
res = (int)(((unsigned long long) * n) % 10U);
*n = (long long)(((unsigned long long) * n) / 10U);
#else
res = (int)(((unsigned long) * n) % 10U);
*n = (long)(((unsigned long) * n) / 10U);
#endif
} else {
#ifdef RT_PRINTF_LONGLONG
res = (int)(((unsigned long long) * n) % 16U);
*n = (long long)(((unsigned long long) * n) / 16U);
#else
res = (int)(((unsigned long) * n) % 16U);
*n = (long)(((unsigned long) * n) / 16U);
#endif
}
return res;
}
static char *print_number(char *buf,
char *end,
#ifdef RT_PRINTF_LONGLONG
long long num,
#else
long num,
#endif
int base,
int s,
#ifdef RT_PRINTF_PRECISION
int precision,
#endif
int type)
{
char c, sign;
#ifdef RT_PRINTF_LONGLONG
char tmp[32];
#else
char tmp[16];
#endif
int precision_bak = precision;
const char *digits;
static const char small_digits[] = "0123456789abcdef";
static const char large_digits[] = "0123456789ABCDEF";
register int i;
register int size;
size = s;
digits = (type & LARGE) ? large_digits : small_digits;
if (type & LEFT)
type &= ~ZEROPAD;
c = (type & ZEROPAD) ? '0' : ' ';
sign = 0;
if (type & SIGN) {
if (num < 0) {
sign = '-';
num = -num;
} else if (type & PLUS)
sign = '+';
else if (type & SPACE)
sign = ' ';
}
#ifdef RT_PRINTF_SPECIAL
if (type & SPECIAL) {
if (base == 16)
size -= 2;
else if (base == 8)
size--;
}
#endif
i = 0;
if (num == 0)
tmp[i++] = '0';
else {
while (num != 0)
tmp[i++] = digits[divide(&num, base)];
}
#ifdef RT_PRINTF_PRECISION
if (i > precision)
precision = i;
size -= precision;
#else
size -= i;
#endif
if (!(type & (ZEROPAD | LEFT))) {
if ((sign) && (size > 0))
size--;
while (size-- > 0) {
if (buf < end)
*buf = ' ';
++ buf;
}
}
if (sign) {
if (buf < end) {
*buf = sign;
}
-- size;
++ buf;
}
#ifdef RT_PRINTF_SPECIAL
if (type & SPECIAL) {
if (base == 8) {
if (buf < end)
*buf = '0';
++ buf;
} else if (base == 16) {
if (buf < end)
*buf = '0';
++ buf;
if (buf < end) {
*buf = type & LARGE ? 'X' : 'x';
}
++ buf;
}
}
#endif
if (!(type & LEFT)) {
while (size-- > 0) {
if (buf < end)
*buf = c;
++ buf;
}
}
#ifdef RT_PRINTF_PRECISION
while (i < precision--) {
if (buf < end)
*buf = '0';
++ buf;
}
#endif
while (i-- > 0 && (precision_bak != 0)) {
if (buf < end)
*buf = tmp[i];
++ buf;
}
while (size-- > 0) {
if (buf < end)
*buf = ' ';
++ buf;
}
return buf;
}
static int skip_atoi(const char **s)
{
register int i = 0;
while (_ISDIGIT(**s))
i = i * 10 + *((*s)++) - '0';
return i;
}
int rt_vsnprintf(char *buf, int size, const char *fmt, va_list args)
{
#ifdef RT_PRINTF_LONGLONG
unsigned long long num;
#else
uint32_t num;
#endif
int i, len;
char *str, *end, c;
const char *s;
uint8_t base;
uint8_t flags;
uint8_t qualifier;
int32_t field_width;
#ifdef RT_PRINTF_PRECISION
int precision;
#endif
str = buf;
end = buf + size;
if (end < buf) {
end = ((char *) - 1);
size = end - buf;
}
for (; *fmt ; ++fmt) {
if (*fmt != '%') {
if (str < end)
*str = *fmt;
++ str;
continue;
}
flags = 0;
while (1) {
++ fmt;
if (*fmt == '-') flags |= LEFT;
else if (*fmt == '+') flags |= PLUS;
else if (*fmt == ' ') flags |= SPACE;
else if (*fmt == '#') flags |= SPECIAL;
else if (*fmt == '0') flags |= ZEROPAD;
else break;
}
field_width = -1;
if (_ISDIGIT(*fmt)) field_width = skip_atoi(&fmt);
else if (*fmt == '*') {
++ fmt;
field_width = va_arg(args, int);
if (field_width < 0) {
field_width = -field_width;
flags |= LEFT;
}
}
#ifdef RT_PRINTF_PRECISION
precision = -1;
if (*fmt == '.') {
++ fmt;
if (_ISDIGIT(*fmt)) precision = skip_atoi(&fmt);
else if (*fmt == '*') {
++ fmt;
precision = va_arg(args, int);
}
if (precision < 0) precision = 0;
}
#endif
qualifier = 0;
#ifdef RT_PRINTF_LONGLONG
if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L')
#else
if (*fmt == 'h' || *fmt == 'l')
#endif
{
qualifier = *fmt;
++ fmt;
#ifdef RT_PRINTF_LONGLONG
if (qualifier == 'l' && *fmt == 'l') {
qualifier = 'L';
++ fmt;
}
#endif
}
base = 10;
switch (*fmt) {
case 'c':
if (!(flags & LEFT)) {
while (--field_width > 0) {
if (str < end) *str = ' ';
++ str;
}
}
c = (uint8_t)va_arg(args, int);
if (str < end) *str = c;
++ str;
while (--field_width > 0) {
if (str < end) *str = ' ';
++ str;
}
continue;
case 's':
s = va_arg(args, char *);
if (!s) s = "(NULL)";
for (len = 0; (len != field_width) && (s[len] != '\0'); len++);
#ifdef RT_PRINTF_PRECISION
if (precision > 0 && len > precision) len = precision;
#endif
if (!(flags & LEFT)) {
while (len < field_width--) {
if (str < end) *str = ' ';
++ str;
}
}
for (i = 0; i < len; ++i) {
if (str < end) *str = *s;
++ str;
++ s;
}
while (len < field_width--) {
if (str < end) *str = ' ';
++ str;
}
continue;
case 'p':
if (field_width == -1) {
field_width = sizeof(void *) << 1;
flags |= ZEROPAD;
}
#ifdef RT_PRINTF_PRECISION
str = print_number(str, end,
(long)va_arg(args, void *),
16, field_width, precision, flags);
#else
str = print_number(str, end,
(long)va_arg(args, void *),
16, field_width, flags);
#endif
continue;
case '%':
if (str < end) *str = '%';
++ str;
continue;
case 'o':
base = 8;
break;
case 'X':
flags |= LARGE;
case 'x':
base = 16;
break;
case 'd':
case 'i':
flags |= SIGN;
case 'u':
break;
default:
if (str < end) *str = '%';
++ str;
if (*fmt) {
if (str < end) *str = *fmt;
++ str;
} else {
-- fmt;
}
continue;
}
#ifdef RT_PRINTF_LONGLONG
if (qualifier == 'L') num = va_arg(args, long long);
else if (qualifier == 'l')
#else
if (qualifier == 'l')
#endif
{
num = va_arg(args, uint32_t);
if (flags & SIGN) num = (int32_t)num;
} else if (qualifier == 'h') {
num = (uint16_t)va_arg(args, int32_t);
if (flags & SIGN) num = (int16_t)num;
} else {
num = va_arg(args, uint32_t);
if (flags & SIGN) num = (int32_t)num;
}
#ifdef RT_PRINTF_PRECISION
str = print_number(str, end, num, base, field_width, precision, flags);
#else
str = print_number(str, end, num, base, field_width, flags);
#endif
}
if (size > 0) {
if (str < end) *str = '\0';
else {
end[-1] = '\0';
}
}
return str - buf;
}
int rt_kprintf(const char *fmt, ...)
{
va_list args;
int length;
static char rt_log_buf[RT_CONSOLEBUF_SIZE];
va_start(args, fmt);
length = rt_vsnprintf(rt_log_buf, sizeof(rt_log_buf) - 1, fmt, args);
if (length > RT_CONSOLEBUF_SIZE - 1)
length = RT_CONSOLEBUF_SIZE - 1;
HAL_UART_Transmit(&huart4, (uint8_t *)&rt_log_buf, length, 100);
va_end(args);
return length;
}
修改sfud_port.c文件:
#include <string.h>
#include <sfud.h>
#include <stdarg.h>
#include "gpio.h"
#include "spi.h"
typedef struct {
SPI_HandleTypeDef *spix;
GPIO_TypeDef *cs_gpiox;
uint16_t cs_gpio_pin;
} spi_user_data, *spi_user_data_t;
static spi_user_data spi1;
static char log_buf[256];
void sfud_log_debug(const char *file, const long line, const char *format, ...);
extern int rt_vsnprintf(char *buf, int size, const char *fmt, va_list args);
extern int rt_kprintf(const char *fmt, ...);
static void spi_lock(const sfud_spi *spi)
{
}
static void spi_unlock(const sfud_spi *spi)
{
}
static void delay_100us(void) {
uint32_t delay = 2000;
while(delay--);
}
static sfud_err spi_write_read(const sfud_spi *spi, const uint8_t *write_buf, size_t write_size, uint8_t *read_buf,
size_t read_size)
{
sfud_err result = SFUD_SUCCESS;
spi_user_data_t spi_dev = (spi_user_data_t) spi->user_data;
HAL_GPIO_WritePin(spi_dev->cs_gpiox, spi_dev->cs_gpio_pin,GPIO_PIN_RESET);
if (write_size) {
HAL_SPI_Transmit(spi_dev->spix, (uint8_t *)write_buf,write_size,1);
}
if (read_size) {
HAL_SPI_Receive(spi_dev->spix, read_buf,read_size,1);
}
exit:
HAL_GPIO_WritePin(spi_dev->cs_gpiox, spi_dev->cs_gpio_pin,GPIO_PIN_SET);
return result;
}
sfud_err sfud_spi_port_init(sfud_flash *flash)
{
sfud_err result = SFUD_SUCCESS;
switch (flash->index) {
case SFUD_W25Q128_DEVICE_INDEX: {
spi1.spix = &hspi1;
spi1.cs_gpiox = GPIOA;
spi1.cs_gpio_pin = GPIO_PIN_4;
flash->spi.wr = spi_write_read;
flash->spi.lock = spi_lock;
flash->spi.unlock = spi_unlock;
flash->spi.user_data = &spi1;
flash->retry.delay = delay_100us;
flash->retry.times = 60 * 10000;
break;
}
}
return result;
}
void sfud_log_debug(const char *file, const long line, const char *format, ...) {
va_list args;
va_start(args, format);
rt_kprintf("[SFUD](%s:%ld) ", file, line);
rt_vsnprintf(log_buf, sizeof(log_buf), format, args);
rt_kprintf("%s\r\n", log_buf);
va_end(args);
}
void sfud_log_info(const char *format, ...) {
va_list args;
va_start(args, format);
rt_kprintf("[SFUD]");
rt_vsnprintf(log_buf, sizeof(log_buf), format, args);
rt_kprintf("%s\r\n", log_buf);
va_end(args);
}
测试SFUD
在main.c中添加测试代码:
#include "main.h"
#include "spi.h"
#include "usart.h"
#include "gpio.h"
void SystemClock_Config(void);
static void MPU_Config(void);
extern int rt_kprintf(const char *fmt, ...);
#include "sfud.h"
#define SFUD_DEMO_TEST_BUFFER_SIZE 1024
static uint8_t sfud_demo_test_buf[SFUD_DEMO_TEST_BUFFER_SIZE];
static void sfud_demo(uint32_t addr, size_t size, uint8_t *data) {
sfud_err result = SFUD_SUCCESS;
const sfud_flash *flash = sfud_get_device_table() + 0;
size_t i;
for (i = 0; i < size; i++) {
data[i] = i;
}
result = sfud_erase(flash, addr, size);
if (result == SFUD_SUCCESS) {
rt_kprintf("Erase the %s flash data finish. Start from 0x%08X, size is %d.\r\n", flash->name, addr,
size);
} else {
rt_kprintf("Erase the %s flash data failed.\r\n", flash->name);
return;
}
result = sfud_write(flash, addr, size, data);
if (result == SFUD_SUCCESS) {
rt_kprintf("Write the %s flash data finish. Start from 0x%08X, size is %d.\r\n", flash->name, addr,
size);
} else {
rt_kprintf("Write the %s flash data failed.\r\n", flash->name);
return;
}
result = sfud_read(flash, addr, size, data);
if (result == SFUD_SUCCESS) {
rt_kprintf("Read the %s flash data success. Start from 0x%08X, size is %d. The data is:\r\n", flash->name, addr,
size);
rt_kprintf("Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\r\n");
for (i = 0; i < size; i++) {
if (i % 16 == 0) {
rt_kprintf("[%08X] ", addr + i);
}
rt_kprintf("%02X ", data[i]);
if (((i + 1) % 16 == 0) || i == size - 1) {
rt_kprintf("\r\n");
}
}
rt_kprintf("\r\n");
} else {
rt_kprintf("Read the %s flash data failed.\r\n", flash->name);
}
for (i = 0; i < size; i++) {
if (data[i] != i % 256) {
rt_kprintf("Read and check write data has an error. Write the %s flash data failed.\r\n", flash->name);
break;
}
}
if (i == size) {
rt_kprintf("The %s flash test is success.\r\n", flash->name);
}
}
int main(void)
{
MPU_Config();
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SPI1_Init();
MX_UART4_Init();
if (sfud_init() == SFUD_SUCCESS) {
sfud_demo(0, sizeof(sfud_demo_test_buf), sfud_demo_test_buf);
}
while (1)
{
}
}
#endif
运行如下:
3. 生成FLM文件
重新生成不带main函数的工程
添加修改编程算法文件FlashPrg.c
模板工程里面提供了FlashOS.h和FlashPrg.c ,复制到此工程中,然后对FlashPrg.c 代码进行填充。
#include "FlashOS.H"
#include "sfud.h"
#include "gpio.h"
#include "usart.h"
#include "spi.h"
static uint32_t base_adr;
#if defined FLASH_MEM || defined FLASH_OTP
int Init (unsigned long adr, unsigned long clk, unsigned long fnc)
{
MX_GPIO_Init();
MX_UART4_Init();
MX_SPI1_Init();
base_adr = adr;
if(sfud_init() == SFUD_SUCCESS) {
return 0;
} else {
return 1;
}
}
#endif
#if defined FLASH_MEM || defined FLASH_OTP
int UnInit (unsigned long fnc)
{
return (0);
}
#endif
int EraseChip (void)
{
int result = 0;
const sfud_flash *flash = sfud_get_device_table();
result = sfud_erase (flash, 0, flash->chip.capacity);
if (result == SFUD_SUCCESS)
return 0;
else
return result;
}
#ifdef FLASH_MEM
int EraseSector (unsigned long adr)
{
int result = 0;
uint32_t block_start;
const sfud_flash *flash;
flash = sfud_get_device_table();
block_start = adr - base_adr;
result = sfud_erase (flash, block_start, 4096);
if (result == SFUD_SUCCESS)
return 0;
else
return result;
}
#endif
#if defined FLASH_MEM || defined FLASH_OTP
int ProgramPage (unsigned long block_start, unsigned long size, unsigned char *buffer)
{
const sfud_flash *flash = sfud_get_device_table() + 0;
uint32_t start_addr = block_start - base_adr;
if(sfud_write(flash, start_addr, size, buffer) == SFUD_SUCCESS)
return 0;
else
return 1;
}
#define PAGE_SIZE 4096
uint8_t aux_buf[PAGE_SIZE];
unsigned long Verify (unsigned long adr, unsigned long sz, unsigned char *buf)
{
int i;
const sfud_flash *flash = sfud_get_device_table();
sfud_read(flash, adr - base_adr, sz, aux_buf);
for (i = 0; i < PAGE_SIZE; i++) {
if (aux_buf[i] != buf[i])
return (adr + i);
}
return (adr + sz);
}
#endif
在工程中定义FLASH_MEM宏
添加修改配置文件FlashDev.c
模板工程里面提供了FlashDev.c ,复制到此工程中,然后对代码进行修改。
#include "FlashOS.H"
#ifdef FLASH_MEM
struct FlashDevice const FlashDevice = {
FLASH_DRV_VERS,
"STM32H750-ARTPI",
EXTSPI,
0x90000000,
0x08000000,
0x00001000,
0x00,
0xFF,
10000,
6000,
0x1000, 0x000000,
SECTOR_END
};
#endif
特别注意:"STM32H750-ARTPI"就是MDK的Option选项里面会识别出这个名字。0x90000000是MDK分散加载文件中定义的外部flash起始地址。
地址无关代码实现
C和汇编的配置都勾选上:
ROPI地址无关实现
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免用户不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:
(1)加载以响应运行事件。
(2)在不同情况下使用其他例程的不同组合加载到内存中。
(3)在执行期间映射到不同的地址。
RWPI数据无关实现
使用Read-Write position independence同理,表示的可读可写数据段。
使用RWPI编译代码,解决RW段即全局变量的加载。首先编译的时候会为每一个全局变量生成一个相对于r9寄存器的偏移量,这个偏移量会在.text段中。
在加载elf阶段,将RW段加载到RAM当中之后,需要将r9寄存器指向此片内存的基地址,然后接下来就可以跳转到加载的elf的代码中去执行,就可以实现全局变量的加载了。这也就是利用MDK的FLM文件生成通用flash驱动中提到的需要在编译选项中添加-ffixed-r9
的原因。
综上所述,勾选ROPI和RWPI选项,可以实现elf文件的动态加载,还遗留的一个小问题是elf模块如何调用系统函数,与此文无关,留在以后再讲。
特别注意:
- 由于模块中不含中断向量表,所以程序中不要开启任何中断。
- startup_stm32h750xx.s不再需要参与编译
修改分散加载文件
复制一份新的分散加载文件到工程目录中,然后修改成如下代码
–diag_suppress L6305用于屏蔽没有入口地址的警告信息。
分散加载文件中的内容如下:
; Linker Control File (scatter-loading)
;
PRG 0 PI ; Programming Functions
{
PrgCode +0 ; Code
{
* (+RO)
}
PrgData +0 ; Data
{
* (+RW,+ZI)
}
}
DSCR +0 ; Device Description
{
DevDscr +0
{
FlashDev.o
}
}
将程序可执行文件axf修改为flm格式
通过这个cmd.exe /C copy "!L" "..\@L.FLM"
命令就可以将生成的axf可执行文件修改为flm。
将生成的flm文件拷贝到…\Keil_v5\ARM\Flash目录,即可被MDK识别到。
DEMO下载地址:https://gitee.com/Aladdin-Wang/STM32H7_W25QXXX
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)