STM32 GD32 标准库移植SFUD

本次移植是在官方源码的基础上进行移植的

本次介绍的两个软件包SFUD/FAL都与FLASH有关,并且都可以独立使用或者结合在一起使用,两个软件包都对操作系统无依赖,可以使用裸机移植,也很方便移植到各种系统。
这两个软件包的作者都是armink,armink的开源仓库地址:https://github.com/armink,更多好玩的软件,请到作者仓库查询。
 

下面给出官方源码的下载链接 

https://gitee.com/Armink/SFUD

0.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 的参数信息(添加方法详细见 2.5 添加库目前不支持的 Flash)。获取到了 Flash 的规格参数后,就可以实现对 Flash 的全部操作。

1、为什么选择 SFUD

  • 避免项目因 Flash 缺货、Flash 停产或产品扩容而带来的风险;
  • 越来越多的项目将固件存储到串行 Flash 中,例如:ESP8266 的固件、主板中的 BIOS 及其他常见电子产品中的固件等等,但是各种 Flash 规格及命令不统一。使用 SFUD 即可避免,在相同功能的软件平台基础下,无法适配不同 Flash 种类的硬件平台的问题,提高软件的可重用性;
  • 简化软件流程,降低开发难度。现在只需要配置好 SPI 通信,即可畅快的开始玩串行 Flash 了;
  • 可以用来制作 Flash 编程器/烧写器

相关的说明大家可以进去链接详细的看一下

目前支持的FLASH类型列表
在这里插入图片描述

总结一下:SFUD就是一个SPI-FLASH的底层驱动,只要是遵循 JEDEC (固态技术协会)的芯片都是可以适配的,目前我理解的好处就是如果更换了硬件,只要是这个表里面的,就不需要改代码!!!

 

RT-Thread中flash管理 — [SFUD组件 和 FAL驱动组件介绍]

本来项目上是想用FlashDB  

在移植嵌入式数据库的基础 上   首先第一步大多数都是先移植SFUD

使用rtthread移植

        在移植过程中一定要参考两个资料:项目的readme文档和demo工程。
对于使用rtthread完整版来说,作者已经把SFUD制作成了rtthread的内置组件了,对于使用者只需要勾选就可以了:

标准库移植:

工具准备
STM32CubeMX:用于配置QSPI外设和串口外设,并生成 MDK 工程;

本文采用的模板是固件库版本
Keil MDK:用于编译和下载工程;
串口助手:用于查看开发板串口调试信息输出;
裸机工程准备
下载完源码后    直接将源码中的SFUD相关的.c  .h文件  添加到你准备的工程模板文件中

  • 先去下载库,然后放入自己的工程中(先不动),基本上就是这个样子的
    在这里插入图片描述
  • 接着就是修改头文件了
    基本上不需要修改什么,先把配置文件改一下

 

 

sfud_cfg.h

填写相关枚举与宏。因为事例工程所使用的芯片STM32F429IG并没有QSPI功能,可以注释掉。

如果Flash型号支持SFDP,可以注释SFUD_USING_SFDP。可以减少需要编译的代码量。

/*
 * This file is part of the Serial Flash Universal Driver Library.
 *
 * Copyright (c) 2016-2018, Armink, <armink.ztl@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * 'Software'), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Function: It is the configure head file for this library.
 * Created on: 2016-04-23
 */

#ifndef _SFUD_CFG_H_
#define _SFUD_CFG_H_

//#define SFUD_DEBUG_MODE		//使能SFUD的打印日志

#define SFUD_USING_SFDP		//使能SFDP:JEDEC标准(JESD216)标准接口	注意:关闭后只会查询该库在 /sfud/inc/sfud_flash_def.h 中提供的 Flash 信息表。\
这样虽然会降低软件的适配性,但减少代码量。

#define SFUD_USING_FLASH_INFO_TABLE	//是否使用该库自带的 Flash 参数信息表注意:关闭后该库只驱动支持 SFDP 规范的 Flash,也会适当的降低部分代码量。\
另外 2.3.2 及 2.3.3 这两个宏定义至少定义一种,也可以两种方式都选择

