RT-Thread记录(十六、SFUD组件 — SPI Flash的读写)

简介: 从本文开始,测试学习一些 RT-Thread 常用的组件与软件包,先从刚学完的 SPI 设备开始。
从本文开始,测试学习一些 RT-Thread 常用的组件与软件包,先从刚学完的 SPI 设备开始。

前言

RT-Thread 专栏更新至今,从开发环境到内核到设备模型,其实我们已经把使用 RT-Thread 的基础知识都讲过一遍,认真学习的朋友实际上都已经可以使用 RT-Thread 完成一些实际小项目了。

上一篇文章最后说过,RT-Thread 有一个很大的特点在于他的生态比一般的 RTOS 完善,我们在实际应用中,有许许多多现成的官方或者很多开发者提供的组件或者软件包,我们可以直接导入工程进行使用。

针对我们 RT-Thread 实际应用,很多时候不仅是要知道基本的理论,还需要真正的知道怎么实际“用”起来。
基于本专栏的开发环境 RT-Thread Studio,本文开始我们来测试几个典型的 组件与软件包,来看看他们实际是如何使用的。

我们刚讲完 SPI 设备,本文就从与 SPI 设备相关的组件 SFUD 组件说起。


说明,对于 RT-Thread记录 中组件与软件包部分的文章,我并不计划讲太多的原理,因为我们的最终目的还是在于应用,
在之间讲解 RT-Thread 的基础中,为了让大家更明白 RT-Thread 内核以及 I/O 设备模型,也没少分析源码以及讲解实现原理,核心的部分都是自己研究源码。
对于 组件与软件包 部分,我侧重点会在与的记录测试使用的过程,使得我们能够快速上手。

