FAL組件
什么是FAL?
FAL (Flash Abstraction Layer)Flash 抽象層,是對 Flash 及基于 Flash 的分區進行管理、操作的抽象層,對上層統一了 Flash 及 分區操作的 API (框架圖如下所示),并具有以下特性:
支持靜態可配置的分區表,并可關聯多個 Flash 設備;
分區表支持自動裝載。避免在多固件項目,分區表被多次定義的問題;
代碼精簡,對操作系統無依賴,可運行于裸機平臺,比如對資源有一定要求的 Bootloader;
統一的操作接口。保證了文件系統、OTA、NVM(例如:EasyFlash) 等對 Flash 有一定依賴的組件,底層 Flash 驅動的可重用性;
自帶基于 Finsh/MSH 的測試命令,可以通過 Shell 按字節尋址的方式操作(讀寫擦) Flash 或分區,方便開發者進行調試、測試;
通過上圖可以清晰明了看到,FAL抽象層向下可以通過Flash硬件進行統一管理,當然也可以使用SFUD框架(串行Flash通用驅動庫,這部分RT-Thread官方已完成框架的移植同時提供多個應用歷程),而對上也可以使用如DFS、NVM提供的Flash硬件統一訪問接口,方便用戶更加直接方便對底層flash硬件的訪問操作。 注:非易失性存儲器 (NVM):在芯片電源關閉期間保存存儲在其中的數據。因此,它被用于沒有磁盤的便攜式設備中的內存,以及用于可移動存儲卡等用途。主要類型有:非易失性半導體存儲器 (Non-volatile semiconductor memory, NVSM) 將數據存儲在浮柵存儲單元中,每個單元都由一個浮柵(floating-gate) MOSFET 組成。
關于存儲,可以用一張圖來解釋:
FAL組件使用ENV配置FAL
在RT-Thread v4.1.0之前,FAL是作為軟件包形式對用戶開放使用的,而v4.1.0之后,FAL被RT-Thread官方重新定義為RTT組件的一部分,這樣也能更加方便用戶的開發。
下面正式講解FAL組件的使用:
首先打開ENV工具,根據以下路徑打開FAL使能RT-Thread Components->FAL: flash abstraction layer,由于后面會用到SFUD,所以這里把FAL uses SFUD drivers一并使能,并修改FAL設備名稱為W25Q128.
完成上述操作后保存退出,并使用scons --target=mdk5重新生成MDK5文件并打開。
FAL組件FAL SFUD移植
示例選用W25Q128 spi flash作為測試模塊,并且使用SFUD框架對spi flash設備進行管理和驅動。 由于目前RT-Thread的SFUD已經對W25Q128完成支持,根據官方的使用手冊,僅需編寫fal_cfg.h文件完成對FAL_FLASH_DEV_TABLE及FAL_PART_TABLE的定義即可。文件存放路徑:. t-threadsplpc55sxxlpc55s69_nxp_evkoardportsfal_cfg.h
// fal.cfg.h /* * Copyright (c) 2006-2023, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2023-04-21 Wangyuqiang the first version */ #ifndef _FAL_CFG_H_ #define _FAL_CFG_H_ #include #include #ifndef FAL_USING_NOR_FLASH_DEV_NAME #define NOR_FLASH_DEV_NAME "norflash0" #else #define NOR_FLASH_DEV_NAME FAL_USING_NOR_FLASH_DEV_NAME #endif /* Flash device Configuration */ extern struct fal_flash_dev nor_flash0; /* flash device table */ #define FAL_FLASH_DEV_TABLE { &nor_flash0, } /* Partition Configuration */ #ifdef FAL_PART_HAS_TABLE_CFG /* partition table */ #define FAL_PART_TABLE { {FAL_PART_MAGIC_WROD, "easyflash", NOR_FLASH_DEV_NAME, 0, 512 * 1024, 0}, {FAL_PART_MAGIC_WROD, "download", NOR_FLASH_DEV_NAME, 512 * 1024, 1024 * 1024, 0}, {FAL_PART_MAGIC_WROD, "wifi_image", NOR_FLASH_DEV_NAME, (512 + 1024) * 1024, 512 * 1024, 0}, {FAL_PART_MAGIC_WROD, "font", NOR_FLASH_DEV_NAME, (512 + 1024 + 512) * 1024, 7 * 1024 * 1024, 0}, {FAL_PART_MAGIC_WROD, "filesystem", NOR_FLASH_DEV_NAME, (512 + 1024 + 512 + 7 * 1024) * 1024, 7 * 1024 * 1024, 0}, } #endif /* FAL_PART_HAS_TABLE_CFG */ #endif /* _FAL_CFG_H_ */
此時編譯的話是找不到該頭文件的,需要在Keil中設置:
在RTT FAL組件中的SFUD提供的fal_flash_dev對象默認的nor_flash0參數中,flash大小默認為8M,而W25Q128最大最16M,可以選擇在. t-threadcomponentsfalsamplesportingfal_flash_sfud_port.c文件中對struct fal_flash_dev nor_flash0進行修改:
struct fal_flash_dev nor_flash0 = { .name = FAL_USING_NOR_FLASH_DEV_NAME, .addr = 0, .len = 16 * 1024 * 1024, .blk_size = 4096, .ops = {init, read, write, erase}, .write_gran = 1 };
當然也可以選擇不進行修改,專家的原話是因為在調用初始化接口函數init后,會從flash設備讀取正確的參數更新到nor_flash0表項中,在使用FAL組件前都需要調用FAL初始化函數fal_init,其內調用flash設備初始化函數fal_flash_init,最后會調用注冊到fal_flash_dev設備表項中的初始化函數device_table->ops.init,所以nor_flash0表項參數會在FAL初始化時被更新。
同時需要開啟SFUD框架支持,打開ENV工具,由于SFUD的使用需要指定一個spi設備,這里我選擇使用最近移植好的軟件spi,路徑Hardware Drivers Config->On-chip Peripheral Drivers-> Enable soft SPI BUS->Enable soft SPI1 BUS (software simulation),這里的測試開發板是恩智浦LPC55S69-EVK,并且這款BSP的軟件模擬SPI由我本人對接,關于這部分的軟件SPI引腳定義可以選用默認即可,當然也可以使用自定義引腳,只要不與其他引腳產生沖突。
此時回到ENV主界面,進入RT-Thread Components->Device Drivers->Using Serial Flash Universal Driver,此時才可以看到SFUD選項出現(如果沒有使能SPI則無法看到),使能后保持默認。
FAL組件FAL SFUD測試用例
為了驗證W25Q128及軟件模擬SPI在SFUD框架上是否能夠成功運行,在. t-threadsplpc55sxxlpc55s69_nxp_evkoardports下新建一個soft_spi_flash_init.c文件,代碼如下:
/* * Copyright (c) 2006-2023, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2023-04-21 Wangyuqiang the first version */ #include #include "spi_flash.h" #include "spi_flash_sfud.h" #include "drv_soft_spi.h" #include "drv_pin.h" #include "rtconfig.h" #define cs_pin GET_PINS(1,9) static int rt_soft_spi_flash_init(void) { int result = -1; result = rt_hw_softspi_device_attach("sspi1", "sspi10", cs_pin); rt_kprintf("value is %d ",result); if(result == RT_EOK) { rt_kprintf("rt_hw_softspi_device_attach successful! "); } if (RT_NULL == rt_sfud_flash_probe("W25Q128", "sspi10")) { return -RT_ERROR; } return RT_EOK; } INIT_COMPONENT_EXPORT(rt_soft_spi_flash_init);
這里需要指定一個片選引腳,這里暫時使用了SSPI2的SCK引腳作為片選,需要注意不要同時打開SSPI1和SSPI2,后續我會專門上傳一個通用GPIO作為片選引腳,避免產生問題。然后軟件SPI設備的掛載使用的是SSPI1 bus及SSPI10 device,并且掛載flash設備到SSPI10。
在 . t-threadsplpc55sxxlpc55s69_nxp_evkoardports 下新建fal_sample.c文件,并編寫測試代碼:
//fal_sample.c /* * Copyright (c) 2006-2023, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2023-04-21 Wangyuqiang the first version */ #include "rtthread.h" #include "rtdevice.h" #include "board.h" #include "fal.h" #define BUF_SIZE 1024 static int fal_test(const char *partiton_name) { int ret; int i, j, len; uint8_t buf[BUF_SIZE]; const struct fal_flash_dev *flash_dev = RT_NULL; const struct fal_partition *partition = RT_NULL; if (!partiton_name) { rt_kprintf("Input param partition name is null! "); return -1; } partition = fal_partition_find(partiton_name); if (partition == RT_NULL) { rt_kprintf("Find partition (%s) failed! ", partiton_name); ret = -1; return ret; } flash_dev = fal_flash_device_find(partition->flash_name); if (flash_dev == RT_NULL) { rt_kprintf("Find flash device (%s) failed! ", partition->flash_name); ret = -1; return ret; } rt_kprintf("Flash device : %s " "Flash size : %dK " "Partition : %s " "Partition size: %dK ", partition->flash_name, flash_dev->len/1024, partition->name, partition->len/1024); /* erase all partition */ ret = fal_partition_erase_all(partition); if (ret < 0) { rt_kprintf("Partition (%s) erase failed! ", partition->name); ret = -1; return ret; } rt_kprintf("Erase (%s) partition finish! ", partiton_name); /* read the specified partition and check data */ for (i = 0; i < partition->len;) { rt_memset(buf, 0x00, BUF_SIZE); len = (partition->len - i) > BUF_SIZE ? BUF_SIZE : (partition->len - i); ret = fal_partition_read(partition, i, buf, len); if (ret < 0) { rt_kprintf("Partition (%s) read failed! ", partition->name); ret = -1; return ret; } for(j = 0; j < len; j++) { if (buf[j] != 0xFF) { rt_kprintf("The erase operation did not really succeed! "); ret = -1; return ret; } } i += len; } /* write 0x00 to the specified partition */ for (i = 0; i < partition->len;) { rt_memset(buf, 0x00, BUF_SIZE); len = (partition->len - i) > BUF_SIZE ? BUF_SIZE : (partition->len - i); ret = fal_partition_write(partition, i, buf, len); if (ret < 0) { rt_kprintf("Partition (%s) write failed! ", partition->name); ret = -1; return ret; } i += len; } rt_kprintf("Write (%s) partition finish! Write size %d(%dK). ", partiton_name, i, i/1024); /* read the specified partition and check data */ for (i = 0; i < partition->len;) { rt_memset(buf, 0xFF, BUF_SIZE); len = (partition->len - i) > BUF_SIZE ? BUF_SIZE : (partition->len - i); ret = fal_partition_read(partition, i, buf, len); if (ret < 0) { rt_kprintf("Partition (%s) read failed! ", partition->name); ret = -1; return ret; } for(j = 0; j < len; j++) { if (buf[j] != 0x00) { rt_kprintf("The write operation did not really succeed! "); ret = -1; return ret; } } i += len; } ret = 0; return ret; } static void fal_sample(void) { /* 1- init */ fal_init(); if (fal_test("font") == 0) { rt_kprintf("Fal partition (%s) test success! ", "font"); } else { rt_kprintf("Fal partition (%s) test failed! ", "font"); } if (fal_test("download") == 0) { rt_kprintf("Fal partition (%s) test success! ", "download"); } else { rt_kprintf("Fal partition (%s) test failed! ", "download"); } } MSH_CMD_EXPORT(fal_sample, fal sample);FAL組件測試結果 打開串口工具,輸入命令:
msh/>easyflash_sample這里就可以進行編譯下載了,成功后的截圖如下:
DFS文件系統什么是DFS?
DFS 是 RT-Thread 提供的虛擬文件系統組件,全稱為 Device File System,即設備虛擬文件系統,文件系統的名稱使用類似 UNIX 文件、文件夾的風格,目錄結構如下圖所示:
在 RT-Thread DFS 中,文件系統有統一的根目錄,使用 / 來表示。而在根目錄下的 f1.bin 文件則使用 /f1.bin 來表示,2018 目錄下的 f1.bin 目錄則使用 /data/2018/f1.bin 來表示。即目錄的分割符號是 /,這與 UNIX/Linux 完全相同,與 Windows 則不相同(Windows 操作系統上使用 來作為目錄的分割符)。
DFS文件系統 DFS架構
RT-Thread DFS 組件的主要功能特點有:
為應用程序提供統一的 POSIX 文件和目錄操作接口:read、write、poll/select 等;
支持多種類型的文件系統,如 FatFS、RomFS、DevFS 等,并提供普通文件、設備文件、網絡文件描述符的管理;
支持多種類型的存儲設備,如 SD Card、SPI Flash、Nand Flash 等;
DFS 的層次架構主要分為 POSIX 接口層、虛擬文件系統層和設備抽象層;
DFS文件系統使用ENV配置DFS
打開ENV, 進入路徑RT-Thread Components → DFS: device virtual file system,使能DFS: device virtual file system
由于DFS使用的是POSIX接口,而dfs_posix.h已經在新版本中移除了,如果想要兼容老版本,可在menuconfig中使能RT-Thread Components->Support legacy version for compatibility
由于elmfat文件系統默認最大扇區大小為512,但這里使用的flash模塊W25Q128的Flash扇區大小為4096,為了將elmfat文件系統掛載到W25Q128上,Maximum sector size需要和W25Q128扇區大小保持一致,修改為4096,路徑:RT-Thread Components → DFS: device virtual file system → Enable elm-chan fatfs / elm-chan's FatFs, Generic FAT Filesystem Module
保存退出后使用scons --target=mdk5生成MDK5工程。
DFS文件系統DFS掛載到FAL分區測試
這里增加FAL flash抽象層,將elmfat文件系統掛載到W25Q128 flash設備的filesystem分區上,由于FAL管理的filesystem分區不是塊設備,需要先使用FAL分區轉BLK設備接口函數將filesystem分區轉換為塊設備,然后再將DFS elmfat文件系統掛載到filesystem塊設備上。
接下來修改fal_sample.c文件,修改后代碼:
/* * Copyright (c) 2006-2023, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2023-04-21 Wangyuqiang the first version */ #include "rtthread.h" #include "rtdevice.h" #include "board.h" #include "fal.h" #include #define FS_PARTITION_NAME "filesystem" #define BUF_SIZE 1024 static int fal_test(const char *partiton_name) { int ret; int i, j, len; uint8_t buf[BUF_SIZE]; const struct fal_flash_dev *flash_dev = RT_NULL; const struct fal_partition *partition = RT_NULL; if (!partiton_name) { rt_kprintf("Input param partition name is null! "); return -1; } partition = fal_partition_find(partiton_name); if (partition == RT_NULL) { rt_kprintf("Find partition (%s) failed! ", partiton_name); ret = -1; return ret; } flash_dev = fal_flash_device_find(partition->flash_name); if (flash_dev == RT_NULL) { rt_kprintf("Find flash device (%s) failed! ", partition->flash_name); ret = -1; return ret; } rt_kprintf("Flash device : %s " "Flash size : %dK " "Partition : %s " "Partition size: %dK ", partition->flash_name, flash_dev->len/1024, partition->name, partition->len/1024); /* erase all partition */ ret = fal_partition_erase_all(partition); if (ret < 0) { rt_kprintf("Partition (%s) erase failed! ", partition->name); ret = -1; return ret; } rt_kprintf("Erase (%s) partition finish! ", partiton_name); /* read the specified partition and check data */ for (i = 0; i < partition->len;) { rt_memset(buf, 0x00, BUF_SIZE); len = (partition->len - i) > BUF_SIZE ? BUF_SIZE : (partition->len - i); ret = fal_partition_read(partition, i, buf, len); if (ret < 0) { rt_kprintf("Partition (%s) read failed! ", partition->name); ret = -1; return ret; } for(j = 0; j < len; j++) { if (buf[j] != 0xFF) { rt_kprintf("The erase operation did not really succeed! "); ret = -1; return ret; } } i += len; } /* write 0x00 to the specified partition */ for (i = 0; i < partition->len;) { rt_memset(buf, 0x00, BUF_SIZE); len = (partition->len - i) > BUF_SIZE ? BUF_SIZE : (partition->len - i); ret = fal_partition_write(partition, i, buf, len); if (ret < 0) { rt_kprintf("Partition (%s) write failed! ", partition->name); ret = -1; return ret; } i += len; } rt_kprintf("Write (%s) partition finish! Write size %d(%dK). ", partiton_name, i, i/1024); /* read the specified partition and check data */ for (i = 0; i < partition->len;) { rt_memset(buf, 0xFF, BUF_SIZE); len = (partition->len - i) > BUF_SIZE ? BUF_SIZE : (partition->len - i); ret = fal_partition_read(partition, i, buf, len); if (ret < 0) { rt_kprintf("Partition (%s) read failed! ", partition->name); ret = -1; return ret; } for(j = 0; j < len; j++) { if (buf[j] != 0x00) { rt_kprintf("The write operation did not really succeed! "); ret = -1; return ret; } } i += len; } ret = 0; return ret; } static void fal_sample(void) { /* 1- init */ fal_init(); if (fal_test("font") == 0) { rt_kprintf("Fal partition (%s) test success! ", "font"); } else { rt_kprintf("Fal partition (%s) test failed! ", "font"); } if (fal_test("download") == 0) { rt_kprintf("Fal partition (%s) test success! ", "download"); } else { rt_kprintf("Fal partition (%s) test failed! ", "download"); } } MSH_CMD_EXPORT(fal_sample, fal sample); static void fal_elmfat_sample(void) { int fd, size; struct statfs elm_stat; struct fal_blk_device *blk_dev; char str[] = "elmfat mount to W25Q flash.", buf[80]; /* fal init */ fal_init(); /* create block device */ blk_dev = (struct fal_blk_device *)fal_blk_device_create(FS_PARTITION_NAME); if(blk_dev == RT_NULL) rt_kprintf("Can't create a block device on '%s' partition. ", FS_PARTITION_NAME); else rt_kprintf("Create a block device on the %s partition of flash successful. ", FS_PARTITION_NAME); /* make a elmfat format filesystem */ if(dfs_mkfs("elm", FS_PARTITION_NAME) == 0) rt_kprintf("make elmfat filesystem success. "); /* mount elmfat file system to FS_PARTITION_NAME */ if(dfs_mount(FS_PARTITION_NAME, "/", "elm", 0, 0) == 0) rt_kprintf("elmfat filesystem mount success. "); /* Get elmfat file system statistics */ if(statfs("/", &elm_stat) == 0) rt_kprintf("elmfat filesystem block size: %d, total blocks: %d, free blocks: %d. ", elm_stat.f_bsize, elm_stat.f_blocks, elm_stat.f_bfree); if(mkdir("/user", 0x777) == 0) rt_kprintf("make a directory: '/user'. "); rt_kprintf("Write string '%s' to /user/test.txt. ", str); /* Open the file in create and read-write mode, create the file if it does not exist*/ fd = open("/user/test.txt", O_WRONLY | O_CREAT); if (fd >= 0) { if(write(fd, str, sizeof(str)) == sizeof(str)) rt_kprintf("Write data done. "); close(fd); } /* Open file in read-only mode */ fd = open("/user/test.txt", O_RDONLY); if (fd >= 0) { size = read(fd, buf, sizeof(buf)); close(fd); if(size == sizeof(str)) rt_kprintf("Read data from file test.txt(size: %d): %s ", size, buf); } } MSH_CMD_EXPORT_ALIAS(fal_elmfat_sample, fal_elmfat,fal elmfat sample);DFS文件系統測試結果
打開串口工具,輸入命令:
msh />fal_elmfat_sample
測試結果如下:
結語
本期我們介紹了FAL組件和DFS文件系統的功能特點和使用方法,下期將給大家介紹使用FAL分區管理與easyflash變量管理的第二部分,如何將EasyFlsh移植到FAL分區。靜待后續,下期見!
審核編輯:湯梓紅
-
FlaSh
+關注
關注
10文章
1638瀏覽量
148162 -
操作系統
+關注
關注
37文章
6842瀏覽量
123409 -
變量
+關注
關注
0文章
613瀏覽量
28402 -
fal
+關注
關注
0文章
5瀏覽量
6756 -
RT-Thread
+關注
關注
31文章
1293瀏覽量
40223
原文標題:【LPC55S69】使用FAL分區管理與easyflash變量管理(上集)
文章出處:【微信號:NXP_SMART_HARDWARE,微信公眾號:恩智浦MCU加油站】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論