你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【福利三:逢7发帖赢大礼】4、基于STM32G070板子SPI flash 移植SFUD库

[复制链接]
STMWoodData 发布时间:2026-7-14 16:23

板上设计有一片SPI FLASH存储器,准备移植SFUD库驱动。

image.png

电路接口如下:

image.png

首先打开STM32CubeMX,初始化SPI外设参数。打开DMA发送接收。

image.png

image.png

接下来实现SPI发送接收数据接口对接SFUD驱动。


#define     SPI_BUS_NUM     2
SPI_HandleTypeDef * spi_table[SPI_BUS_NUM]=
{
    &hspi1,
    &hspi2,
};

typedef  void (*CallbackHook)(void);
typedef struct stm32_spi_para_t
{
    SPI_HandleTypeDef   * _spi;
    volatile uint16_t   spi_transmit_ok;
    volatile uint16_t   spi_receive_ok;
    CallbackHook        spi_send_fn;
    CallbackHook        spi_recv_fn;

}stm32_spi_para_t;
stm32_spi_para_t    stm32_spi_p[SPI_BUS_NUM] = 
{
    {
        ._spi = &hspi1,
        .spi_transmit_ok = 0,
        .spi_receive_ok = 0,
        .spi_send_fn = NULL,
        .spi_recv_fn = NULL,
    },
    {
        ._spi = &hspi2,
        .spi_transmit_ok = 0,
        .spi_receive_ok = 0,
        .spi_send_fn = NULL,
        .spi_recv_fn = NULL,
    },
};


void spi_write_read_buff(uint8_t index ,uint8_t * wbuf,uint32_t wl,uint8_t * rbuf,uint32_t rl)
{
    stm32_spi_p[index].spi_send_fn = NULL;
    stm32_spi_p[index].spi_recv_fn = NULL;
    if((wbuf != NULL) && (wl != 0))
    {
        stm32_spi_p[index].spi_transmit_ok = 1;
        HAL_SPI_Transmit(stm32_spi_p[index]._spi,wbuf, wl, 100);
        stm32_spi_p[index].spi_transmit_ok = 0;
    }

    if((rbuf != NULL) && (rl != 0))
    {
        memset(rbuf,0,rl);
        stm32_spi_p[index].spi_receive_ok = 1;
        HAL_SPI_Receive(stm32_spi_p[index]._spi,rbuf, rl, 100);
        stm32_spi_p[index].spi_receive_ok = 0;
    }
}

SFUD库地址:https://gitee.com/Armink/SFUD [SFUD]是一款开源的串行 SPI Flash 通用驱动库。由于现有市面的串行 Flash 种类居多,各个 Flash 的规格及命令存在差异, SFUD 就是为了解决这些 Flash 的差异现状而设计,让我们的产品能够支持不同品牌及规格的 Flash,提高了涉及到 Flash 功能的软件的可重用性及可扩展性,同时也可以规避 Flash 缺货或停产给产品所带来的风险。 下载SFUD库,将源码添加到keil工程内。移植SFUD库主要关注配置sfud_cfg.h和接口驱动sfud_port.c。

image.png

配置文件很简单,所有配置位于 /sfud/inc/sfud_cfg.h ,请参考下面的配置介绍,选择适合自己项目的配置。

打开/关闭 SFUD_DEBUG_MODE 宏定义,用于打印调试信息。

再就是打开 SFUD_USING_SFDPSFUD_USING_FLASH_INFO_TABLE 宏定义。

继续移植接口文件sfud_port.c中的 sfud_err sfud_spi_port_init(sfud_flash *flash)函数,方法是库提供的移植方法,在里面完成各个设备 SPI 读写驱动(必选)、重试次数(必选)、重试接口(可选)及 SPI 锁(可选)的配置。 下面就是我移植的接口程序:

#include <sfud.h>
#include <stdarg.h>
#include "main.h"
#include "drv_spi.h"

static char log_buf[256];

void sfud_log_debug(const char *file, const long line, const char *format, ...);
void sfud_log_info(const char *format, ...);

typedef struct 
{
    uint32_t    spi_index;
    void (*spi_cs_gpio_set)(uint32_t bitflag);
    void (*spi_write_read)(uint8_t index,uint8_t * wbuf,uint32_t wl,uint8_t * rbuf,uint32_t rl);
} spi_user_data, *spi_user_data_t;


/* about 100 microsecond delay */
static void retry_delay_ms(void) 
{
    delay_ms(1);
}
static void spi_lock(const sfud_spi *spi) 
{
//    __disable_irq();
}
static void spi_unlock(const sfud_spi *spi) 
{
 //   __enable_irq();
}

static void spi_cs_gpio(uint32_t bitflag) 
{
    if(bitflag) HAL_GPIO_WritePin(F_CS_GPIO_Port, F_CS_Pin, GPIO_PIN_SET);
    else        HAL_GPIO_WritePin(F_CS_GPIO_Port, F_CS_Pin, GPIO_PIN_RESET);
}

static spi_user_data spi_cfg =
{
    .spi_index = 0,
    .spi_cs_gpio_set = spi_cs_gpio,
    .spi_write_read  = spi_write_read_buff,

};
/**
 * SPI write data then read data
 */