本 RT-Thread 专栏记录的开发环境:
RT-Thread记录(一、RT-Thread 版本、RT-Thread Studio开发环境 及 配合CubeMX开发快速上手)
RT-Thread记录(二、RT-Thread内核启动流程 — 启动文件和源码分析
RT-Thread 内核篇系列博文链接:
RT-Thread记录(三、RT-Thread 线程操作函数及线程管理与FreeRTOS的比较)
RT-Thread记录(四、RT-Thread 时钟节拍和软件定时器)
RT-Thread记录(五、RT-Thread 临界区保护)
RT-Thread记录(六、IPC机制之信号量、互斥量和事件集)
RT-Thread记录(七、IPC机制之邮箱、消息队列)
RT-Thread记录(八、理解 RT-Thread 内存管理)
RT-Thread记录(九、RT-Thread 中断处理与阶段小结)
RT-Thread 设备篇系列博文链接:
RT-Thread记录(十、全面认识 RT-Thread I/O 设备模型)
RT-Thread记录(十一、I/O 设备模型之UART设备 — 源码解析)
RT-Thread记录(十二、I/O 设备模型之UART设备 — 使用测试)
RT-Thread记录(十三、I/O 设备模型之PIN设备)
RT-Thread记录(十四、I/O 设备模型之ADC设备)
RT-Thread记录(十五、I/O 设备模型之SPI设备)
RT-Thread 组件与软件包系列博文链接:
本文是第一篇

一、SFUD 组件简介

SFUD (全称 Serial Flash Universal Driver)是一款开源的串行 SPI Flash 通用驱动库。

1.1 基本简介

基础介绍借用官方的说明:由于现有市面的串行 Flash 种类居多,各个 Flash 的规格及命令存在差异, SFUD 就是为了解决这些 Flash 的差异现状而设计,让我们的产品能够支持不同品牌及规格的 Flash,提高了涉及到 Flash 功能的软件的可重用性及可扩展性,同时也可以规避 Flash 缺货或停产给产品所带来的风险。

在 RT-Thread 中,SFUD 组件的 SPI 驱动是以 RTThread 的I/O设备模型框架为基础设计的。

使用 SFUD 组件,我们不用自己写 SPI Flash 的驱动。

支持 SPI/QSPI 接口、面向对象(同时支持多个 Flash 对象)、可灵活裁剪、扩展性强、支持 4 字节地址。

☆ SFUD是个开源的组件,对于该组件真正权威的参考说明就是该组件作者写好的 README.md 文件(永远要记住第一作者的文档、官方的文档永远是最具有参考价值的)。☆

使用 RT-Thread Studio 打开 README 文件如下图,基本的介绍,函数使用,说明该有的都有,大家可自行查看:

在这里插入图片描述

在这里插入图片描述

本文不深入分析源码实现原理,对于理论只做简单说明。这里关于理论的分析说明推荐几篇文章,在文章说明理论基础的时候也参考了下面的文章:

SFUD源码浅析

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

1.2 SFUD 对 Flash 的管理

我们以前讲过,面向对象思想的程序设计,一般都会使用一个结构体 表示一个对象,我们讲过的线程、IPC机制,I/O 设备都有他们的设备控制块结构体。

对于 SPI Flash 设备,SFUD 也定义了一个结构体 sfud_flash 进行管理,其位置和内容如下图:

在这里插入图片描述

在这个对象控制块中,有一个成员为 chip ,其类型为芯片信息的结构体sfud_flash_chip,如下图:

在这里插入图片描述

在 SFUD 组件中,已经定义好了一些支持的 chip 信息,如下图:

在这里插入图片描述

基本上包括了市面上通用的 SPI Flash 芯片,如果使用的flash不支持 SFUD 组件,可根据 README 文件自行添加。

简单的概述就到这里,下面我们来看看 SFUD 组件提供的操作函数。

二、SFUD 组件操作函数

根据 SFUD 组件的 README 文件,SFUD 组件提供的 API 框架图如下:

在这里插入图片描述

这里要说明一下,上面的 API 是 SFUD 对外标准的通用 API,就是不管用什么系统,或者使用裸机,移植好了 SFUD组件这些 API 都可以使用。

对于我们使用的 RT-Thread 来说,访问设备的函数就是 SFUD 设备的标准 API。

但是对于初始化相关的部分来说,RT-Thread 官方给我们写好了标准的驱动函数。

2.1 初始化相关函数

在工程文件中,与 RT-Thread 初始化驱动文件如下:

在这里插入图片描述

其提供的函数有( 对于 RT-Thread 中初始化相关的函数使用,在本文后面使用测试小节会有详细示例说明):

/**
 * Probe SPI flash by SFUD(Serial Flash Universal Driver) driver library and though SPI device.
使用 SFUD 探测 spi_dev_name 从设备,
并将 spi_dev_name 连接的 flash 初始化为块设备,名称 spi_flash_dev_name
 */
rt_spi_flash_device_t rt_sfud_flash_probe(const char *spi_flash_dev_name, const char *spi_dev_name);

/**
 * Probe SPI flash by SFUD (Serial Flash Universal Driver) driver library and though SPI device by specified configuration.
 * rt_sfud_flash_probe 调用了此函数
使得与底层 SFUD 本身的初始化文件关联起来
 */
rt_spi_flash_device_t rt_sfud_flash_probe_ex(const char *spi_flash_dev_name, const char *spi_dev_name,
        struct rt_spi_configuration *spi_cfg, struct rt_qspi_configuration *qspi_cfg);

/**
 * Delete SPI flash device
     删除SPI SFUD 设备
 */
rt_err_t rt_sfud_flash_delete(rt_spi_flash_device_t spi_flash_dev);

/**
 * Find sfud flash device by SPI device name
通过 SPI 设备名称 找到一个 SFUD Flash 设备
 */
sfud_flash_t rt_sfud_flash_find(const char *spi_dev_name);

/**
 * Find sfud flash device by flash device name
 通过 Flash 设备名称 找到一个 SFUD Flash 设备
 */
sfud_flash_t rt_sfud_flash_find_by_dev_name(const char *flash_dev_name);

函数我们不做深入分析,大家需要学会使用,以前有很多文章都有源码分析说明,源码自己查看,比如其中比较关键的一个函数 rt_sfud_flash_probe_ex

在这里插入图片描述

2.2 设备访问函数

设备访问函数,SFUD 组件中 README 文件都有说明的,函数使用的注意事项可查看组件说明文件。

这里统一列一下方便以后复制使用:

2.2.1 读数据

/*
参数    描述
flash    Flash 设备对象
addr    起始地址
size    从起始地址开始读取数据的总大小
data    读取到的数据
*/
sfud_err sfud_read(const sfud_flash *flash, uint32_t addr, size_t size, uint8_t *data)

2.2.2 擦除数据

部分擦除:

/*
参数    描述
flash    Flash 设备对象
addr    起始地址
size    从起始地址开始擦除数据的总大小
*/
sfud_err sfud_erase(const sfud_flash *flash, uint32_t addr, size_t size)

全片擦除:

/*
参数    描述
flash    Flash 设备对象
*/
sfud_err sfud_chip_erase(const sfud_flash *flash)

2.2.3 写数据

直接写:

/*
参数    描述
flash    Flash 设备对象
addr    起始地址
size    从起始地址开始写入数据的总大小
data    待写入的数据
*/
sfud_err sfud_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data)

