1024程序员节|征文
一、sfud 通用驱动库介绍
SFUD 是一款开源的串行 SPI Flash 通用驱动库。由于现有市面的串行 Flash 种类居多,各个 Flash 的规格及命令存在差异, SFUD 就是为了解决这些 Flash 的差异现状而设计,能够支持不同品牌及规格的 Flash,提高了涉及到 Flash 功能的软件的可重用性及可扩展性,同时也可以规避 Flash 缺货或停产给产品所带来的风险。
主要特点
支持 SPI/QSPI 接口、面向对象(同时支持多个 Flash 对象)、可灵活裁剪、扩展性强、支持 4 字节地址
资源占用
1、标准占用:RAM:0.2KB ROM:5.5KB
2、最小占用:RAM:0.1KB ROM:3.6KB
设计思路
1、什么是 SFDP :它是 JEDEC (固态技术协会)制定的串行 Flash 功能的参数表标准,最新版 V1.6B (点击这里查看)。该标准规定了,每个 Flash 中会存在一个参数表,该表中会存放 Flash 容量、写粒度、擦除命令、地址模式等 Flash 规格参数。目前,除了部分厂家旧款 Flash 型号会不支持该标准,其他绝大多数新出厂的 Flash 均已支持 SFDP 标准。所以该库在初始化时会优先读取 SFDP 表参数。
2、不支持 SFDP 怎么办 :如果该 Flash 不支持 SFDP 标准,SFUD 会查询配置文件 ( /sfud/inc/sfud_flash_def.h ) 中提供的 Flash 参数信息表 中是否支持该款 Flash。如果不支持,则可以在配置文件中添加该款 Flash 的参数信息。获取到了 Flash 的规格参数后,就可以实现对 Flash 的全部操作。
二、SPI Flash 硬件介绍
野火stm32f407 开发板原理图如下:
FLASH芯片(型号:W25Q128)是一种使用SPI通讯协议的NOR FLASH存储器,它的CS/CLK/DIO/DO引脚分别连接到了 STM32对应的SDI引脚NSS/SCK/MOSI/MISO上,其中STM32的NSS引脚是一个普通的GPIO,不是SPI的专用NSS引脚,所以程序中要使用软件控制的方式。
FLASH芯片中还有WP和HOLD引脚。WP引脚可控制写保护功能,当该引脚为低电平时,禁止写入数据。我们直接接电源,不使用写保护功能。HOLD引脚可用于暂停通讯,该引脚为低电平时,通讯暂停,数据输出引脚输出高阻抗状态,时钟和数据输入引脚无效。我们直接接电源,不使用通讯暂停功能。
关于FLASH芯片的更多信息,可参考其数据手册《W25Q128》。
三、使用流程
参考官方文档:<在潘多拉上使用 SFUD 操作 Flash>
SFUD 的使用流程图,首先需要移植 SFUD 组件、对 flash 进行初始化,然后再进行应用:根据名称获取 sfud_dev,对 sfud_dev 进行擦写读的操作。
四、软件系统配置和测试
Env 配置
使用 SFUD 操作 Flash,需要在 Env 中打开: QSPI 或 SPI 总线、SFUD 组件。
在片上外设中,SPI1 使能。也可以选择 soft spi。
在组件中,选中 SFUD 组件,打开调试信息(可选),保存并生成工程。
编译下载到开发板,可以看到 SPI1 总线。
挂载 SPI FLASH W25Q128 到 SPI 总线.
方式一:
#include <drv_spi.h>
#include "spi_flash_sfud.h"
static int rt_hw_spi_flash_init(void)
{
//rt_hw_spi_device_attach():SPI 驱动会注册 SPI 总线,SPI 设备需要挂载到已经注册好的 SPI 总线上
// V4.1.1版本 也有此函数 和 RT-Thread 5.0.0 的函数有区别
//往总线 spi1 上挂载一个 spi10 从设备
if (RT_EOK != rt_hw_spi_device_attach("spi1", "spi10", GPIOG, GPIO_PIN_6))
{
rt_kprintf("Failed to attach the spi device.");
return -RT_ERROR;
}
//使用 SFUD 探测 spi10 从设备,并将 spi10 连接的 flash 初始化为块设备,名称 W25Q128
if (RT_NULL == rt_sfud_flash_probe("W25Q128", "spi10"))
{
rt_kprintf("Failed to probe the W25Q128.");
return -RT_ERROR;
}
return RT_EOK;
}
/* 导出到自动初始化 */
INIT_COMPONENT_EXPORT(rt_hw_spi_flash_init);
方式二:
参考 rt_hw_spi_device_attach 函数的使用,调用rt_spi_bus_attach_device 函数,代码示例如下:
#include <drv_spi.h>
#include "spi_flash_sfud.h"
#define CS_PIN GET_PIN(G, 6)
static struct stm32_hw_spi_cs spi_cs;
static int rt_hw_spi_flash_init(void)
{
struct rt_spi_device *spi_device = RT_NULL;
spi_cs.GPIOx=GPIOG;
spi_cs.GPIO_Pin= GPIO_PIN_6;
rt_pin_mode(CS_PIN, PIN_MODE_OUTPUT);
rt_pin_write(CS_PIN, PIN_HIGH);
spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));
if(RT_NULL == spi_device)
{
rt_kprintf("Failed to malloc the spi device.");
return -RT_ENOMEM;
}
// //此函数是RT-Thread 5.0.0 添加的新函数,如果低于5.0.0版本不支持这个函数。
// if (RT_EOK != rt_spi_bus_attach_device_cspin(spi_device, "spi10", "spi1",GET_PIN(G, 6), RT_NULL))
// {
// rt_kprintf("Failed to attach the spi device.");
// return -RT_ERROR;
// }
//
if (RT_EOK != rt_spi_bus_attach_device(spi_device, "spi10", "spi1", (void*)&spi_cs))
{
rt_kprintf("Failed to attach the spi device.");
return -RT_ERROR;
}
//不配置也行,也可以使用默认配置
{
spi_device->bus->owner = spi_device;
{
struct rt_spi_configuration cfg;
cfg.data_width = 16;
cfg.mode = RT_SPI_MODE_1 | RT_SPI_MSB;
cfg.max_hz = 40 * 1000 *1000; /* 40M,SPI max 42MHz, 3-wire spi */
if(spi_device->bus->owner==RT_NULL)
{
rt_kprintf("RT_OK\r\n");
}
rt_kprintf("owner= %d\r\n",*spi_device->bus->owner);
rt_spi_configure(spi_device, &(cfg));
}
}
if (RT_NULL == rt_sfud_flash_probe("W25Q128", "spi10"))
{
rt_kprintf("Failed to probe the W25Q128.");
return -RT_ERROR;
}
return RT_EOK;
}
/* 导出到自动初始化 */
INIT_COMPONENT_EXPORT(rt_hw_spi_flash_init);