当我们要下载编译好的镜像到Flash时,首先要做的一步就是选择合适的Flash下载算法,而这个算法本身就是一个FLM文件: 代码既可以下载到内部flash,也可以下载到外部flash,或者一部分下载到内部,一部分下载到外部。
在UI设计中往往需要大量的图片和字体,图片和字体资源在代码中以静态数组的形式存在,这些大数组在内部flash中一般存放不下,所以需要把这些占用资源比较大的数组放在外部flash中,然后通过QSPI地址映射的方式访问,或者通过SPI将flash中的资源分批读取到RAM缓存中使用。
; *************************************************************
; *** 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 。
#define LOCATION_ATTRIBUTE(name) __attribute__((section(name))) __attribute__((aligned(4)))
KEEP extern const unsigned char image_watch_seconds[] LOCATION_ATTRIBUTE("ExtFlashSection") = // 4x202 ARGB8888 pixels.
{
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
};
通过MDK创建一批与地址信息无关的函数,实现的功能主要有初始化,擦除,编程,读取,校验等,然后MDK调试下载阶段,会将算法文件加载到芯片的内部RAM里面(加载地址可以通过MDK设置),然后MDK通过与这个算法文件的交互,实现程序下载,调试阶段数据读取等操作。
以上步骤是利用官方的工程模板修改代码,这种方式网上已有很多教程(推荐使用这种方法),不再重复介绍,接下来介绍一种不使用模板工程制作的方法,目的是为了了解其实现原理。
硬件平台: RT-Thread官方ART-PI H750开发版 软件: STM32CubeMX,MDK
SFUD 是一款开源的串行 SPI Flash 通用驱动库。由于现有市面的串行 Flash 种类居多,各个 Flash 的规格及命令存在差异, SFUD 就是为了解决这些 Flash 的差异现状而设计,让我们的产品能够支持不同品牌及规格的 Flash,提高了涉及到 Flash 功能的软件的可重用性及可扩展性,同时也可以规避 Flash 缺货或停产给产品所带来的风险。
/sfud/inc/sfud_flash_def.h
) 中提供的 Flash 参数信息表 中是否支持该款 Flash。如果不支持,则可以在配置文件中添加该款 Flash 的参数信息。获取到了 Flash 的规格参数后,就可以实现对 Flash 的全部操作。将下载到sfud源代码放置在工程目录中 将sfud添加到工程目录: 添加串口打印文件: 为了测试方便,添加一个串口打印的文件my_printf.c
#include <stdarg.h>
#include <stdint.h>
#include "usart.h"
#define RT_PRINTF_PRECISION
#define RT_CONSOLEBUF_SIZE 512
/* private function */
#define _ISDIGIT(c) ((unsigned)((c) - '0') < 10)
#define ZEROPAD (1 << 0) /* pad with zero */
#define SIGN (1 << 1) /* unsigned/signed long */
#define PLUS (1 << 2) /* show plus */
#define SPACE (1 << 3) /* space if plus */
#define LEFT (1 << 4) /* left justified */
#define SPECIAL (1 << 5) /* 0x */
#define LARGE (1 << 6) /* use 'ABCDEF' instead of 'abcdef' */
/**
* This function will duplicate a string.
*
* @param n is the string to be duplicated.
*
* @param base is support divide instructions value.
*
* @return the duplicated string pointer.
*/
#ifdef RT_PRINTF_LONGLONG
inline int divide(long long *n, int base)
#else
inline int divide(long *n, int base)
#endif /* RT_PRINTF_LONGLONG */
{
int res;
/* optimized for processor which does not support divide instructions. */
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 /* RT_PRINTF_LONGLONG */
int base,
int s,
#ifdef RT_PRINTF_PRECISION
int precision,
#endif /* RT_PRINTF_PRECISION */
int type)
{
char c, sign;
#ifdef RT_PRINTF_LONGLONG
char tmp[32];
#else
char tmp[16];
#endif /* RT_PRINTF_LONGLONG */
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' : ' ';
/* get sign */
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 /* RT_PRINTF_SPECIAL */
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 /* RT_PRINTF_PRECISION */
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 /* RT_PRINTF_SPECIAL */
/* no align to the left */
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 /* RT_PRINTF_PRECISION */
/* put number in the temporary buffer */
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;
}
/**
* This function will fill a formatted string to buffer.
*
* @param buf is the buffer to save formatted string.
*
* @param size is the size of buffer.
*
* @param fmt is the format parameters.
*
* @param args is a list of variable parameters.
*
* @return The number of characters actually written to buffer.
*/
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 /* RT_PRINTF_LONGLONG */
int i, len;
char *str, *end, c;
const char *s;
uint8_t base; /* the base of number */
uint8_t flags; /* flags to print number */
uint8_t qualifier; /* 'h', 'l', or 'L' for integer fields */
int32_t field_width; /* width of output field */
#ifdef RT_PRINTF_PRECISION
int precision; /* min. # of digits for integers and max for a string */
#endif /* RT_PRINTF_PRECISION */
str = buf;
end = buf + size;
/* Make sure end is always >= buf */
if (end < buf) {
end = ((char *) - 1);
size = end - buf;
}
for (; *fmt ; ++fmt) {
if (*fmt != '%') {
if (str < end)
*str = *fmt;
++ str;
continue;
}
/* process flags */
flags = 0;
while (1) {
/* skips the first '%' also */
++ 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;
}
/* get field width */
field_width = -1;
if (_ISDIGIT(*fmt)) field_width = skip_atoi(&fmt);
else if (*fmt == '*') {
++ fmt;
/* it's the next argument */
field_width = va_arg(args, int);
if (field_width < 0) {
field_width = -field_width;
flags |= LEFT;
}
}
#ifdef RT_PRINTF_PRECISION
/* get the precision */
precision = -1;
if (*fmt == '.') {
++ fmt;
if (_ISDIGIT(*fmt)) precision = skip_atoi(&fmt);
else if (*fmt == '*') {
++ fmt;
/* it's the next argument */
precision = va_arg(args, int);
}
if (precision < 0) precision = 0;
}
#endif /* RT_PRINTF_PRECISION */
/* get the conversion qualifier */
qualifier = 0;
#ifdef RT_PRINTF_LONGLONG
if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L')
#else
if (*fmt == 'h' || *fmt == 'l')
#endif /* RT_PRINTF_LONGLONG */
{
qualifier = *fmt;
++ fmt;
#ifdef RT_PRINTF_LONGLONG
if (qualifier == 'l' && *fmt == 'l') {
qualifier = 'L';
++ fmt;
}
#endif /* RT_PRINTF_LONGLONG */
}
/* the default base */
base = 10;
switch (*fmt) {
case 'c':
if (!(flags & LEFT)) {
while (--field_width > 0) {
if (str < end) *str = ' ';
++ str;
}
}
/* get character */
c = (uint8_t)va_arg(args, int);
if (str < end) *str = c;
++ str;
/* put width */
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 /* RT_PRINTF_PRECISION */
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 /* RT_PRINTF_PRECISION */
continue;
case '%':
if (str < end) *str = '%';
++ str;
continue;
/* integer number formats - set up the flags and "break" */
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 /* RT_PRINTF_LONGLONG */
{
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 /* RT_PRINTF_PRECISION */
}
if (size > 0) {
if (str < end) *str = '\0';
else {
end[-1] = '\0';
}
}
/* the trailing null byte doesn't count towards the total
* ++str;
*/
return str - buf;
}
/**
* This function will print a formatted string on system console.
*
* @param fmt is the format parameters.
*
* @return The number of characters actually written to buffer.
*/
int rt_kprintf(const char *fmt, ...)
{
va_list args;
int length;
static char rt_log_buf[RT_CONSOLEBUF_SIZE];
va_start(args, fmt);
/* the return value of vsnprintf is the number of bytes that would be
* written to buffer had if the size of the buffer been sufficiently
* large excluding the terminating null byte. If the output string
* would be larger than the rt_log_buf, we have to adjust the output
* length. */
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)
{
}
/* about 100 microsecond delay */
static void delay_100us(void) {
uint32_t delay = 2000;
while(delay--);
}
/**
* SPI write data then read data
*/
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;
/**
* add your spi write and read code
*/
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 移植所需的接口及数据 */
flash->spi.wr = spi_write_read;
flash->spi.lock = spi_lock;
flash->spi.unlock = spi_unlock;
flash->spi.user_data = &spi1;
/* about 100 microsecond delay */
flash->retry.delay = delay_100us;
/* adout 60 seconds timeout */
flash->retry.times = 60 * 10000;
break;
}
}
return result;
}
void sfud_log_debug(const char *file, const long line, const char *format, ...) {
va_list args;
/* args point to the first variable parameter */
va_start(args, format);
rt_kprintf("[SFUD](%s:%ld) ", file, line);
/* must use vprintf to print */
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;
/* args point to the first variable parameter */
va_start(args, format);
rt_kprintf("[SFUD]");
/* must use vprintf to print */
rt_vsnprintf(log_buf, sizeof(log_buf), format, args);
rt_kprintf("%s\r\n", log_buf);
va_end(args);
}
在main.c中添加测试代码:
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "spi.h"
#include "usart.h"
#include "gpio.h"
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MPU_Config(void);
/* USER CODE BEGIN PFP */
extern int rt_kprintf(const char *fmt, ...);
#include "sfud.h"
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#define SFUD_DEMO_TEST_BUFFER_SIZE 1024
static uint8_t sfud_demo_test_buf[SFUD_DEMO_TEST_BUFFER_SIZE];
/**
* SFUD demo for the first flash device test.
*
* @param addr flash start address
* @param size test flash size
* @param size test flash data buffer
*/
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;
/* prepare write data */
for (i = 0; i < size; i++) {
data[i] = i;
}
/* erase test */
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;
}
/* write test */
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;
}
/* read test */
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);
}
/* data check */
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);
}
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MPU Configuration--------------------------------------------------------*/
MPU_Config();
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_SPI1_Init();
MX_UART4_Init();
/* USER CODE BEGIN 2 */
if (sfud_init() == SFUD_SUCCESS) {
sfud_demo(0, sizeof(sfud_demo_test_buf), sfud_demo_test_buf);
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
#endif /* USE_FULL_ASSERT */
运行如下:
模板工程里面提供了FlashOS.h和FlashPrg.c ,复制到此工程中,然后对FlashPrg.c 代码进行填充。
/***********************************************************************/
/* This file is part of the ARM Toolchain package */
/* Copyright (c) 2020 Keil - An ARM Company. All rights reserved. */
/***********************************************************************/
/* */
/* FlashPrg.c: Flash Programming Functions adapted for */
/* ST Microelectronics STM32h747I-DISCO Flash */
/* */
/***********************************************************************/
#include "FlashOS.H"
#include "sfud.h"
#include "gpio.h"
#include "usart.h"
#include "spi.h"
static uint32_t base_adr;
/*
* Initialize Flash Programming Functions
* Parameter: adr: Device Base Address
* clk: Clock Frequency (Hz)
* fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify)
* Return Value: 0 - OK, 1 - Failed
*/
#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
/*
* De-Initialize Flash Programming Functions
* Parameter: fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify)
* Return Value: 0 - OK, 1 - Failed
*/
#if defined FLASH_MEM || defined FLASH_OTP
int UnInit (unsigned long fnc)
{
return (0);
}
#endif
/*
* Erase complete Flash Memory
* Return Value: 0 - OK, 1 - Failed
*/
int EraseChip (void)
{
int result = 0;
const sfud_flash *flash = sfud_get_device_table();
/* Add your Code */
result = sfud_erase (flash, 0, flash->chip.capacity);
if (result == SFUD_SUCCESS)
return 0;
else
return result; // Finished without Errors
}
/*
* Erase Sector in Flash Memory
* Parameter: adr: Sector Address
* Return Value: 0 - OK, 1 - Failed
*/
#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
/*
* Program Page in Flash Memory
* Parameter: adr: Page Start Address
* sz: Page Size
* buf: Page Data
* Return Value: 0 - OK, 1 - Failed
*/
#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); // Verification Failed (return address)
}
return (adr + sz); // Done successfully
}
#endif
在工程中定义FLASH_MEM宏
模板工程里面提供了FlashDev.c ,复制到此工程中,然后对代码进行修改。
/***********************************************************************/
/* This file is part of the ARM Toolchain package */
/* Copyright (c) 2020 Keil - An ARM Company. All rights reserved. */
/***********************************************************************/
/* */
/* FlashDev.c: Device Description for ST STM32H747I-DISCO Flash */
/* */
/***********************************************************************/
#include "FlashOS.H"
#ifdef FLASH_MEM
struct FlashDevice const FlashDevice = {
FLASH_DRV_VERS, // Driver Version, do not modify!
"STM32H750-ARTPI", // Device Name
EXTSPI, // Device Type
0x90000000, // Device Start Address
0x08000000, // Device Size in Bytes (128MB)
0x00001000, // Programming Page Size 4096 Bytes
0x00, // Reserved, must be 0
0xFF, // Initial Content of Erased Memory
10000, // Program Page Timeout 100 mSec
6000, // Erase Sector Timeout 6000 mSec
// Specify Size and Address of Sectors
0x1000, 0x000000, // Sector Size 4kB
SECTOR_END
};
#endif // FLASH_MEM
特别注意:"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模块如何调用系统函数,与此文无关,留在以后再讲。
特别注意:
复制一份新的分散加载文件到工程目录中,然后修改成如下代码 --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
}
}
通过这个cmd.exe /C copy "!L" "..\@L.FLM"
命令就可以将生成的axf可执行文件修改为flm。
将生成的flm文件拷贝到...\Keil_v5\ARM\Flash目录,即可被MDK识别到。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。