static sfud_err sfud_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;
    uint8_t send_data, read_data;

    /**
     * add your spi write and read code
     */
    spi_user_data_t spi_dev = (spi_user_data_t) spi->user_data;
    if (write_size) {
        SFUD_ASSERT(write_buf);
    }
    if (read_size) {
        SFUD_ASSERT(read_buf);
    }

    spi_dev->spi_cs_gpio_set(0);
    spi_dev->spi_write_read(spi_dev->spi_index, (uint8_t *)write_buf, write_size, read_buf, read_size);
    spi_dev->spi_cs_gpio_set(1);

    return result;
}
sfud_err sfud_spi_port_init(sfud_flash *flash) 
{
    sfud_err result = SFUD_SUCCESS;

    /**
     * add your port spi bus and device object initialize code like this:
     * 1. rcc initialize
     * 2. gpio initialize
     * 3. spi device initialize
     * 4. flash->spi and flash->retry item initialize
     *    flash->spi.wr = spi_write_read; //Required
     *    flash->spi.qspi_read = qspi_read; //Required when QSPI mode enable
     *    flash->spi.lock = spi_lock;
     *    flash->spi.unlock = spi_unlock;
     *    flash->spi.user_data = &spix;
     *    flash->retry.delay = null;
     *    flash->retry.times = 10000; //Required
     */

    /* 4 同步 Flash 移植所需的接口及数据 */
    flash->spi.wr = sfud_write_read;
#ifdef SFUD_USING_QSPI
    flash->spi.qspi_read = qspi_read; //Required when QSPI mode enable
#endif
    flash->spi.lock   = spi_lock;
    flash->spi.unlock = spi_unlock;

    flash->spi.user_data = &spi_cfg;

    /* about 100 microsecond delay */
    flash->retry.delay = retry_delay_ms;
    /* adout 1 seconds timeout */
    flash->retry.times = 1000;  //1000 * retry_delay_ms()

    return result;
}

以上就完成了移植,下面测试一下效果。

在需要sfud接口文件中包含#include "sfud.h"头文件。然后使用前先调用sfud_err sfud_init(void)初始化所有flash。sfud_init()将会调用 sfud_err sfud_device_init(sfud_flash *flash) 初始化 Flash 设备表中的全部设备。如果只有一个 Flash 也可以只使用 sfud_device_init 进行单一初始化。

image.png

测试flash擦除,读写数据,使用shell命令测试,代码如下:

#define SFUD_DEMO_TEST_BUFFER_SIZE                     1024
static void sfud_demo(uint32_t addr, size_t size, uint8_t *data);
uint8_t sfud_demo_test_buf[SFUD_DEMO_TEST_BUFFER_SIZE];

static void sfud_demo(uint32_t addr, size_t size, uint8_t *data)
{
    sfud_err result = SFUD_SUCCESS;
    const sfud_flash *flash = sfud_get_device_table() + 0;
    size_t i;

    if(flash->init_ok == false)  sfud_init();

    /* prepare write data */
    for (i = 0; i < size; i++) {
        data[i] = i+0xa0;
    }
    /* erase test */
    result = sfud_erase(flash, addr, size);
    if (result == SFUD_SUCCESS) {
        printf("Erase the %s flash data finish. Start from 0x%08X, size is %u.\r\n", flash->name, addr,
                size);
    } else {
        printf("Erase the %s flash data failed.\r\n", flash->name);
        return;
    }
    /* write test */
    result = sfud_write(flash, addr, size, data);
    if (result == SFUD_SUCCESS) {
        printf("Write the %s flash data finish. Start from 0x%08X, size is %u.\r\n", flash->name, addr,
                size);
    } else {
        printf("Write the %s flash data failed.\r\n", flash->name);
        return;
    }
    /* read test */
    result = sfud_read(flash, addr, size, data);
    if (result == SFUD_SUCCESS) {
        printf("Read the %s flash data success. Start from 0x%08X, size is %u. The data is:\r\n", flash->name, addr,
                size);
        printf("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) {
                printf("[%08X] ", addr + i);
            }
            printf("%02X ", data[i]);
            if (((i + 1) % 16 == 0) || i == size - 1) {
                printf("\r\n");
            }
        }
        printf("\r\n");
    } else {
        printf("Read the %s flash data failed.\r\n", flash->name);
    }
    /* data check */
    for (i = 0; i < size; i++) {
        if (data[i] != i % 256) {
            printf("Read and check write data has an error. Write the %s flash data failed.\r\n", flash->name);
            break;
        }
    }
    if (i == size) {
        printf("The %s flash test is success.\r\n", flash->name);
    }
}

#if UART_SHELL == 2
#include "nr_micro_shell.h"
void sfud_flash_demo(char argc, char *argv)
{
    sfud_demo(0, sizeof(sfud_demo_test_buf), sfud_demo_test_buf);
}
NR_SHELL_CMD_EXPORT(sfud_flash, sfud_flash_demo, "Test flash write and read");

#elif UART_SHELL == 3
//LSY_SHELL
#include "lsy_shell_core.h"

int cmd_sfud_demo(int argc, char **argv)
{
    sfud_demo(0, sizeof(sfud_demo_test_buf), sfud_demo_test_buf);
    return SHELL_OK;
}
LSY_SHELL_CMD_EXPORT(sfud, cmd_sfud_demo, "test sfud demo.");
#else

#endif

通过串口命令测试,可以看到数据成功写入读取。测试效果:

image.png

image.png

收藏 评论0 发布时间:2026-7-14 16:23

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版