讓QSPI FLASH(W25Q64)支持Fatfs文件系統(tǒng)
今天是過年放假的第一天(一共16天年假),但是說實話放不放假對我們做技術的人來說有放跟沒放其實區(qū)別不大,因為自驅(qū)力是我們維持自身實力和飯碗的根本,16天的假期可以做很多事情學不少東西了,唯一的區(qū)別是終于可以好好睡個覺了,然后睡醒接著干就完了!想著小熊派板子上帶了一個QSPI,有8MB的存儲空間,那可不能浪費了呀!之前寫的那些開源項目的圖片資源其實放在這上面的,如何實現(xiàn)呢?方法如下:
- 使用SD卡將文件拷貝到QSPI FLASH(采用fatfs文件系統(tǒng))
- 寫一個QSPI FLASH MDK下載算法,直接將圖片數(shù)據(jù)放在主程序中
接下來進入正文:
小熊派上自帶了一個QSPI接口的8M大小的SPI_FLASH,如下圖所示:
小熊派官方也提供了驅(qū)動編寫的視頻教程以及代碼編寫例程,關于怎么實現(xiàn)的,這里就不多說了,如果想詳細了解原理,可以看看世偉兄以及小熊派之前寫的文章:
STM32Cube-18 | 使用QSPI讀寫SPI Flash(W25Q64)
單片機基礎 —— 使用QSPI讀寫SPI Flash(W25Q64)
今天我們主要來講解下Fatfs系統(tǒng)功能的配置,在進入正題之前,我已經(jīng)按上面的教程將QSPI Flash正常驅(qū)動起來了,接下來進入主題,如下圖所示:
在中間件的地方選擇fatfs,然后再Mode處選擇User-defined,因為這個不是官方默認支持的,需要用戶自己去實現(xiàn)Fatfs關于底層的驅(qū)動接口。
1、功能參數(shù)配置
其中,關于功能參數(shù)的配置,主要是用到了才去配置,不用到的選項默認就行了,這部分請參考ST官網(wǎng)有關STM32cube Fatfs的應用開發(fā)文檔,如下所示:
2、幾個重要參數(shù)配置說明
CODE PAGE這個選項主要是提供編碼格式的支持,根據(jù)個人需求配置,這里配置為簡體中文:
USE_LFN這個選項主要是為了支持長文件名,并且當需要支持這個功能的時候需要提供緩存區(qū)存放,fatfs提供了BSS、STACK、HEAP三種方式。
根據(jù)個人需求選擇存放在STACK中,因為存放在BSS上,則是帶有靜態(tài)工作緩沖區(qū)的LFN,不能進行動態(tài)分配,而存放在HEP上,則需要重寫實現(xiàn)fatfs提供的ff_memalloc和ff_memfree函數(shù),所以一般情況下就把它放在棧區(qū)即可。
MAX_SS這個選項配置為4096,為什么要配置為4096呢?請看W25Q64的手冊描述:
如上,W25Q64這款芯片的最小擦除單位是4KB,也就是4096字節(jié),為了提高擦寫效率,一般情況下就直接寫4096。
其余的參數(shù)用到的時候再去做進一步的配置,均系統(tǒng)默認即可。
由于對長文件名做了支持,緩存區(qū)是在棧區(qū)的,所以把堆棧加大一些,自己喜歡就好,只要不溢出就行,根據(jù)個人習慣隨便填了兩個參數(shù),然后生成代碼工程。
3、Fatfs驅(qū)動QSPI接口實現(xiàn)
對于fatfs,ST官方多封裝了一層抽象接口給用戶進行填寫函數(shù),這個文件是:user_diskio.c,主要提供了如下給用戶編寫的接口:
Diskio_drvTypeDef USER_Driver =
{
USER_initialize, //初始化驅(qū)動盤
USER_status, //獲取硬盤狀態(tài)函數(shù)
USER_read, //讀磁盤 #if _USE_WRITE USER_write, //寫磁盤 #endif /* _USE_WRITE == 1 */ #if _USE_IOCTL == 1 USER_ioctl, //I/O操作 #endif /* _USE_IOCTL == 1 */ };
以上這些函數(shù)直接操作的就是以下fatfs原生的接口:
接下來我們需要依次實現(xiàn)它們:
初始化磁盤實現(xiàn):
DSTATUS USER_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
/* USER CODE BEGIN INIT */
uint32_t id ;
id = hal_spi_flash_get_id(); if(0xEF4017 == id)
{ printf("讀取ID:0x%x\n",id); return RES_OK;
} else return RES_ERROR ;
/* USER CODE END INIT */
}
磁盤狀態(tài)函數(shù)實現(xiàn)(可以留空):
/**
* @brief Gets Disk Status
* @param pdrv: Physical drive number (0..)
* @retval DSTATUS: Operation status
*/
DSTATUS USER_status (
BYTE pdrv /* Physical drive number to identify the drive */
)
{
/* USER CODE BEGIN STATUS */ return RES_OK;
/* USER CODE END STATUS */
}
讀磁盤函數(shù)實現(xiàn):
/**
* @brief Reads Sector(s)
* @param pdrv: Physical drive number (0..)
* @param *buff: Data buffer to store read data
* @param sector: Sector address (LBA)
* @param count: Number of sectors to read (1..128)
* @retval DRESULT: Operation result
*/
DRESULT USER_read (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
BYTE *buff, /* Data buffer to store read data */
DWORD sector, /* Sector address in LBA */
UINT count /* Number of sectors to read */
)
{
/* USER CODE BEGIN READ */
// 以4K字節(jié)為單位
hal_spi_flash_read(buff, count << 12, sector << 12); return RES_OK;
/* USER CODE END READ */
}
寫磁盤函數(shù)實現(xiàn):
SPI_FLASH的特性,需要先擦除后寫入。
/**
* @brief Writes Sector(s)
* @param pdrv: Physical drive number (0..)
* @param *buff: Data to be written
* @param sector: Sector address (LBA)
* @param count: Number of sectors to write (1..128)
* @retval DRESULT: Operation result
*/ #if _USE_WRITE == 1 DRESULT USER_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Sector address in LBA */
UINT count /* Number of sectors to write */
)
{
/* USER CODE BEGIN WRITE */
/* USER CODE HERE */
uint32_t write_addr;
write_addr = sector << 12; // 以4K字節(jié)為單位 hal_spi_flash_erase_write((uint8_t *)buff, count << 12, write_addr); return RES_OK;
/* USER CODE END WRITE */
} #endif /* _USE_WRITE == 1 */
磁盤命令操作實現(xiàn):
/**
* @brief I/O control operation
* @param pdrv: Physical drive number (0..)
* @param cmd: Control code
* @param *buff: Buffer to send/receive control data
* @retval DRESULT: Operation result
*/ #if _USE_IOCTL == 1 DRESULT USER_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
/* USER CODE BEGIN IOCTL */
switch (cmd)
{ case GET_SECTOR_COUNT:
*(DWORD * )buff = 2048; // 總的扇區(qū)數(shù) break; case GET_SECTOR_SIZE :
*(WORD * )buff = 4096; // 定義一個扇區(qū)大小為4K break; case GET_BLOCK_SIZE :
*(DWORD * )buff = 65536; // 定義一個塊大小為64K break;
} return RES_OK ;
/* USER CODE END IOCTL */
} #endif /* _USE_IOCTL == 1 */
4、編寫測試QSPI FLASH fatfs的程序
測試案例如下:
int main(void)
{
/* USER CODE BEGIN 1 */
uint8_t res ;
uint32_t Total = 0; //讀取FLASH總?cè)萘?
uint32_t Free = 0; //讀取FLASH剩余容量
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
MX_USART1_UART_Init();
MX_QUADSPI_Init();
MX_FATFS_Init();
/* USER CODE BEGIN 2 */
Mount_Fatfs();
f_GetTotal_Free((uint8_t*)"0:", &Total, &Free); //獲取SD卡總?cè)萘亢褪S嗳萘?printf("當前Fatfs總?cè)萘?%dKB==>%dMB 剩余容量:%dKB==>%dMB\n", Total, Total / 1024, Free, Free / 1024);
/*----------------------- 文件系統(tǒng)測試:寫測試 -----------------------------*/ printf("\r\n****** 即將進行文件寫入測試... ******\r\n");
res = f_open(&USERFile, "0:BearPi.txt", FA_OPEN_ALWAYS | FA_WRITE); if(res == FR_OK)
{ printf("打開/創(chuàng)建BearPi.txt文件成功,向文件寫入數(shù)據(jù)。\r\n");
res = f_write(&USERFile, write_buf, strlen((const char *)write_buf), &count); if(res != FR_OK)
{ printf("f_write 發(fā)生錯誤,err = %d\r\n", res); printf("關閉打開的BearPi.txt文件\r\n");
count = 0;
f_close(&USERFile);
} else { printf("文件寫入成功,寫入字節(jié)數(shù)據(jù):%d\n", count); printf("向文件寫入的數(shù)據(jù)為:\r\n%s\r\n", write_buf); printf("關閉打開的BearPi.txt文件\r\n");
count = 0;
f_close(&USERFile);
}
} else printf("打開/創(chuàng)建BearPi.txt文件失敗,err = %d\r\n", res);
/*------------------- 文件系統(tǒng)測試:讀測試 ------------------------------------*/ printf("****** 即將進行文件讀取測試... ******\r\n");
res = f_open(&USERFile, "0:BearPi.txt", FA_OPEN_EXISTING | FA_READ); if(res == FR_OK)
{ printf("打開BearPi.txt文件成功\r\n");
res = f_read(&USERFile, read_buf, sizeof(read_buf), &count); if(res != FR_OK)
{ printf("f_read 發(fā)生錯誤,err = %d\r\n", res); printf("關閉打開的BearPi.txt文件\r\n");
f_close(&USERFile);
} else { printf("文件讀取成功,讀取字節(jié)數(shù)據(jù):%d\n", count); printf("向文件讀取的數(shù)據(jù)為:\r\n%s\r\n", read_buf); printf("關閉打開的BearPi.txt文件\r\n");
f_close(&USERFile);
}
} else printf("打開BearPi.txt文件失敗,err = %d\r\n", res);
/*------------------- 不再使用文件系統(tǒng),取消掛載文件系統(tǒng) ------------------------------------*/ printf("不再使用文件系統(tǒng),取消掛載文件系統(tǒng)\r\n");
res = f_mount(NULL, "0:", 1); if(res == FR_OK) printf("取消掛載文件系統(tǒng)成功\r\n"); else printf("取消掛載文件系統(tǒng)失敗,err = %d\r\n", res); printf("文件系統(tǒng)測試結(jié)束\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */ while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
運行結(jié)果:
獲取完整demo:
碼云倉庫:https://gitee.com/morixinguan/bear-pi/tree/master/19.QSPI_Fatfs
獲取方法:
git clone https://gitee.com/morixinguan/bear-pi.git
即可獲取本次實驗工程全部代碼。
免責聲明:本文內(nèi)容由21ic獲得授權后發(fā)布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!