【STM32H750】从零编写MDK的FLM烧录算法

这篇具有很好参考价值的文章主要介绍了【STM32H750】从零编写MDK的FLM烧录算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

当我们要下载编译好的镜像到Flash时,首先要做的一步就是选择合适的Flash下载算法,而这个算法本身就是一个FLM文件:
【STM32H750】从零编写MDK的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. 编译代码
    【STM32H750】从零编写MDK的FLM烧录算法
    查看map文件,image_watch_seconds这个数组已经被分配到了0X90138690这个地址了,这个地址正是LR_EROM1 所在的区间。

二、MDK下载算法原理

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

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

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

【STM32H750】从零编写MDK的FLM烧录算法

  • 加载算法到芯片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)

【STM32H750】从零编写MDK的FLM烧录算法

配置SPI

【STM32H750】从零编写MDK的FLM烧录算法

配置UART

【STM32H750】从零编写MDK的FLM烧录算法

配置时钟树

【STM32H750】从零编写MDK的FLM烧录算法

设置调试接口

【STM32H750】从零编写MDK的FLM烧录算法

设置工程并生成工程

【STM32H750】从零编写MDK的FLM烧录算法

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源代码放置在工程目录中
【STM32H750】从零编写MDK的FLM烧录算法
将sfud添加到工程目录:
【STM32H750】从零编写MDK的FLM烧录算法
添加串口打印文件:
为了测试方便,添加一个串口打印的文件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 */

运行如下:
【STM32H750】从零编写MDK的FLM烧录算法

3. 生成FLM文件

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

【STM32H750】从零编写MDK的FLM烧录算法

添加修改编程算法文件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宏
【STM32H750】从零编写MDK的FLM烧录算法

添加修改配置文件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和汇编的配置都勾选上:
【STM32H750】从零编写MDK的FLM烧录算法
【STM32H750】从零编写MDK的FLM烧录算法

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不再需要参与编译
    【STM32H750】从零编写MDK的FLM烧录算法

修改分散加载文件

复制一份新的分散加载文件到工程目录中,然后修改成如下代码
【STM32H750】从零编写MDK的FLM烧录算法
–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。
【STM32H750】从零编写MDK的FLM烧录算法
【STM32H750】从零编写MDK的FLM烧录算法
将生成的flm文件拷贝到…\Keil_v5\ARM\Flash目录,即可被MDK识别到。

DEMO下载地址:https://gitee.com/Aladdin-Wang/STM32H7_W25QXXX文章来源地址https://www.toymoban.com/news/detail-417101.html

到了这里,关于【STM32H750】从零编写MDK的FLM烧录算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32H723 + DP83848 + LWIP + RT-Thread(FreeRTOS) + STM32CubeMX + Keil MDK 超详细

    MCU:STM32H723ZGT ETH PHY :DP83848 RT-Thread:RT-Thread nano 3.1.5 Software Pack:STM32CubeH7 Firmware Package V1.10.0 / 11-February-2022 STM32H723配置以太网+Freertos注意事项 STM32H723+Lwip+ETH+CUBE 完整配置(排了巨多坑!) Cube配置STM32H743+DP83848以太网工程 STM32H743+CubeMX-梳理MPU的设置 首先使用 CubeMX配置相关外

    2024年02月13日
    浏览(52)
  • 【正点原子H750MiniPro H750】按键以及LCD

    正点原子的LCD驱动与蓝桥杯的LCD代码驱动有所区别,编代码的时候不太舒服,所以把驱动包完善了一下。 1.CubeMx的配置步骤 运用完善后的驱动之后就不用重新配置CubeMx了,注意避开LCD屏幕的引脚就行了。 点击GENERATE CODE生成代码 至此工程就建立完毕了。 2.测试代码 添加除了

    2024年02月15日
    浏览(33)
  • 利用MDK的FLM文件生成通用flash驱动

    在进行Flash操作时,一般我们需要设计一套Flash抽象层,至少要包括flash的init,read、write、erase这些操作。但每更换一个单片机或者flash器件就要额外去编写flash驱动去适配init,read、write、erase。尽管有会者不难的属性加持,但适配所有的单片机或者flash器件,工作量也可想而知

    2023年04月16日
    浏览(36)
  • (STM32H5系列)STM32H573RIT6、STM32H573RIV6、STM32H573ZIT6嵌入式微控制器基于Cortex®-M33内核

    工业(PLC、工业电机控制、泵和压缩机) 智能家居(空调、冰箱、冰柜、中央警报系统、洗衣机) 个人电子产品(键盘、智能手机、物联网标签、跟踪设备) 智能城市(工业通信、照明控制、数字电源) 医疗和保健(CPAP和呼吸器、透析机、药丸分配器、电动病床) 1、ST

    2024年02月09日
    浏览(45)
  • STM32H5开发(1)----总览

    STM32H5系列微控制器是意法半导体公司推出的一款高性能MCU, CortexM33内核的微控制器产品。 他和STM32F2、F4、F7、H7同属于高端系列产品线。同时他的主频能够达到250MHz。STM32H5属于STM32F4的升级. 最近在弄ST的课程,需要样片的可以加群申请:6_15061293 。 https://www.wjx.top/vm/PpC1kRR.asp

    2024年02月12日
    浏览(35)
  • STM32H5移植zbar记录

    ZBar是一种流行的二维码扫描和解码工具,它在嵌入式系统中拥有广泛的应用。在嵌入式系统中,我们面临着有限的资源和更严格的性能要求,因此,选择适当的库来完成特定的任务非常重要。 ZBar适用于各种嵌入式平台,包括ARM、x86和MIPS等处理器架构。它可以轻松地整合到各

    2024年02月06日
    浏览(42)
  • STM32H5开发(2)----新特性

    性能提升 搭载Cortex-M33内核,每兆赫1.5 DMIPS和4.09 CoreMark,为系统提供更强的计算能力。 采用先进的40nm工艺,带来更高的系统主频和更快的flash访问速度。 具备增强的系统架构,进一步提升整体性能。 新特性,高集成,高性价比 利用40nm工艺,内部存储器(FLASH+RAM)得到扩充,可

    2024年02月13日
    浏览(36)
  • STM32H5开发(4)----开发板介绍

    STM32H503RBTx_LQFP64是STM32H5系列微控制器的一款出色评估套件,它采用了先进的40nm工艺制造,为开发者提供了卓越的性能和能效。主频高达250MHz的Arm® Cortex®-M33内核使其处理能力非常强大,可以轻松应对各种复杂的计算和任务。 这个评估套件在存储方面同样表现优异,拥有128k

    2024年02月13日
    浏览(45)
  • STM32H5开发(5)----串口打印配置

    在使用STM32CUBEIDE开发STM32H5项目时,串口打印被证明是一项极其有益的调试工具,能够在开发过程中实时输出信息和调试数据,起到了至关重要的作用。通过充分利用串口打印功能,开发者可以轻松地在代码中插入打印语句,通过串口将这些信息传输至连接于PC端的终端软件,

    2024年02月11日
    浏览(39)
  • STM32H7使用外部flash运行程序

    在淘宝上买了一块核心板,使用的STM32H7B0VBT6。 客服很尽责,帮助了我很多。 H7系列的功能很强大,但是H7B0他有个问题,只有128k的内部flash,这么强大的芯片只有这么小的flash,想搞个RTTreadOS都不行。无奈,智能选择使用外部flash,好在核心板上有两个W25Q64,一个SPI,一个QS

    2023年04月18日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包