一、工具
1、硬件:GD32F30x系列單片機
2、編譯環境:KEIL
3、Flash芯片:GD25Q256DF
二、移植FatFs文件系統到單片機
關於外部Flash的驅動程序,請看鏈接:https://www.cnblogs.com/wenhao-Web/p/14052266.html
關於USB設備模式下把外部Flash模擬成U盤的程序,請看鏈接:https://www.cnblogs.com/wenhao-Web/p/14052863.html
1、找到官方提供的固件庫中的FatFs文件夾,整體拷貝到自己工程中,如下圖所示:
2、打開自己的工程,創建一個“Fatfs”文件夾並添加上面拷貝的文件夾的部分文件,如下圖所示:
- ff.c文件包含了FatFs文件系統的整個核心內容,包含了操作文件系統的功能函數,不用修改;
- fattime.c文件是用於獲取當前的時間的,該時間用於設定文件的創建或修改時間,有需求的可以進去添加獲取實時時間的操作;
- diskio.c文件屬於接口文件,用於和底層讀寫函數進行銜接的地方,是移植Fatfs文件系統重點修改的地方;
- ccsbcs.c文件用於不同編碼的轉換,不用修改;
- ffconf.h文件是FatFs文件系統的配置文件,用戶可根據需求對文件系統進行配置,也是移植Fatfs文件系統重點修改的地方。
3、修改diskio.c文件
這三個宏定義代表着三個不同的設備,我只用一個,故刪掉。
修改初始化設備函數,我要放在外面進行初始化,這里就默認初始化成功,如下所示:
/*-----------------------------------------------------------------------*/ /* Inicializes a Drive */ DSTATUS disk_initialize (BYTE drv) /* Physical drive nmuber (0..) */ { DSTATUS stat = STA_NOINIT; stat &= ~STA_NOINIT; return stat; }
修改獲取狀態函數,通過讀取外部Flash的ID判斷其狀態,因為只有一個設備,設備號可以不用管,如下所示:
/*-----------------------------------------------------------------------*/ /* Return Disk Status */ DSTATUS disk_status ( BYTE drv /* Physical drive nmuber (0..) */ ) { DSTATUS stat; if(gd25q256df_read_id() == 0xC84019) { stat = RES_OK; } else { stat = RES_ERROR; } return stat; }
修改磁盤讀函數,因為只有一個設備,設備號可以不用管,如下所示:
/*-----------------------------------------------------------------------*/ /* Read Sector(s) */ DRESULT disk_read ( BYTE drv, /* Physical drive nmuber (0..) */ BYTE *buff, /* Data buffer to store read data */ DWORD sector, /* Sector address (LBA) */ BYTE count /* Number of sectors to read (1..255) */ ) { DRESULT res; gd25q256df_read_data(buff, sector*512, count*512); res = RES_OK; return res; }
修改磁盤寫函數,因為只有一個設備,設備號可以不用管,如下所示:
/*-----------------------------------------------------------------------*/ /* Write Sector(s) */ #if _READONLY == 0 DRESULT disk_write ( BYTE drv, /* Physical drive nmuber (0..) */ const BYTE *buff, /* Data to be written */ DWORD sector, /* Sector address (LBA) */ BYTE count /* Number of sectors to write (1..255) */ ) { DRESULT res; gd25q256df_write_data(buff, sector*512, count*512); res = RES_OK; return res; }
修改磁盤I/O控制函數,因為只有一個設備,設備號可以不用管,如下所示:
/*-----------------------------------------------------------------------*/ /* Miscellaneous Functions */ DRESULT disk_ioctl ( BYTE drv, /* Physical drive nmuber (0..) */ BYTE ctrl, /* Control code */ void *buff /* Buffer to send/receive control data */ ) { DRESULT res; switch(ctrl) { case GET_SECTOR_COUNT: *(DWORD *)buff = 65536; break; case GET_SECTOR_SIZE: *(WORD *)buff = 512; break; case GET_BLOCK_SIZE: /* 暫時未用 */ break; } res = RES_OK; return res; }
4、文件系統讀寫測試
相關變量定義
uint32_t flash_id; FATFS fs; FIL file; FRESULT res_flash; uint8_t g_TestBuf1[] = "12345hello world 今天是個好日子,123456\r\n"; uint8_t g_TestBuf2[] = "hello world\r\n"; uint8_t g_ReadBuf[128]; uint32_t bw;
主函數中實現文件的創建和讀取,驗證FatFs移植是否成功
/*! \brief main function \param[in] none \param[out] none \retval none */ int main(void) { /* configure 4 bits pre-emption priority */ nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0); bsp_spi1_init(); gd25q256df_init(); flash_id = gd25q256df_read_id(); if(flash_id == 0xC84019) { /* 掛載設備 */ res_flash = f_mount (1, &fs); if (res_flash == FR_OK) { /* 打開或者創建文件 */ res_flash = f_open(&file, "1:test1.txt", FA_CREATE_ALWAYS | FA_WRITE); if(res_flash == FR_OK) { /* 向文件件中寫數據 */ res_flash = f_write(&file, g_TestBuf1, sizeof(g_TestBuf1), &bw); if (res_flash != FR_OK) { while(1); } /* 關閉文件 */ f_close(&file); } /* 打開或者創建文件 */ res_flash = f_open(&file, "1:test2.txt", FA_CREATE_ALWAYS | FA_WRITE); if(res_flash == FR_OK) { /* 向文件件中寫數據 */ res_flash = f_write(&file, g_TestBuf2, sizeof(g_TestBuf2), &bw); if (res_flash != FR_OK) { while(1); } /* 關閉文件 */ f_close(&file); } /* 以讀的方式打開文件 */ res_flash = f_open(&file, "1:test2.txt", FA_OPEN_EXISTING | FA_READ); if(res_flash == FR_OK) { res_flash = f_read(&file, g_ReadBuf, sizeof(g_ReadBuf), &bw); if(res_flash != FR_OK) { while(1); } /* 關閉文件 */ f_close(&file); } } /* 卸載文件 */ res_flash = f_mount (1, NULL); } }
通過調試,可以發現是能夠成功讀取前面創建的文件中的內容,如下圖所示:
四、擴展功能
為了能夠更直觀看到文件系統創建的文件和寫入的內容,我結合了單片機的USB設備模式,把外部Flash模擬成一個U盤,能夠直接在電腦查看創建的文件。
USB相關變量定義及初始化
usb_core_handle_struct usbhs_core_dev = { .dev = { .dev_desc = (uint8_t *)&device_descripter, .config_desc = (uint8_t *)&configuration_descriptor, .strings = usbd_strings, .class_init = msc_init, .class_deinit = msc_deinit, .class_req_handler = msc_req_handler, .class_data_handler = msc_data_handler }, .udelay = delay_us, .mdelay = delay_ms }; void usb_clock_config(void); void usb_gpio_config(void); void usb_interrupt_config(void); uint8_t timer_prescaler = 0; uint32_t usbfs_prescaler = 0;
主函數中實現文件的創建並寫入數據,寫入完成后初始化並啟動USB設備
/*! \brief main function \param[in] none \param[out] none \retval none */ int main(void) { /* configure 4 bits pre-emption priority */ nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0); bsp_spi1_init(); gd25q256df_init(); flash_id = gd25q256df_read_id(); if(flash_id == 0xC84019) { /* 掛載設備 */ res_flash = f_mount (1, &fs); if (res_flash == FR_OK) { /* 打開或者創建文件 */ res_flash = f_open(&file, "1:test1.txt", FA_CREATE_ALWAYS | FA_WRITE); if(res_flash == FR_OK) { /* 向文件件中寫數據 */ res_flash = f_write(&file, g_TestBuf1, sizeof(g_TestBuf1), &bw); if (res_flash != FR_OK) { while(1); } /* 關閉文件 */ f_close(&file); } /* 打開或者創建文件 */ res_flash = f_open(&file, "1:test2.txt", FA_CREATE_ALWAYS | FA_WRITE); if(res_flash == FR_OK) { /* 向文件件中寫數據 */ res_flash = f_write(&file, g_TestBuf2, sizeof(g_TestBuf2), &bw); if (res_flash != FR_OK) { while(1); } /* 關閉文件 */ f_close(&file); } } /* 卸載文件 */ res_flash = f_mount (1, NULL); } /* configure USB clock */ usb_clock_config(); /* USB timer configure */ timer_nvic_init(); /* USB device stack configure */ usbd_init(&usbhs_core_dev, USB_FS_CORE_ID); /* USB interrupt configure */ usb_interrupt_config(); /* check if USB device is enumerated successfully */ while (usbhs_core_dev.dev.status != USB_STATUS_CONFIGURED) {} while(1); }
測試結果如下圖所示:
#endif