如何製作一個讀取U盤文件系統的工程

1 前言

本文介紹基於CubeMx如何創建一個讀取U盤的工程,並通過FAT32文件系統創建和讀取文件。

2 創建工程

讀取U盤的程序在實際項目中經常會用到,這裏我們基於STM3240G-EVAL評估板來示例如何創建一個讀取U盤的程序。

在這個示例中,我們將通過一個按鍵來觸發文件的讀寫。

2.1 硬件介紹

在正式創建工程之前,我們首先非常必要了解STM3240G-EVAL評估板相關的電路設計。
STM3240G-EVAL使用25M外部HSE,USB相關電路如下:

USB電路

圖1 USB電路

如上圖所示,MCU通過PH5管腳來控制外部U盤的VBUS,低電平爲使能。
按鍵相關電路如下:

按鍵相關電路

圖2 按鍵相關電路

2.2 創建CubeMx工程

STM3240G-EVAL評估板使用的是STM32F407IGH6,因此創建對應的CubeMx工程,在pingout頁面中使能USB_OTG_FS外設,並設置爲Host_Only:

USB_OTG_FS設置爲主機模式

圖3 USB_OTG_FS設置爲主機模式

並見PH5設置爲GPIO_Output模式,用作使能VBUS。同時將PH15設置爲外部中斷,檢測下降沿。

 pinout分佈

圖4 pinout分佈

時鐘樹方面外部25M HSE,主頻設置爲168M,USB時鐘48M,如下所示:

時鐘樹設置

圖5 時鐘樹設置

在配置方面,HAL層GPIO基本沒有什麼特殊,記得外部中斷使用檢測下降沿,這個是使用在按鍵中:

按鍵檢測外部中斷

圖6 按鍵檢測外部中斷

在NVIC中,一定要將USB的中斷優先級高於按鍵中斷,否則U盤不能正常讀取。這裏USB中斷優先級設置爲1,而外部中斷15設置爲5,如下圖所示:

中斷優先級配置

圖7 中斷優先級配置

同時爲USB選上MSC類,且勾上使用USB disk FAT文件系統:

USB MSC類和FAT文件系統

圖8 USB MSC類和FAT文件系統

中間件配置方面,其實可以使用默認,不過我們還是修改部分參數。
在文件系統配置方面:

文件系統參數配置

圖9 文件系統參數配置

我們只是簡單地將其配置成支持中文,並支持長文件名。
最後,將棧和堆的空間大小都設置爲2K:

堆棧大小設置

圖10 堆棧大小設置

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 結論

  1. 按鍵的中斷EXIT15的優先級一定不能過於USB的中斷優先級,否則代碼運行到操作文件的時候回卡死!

  2. 通過CubeMx工具將一個比較複雜的讀取文件系統工程大大簡化的。

示例代碼:http://download.csdn.net/detail/flydream0/9717879

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