利用RT-thread 在STM32F407在W25Q128上實現虛擬文件系統

1:環境

STM32F407板子上有一個W25Q128的flash芯片。通過SPI連接在一起

W25Q128
(128M-bit),被組織爲65536個可編程的頁,每頁256bytes。擦除方式分爲16頁一組(即一個扇區4kbytes),128頁一組(即8個扇區32kbytes),256頁一組(即16個扇區或1個塊64kbytes),或整個芯片擦除。該芯片有4096個可擦除扇區,或256個可擦除塊。該芯片支持
standard spi,Dual/Quad I/O SPI

在這裏插入圖片描述
接在MCU的 PB3 PB4 PB5
在這裏插入圖片描述
我們要做這樣幾件事,配置SPI來對這個flash進行讀寫,然後進行文件系統的設置,利用RT-thread上虛擬文件系統的接口來進行讀寫。

2:配置SPI

2.1 通過cubeMX來配置SPI的引腳設置
在這裏插入圖片描述
2.2 配置ENV來使能SPI1的配置
在這裏插入圖片描述
在env上輸入scons --target=mdk5來保存配置信息

3:註冊SPI設備

  1. 在main文件中添加,在spi1總線上註冊spi10設備的函數
#include "drv_spi.h"

int w25q_spi_device_init()
{
    __HAL_RCC_GPIOB_CLK_ENABLE();
    return rt_hw_spi_device_attach("spi1", "spi10", GPIOB, GPIO_PIN_14);
}
INIT_DEVICE_EXPORT(w25q_spi_device_init);

編譯執行後,可以查看ptshell下是否有設備出現
在這裏插入圖片描述

4:對W25Q128進行讀寫訪問

利用RT-Thread上提供的訪問SPI設備的接口函數,來讀寫設備的ID。先寫入0x90,然後再讀,可以獲得設備的ID數據
下面採用了兩種方式:第一種:rt_spi_send_then_recv 先發送後接收
第二種:rt_spi_transfer_message 以鏈表結構體的方式發送

#define W25Q_SPI_DEVICE_NAME     "spi10"

static void spi_w25q_sample(int argc, char *argv[])
{
	struct rt_spi_device *spi_dev_w25q;
	char name[RT_NAME_MAX];
	rt_uint8_t w25x_read_id = 0x90;
	rt_uint8_t id[5] = {0};
	
	if(argc == 2){
		rt_strncpy(name, argv[1], RT_NAME_MAX);
	} else {
		rt_strncpy(name, W25Q_SPI_DEVICE_NAME, RT_NAME_MAX);
	}
	
	spi_dev_w25q = (struct rt_spi_device *)rt_device_find(name);
	if(!spi_dev_w25q){
		rt_kprintf("spi sample run failed! can't find %s device!\n", name);
	} else {
		
		 struct rt_spi_configuration cfg;
         cfg.data_width = 8;
         cfg.mode = RT_SPI_MODE_0 | RT_SPI_MSB; /* SPI Compatible: Mode 0 and Mode 3 */
         cfg.max_hz = 50 * 1000 * 6 ; /* 50M */
         rt_spi_configure(spi_dev_w25q, &cfg);
		
		rt_spi_send_then_recv(spi_dev_w25q, &w25x_read_id, 1, id, 5);
		rt_kprintf("use rt_spi_send_then_recv() read w25q ID is %x%x\n", id[3], id[4]);
		
		struct rt_spi_message msg1, msg2;
		msg1.send_buf = &w25x_read_id;
		msg1.recv_buf = RT_NULL;
		msg1.length = 1;
		msg1.cs_take = 1;
		msg1.cs_release = 0;
		msg1.next = &msg2;
		
		msg2.send_buf = RT_NULL;
		msg2.recv_buf = id;
		msg2.length = 5;
		msg2.cs_take = 0;
		msg2.cs_release = 1;
		msg2.next = RT_NULL;
		
		rt_spi_transfer_message(spi_dev_w25q, &msg1);
		rt_kprintf("use rt_spi_transfer_mesage() read w25q ID is :%x%x\n", id[3], id[4]);
	}
	
}
MSH_CMD_EXPORT(spi_w25q_sample, spi_w25q_sample samole);

結果:讀到最後兩位的值是 17 ef

msh />spi_w25q_sample
use rt_spi_send_then_recv() read w25q ID is 17ef
use rt_spi_transfer_mesage() read w25q ID is :17ef
msh />

5:搭建文件系統

5.1: 配置ENV

1.選擇使用虛擬系統,再flash上要部署elmfs 所以要使能
在這裏插入圖片描述
2.進入elm-chan’s Fats中,把扇區配置爲4096
在這裏插入圖片描述
3. 使能libc
在這裏插入圖片描述
4.DFS 框架的文件系統實現層需要存儲設備驅動層提供驅動接口用於對接,本次使用的存儲設備爲 SPI Flash,把下面三項選擇上
在這裏插入圖片描述
5.爲了方便地使用 shell 命令,我們在 RT-Thread Components → Command shell 選項中開啓 Using module shell 選項
在這裏插入圖片描述

5.2:創建存儲設備

由於只有塊設備類型的設備才能和文件系統對接,所以需要根據 SPI Device 找到 SPI Flash 設備,並創建與其對應的 Block Device。

