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

描述

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

目录

    前言
   一、SFUD 组件简介
       1.1 基本简介
       1.2 SFUD 对 Flash 的管理
   二、SFUD 组件操作函数
       2.1 初始化相关函数
       2.2 设备访问函数
           2.2.1 读数据
           2.2.2 擦除数据
           2.2.3 写数据
           2.2.4 Flash 状态相关
   三、使用测试
       3.1 使用步骤
           3.1.1 使能 SPI 设备
           3.1.2 使能 SFUD 组件包
           3.1.3 挂载 SFUD 设备
           3.1.4 应用程序查找设备
           3.1.5 使用 API 进行读写操作
       3.2 读写测试
   结语

前言

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 中断处理与阶段小结)
❤️
在STM32L051C8 上使用 RT-Thread 应用篇系列博文连接:
RT-Thread 应用篇 — 在STM32L051上使用 RT-Thread (一、无线温湿度传感器 之 新建项目)
RT-Thread 应用篇 — 在STM32L051上使用 RT-Thread (二、无线温湿度传感器 之 CubeMX配置)
RT-Thread 应用篇 — 在STM32L051上使用 RT-Thread (三、无线温湿度传感器 之 I2C通讯)
RT-Thread 应用篇 — 在STM32L051上使用 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 文件如下图,基本的介绍,函数使用,说明该有的都有,大家可自行查看:

FlaSh

 

FlaSh

 

本文不深入分析源码实现原理,对于理论只做简单说明。

1.2 SFUD 对 Flash 的管理

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

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

FlaSh

 

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

FlaSh

 

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

FlaSh

 

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

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

二、SFUD 组件操作函数

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

FlaSh

 

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

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

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

2.1 初始化相关函数

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

FlaSh

 

其提供的函数有( 对于 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

FlaSh

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可查看结果:

FlaSh

3.1.2 使能 SFUD 组件包

和使能 SPI 设备一样,在 RT-Thread Studio 打开 RT-Thread Settings 打开 SFUD 组件使能,如下图:

FlaSh

 

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

在工程中, SFUD 组件相关的程序位置如下:

FlaSh

3.1.3 挂载 SFUD 设备

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

FlaSh

 

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

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

FlaSh

3.1.4 应用程序查找设备

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

FlaSh

3.1.5 使用 API 进行读写操作

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

比如:

FlaSh

3.2 读写测试

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

#include 
#include "main.h"
#include "usart.h"
#include "gpio.h"
#include 
#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 

//省略...

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

 

测试细节说明:

在测试的时候我使用了一个按键线程写 flash,最开始的时候使用的是 512 字节大小的线程栈:

FlaSh

 

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

结语

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

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

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

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

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分