1 前言
本文介紹基於CubeMx如何創建一個讀取U盤的工程,並通過FAT32文件系統創建和讀取文件。
2 創建工程
讀取U盤的程序在實際項目中經常會用到,這裏我們基於STM3240G-EVAL評估板來示例如何創建一個讀取U盤的程序。
在這個示例中,我們將通過一個按鍵來觸發文件的讀寫。
2.1 硬件介紹
在正式創建工程之前,我們首先非常必要了解STM3240G-EVAL評估板相關的電路設計。
STM3240G-EVAL使用25M外部HSE,USB相關電路如下:
如上圖所示,MCU通過PH5管腳來控制外部U盤的VBUS,低電平爲使能。
按鍵相關電路如下:
2.2 創建CubeMx工程
STM3240G-EVAL評估板使用的是STM32F407IGH6,因此創建對應的CubeMx工程,在pingout頁面中使能USB_OTG_FS外設,並設置爲Host_Only:
並見PH5設置爲GPIO_Output模式,用作使能VBUS。同時將PH15設置爲外部中斷,檢測下降沿。
時鐘樹方面外部25M HSE,主頻設置爲168M,USB時鐘48M,如下所示:
在配置方面,HAL層GPIO基本沒有什麼特殊,記得外部中斷使用檢測下降沿,這個是使用在按鍵中:
在NVIC中,一定要將USB的中斷優先級高於按鍵中斷,否則U盤不能正常讀取。這裏USB中斷優先級設置爲1,而外部中斷15設置爲5,如下圖所示:
同時爲USB選上MSC類,且勾上使用USB disk FAT文件系統:
中間件配置方面,其實可以使用默認,不過我們還是修改部分參數。
在文件系統配置方面:
我們只是簡單地將其配置成支持中文,並支持長文件名。
最後,將棧和堆的空間大小都設置爲2K:
OK,接下來就可以生成工程了。
2.3 代碼修改
在生成的代碼中,首先我們對main函數進行修改,在main函數中我們需要做的工作主要有以下幾件:
- 完成系統初始化
- 使能VBUS
- 當檢測到插入U盤時掛載文件系統
- 當檢測到U盤拔出時,卸載文件系統
於是main函數如下所示:
int main(void)
{
/* USER CODE BEGIN 1 */
static ApplicationTypeDef pre_state = APPLICATION_IDLE;
FATFS fs;
/* USER CODE END 1 */
/* MCU Configuration----------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_FATFS_Init();
MX_USB_HOST_Init();
/* USER CODE BEGIN 2 */
HAL_GPIO_WritePin(GPIOH, GPIO_PIN_5, GPIO_PIN_RESET);//enable usb VBUS
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
MX_USB_HOST_Process();
/* USER CODE BEGIN 3 */
if (pre_state != Appli_state)
{
switch(Appli_state)
{
case APPLICATION_DISCONNECT: //USB flash disk remove
/* Register the file system object to the FatFs module */
if(f_mount(NULL, "", 0) != FR_OK)
{
USBH_UsrLog("ERROR : Cannot exit FatFs! \n");
}
break;
case APPLICATION_READY: //USB flash disk plugin
/* Open or create a log file and ready to append */
if(f_mount(&fs, "", 0) != FR_OK)
{
break;
}
break;
default:
break;
}
}
pre_state = Appli_state;
}
/* USER CODE END 3 */
}
如上代碼,程序通過拉低PH5管腳來使能VBUS。使用使用一個靜態的局部變量pre_state來記錄之前的U盤拔插狀態,用以跟蹤U盤的連接狀態,當U盤插入時,立即掛載文件系統。當檢測到U盤拔出時則卸載文件系統。
接下來看按鍵中斷處理,這裏,我們在按鍵中斷中分別做3次測試,寫文件測試,和文件掃描測試 :
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
static uint8_t step =0;
if(USBH_MSC_IsReady(&hUsbHostFS))
{
switch(step ++)
{
case 0:
writefile_test(); //寫文件測試
break;
case 1:
readfile_test(); //讀文件測試
break;
case 2:
Explore_Disk("0:/", 1); //文件掃描測試
break;
default:
break;
}
if(step >2)
{
step =0;
}
}
else
{
USBH_UsrLog("USB Device is not ready.\n");
}
}
文件掃面測試,寫問津測試,讀文件測試:
FRESULT Explore_Disk(char *path, uint8_t recu_level)
{
FRESULT res = FR_OK;
FILINFO fno;
DIR dir;
char *fn;
char tmp[14];
uint8_t line_idx = 0;
#if _USE_LFN
static char lfn[_MAX_LFN + 1]; /* Buffer to store the LFN */
fno.lfname = lfn;
fno.lfsize = sizeof lfn;
#endif
res = f_opendir(&dir, path);
if(res == FR_OK)
{
while(USBH_MSC_IsReady(&hUsbHostFS))
{
res = f_readdir(&dir, &fno);
if(res != FR_OK || fno.fname[0] == 0)
{
break;
}
if(fno.fname[0] == '.')
{
continue;
}
#if _USE_LFN
fn = *fno.lfname ? fno.lfname : fno.fname;
#else
fn = fno.fname;
#endif
strcpy(tmp, fn);
line_idx++;
if(line_idx > 9)
{
line_idx = 0;
}
if(recu_level == 1)
{
USBH_UsrLog(" |__");
}
else if(recu_level == 2)
{
USBH_UsrLog(" | |__");
}
if((fno.fattrib & AM_MASK) == AM_DIR)
{
strcat(tmp, "\n");
USBH_UsrLog((void *)tmp);
Explore_Disk(fn, 2);
}
else
{
strcat(tmp, "\n");
USBH_UsrLog((void *)tmp);
}
if(((fno.fattrib & AM_MASK) == AM_DIR)&&(recu_level == 2))
{
Explore_Disk(fn, 2);
}
}
f_closedir(&dir);
}
return res;
}
void writefile_test(void)
{
FIL fil;
FRESULT fr;
/* Opens an existing file. If not exist, creates a new file. */
fr = f_open(&fil, "0:/mytest.txt", FA_READ | FA_WRITE | FA_CREATE_ALWAYS);
if (fr != FR_OK)
{
return;
}
f_printf(&fil, "%s\n", "[USB]write txt OK!0123456789");
/* Close the file */
f_close(&fil);
}
void readfile_test(void)
{
FIL fil;
FRESULT fr;
uint8_t buff[20];
UINT off = 0;
/* Opens an existing file. If not exist, creates a new file. */
fr = f_open(&fil, "0:/mytest.txt", FA_READ);
if (fr != FR_OK)
{
return;
}
f_read(&fil, buff, 16, &off);
buff[16] = 0;
/* Close the file */
f_close(&fil);
}
最後將U盤去下來插入到windows查看,可以正常考到測試文件mytest.txt文件,這證明結果是OK的。
3 結論
按鍵的中斷EXIT15的優先級一定不能過於USB的中斷優先級,否則代碼運行到操作文件的時候回卡死!
通過CubeMx工具將一個比較複雜的讀取文件系統工程大大簡化的。