這裏需要使用到萬能 SPI Flash 驅動庫:SFUD ,RT-Thread 已經集成了該組件,在上面的配置過程中我們已經開啓這個功能。此時只需要使用 SFUD 提供的 rt_sfud_flash_probe 函數即可。該函數將執行如下操作:

  1. 根據名爲 spi10 的 SPI Device 設備找到對應的 Flash 存儲設備。

  2. 初始化 Flash 設備。

  3. 在 Flash 存儲設備上創建名爲 W25Q256 的 Block Device。

如果開啓了組件自動初始化功能,該函數會被自動執行,否則需要手動調用運行。

static int rt_hw_spi_flash_with_sfud_init(void)
{
    if (RT_NULL == rt_sfud_flash_probe("W25Q128", "spi10"))
    {
        return RT_ERROR;
    };
	
    return RT_EOK;
}
INIT_COMPONENT_EXPORT(rt_hw_spi_flash_with_sfud_init);

在終端輸入 list_device
在這裏插入圖片描述

6:文件系統初始化

6.1 DFS 框架的初始化

DFS 框架的初始化主要是對內部數據結構以及資源的初始化。這一過程包括初始化文件系統必須的數據表,以及互斥鎖。該功能由如下函數完成。如果開啓了組件自動初始化功能,該函數會被自動執行,否則需要手動調用運行。

int dfs_init(void)
{
    static rt_bool_t init_ok = RT_FALSE;

    if (init_ok)
    {
        rt_kprintf("dfs already init.\n");
        return 0;
    }

    /* clear filesystem operations table */
    memset((void *)filesystem_operation_table, 0, sizeof(filesystem_operation_table));
    /* clear filesystem table */
    memset(filesystem_table, 0, sizeof(filesystem_table));
    /* clean fd table */
    memset(&_fdtab, 0, sizeof(_fdtab));

    /* create device filesystem lock */
    rt_mutex_init(&fslock, "fslock", RT_IPC_FLAG_FIFO);

#ifdef DFS_USING_WORKDIR
    /* set current working directory */
    memset(working_directory, 0, sizeof(working_directory));
    working_directory[0] = '/';
#endif

#ifdef RT_USING_DFS_DEVFS
    {
        extern int devfs_init(void);

        /* if enable devfs, initialize and mount it as soon as possible */
        devfs_init();

        dfs_mount(NULL, "/dev", "devfs", 0, 0);
    }
#endif

    init_ok = RT_TRUE;

    return 0;
}
INIT_PREV_EXPORT(dfs_init);

6.2 中間層文件系統的初始化,初始化具體文件系統

這一步的初始化主要是將 elm FatFS 的操作函數註冊到 DFS 框架中。該功能由如下函數完成。如果開啓了組件自動初始化功能,該函數會被自動執行,否則需要手動調用運行。

int elm_init(void)
{
    /* register fatfs file system */
    dfs_register(&dfs_elm);

    return 0;
}
INIT_COMPONENT_EXPORT(elm_init);

7:文件系統掛載

7.1 添加掛載函數

再main中調用w25q128_mount(),可以把設備掛載再根目錄下

void w25q128_mount(void)
{
	rt_device_t dev;
    dev = rt_device_find("W25Q128");
	if(dev != RT_NULL) {
	if(dfs_mount("W25Q128", "/", "elm", 0, 0) == 0){
			rt_kprintf("spi_flash mount to spi!\n");
		} else {
			rt_kprintf("spi_flash mount to spi failed!\n");
		}
	}
}

7.2初始化存儲設備,在flash中創建文件系統

第一次使用 SPI Flash 作爲文件系統地存儲設備時,如果我們直接重啓開發板來掛載文件系統,就會看到 spi flash mount to /spi failed! 的提示。這是因爲此時在 SPI Flash 中還沒有創建相應類型的文件系統,這就用到了創建文件系統 shell 命令:mkfs。

mkfs 命令的功能是在指定的存儲設備上創建指定類型的文件系統。使用格式爲:mkfs [-t type] device 。第一次掛載文件系統前需要使用 mkfs 命令在存儲設備上創建相應的文件系統,否則就會掛載失敗。如果要在 W25Q256 設備上創建 elm 類型的文件系統,就可以使用 mkfs -t elm W25Q256 命令,使用方法如下:

msh />ls mkfs -t elm W25Q256 

文件系統創建完成後需要重啓設備。該過操作一次,就保存在flash中。感覺就像,第一次使用U盤要格式化,後面就直接可以用了

顯示:輸入ls查看

msh />ls
Directory /:
spi                 <DIR>                    
text.txt            23                       
msh />

8:使用虛擬文件的系統接口讀寫內存

static void readwrite_sample(void)
{
	int fd;
	int size;
	char s[] = "RT-Thread Programmer!"; 
	char buffer[80];
	rt_kprintf("write string %s to test.txt\n", s);
	
	fd = open("/text.txt", O_WRONLY | O_CREAT);
	if(fd >= 0){
		write(fd, s, sizeof(s));
		close(fd);
		rt_kprintf("Write done.\n");
	}
	
	fd = open("/text.txt", O_RDONLY);
	if(fd >= 0){
		size = read(fd, buffer, sizeof(buffer));
		rt_kprintf("Read form file test.txt: %s\n", buffer);
		if(size < 0)
			return;
	}
}


MSH_CMD_EXPORT(readwrite_sample, spi_w25q_sample samole);

執行

msh />readwrite_sample
write string RT-Thread Programmer! to test.txt
Write done.
Read form file test.txt: RT-Thread Programmer!
msh />

查看文件test.txt

msh />cat text.txt
RT-Thread Programmer!msh />
msh />
msh /> 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章