//支持多路外设

enum
{
    SFUD_W25Q64_DEVICE_INDEX = 0,
};
//把实际使用的硬件和外设接口对应配置好
#define SFUD_FLASH_DEVICE_TABLE                                                \
{                                                                              \
    [SFUD_W25Q64_DEVICE_INDEX] = {.name = "W25Q64JV", .spi.name = "SPI1"},           \
}

#define SFUD_USING_QSPI

#endif /* _SFUD_CFG_H_ */




判断芯片支持SFUD

1.FLASH芯片支持SFDP标准;(在芯片datesheet中全局搜索SFDP)。
2.如果该 Flash 不支持 SFDP 标准,SFUD 会查询配置文件 ( \sfud\inc\sfud_flash_def.h ) 中提供的 Flash 参数信息表 是否支持该款 Flash。

#ifdef SFUD_USING_FLASH_INFO_TABLE
/* SFUD supported flash chip information table. If the flash not support JEDEC JESD216 standard,
 * then the SFUD will find the flash chip information by this table. You can add other flash to here then
 *  notice me for update it. The configuration information name and index reference the sfud_flash_chip structure.
 * | name | mf_id | type_id | capacity_id | capacity | write_mode | erase_gran | erase_gran_cmd |
 */
#define SFUD_FLASH_CHIP_TABLE                                                                                       \
{                                                                                                                   \
    {"AT45DB161E", SFUD_MF_ID_ATMEL, 0x26, 0x00, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_DUAL_BUFFER, 512, 0x81},      \
    {"W25Q40BV", SFUD_MF_ID_WINBOND, 0x40, 0x13, 512L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                        \
    {"W25Q16BV", SFUD_MF_ID_WINBOND, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                    \
    {"W25Q32BV", SFUD_MF_ID_WINBOND, 0x40, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                    \
    {"W25Q64CV", SFUD_MF_ID_WINBOND, 0x40, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                    \
    {"W25Q64DW", SFUD_MF_ID_WINBOND, 0x60, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                    \
    {"W25Q128BV", SFUD_MF_ID_WINBOND, 0x40, 0x18, 16L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                  \
    {"W25Q256FV", SFUD_MF_ID_WINBOND, 0x40, 0x19, 32L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                  \
    {"SST25VF080B", SFUD_MF_ID_SST, 0x25, 0x8E, 1L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20},              \
    {"SST25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20},              \
    {"M25P32", SFUD_MF_ID_MICRON, 0x20, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8},                  \
    {"M25P80", SFUD_MF_ID_MICRON, 0x20, 0x14, 1L*1024L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8},                  \
    {"M25P40", SFUD_MF_ID_MICRON, 0x20, 0x13, 512L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8},                      \
    {"EN25Q32B", SFUD_MF_ID_EON, 0x30, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                        \
    {"GD25Q64B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                 \
    {"GD25Q16B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                 \
    {"S25FL216K", SFUD_MF_ID_CYPRESS, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                   \
    {"S25FL032P", SFUD_MF_ID_CYPRESS, 0x02, 0x15, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                   \
    {"A25L080", SFUD_MF_ID_AMIC, 0x30, 0x14, 1L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                        \
    {"F25L004", SFUD_MF_ID_ESMT, 0x20, 0x13, 512L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20},                     \
    {"PCT25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20},              \
}
#endif /* SFUD_USING_FLASH_INFO_TABLE */


 

但是在添加spi来读取外部FLASH的时候,遇到了一些情况。

例如,找不到设备,或者说无法读取FLASH的ID。

读出的设备ID、厂商ID等是正确的,但是检测出来不符合SFDP标准。

[SFUD](..\..\..\sfud\src\sfud.c:116) Start initialize Serial Flash Universal Driver(SFUD) V1.1.0.
[SFUD](..\..\..\sfud\src\sfud.c:117) You can get the latest version on https://github.com/armink/SFUD .
[SFUD](..\..\..\sfud\src\sfud.c:861) The flash device manufacturer ID is 0xEF, memory type ID is 0x40, capacity ID is 0x16.
[SFUD](..\..\..\sfud\src\sfud_sfdp.c:122) Error: Check SFDP signature error. It's must be 50444653h('S' 'F' 'D' 'P').
[SFUD]Warning: Read SFDP parameter header information failed. The W25Q32BV is not support JEDEC SFDP.
[SFUD]Find a Winbond W25Q32BV flash chip. Size is 4194304 bytes.
[SFUD](..\..\..\sfud\src\sfud.c:840) Flash device reset success.
[SFUD]W25Q32BV flash device is initialize success.
[SFUD]Error: Can't enable write status.
Erase the W25Q32BV flash data failed.

sfud_port.c 修改引脚

 以上全部修改完之后

在主函数添加测试代码

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

打印的结果显示

 

 关于HAL库移植SFUD 参考以下的文章

SFUD移植 - 简书

本文中所使用的裸机工程是基于HAL库的,在SFUD源码的Demo中也有一份HAL库的工程,因为基于HAL库的移植接口实现都是一样的,所以我直接将Demo中的sfud_port.c文件复制过来替换:

推荐看官方文档SFUD: 一款使用 JEDEC SFDP 标准的串行 (SPI) Flash 通用驱动库 (gitee.com)

FLASH芯片型号:W25Q64JVSSIQ
RTOS:liteos-m
MCU:STM32F407ZGT6 HAL库

判断芯片支持SFUD

1.FLASH芯片支持SFDP标准;(在芯片datesheet中全局搜索SFDP)。
2.如果该 Flash 不支持 SFDP 标准,SFUD 会查询配置文件 ( \sfud\inc\sfud_flash_def.h ) 中提供的 Flash 参数信息表 是否支持该款 Flash。

#ifdef SFUD_USING_FLASH_INFO_TABLE
/* SFUD supported flash chip information table. If the flash not support JEDEC JESD216 standard,
 * then the SFUD will find the flash chip information by this table. You can add other flash to here then
 *  notice me for update it. The configuration information name and index reference the sfud_flash_chip structure.
 * | name | mf_id | type_id | capacity_id | capacity | write_mode | erase_gran | erase_gran_cmd |
 */
#define SFUD_FLASH_CHIP_TABLE                                                                                       \
{                                                                                                                   \
    {"AT45DB161E", SFUD_MF_ID_ATMEL, 0x26, 0x00, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_DUAL_BUFFER, 512, 0x81},      \
    {"W25Q40BV", SFUD_MF_ID_WINBOND, 0x40, 0x13, 512L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                        \
    {"W25Q16BV", SFUD_MF_ID_WINBOND, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                    \
    {"W25Q32BV", SFUD_MF_ID_WINBOND, 0x40, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                    \
    {"W25Q64CV", SFUD_MF_ID_WINBOND, 0x40, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                    \
    {"W25Q64DW", SFUD_MF_ID_WINBOND, 0x60, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                    \
    {"W25Q128BV", SFUD_MF_ID_WINBOND, 0x40, 0x18, 16L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                  \
    {"W25Q256FV", SFUD_MF_ID_WINBOND, 0x40, 0x19, 32L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                  \
    {"SST25VF080B", SFUD_MF_ID_SST, 0x25, 0x8E, 1L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20},              \
    {"SST25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20},              \
    {"M25P32", SFUD_MF_ID_MICRON, 0x20, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8},                  \
    {"M25P80", SFUD_MF_ID_MICRON, 0x20, 0x14, 1L*1024L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8},                  \
    {"M25P40", SFUD_MF_ID_MICRON, 0x20, 0x13, 512L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8},                      \
    {"EN25Q32B", SFUD_MF_ID_EON, 0x30, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                        \
    {"GD25Q64B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                 \
    {"GD25Q16B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                 \
    {"S25FL216K", SFUD_MF_ID_CYPRESS, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                   \
    {"S25FL032P", SFUD_MF_ID_CYPRESS, 0x02, 0x15, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                   \
    {"A25L080", SFUD_MF_ID_AMIC, 0x30, 0x14, 1L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20},                        \
    {"F25L004", SFUD_MF_ID_ESMT, 0x20, 0x13, 512L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20},                     \
    {"PCT25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20},              \
}
#endif /* SFUD_USING_FLASH_INFO_TABLE */

3.如果列表中还没有你用的芯片,按照格式要求在表的末尾添加自己的芯片即可。

接口适配

1.\sfud\inc\sfud_cfg.h

 

#ifndef _SFUD_CFG_H_
#define _SFUD_CFG_H_

#define SFUD_DEBUG_MODE

#define SFUD_USING_SFDP

// #define SFUD_USING_FLASH_INFO_TABLE

enum {
    SFUD_W25Q64JV_DEVICE_INDEX = 0,
};

#define SFUD_FLASH_DEVICE_TABLE                                                \
{                                                                              \
    [SFUD_W25Q64JV_DEVICE_INDEX] = {.name = "W25Q64JV", .spi.name = "SPI2"},           \
}

// #define SFUD_USING_QSPI

#endif /* _SFUD_CFG_H_ */

我这里用的是FLASH芯片支持SFDP标准,用的是SPI通信。所以可以屏蔽掉SFUD_USING_FLASH_INFO_TABLE和SFUD_USING_QSPI两个宏。
SFUD_FLASH_DEVICE_TABLE中定义了自己使用的FLASH芯片。

2.spi.c

配置完成后,需要初始化spi。我这里使用cubemx生成spi初始化代码。并增加spi_cs的初始化代码。为了让SFUD能支持一个MCU通过任意SPI接任意数量的FLASH芯片,增加spi_cs_low()spi2_cs_high()。源码如下:

/* Includes ------------------------------------------------------------------*/
#include "spi.h"

/* USER CODE BEGIN 0 */
#define SPI2_CS_PIN GPIO_PIN_0
#define SPI2_CS_GPIO_PORT GPIOC
/* USER CODE END 0 */

SPI_HandleTypeDef hspi2;

/* SPI2 init function */
void MX_SPI2_Init(void)
{

    /* USER CODE BEGIN SPI2_Init 0 */

    /* USER CODE END SPI2_Init 0 */

    /* USER CODE BEGIN SPI2_Init 1 */

    /* USER CODE END SPI2_Init 1 */
    hspi2.Instance = SPI2;
    hspi2.Init.Mode = SPI_MODE_MASTER;
    hspi2.Init.Direction = SPI_DIRECTION_2LINES;
    hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
    hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
    hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
    hspi2.Init.NSS = SPI_NSS_SOFT;
    hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
    hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
    hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
    hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    hspi2.Init.CRCPolynomial = 10;
    if (HAL_SPI_Init(&hspi2) != HAL_OK)
    {
        Error_Handler();
    }
    /* USER CODE BEGIN SPI2_Init 2 */

    /* USER CODE END SPI2_Init 2 */
}

void HAL_SPI_MspInit(SPI_HandleTypeDef *spiHandle)
{

    GPIO_InitTypeDef GPIO_InitStruct = {0};
    if (spiHandle->Instance == SPI2)
    {
        /* USER CODE BEGIN SPI2_MspInit 0 */

        /* USER CODE END SPI2_MspInit 0 */
        /* SPI2 clock enable */
        __HAL_RCC_SPI2_CLK_ENABLE();

        __HAL_RCC_GPIOC_CLK_ENABLE();
        __HAL_RCC_GPIOB_CLK_ENABLE();
        /**SPI2 GPIO Configuration
        PC2     ------> SPI2_MISO
        PC3     ------> SPI2_MOSI
        PB10     ------> SPI2_SCK
        */
        GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_3;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
        HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

        GPIO_InitStruct.Pin = GPIO_PIN_10;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

        /* USER CODE BEGIN SPI2_MspInit 1 */
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET);

        /*Configure GPIO pin : SPI_FLASH_CS_Pin */
        GPIO_InitStruct.Pin = GPIO_PIN_0;
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
        /* USER CODE END SPI2_MspInit 1 */
    }
}

void HAL_SPI_MspDeInit(SPI_HandleTypeDef *spiHandle)
{

    if (spiHandle->Instance == SPI2)
    {
        /* USER CODE BEGIN SPI2_MspDeInit 0 */

        /* USER CODE END SPI2_MspDeInit 0 */
        /* Peripheral clock disable */
        __HAL_RCC_SPI2_CLK_DISABLE();

        /**SPI2 GPIO Configuration
        PC2     ------> SPI2_MISO
        PC3     ------> SPI2_MOSI
        PB10     ------> SPI2_SCK
        */
        HAL_GPIO_DeInit(GPIOC, GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3);

        HAL_GPIO_DeInit(GPIOB, GPIO_PIN_10);

        /* USER CODE BEGIN SPI2_MspDeInit 1 */

        /* USER CODE END SPI2_MspDeInit 1 */
    }
}

/* USER CODE BEGIN 1 */
void spi2_cs_low(void)
{
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET);
}

void spi2_cs_high(void)
{
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET);
}
/* USER CODE END 1 */

3.\sfud\port\sfud_port.c

为了让SFUD能支持一个MCU通过任意SPI接任意数量的FLASH芯片,定义了结构体sfud_port_t
sfud_spi_port_init()sfud_port_t初始化。源码如下:

#include <sfud.h>
#include <stdarg.h>
#include "main.h"
#include "los_mux.h"
#include "los_task.h"
#include "los_memory.h"
#include "string.h"
#include "spi.h"
static char log_buf[256];

typedef struct
{
    SPI_HandleTypeDef *hspi;
    UINT32 lock;
    void (*cs_low)(void);
    void (*cs_high)(void);
} sfud_port_t;

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

static void spi_lock(const struct __sfud_spi *spi)
{
    sfud_port_t *sfud_port = (sfud_port_t *)(spi->user_data);
    LOS_MuxPend(sfud_port->lock, 1000);
}

static void spi_unlock(const struct __sfud_spi *spi)
{
    sfud_port_t *sfud_port = (sfud_port_t *)(spi->user_data);
    LOS_MuxPost(sfud_port->lock);
}

/**
 * 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;
    sfud_port_t *sfud_port = (sfud_port_t *)(spi->user_data);
    // uint8_t send_data, read_data;

    /**
     * add your spi write and read code
     */
    sfud_port->cs_low();
    if (HAL_SPI_Transmit(sfud_port->hspi, (uint8_t *)write_buf, write_size, 3000) != HAL_OK)
    {
        // sfud_log_debug(__FILE__, __LINE__, "spi write fail.");
        result = SFUD_ERR_WRITE;
        goto ERR;
    }
    if ((read_buf != NULL) && (read_size != 0))
    {
        if (HAL_SPI_Receive(sfud_port->hspi, read_buf, read_size, 3000) != HAL_OK)
        {
            // sfud_log_debug(__FILE__, __LINE__, "spi read fail.");
            result = SFUD_ERR_READ;
        }
    }

    // return result;
ERR:
    sfud_port->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 */

static void retry_delay(void)
{
    LOS_TaskDelay(1);
}

sfud_err sfud_spi_port_init(sfud_flash *flash)
{
    sfud_err result = SFUD_SUCCESS;
    sfud_port_t *sfud_port = NULL;
    /**
     * 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
     */
    /*Configure GPIO pin Output Level */
    if (strcmp(flash->spi.name, "SPI2") == 0)
    {
        MX_SPI2_Init();

        sfud_port = LOS_MemAlloc(m_aucSysMem0, sizeof(sfud_port_t));
        if (sfud_port == NULL)
        {
            sfud_log_debug(__FILE__, __LINE__, "memory alloc fail.");
            goto ERR_MEMALLOC;
        }
        if (LOS_MuxCreate(&sfud_port->lock) != LOS_OK)
        {
            sfud_log_debug(__FILE__, __LINE__, "mux create fail.");
            result = SFUD_ERR_NOT_FOUND;
            goto ERR_MUX;
        }
        sfud_port->hspi = &hspi2;
        sfud_port->cs_high = spi2_cs_high;
        sfud_port->cs_low = spi2_cs_low;

        flash->spi.wr = spi_write_read; // Required
        flash->spi.lock = spi_lock;
        flash->spi.unlock = spi_unlock;
        flash->spi.user_data = sfud_port;
        flash->retry.delay = retry_delay;
        flash->retry.times = 10000; // Required
    }
    else
    {
        result = SFUD_ERR_NOT_FOUND;
    }

    return result;

ERR_MUX:
    LOS_MemFree(m_aucSysMem0, sfud_port);
ERR_MEMALLOC:
    HAL_SPI_DeInit(sfud_port->hspi);

    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.h中的api。初始化过程如下:

sfud_flash *w25q64_handle;

sfud_init();
// SFUD_W25Q64JV_DEVICE_INDEX在sfud_cfg.h中自己定义。
w25q64_handle = sfud_get_device(SFUD_W25Q64JV_DEVICE_INDEX);

获得句柄w25q64_handle后,就调用读、写、擦除等API了。
测试结果如下:

[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud.c:116) Start initialize Serial Flash Universal Driver(SFUD) V1.1.0.
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud.c:117) You can get the latest version on https://github.com/armink/SFUD .
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud.c:861) The flash device manufacturer ID is 0xEF, memory type ID is 0x40, capacity ID is 0x17.
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:131) Check SFDP header is OK. The reversion is V1.5, NPN is 0.
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:173) Check JEDEC basic flash parameter header is OK. The table id is 0, reversion is V1.5, length is 16, parameter table pointer is 0x000080.
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:203) JEDEC basic flash parameter table info:
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:204) MSB-LSB  3    2    1    0
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0001] 0xFF 0xF9 0x20 0xE5
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0002] 0x03 0xFF 0xFF 0xFF
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0003] 0x6B 0x08 0xEB 0x44
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0004] 0xBB 0x42 0x3B 0x08
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0005] 0xFF 0xFF 0xFF 0xFE
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0006] 0x00 0x00 0xFF 0xFF
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0007] 0xEB 0x40 0xFF 0xFF
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0008] 0x52 0x0F 0x20 0x0C
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:206) [0009] 0x00 0x00 0xD8 0x10
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:215) 4 KB Erase is supported throughout the device. Command is 0x20.
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:234) Write granularity is 64 bytes or larger.
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:245) Target flash status register is non-volatile.
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:271) 3-Byte only addressing.
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:305) Capacity is 8388608 Bytes.
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:311) Flash device supports 4KB block erase. Command is 0x20.
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:311) Flash device supports 32KB block erase. Command is 0x52.
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud_sfdp.c:311) Flash device supports 64KB block erase. Command is 0xD8.
[SFUD]Find a Winbond flash chip. Size is 8388608 bytes.
[SFUD](E:/project/ohos_tunnel/code/ohos_tunnel_dev/third_party/sfud/src/sfud.c:840) Flash device reset success.
[SFUD]W25Q64JV flash device is initialize success.

参考文章

关于万能SPI-Flash驱动库SFUD移植心得

万能SPI-Flash驱动库SFUD移植心得 (amobbs.com 阿莫电子论坛 - 东莞阿莫电子网站)

 SFUD | 一款串行 Flash 通用驱动库

SFUD | 一款串行 Flash 通用驱动库_Mculover666的博客-CSDN博客

SFUD简介和移植感受_¥风笛¥的博客-CSDN博客

【学习笔记】RT-Thread中flash管理 — [SFUD组件 和 FAL驱动组件介绍]_黄逸芬的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值