RT-Thread 学习记录(三)W25Q128使用FatFs文件系统

一、前言

学习记录,已建好工程,已经配置好FAL、SFUD、硬件SPI、W25Q128、EasyFlash。在Flash上使用FatFs文件系统。

二、步骤

一)配置RT-thread Settings

双击 “RT-Thread Settings”打开配置页面,添加组件FatFs。打开配置项,修改“设置要处理的最大扇区大小”为“4096”。

设置好后,图形配置界面会点亮这两个驱动。

二)配置工程

1、添加文件

若编译时缺少某些文件,可在软件左上角的“导航器”中选择项目,在“rt_thrread”->“components”文件夹中寻找并添加构建。

2、文件系统格式化和挂载

在之前建立的“W25Qxx.c”文件中添加文件系统格式化和挂载函数,并在fal初始化函数后进行调用。

/*********************************************************************************/
#include <dfs_fs.h>
#define DBG_TAG "FatFs"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

#define FS_PARTITION_NAME "datafile"     //必须与分区表内设置的名称相同
int dfs_mount_init(void)
{
    /* 在 spi flash 中名为 "datafile" 的分区上创建一个块设备 */
    struct rt_device *flash_dev = fal_blk_device_create(FS_PARTITION_NAME);
    if (flash_dev == NULL)
    {
        LOG_E("Can't create a block device on '%s' partition.", FS_PARTITION_NAME);
    }
    else
    {
        LOG_D("Create a block device on the %s partition of flash successful.", FS_PARTITION_NAME);
    }

    /* 挂载 spi flash 中名为 "datafile" 的分区上的文件系统 */
    if (dfs_mount(flash_dev->parent.name, "/", "elm", 0, 0) == 0)
    {
        LOG_I("Filesystem initialized!");
    }
    else
    {
        LOG_E("Failed to initialize filesystem !");
        LOG_D("Create a filesystem on the block device !");
        int res = dfs_mkfs("elm", flash_dev->parent.name);  //格式化
        if(res == 0)
        {
            LOG_D("Create success !");
            LOG_D("reboot ... !");
            rt_thread_delay(200);
            NVIC_SystemReset();  //重启设备,由于已格式化,所以重启直接能够挂载成功
        }
        else
        {
            LOG_E("Create fail !");
        }
    }
    return 0;
}
/*********************************************************************************/

注意宏“FS_PARTITION_NAME”必须与分区表内设置的名称相同,否则创建块设备时无法找到分区名。分区表一般在“fal_cfg.h”文件中。

此时编译下载程序,可以在打印出的调试信息中看到块设备创建成功,文件系统已经初始化。

3、测试功能

在main函数中新建2个线程,1个信号量。在线程1中打开文件并写入内容,若不存在则创建文件,超过最大长度后删除文件再重新创建,在线程2中打开文件,获取文件长度并打印文件全部内容。

fal_init();       //fal初始化
easyflash_init(); //easyflash初始化
dfs_mount_init(); //文件系统格式化和挂载
env_load();       //环境变量载入

test_sem1 = rt_sem_create("sem1", 0, RT_IPC_FLAG_FIFO);

rt_thread_t thread1 = rt_thread_create("write", (void *)G_Write_entry, RT_NULL, 2048, 25, 100);
if (thread1 != RT_NULL)
{
    rt_thread_startup(thread1);
}

rt_thread_t thread2 = rt_thread_create("read", (void *)G_Read_entry, RT_NULL, 2048, 25, 10);
if (thread2 != RT_NULL)
{
    rt_thread_startup(thread2);
}
/*********************************************************************************/
void G_Write_entry(void)
{
    char chartemp[15] = "file.txt";
    char l_DataBuf_i8[100] = "";
    uint8_t l_DataBuf_u8[20] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
//    int data1 = 20;
//    int data2 = 11;
    int fd = 0;

    while(1)
    {
//        rt_memset(chartemp, 0, sizeof(chartemp));
//        itoa(++data1, chartemp, 10);
//        ef_set_env("testdata1", chartemp); //存入env中
//        LOG_I("write testdata1: %d", data1);


        fd = open(chartemp, O_RDWR);  //打开文件,成功则返回文件描述符,失败返回-1
        if(fd < 0)
        {
            LOG_E("write/ File: '%s' not open! err %d.", chartemp, fd);
            fd = open(chartemp, O_RDWR | O_CREAT);  //新建文件
            LOG_D("write/ create file : %s", chartemp);
        }

        int l_FileLen = lseek(fd, 0, SEEK_END);  //获取文件长度
        LOG_D("write/ file len : %d", l_FileLen);
        if(l_FileLen >= 100)
        {
            LOG_W("File: '%s' is full !", chartemp);
            close(fd);      //关闭文件
            rm(chartemp);   //清除文件

            fd = open(chartemp, O_RDWR | O_CREAT);  //新建文件
            LOG_D("create file : %s", chartemp);
        }

        if(fd > 0)
        {
            LOG_I("write/ open file succ.");
            lseek(fd, 0, SEEK_END);  //将指针置到文件尾

            l_DataBuf_u8[0]++;
            rt_memset(l_DataBuf_i8, 0, 100);
            sprintf(l_DataBuf_i8, "%03d,%03d,%03d,%03d,%03d\r\n",\
                    l_DataBuf_u8[0],l_DataBuf_u8[1],l_DataBuf_u8[2],l_DataBuf_u8[3],l_DataBuf_u8[4]);

            write(fd, l_DataBuf_i8, strlen(l_DataBuf_i8)); //写入文件,长度3 * 5 + 4 + 2 = 21
            LOG_D("file write : %s.", l_DataBuf_i8);
            LOG_I("file write succ.");
        }
        close(fd);  //关闭文件

        rt_sem_release(test_sem1);

        rt_thread_delay(2000);
    }
}

