STM32 使用SFUD库驱动Flash(Hal库)

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

目前,除了部分厂家旧款 Flash 型号会不支持该标准,其他绝大多数新出厂的 Flash 均已支持 SFDP 标准。所以该库在初始化时会优先读取 SFDP 表参数。

链接在这里:https://github.com/armink/SFUD


本期环境说明:STM32F407VET6 W25Q128

第一步:使用CUBEMX生成一个带SPI读写和串口发送的工程

第二步:完成串口重定向

关于这点网上有不少的一键复制代码,有开启微库(microlib)的,有AC6的

这里我用AC6不开microlib的做法

cpp 复制代码
/* ------------------通过重定向将printf函数映射到串口1上-------------------*/
#if !defined(__MICROLIB)

//#pragma import(__use_no_semihosting)
__asm (".global __use_no_semihosting\n\t");

// Stub functions required when disabling semihosting
void _sys_exit(int x) //避免使用半主机模式
{
  x = x;
}

void _ttywrch(int ch)
{
    ch = ch;
}

// Additional stub functions required by ARM Compiler 6
#include <rt_sys.h>

FILEHANDLE _sys_open(const char *name, int openmode)
{
    return -1;
}

int _sys_close(FILEHANDLE fh)
{
    return 0;
}

int _sys_write(FILEHANDLE fh, const unsigned char *buf, unsigned len, int mode)
{
    return 0;
}

int _sys_read(FILEHANDLE fh, unsigned char *buf, unsigned len, int mode)
{
    return -1;
}

int _sys_istty(FILEHANDLE fh)
{
    return 0;
}

int _sys_seek(FILEHANDLE fh, long pos)
{
    return -1;
}

long _sys_flen(FILEHANDLE fh)
{
    return -1;
}
//struct __FILE
//{
//  int handle;
//};
// FILE __stdout; // Not needed for ARM Compiler 6 (ARMCLANG)

#endif

#if defined ( __GNUC__ ) && !defined (__clang__) 
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif 
PUTCHAR_PROTOTYPE
{
  /* 实现串口发送一个字节数据的函数 */
  //serial_write(&serial1, (uint8_t)ch); //发送一个自己的数据到串口
	HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);
  return ch;
}

然后可以在串口助手测试一下,这里给大家推荐一个在线串口助手

Web Serial Debug - 在线串口调试工具 - 土巴士


**第三步:**将sfud七个文件放进工程中

在这里我们只需要修改sfud_port.c和sfud_cfg.h两个文件

先看sfud_cfg.h,解释一下这里的宏定义

cpp 复制代码
#ifndef _SFUD_CFG_H_
#define _SFUD_CFG_H_

#define SFUD_DEBUG_MODE

#define SFUD_USING_SFDP

// #define SFUD_USING_FAST_READ

#define SFUD_USING_FLASH_INFO_TABLE

enum {
    SFUD_XXXX_DEVICE_INDEX = 0,
};

#define SFUD_FLASH_DEVICE_TABLE                                                \
{                                                                              \
    [SFUD_XXXX_DEVICE_INDEX] = {.name = "XXXX", .spi.name = "SPIX"},           \
}

#define SFUD_USING_QSPI

#endif /* _SFUD_CFG_H_ */

调试模式:打开/关闭 SFUD_DEBUG_MODE 宏定义

是否使用 SFDP 参数(内置 Flash 信息表)功能:打开/关闭 SFUD_USING_SFDP 宏定义

是否使用快速读模式(SPI模式):打开/关闭 SFUD_USING_FAST_READ 宏定义。

是否使用该库自带的 Flash 参数信息表:打开/关闭 SFUD_USING_FLASH_INFO_TABLE 宏定义

如果产品中存在多个 Flash ,可以添加 Flash 设备表。修改 SFUD_FLASH_DEVICE_TABLE

QSPI 模式:打开/关闭 SFUD_USING_QSPI 宏定义

这里改动为

cpp 复制代码
#ifndef _SFUD_CFG_H_
#define _SFUD_CFG_H_

#define SFUD_DEBUG_MODE

#define SFUD_USING_SFDP

// #define SFUD_USING_FAST_READ

#define SFUD_USING_FLASH_INFO_TABLE

enum {
    SFUD_W25Q128_DEVICE_INDEX = 0,
};

#define SFUD_FLASH_DEVICE_TABLE                                                \
{                                                                              \
    [SFUD_W25Q128_DEVICE_INDEX] = {.name = "W25Q128BV", .spi.name = "SPI1"},           \
}

//#define SFUD_USING_QSPI

#endif /* _SFUD_CFG_H_ */

然后在sfud_port.c里我们需要修改两个函数

spi_write_read和sfud_spi_port_init,剩下默认就好

cpp 复制代码
#include <sfud.h>
#include <stdarg.h>
#include "gpio.h"
#include "spi.h"


/* 片选控制宏 */
#define CS_GPIO_PORT    GPIOA
#define CS_GPIO_PIN     GPIO_PIN_4
#define CS_LOW()        HAL_GPIO_WritePin(CS_GPIO_PORT, CS_GPIO_PIN, GPIO_PIN_RESET)
#define CS_HIGH()       HAL_GPIO_WritePin(CS_GPIO_PORT, CS_GPIO_PIN, GPIO_PIN_SET)