先擦除再写:

/*
参数    描述
flash    Flash 设备对象
addr    起始地址
size    从起始地址开始写入数据的总大小
data    待写入的数据
*/
sfud_err sfud_erase_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data)

2.2.4 Flash 状态相关

读取 Flash 状态:

/*
参数    描述
flash    Flash 设备对象
status    当前状态寄存器值
*/
sfud_err sfud_read_status(const sfud_flash *flash, uint8_t *status)

修改 Flash 状态:

/*
参数    描述
flash    Flash 设备对象
is_volatile    是否为易闪失的,true: 易闪失的,及断电后会丢失
status    当前状态寄存器值
*/
sfud_err sfud_write_status(const sfud_flash *flash, bool is_volatile, uint8_t status)

三、使用测试

本小节说明一下在 RT-Thread Studio 上使用 SFUD组件的步骤,然后我们使用示例进行基本的测试:

3.1 使用步骤

3.1.1 使能 SPI 设备

根据文章 RT-Thread记录(十五、I/O 设备模型之SPI设备)接描述 中 《3.1 SPI 设备使用步骤》说明使能 SPI 总线。

注册 SPI 总线设备,使用list_device可查看结果:

在这里插入图片描述
.

3.1.2 使能 SFUD 组件包

和使能 SPI 设备一样,在 RT-Thread Studio 打开 RT-Thread Settings 打开 SFUD 组件使能,如下图:
在这里插入图片描述

使能完成,我们在应用层就可直接调用上一小节将的 SFUD 操作函数了。

在工程中, SFUD 组件相关的程序位置如下:
在这里插入图片描述

.

3.1.3 挂载 SFUD 设备

在使用 SFUD 设备前,需要挂载,类似把 SPI 设备挂载至 SPI 总线上一样,使用如下操作:

在这里插入图片描述

忘了另外一块开发板不是 W25Q128 而是 W25Q64,所以最终找到的是 W25Q64DW。

这里有个小问题说明一下, DBG 定义的问题,自己把mian里面的注释稍微修改一下:

在这里插入图片描述
.

3.1.4 应用程序查找设备

使用 rt_sfud_flash_find 或者 rt_sfud_flash_find_by_dev_name 获取设备句柄:

在这里插入图片描述
.

3.1.5 使用 API 进行读写操作

完成上述步骤,就可以根据自己的应用,使用上面介绍的 SFUD 组件操作函数访问设备部分进行 Flash 的操作了。

比如:
在这里插入图片描述

3.2 读写测试

在上面的使用步骤说明中,其实我已经把自己做的简单测试都说了一遍,这里我们上一下测试部分代码,然后看一下测试效果:

#include <rtthread.h>
#include "main.h"
#include "usart.h"
#include "gpio.h"
#include <rtdevice.h>
#include "board.h"
#include "drv_spi.h"
#include "spi_flash_sfud.h"


#ifndef DBG_TAG
#define DBG_TAG "main"
#endif
#ifndef DBG_LVL
#define DBG_LVL DBG_LOG
#endif

#include <rtdbg.h>

//省略...

sfud_flash *test_sfud = NULL;

const uint8_t test_data[] = "this is a test data!";

//省略...

static void key1_thread_entry(void *par){

    while(1){
        if(key1_read == 0){
            rt_thread_mdelay(10); //去抖动
            if(key1_read == 0){
                rt_kprintf("write flash ..\r\n");
//                sfud_write(test_sfud, 13, sizeof(test_data), test_data);
                sfud_erase_write(test_sfud, 13, sizeof(test_data), test_data);
             }
             while(key1_read == 0){rt_thread_mdelay(10);//去抖动
            }
        }
        rt_thread_mdelay(1);
   }
}

static void key2_thread_entry(void *par){
    uint8_t read_data[30] = {0};
//    void *str = RT_NULL;
    while(1){
        if(key2_read == 0){
            rt_thread_mdelay(10); //去抖动
            if(key2_read == 0){
                rt_kprintf("read flash ..\r\n");
//                sfud_read(test_sfud, 0, sizeof(test_data), (uint8_t *)str);
                sfud_read(test_sfud, 13, sizeof(test_data), read_data);
                rt_kprintf("%s",read_data);
             }
             while(key2_read == 0){rt_thread_mdelay(10);//去抖动
            }
        }
        rt_thread_mdelay(1);
   }
}