void G_Read_entry(void)
{
    char chartemp[15] = "file.txt";
    char l_DataBuf_i8[200] = "";
    int fd = 0;

    while(1)
    {
        rt_sem_take(test_sem1, RT_WAITING_FOREVER);

        fd = open(chartemp, O_RDONLY);  //打开文件,成功则返回文件描述符,失败返回-1
        if(fd > 0)
        {
            int l_FileLen = lseek(fd, 0, SEEK_END);  //获取文件长度
            LOG_D("read/ file len : %d", l_FileLen);

            rt_memset(l_DataBuf_i8, 0, 200);
            lseek(fd, 0, SEEK_SET);  //将指针移动到文件头
            int l_ReadResult = read(fd, l_DataBuf_i8, l_FileLen);  //读取文件内全部内容
            if(l_ReadResult > 0) //成功
            {
                LOG_D("read/ file data : \r\n%s.", l_DataBuf_i8);
            }
            else
            {
                LOG_W("read file fail ! err : %d", l_ReadResult);
            }
        }
        else
        {
            LOG_E("read/ open file fail ! err : %d", fd);
        }
        close(fd);  //关闭文件

        rt_thread_delay(50);
    }
}
/*********************************************************************************/

编译下载后,可以看到线程1可以创建打开删除文件并写入内容,线程2可以获取文件长度并打印文件全部内容。因为创建或写入文件时需要获取时间,但工程未开启RTC,所以会打印警告。

三、工程代码

https://gitee.com/chongwang1013/stm32-l431_-test1.0/tree/FatFs/

### 实现RT-Thread中通过SPI接口读取W25Q64闪存芯片数据 要在RT-Thread操作系统中通过SPI接口读取W25Q64闪存芯片的数据,可以按照以下方法完成: #### 1. 开启SFUD组件并配置环境 为了简化对SPI Flash的操作,推荐使用SFUD(Simple Full Unified Driver)组件来管理SPI Flash设备。首先需要在Env工具的配置选项中启用SFUD功能,并重新生成MDK5工程。 执行如下命令以更新项目配置: ```bash scons --target=mdk5 ``` 此过程会根据当前项目的配置自动生成适合开发板的目标工程文件[^1]。 #### 2. 添加支持W25Q64到SFUD 确保SFUD已经支持W25Q64型号。如果尚未添加对该型号的支持,则需手动扩展SFUD源码中的`sfud_device.c`文件,注册新的Flash设备类型及其对应的参数表。通常情况下,SFUD已内置对主流SPI Flash芯片的支持,因此可能无需额外修改即可正常工作[^2]。 #### 3. 初始化SPI总线和设备 创建一个新的C语言模块用于初始化SPI外设以及绑定具体的Flash实例对象。以下是示例代码片段展示如何设置SPI通信链路并与目标存储器建立连接关系: ```c #include <rtthread.h> #include "drv_config.h" #include "sfud.h" /* 定义全局变量 */ static struct rt_spi_bus *spi_bus; static sfud_flash flash; int init_w25q64(void){ /* 获取指定名称的SPI控制器句柄 */ spi_bus = rt_spi_bus_attach_device("spi1", NULL, "/dev/spi1"); if (!spi_bus) { rt_kprintf("Failed to attach SPI device!\n"); return -RT_ERROR; } /* 设置默认传输模式 */ rt_spi_configure(spi_bus, RT_SPI_MASTER | RT_SPI_MODE_0); /* 注册flash设备至SFUD框架下 */ if(sfud_flash_probe(&flash,"/dev/w25q64") != SFUD_RES_OK ){ rt_kprintf("Probe W25Q64 failed.\n"); return -RT_ERROR; } return RT_EOK; } INIT_APP_EXPORT(init_w25q64); ``` 上述函数实现了以下几个关键步骤: - 调用 `rt_spi_bus_attach_device()` 将硬件级SPI资源映射成逻辑上的设备节点; - 使用 `rt_spi_configure()` 方法设定基本的工作方式(如主从角色切换及时序相位调整等); - 利用SFUD库提供的探测机制确认实际存在的物理介质是否匹配预期规格——即这里指代的是容量大小为8MBit的标准版W25系列NOR型Flash产品[^3]。 #### 4. 文件系统的挂载与测试 当底层驱动程序准备就绪之后,就可以进一步考虑上层应用层面的需求了。比如可以通过ELM FatFS或其他轻量级嵌入式文件系统解决方案把裸盘抽象成为可寻址路径结构形式以便于后续访问操作更加直观便捷。下面给出了一段关于如何定义一个静态入口点从而实现开机自动加载特定分区作为根目录的例子: ```c /** * 挂载文件系统 * @return 成功返回RT_EOK;失败则返回负数错误码 */ static int bsp_spi_flash_mnt_init(void){ dfs_mkfs("elm","W25Q64");//格式化设备 if(dfs_mount("W25Q64","/", "elm", 0 ,0 )==0)//尝试挂载 LOG_I("dfs mount success\r\n"); else{ LOG_E("dfs mount failed\r\n"); return -RT_ERROR; } return RT_EOK; } /* 组件自动初始化 */ INIT_COMPONENT_EXPORT(bsp_spi_flash_mnt_init); //编译验证完成后烧录固件镜像,在串口调试界面运行命令查看效果:>ls Directory / ``` 最后一步就是将整个应用程序部署到真实环境中去检验其功能性表现情况了。一旦成功启动后便可以在任何时刻调用标准POSIX API或者专用封装好的辅助类库来进行任意位置处字节粒度级别的随机存取动作[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值