static char log_buf[256];

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

/**
 * 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_HandleTypeDef *hspi = (SPI_HandleTypeDef *)spi->user_data;
    
    /* 拉低片选 */
    CS_LOW();
    
    /* 发送数据(命令+地址)*/
    if (write_size > 0 && write_buf != NULL) {
        if (HAL_SPI_Transmit(hspi, (uint8_t *)write_buf, write_size, 1000) != HAL_OK) {
            result = SFUD_ERR_TIMEOUT;
            goto error_exit;
        }
    }
    
    /* 接收数据 */
    if (read_size > 0 && read_buf != NULL) {
        if (HAL_SPI_Receive(hspi, read_buf, read_size, 1000) != HAL_OK) {
            result = SFUD_ERR_TIMEOUT;
            goto error_exit;
        }
    }
    
error_exit:
    /* 拉高片选 */
    CS_HIGH();
    return result;
}

#ifdef SFUD_USING_QSPI
/**
 * read flash data by QSPI
 */
static sfud_err qspi_read(const struct __sfud_spi *spi, uint32_t addr, sfud_qspi_read_cmd_format *qspi_read_cmd_format,
        uint8_t *read_buf, size_t read_size) {
    sfud_err result = SFUD_SUCCESS;

    /**
     * add your qspi read flash data code
     */

    return result;
}
#endif /* SFUD_USING_QSPI */

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
     */
  
    /* flash结构体初始化 */
    flash->spi.wr = spi_write_read;
    flash->spi.lock = NULL;       // 如需线程安全,实现互斥锁
    flash->spi.unlock = NULL;
    flash->spi.user_data = &hspi1;
    flash->retry.delay = NULL;    // 如需延时函数,可设置
    flash->retry.times = 10000;   // 重试次数	
    return result;
}

/**
 * This function is print debug info.
 *
 * @param file the file which has call this function
 * @param line the line number which has call this function
 * @param format output format
 * @param ... args
 */
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);
    printf("[SFUD](%s:%ld) ", file, line);
    /* must use vprintf to print */
    vsnprintf(log_buf, sizeof(log_buf), format, args);
    printf("%s\n", log_buf);
    va_end(args);
}

/**
 * This function is print routine info.
 *
 * @param format output format
 * @param ... args
 */
void sfud_log_info(const char *format, ...) {
    va_list args;

    /* args point to the first variable parameter */
    va_start(args, format);
    printf("[SFUD]");
    /* must use vprintf to print */
    vsnprintf(log_buf, sizeof(log_buf), format, args);
    printf("%s\n", log_buf);
    va_end(args);
}

最后参考SFUD | 一个简洁实用的开源项目,帮你轻松搞定SPI Flash-腾讯云开发者社区-腾讯云的测试demo程序

cpp 复制代码
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "spi.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "sfud.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* 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];
void sfud_demo(uint32_t addr, size_t size, uint8_t *data)
{
    sfud_err result = SFUD_SUCCESS;
    extern sfud_flash *sfud_dev;
    const sfud_flash *flash = sfud_get_device(SFUD_W25Q128_DEVICE_INDEX);
    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)
    {
        printf("Erase the %s flash data finish. Start from 0x%08X, size is %zu.\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 %zu.\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 %zu. 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);
    }
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* 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_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
printf("SFUD port by mculover666.\r\n");
/* SFUD initialize */
if (sfud_init() == SFUD_SUCCESS)
{
        /* enable qspi fast read mode, set one data lines width */
        sfud_demo(0, sizeof(sfud_demo_test_buf), sfud_demo_test_buf);
}
  /* USER CODE END 2 */

没有问题

相关推荐
帅次5 小时前
系统分析师-信息物理系统分析与设计
stm32·单片机·嵌入式硬件·mcu·物联网·iot·rtdbs
澜莲Alice5 小时前
STM32 MPLAB X IDE 软件安装-玩转单片机-英文版沉浸式安装
stm32·单片机·嵌入式硬件
良许Linux5 小时前
IIC总线的硬件部分的两个关键点:开漏输出+上拉电阻
单片机·嵌入式硬件
✎ ﹏梦醒͜ღ҉繁华落℘6 小时前
单片机基础知识 -- ADC分辨率
单片机·嵌入式硬件
Q_21932764556 小时前
车灯控制与报警系统设计
人工智能·嵌入式硬件·无人机
雾削木7 小时前
树莓派部署 HomeAssistant 教程
stm32·单片机·嵌入式硬件
Q_21932764557 小时前
基于单片机的破壁机自动控制系统设计
单片机·嵌入式硬件
我是一棵无人问荆的小草7 小时前
stm32f103芯片多个IO配置成外部中断
stm32·单片机·嵌入式硬件
wjykp7 小时前
ESP32xxx烧录
stm32·单片机·嵌入式硬件
早起huo杯黑咖啡8 小时前
【NOR Flash】关于芯片的高耐久性分区的编程/擦除周期和最小保留时间的数据
单片机·嵌入式硬件