1 Star 4 Fork 8

Aladdin-Wang / STM32H7_W25QXX

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
Apache-2.0

前言

当我们要下载编译好的镜像到Flash时,首先要做的一步就是选择合适的Flash下载算法,而这个算法本身就是一个FLM文件: 在这里插入图片描述 代码既可以下载到内部flash,也可以下载到外部flash,或者一部分下载到内部,一部分下载到外部。

一、将代码中的图片资源下载到外部flash

在UI设计中往往需要大量的图片和字体,图片和字体资源在代码中以静态数组的形式存在,这些大数组在内部flash中一般存放不下,所以需要把这些占用资源比较大的数组放在外部flash中,然后通过QSPI地址映射的方式访问,或者通过SPI将flash中的资源分批读取到RAM缓存中使用。

1. 修改分散加载文件

  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 。

  1. 在代码中将图片资源分配到ExtFlashSection段
#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
};
  1. 编译代码 在这里插入图片描述 查看map文件,image_watch_seconds这个数组已经被分配到了0X90138690这个地址了,这个地址正是LR_EROM1 所在的区间。

二、MDK下载算法原理

1. 程序能够通过下载算法下载到芯片的原理

通过MDK创建一批与地址信息无关的函数,实现的功能主要有初始化,擦除,编程,读取,校验等,然后MDK调试下载阶段,会将算法文件加载到芯片的内部RAM里面(加载地址可以通过MDK设置),然后MDK通过与这个算法文件的交互,实现程序下载,调试阶段数据读取等操作。

2. 算法程序中擦除操作执行流程

算法程序中擦除操作执行流程

  • 加载算法到芯片RAM。
  • 执行初始化函数Init。
  • 执行擦除操作,根据用户的MDK配置,这里可以选择整个芯片擦除或者扇区擦除。
  • 执行Uinit函数。
  • 操作完毕。

3. 制作FLM文件步骤

  1. 将ARM:CMSIS Pack文件夹(通常是C:\Keil\ARM\Pack\ARM\CMSIS\ version \Device_Template_Flash)中的工程复制到一个新文件夹中,取消文件夹的只读属性,重命名项目文件NewDevice.uvprojx以表示新的flash 设备名称,例如MyDevice.uvprojx。
  2. 打开工程,从工具栏中,使用下拉选择目标来选择处理器架构。
  3. 打开对话框Project - Options for Target - Output并更改Name of Executable字段的内容以表示设备,例如MyDevice。
  4. 调整文件FlashPrg中的编程算法。
  5. 调整文件FlashDev中的设备参数。
  6. 使用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
/* 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);
}

测试SFUD

在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 */

运行如下: 在这里插入图片描述

3. 生成FLM文件

重新生成不带main函数的工程

在这里插入图片描述

添加修改编程算法文件FlashPrg.c

模板工程里面提供了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

模板工程里面提供了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模块如何调用系统函数,与此文无关,留在以后再讲。

特别注意:

  • 由于模块中不含中断向量表,所以程序中不要开启任何中断。
  • 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识别到。

Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

简介

STM32H7 MDK下载算法demo 展开 收起
C 等 3 种语言
Apache-2.0
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
C
1
https://gitee.com/Aladdin-Wang/STM32H7_W25QXXX.git
git@gitee.com:Aladdin-Wang/STM32H7_W25QXXX.git
Aladdin-Wang
STM32H7_W25QXXX
STM32H7_W25QXX
master

搜索帮助