//省略...

int main(void)
{

//省略...
    rt_hw_spi_device_attach("spi1", "spi10", GPIOA, GPIO_PIN_4);  // CS 脚:PA4

    /* 使用 SFUD 探测 spi10 从设备,并将 spi10 连接的 flash 初始化为块设备,名称 W25Q128 */
    if (RT_NULL == rt_sfud_flash_probe("W25Q64", "spi10"))
    {
        return -RT_ERROR;
    };

//  test_sfud = rt_sfud_flash_find("spi10"); //
    test_sfud = rt_sfud_flash_find_by_dev_name("W25Q64");

    if(RT_NULL == test_sfud){
          LOG_E("find sfud_flash failed!...\n");
    }

   //省略...
    return RT_EOK;
}

测试结果:

在这里插入图片描述

测试细节说明:

在测试的时候我使用了一个按键线程写 flash,最开始的时候使用的是 512 字节大小的线程栈:
在这里插入图片描述

使用函数sfud_erase_write 会比 sfud_write 函数占用更多的内存。

结语

本文我们从上一篇文章刚学完的 SPI 设备相关的 SFUD 组件开始,接触到了 RT-Thread 的组件与软件包,可以看出,对于常用的设备使用 RT-Thread 开发有多么的方便了。

<3
但是前提当然是得对 RT-Thread 的面向对象的思想,I/O 设备模型等基础有一定的认识,如果只是为了使用,看一篇文章即可,如果是为了理解掌握,还得多多了解 RT-Thread 基础相关知识,比如博主的 RT-Thread 专栏 = =! O(∩_∩)O哈哈~

再次申明一下,对于组件与软件包,因为都是大佬开发者们写好的驱动,所以我偏向的重点是在于学会使用,说明文档在每个组件或者软件包都有作者详细的说明,那才是最好的参考资料。
<3

希望大家多多支持!本文就到这里,谢谢!

相关文章
|
5月前
|
索引
quartus 小技巧—— 分线。例如总线data[31..0],引出的分线为data[7..0]
在数字电路设计中,总线用于并行传输数据,而分线是从总线中提取特定数据位。Quartus II,Altera(现Intel)的EDA工具,支持灵活的总线分线操作。本文介绍了两种在Quartus II中实现分线的方法:一是直接索引,如`data[7:0]`;二是使用Verilog的`extract`操作,尽管在Verilog中直接索引更常见。这些技巧有助于提升设计效率。
|
6月前
|
Java
Direct Memory读写流程分析
Direct Memory读写流程分析
30 0
|
调度 C语言 芯片
RT-Thread记录(二、RT-Thread内核启动流程 — 启动文件和源码分析)
今天就在前面我们RT-Thread Studio工程基础之上讲一讲RT-Thread内核启动流程
637 0
RT-Thread记录(二、RT-Thread内核启动流程 — 启动文件和源码分析)
|
消息中间件 缓存 Shell
RT-Thread记录(十七、AT组件 — ESP8266使用 at_device 软件包联网)
AT 组件:RT-Thread 一个比较典型的组件, 解决了不同网络模块AT命令之间的差异导致的重复开发的问题,大幅度简化了MCU+无线模块方案开发。
1002 0
RT-Thread记录(十七、AT组件 — ESP8266使用 at_device 软件包联网)
|
传感器 移动开发 Linux
RT-Thread UART设备驱动框架初体验(中断方式接收带\r\n的数据)
RT-Thread UART设备驱动框架初体验(中断方式接收带\r\n的数据)
220 0
|
Shell
RTOS支持STemWin(以RT-Thread为例)
RTOS支持STemWin(以RT-Thread为例)
138 0
|
移动开发 C语言 芯片
RT-Thread PIN设备学习笔记
RT-Thread PIN设备学习笔记
170 0
|
调度 芯片
【玩转RT-Thread】 时钟管理(原理+实战)
【玩转RT-Thread】 时钟管理(原理+实战)
366 0
|
缓存
ALSTOM V4550220-EN 复制CPU中的少量组件来支持MT
ALSTOM V4550220-EN 复制CPU中的少量组件来支持MT
99 0
ALSTOM V4550220-EN 复制CPU中的少量组件来支持MT
|
存储 内存技术
STM32内部flash详解(1)(下)
STM32内部flash详解(1)