YS電子工作ラボ

USB(ホストモード)
USBメモリ 読み書き(MSDクラス)

(→プロジェクトファイル(Harmony Ver.2.04版 ) ダウンロード


    USBメモリの中にファイルを作成してテキストを書き込み、 これを読みだし表示する例を紹介します。 Harmonyのライブラリとしては、USBホストモード制御のMSDクラスを使用しています。

 ・ プログラムはマイクロチップのサンプルソフト"C:\microchip\harmony\v2_04\apps\usb\host\msd_basic"を参考にして作成しています。

 ・ マイクロチップが提供してくれるMSDクラスのフレームワークには、USBメモリがPICに接続するためのドライバー(部分)も含まれています。


<仕様>
   ・PIC32MZのUSBモジュールを使用してUSBメモリの読み書きを行う。
 ・読み書きするファイルはテキストファイルとする。
 ・USBメモリの挿入を検出した場合は以下の動作を行う。
  ①キャラクタ液晶に以下を表示して1.5sec待つこと。
    1行目: USB Memory
    2行目:  has connected
  ②(ロング)ファイル名LonfFileName.txt のテキストファイルを新規作成して書き込み可能モードでオープンする。 
    尚、ファイル名LonfFileName.txtの ファイルが  既にUSBメモリの(ルート)中に存在する場合は、この既存ファイルをオープンする。
     キャラクタ液晶に以下を表示して1.5sec待つこと。
     1行目: FileOpened !!
     2行目: ファイル名
  ③オープンしたファイルに  "How are you?  "  を書き込む。 既存のファイルを開いた場合は"How are you?  "  を追記する。
  ④ファイルを閉じて、以下を表示して1.5sec待つこと
     1行目: Writed
     2行目: and Closed
  ⑤再び、ファイル名LonfFileName.txt のファイルを読み込み専用モードで開き、ファイルの内容を読みだし、以下をキャラクタ液晶に表示する。
     1行目: ファイルの先頭から16文字
     2行目: ファイルの内容が読みだされた回数XXを "Number=XX" の形式で表示する。
 ・起動後、液晶に以下表示後、2sec待つこと。   
     1行目: USB MSD
     2行目: File RW start !
    


<回路図>(→ PDFファイル



<外観>PIC32MZ評価ボード(→購入方法)を使った実験品の外観です。


<動作結果> (→ 動画:1080pのHD動画を見ることができます。)

キャラクタ液晶表示

①起動後  ②USBメモリ挿入検出後  ③ファイル新規作成
  or 既存ファイルオープン後
 
 
④ファイルへの書き込み完了
    &ファイルクローズ後   
⑤ファイル内容を読み出して表示(1行目)
  & 挿入検出回数表示(2行目)
     (1回目の挿入検出後)
⑥ファイル内容を読み出して表示(1行目)
  & 挿入検出回数表示(2行目)
   (2回目の挿入検出後)
 
 
⑦ ファイル内容を読み出して表示(1行目)
  & 挿入検出回数表示(2行目)
   (3回目の挿入検出後)
     
     


■ USBメモリのファイルの表示
  PCのWindowsエクスプローラから USBメモリ内のLongFileName.txtファイルの内容をエディタで表示したものです。 尚、実験では
 PIC起動前のUSBメモリはフォーマット済みでファイルは何もない状態にしています。

1回目のUSBメモリ挿入検出後
(上記の液晶表示⑤の場合)
3回目のUSBメモリ挿入検出後
(上記の液晶表示⑦の場合)
 

 
 ■ 備考
   ・ プログラムを書き込んだ後 一度PICの電源をOFFにしなと動作しないPICがありました。
   ・ USBメモリのアタッチ検出が 若干不安定な時がありました。 ノイズが原因?



<解説>記載してある内容は要点だけです。 詳細はプロジェクトファイルを精読願います。
        (以下は、Harmony v2.04 をもとに作成しています。最新のバージョンとは異なる点があるかもしれませんので注意してください。)

 MHC設定
  Options

項目 ①Configuration設定
Device & Project Configuration
  > PIC32MZ2048 Device Configuration
②Heap Size設定
Device & Project Configuration
 >Project Configuration >XC32(Global Opton)
 >XC32-ld >General
③USB Library設定
Harmony Framework Configuration
 > USB Library
MHC  
備考 デフォルトからの変更要領:
 FPLLIDIV: DIV3
 FPLLICLK: PLL_POSC
 POSCMOD: EC

システムクロック周波数: 200MHz
外部
 主発振器: 24MHz

#pragma config FNOSC = SPLL
#pragma config POSCMOD = EC
#pragma config FPLLIDIV = DIV_3
#pragma config FPLLICLK = PLL_POSC
#pragma config FPLLMULT = MUL_50
#pragma config FPLLODIV = DIV_2
デフォルトからの変更要領:
 Heap Size(byte): 500



デフォルトからの変更要領:
 □USE USB Stack? チェック追加
 □USB Host チェック追加
 □USE MSD Host Client Driver チェック追加
項目 ④File System
Harmony Framework Configuration
 >System Services
⑤Timer
Harmony Framework Configuration
 > Driver
 
MHC  
備考 デフォルトからの変更要領:
 □USE File System Services? チェック追加
 □USE File System Auto Mount Feature?
                    チェック追加 
 Mdedia Type: SYS_FS_MEDIA_TYPE_MSD
デフォルトからの変更要領: 
 Timer Module ID: TMR_ID_2
 Interrupt Priority: INT_PRIORITY_LEVEL4
 

 Pin Settings
   特に設定なし


■キャラクタ液晶表示ライブラリの追加要領
  キャラクタ液晶表示のライブラリ 1lcd_lib_XC32.h と 1lcd_lib_XC32.cを main.cと同じフォルダにコピーして、プロジェクトに追加します。
                                              (使い方 → キャラクタ液晶表示方法 参照
  プロジェクトへの追加は以下のようにします。


■app.h を以下の要領で変更します。

 ①列挙型変数 APP_STATES の定義を以下のように変更します。
typedef enum
{
  APP_STATE_BUS_ENABLE = 0,
  PP_STATE_WAIT_FOR_BUS_ENABLE_COMPLETE,
  APP_STATE_WAIT_FOR_DEVICE_ATTACH,
  APP_STATE_DEVICE_CONNECTED,
  PP_STATE_MOUNT_DISK,
  APP_STATE_UNMOUNT_DISK,
  PP_STATE_OPEN_FILE,
  PP_STATE_WRITE_TO_FILE,
  APP_STATE_CLOSE_FILE,
  APP_STATE_IDLE,
  PP_STATE_ERROR
} APP_STATES;



 ②構造体 APP_DATAのメンバーを以下のように変更します。
typedef struct
{
  /* The application's current state */
  /* SYS_FS File handle for 1st file */
  SYS_FS_HANDLE fileHandle;
  /* Application's current state */
  APP_STATES state;
  /* Application data buffer */
  uint8_t data[1024];
  /* Number of bytes written */
  uint32_t nBytesWritten;
  /* Number of bytes read */
  uint32_t nBytesRead;
  bool deviceIsConnected;
} APP_DATA;


以下、app.h



■ app.cに、青字部の修正、追加をします。 

  ①インクルードファイルを追加します。stdio.hがないと、sprintf( )がコンパイラのバージョンによってコンパイルで警告となることが
  あります。  
  #include "stdio.h"
  #include "1lcd_lib_XC32.h"



  ②液晶表示のバッファー、ファイル読み出しに係るファッバー、またファイルに下記込む文字列などを定義します。
  char Buf[32]; //液晶表示バッファー
  char tempBuf[1];
  char tempBuf2[512]; //ファイル読み書きバッファ
  char tempBuf3[512]; //ファイル読み書きバッファ
  int eof; //ファイルのEOF
  int ix = 0;
  int Num = 0; //USBメモリ 読み出し回数
  char myData[] = "How are you? "; //ファイルへの書き込み文字列



  ③NOPによる遅延関数delay_us(), delay_ms()を定義します。
  void delay_us(volatile unsigned int usec) //1μsec遅延
  {
    volatile int count;
    count = (int)(delay_Clock/20000000)*usec;
    ……
    ……



  ④液晶を初期化して、起動直後の文字列表示を行います。
  lcd_init(); // LCD初期化
  lcd_cmd(0b00001100); // カーソル:OFF ブリンク:OFF
  lcd_cmd(0x80); //1目の先頭へ
  sprintf(Buf,"USB MSD ");//
  lcd_str(Buf); //液晶表示
  ……
  ……

  ⑤以下の関数はUSBのホストレーヤのイベントをチェックするコールバック関数です。 関数 USB_HOST_EventHandlerSet()により
    このコールバック関数は、APP_Tasks()の中で設定されています。 ホストレーヤに問題が発生した場合に、引数 eventに情報が
   送られてきます。  尚、サンプルプログラムではイベントに対する処理は 記述していません。
   (例)USB_HOST_EVENT_DEVICE_UNSUPPORTED: 接続不可 or エラー発生
      USB_HOST_EVENT_HUB_TIER_LEVEL_EXCEEDED: 接続されているハブの数がオーバー
      USB_HOST_EVENT_PORT_OVERCURRENT_DETECTED: USB負荷電流過大
  USB_HOST_EVENT_RESPONSE APP_USBHostEventHandler (USB_HOST_EVENT event, void * eventData, uintptr_t context)
  {
    switch (event)
    {
      case USB_HOST_EVENT_DEVICE_UNSUPPORTED:
      break;

      default:
      break;
    }
    return(USB_HOST_EVENT_RESPONSE_NONE);
  }



  ⑥以下の関数はファイルシステムがマウントされたか否かのイベントをチェックするコールバック関数です。 関数
    SYS_FS_EventHandlerSet()によりこのコールバック関数は、APP_Tasks()の中で設定されています。

  void APP_SYSFSEventHandler(SYS_FS_EVENT event, void * eventData, uintptr_t context)
  {
    switch(event)
    {
      case SYS_FS_EVENT_MOUNT: //マウント検出の場合
        appData.deviceIsConnected = true;

        lcd_cmd(0x80); //1目の先頭へ
        sprintf(Buf,"USB Memory ");//
        lcd_str(Buf); //液晶表示
        lcd_cmd(0xC0); //2行目の先頭へ
        sprintf(Buf," has connected "); //
        lcd_str(Buf); // 開始メッセージ1行目表示

        delay_ms(1500);
        break;

      case SYS_FS_EVENT_UNMOUNT:
        appData.deviceIsConnected = false;

        break;

      default:
      break;
    }
  }



  ⑦ SYS_FS_EventHandlerSet()でファイルシステムマウントチェック用のコールバック関数APP_SYSFSEventHandler()を設定しています。
    また、USB_HOST_EventHandlerSet()でホストレーヤイベントチェック用コールバック関数APP_USBHostEventHandler()を設定しています。
    USB_HOST_BusEnable()でUSBホストバスを有効化しています。
  case APP_STATE_BUS_ENABLE:
    /* Set the event handler and enable the bus */
    SYS_FS_EventHandlerSet(APP_SYSFSEventHandler, (uintptr_t)NULL);//FSマウントチェック用コールバック関数を設定
    USB_HOST_EventHandlerSet(APP_USBHostEventHandler, 0);//ホストレーヤイベントチェック用コールバック関数を設定
    USB_HOST_BusEnable(0); //USBホストバス有効化
    appData.state = APP_STATE_WAIT_FOR_BUS_ENABLE_COMPLETE;
    break;



  ⑧USBホストバスが有効になるまで待ちます。
  case APP_STATE_WAIT_FOR_BUS_ENABLE_COMPLETE:
    if(USB_HOST_BusIsEnabled(0))
    {
      appData.state = APP_STATE_WAIT_FOR_DEVICE_ATTACH;
    }
    break;



  ⑨USBデバイスがアタッチされた場合、次のステートに移行します。
  case APP_STATE_WAIT_FOR_DEVICE_ATTACH: //USBメモリのアタッチを待つ
    
    /* Wait for device attach. The state machine will move
    * to the next state when the attach event
    * is received. */
    if(appData.deviceIsConnected) // //USBデバイスがアタッチされた場合
    {
      appData.state = APP_STATE_DEVICE_CONNECTED;
    }
    break;



  ⑩ファイルオープンステートに移行します。
  case APP_STATE_DEVICE_CONNECTED:
    /* Device was connected. We can try mounting the disk */
    appData.state = APP_STATE_OPEN_FILE;
    break;



  ⑪SYS_FS_FileOpen()でファイル名LongFileName.txtのファイルを開きます。ファイルが存在しない場合は新規に作成します。
   成功した場合は、液晶の1行目に"File Opened !!"を2行目にLongFileName.txt"を表示します。
  case APP_STATE_OPEN_FILE:
    /* Try opening the file for append */
    appData.fileHandle = SYS_FS_FileOpen("/mnt/myDrive1/LongFileName.txt", (SYS_FS_FILE_OPEN_APPEND_PLUS));
    if(appData.fileHandle == SYS_FS_HANDLE_INVALID)
    {
      /* Could not open the file. Error out*/
      appData.state = APP_STATE_ERROR;
    }
    else
    {
      /* File opened successfully. Write to file */
      lcd_cmd(0x80); //1目の先頭へ
      sprintf(Buf,"FileOpened !! ");//
      lcd_str(Buf); //液晶表示
      lcd_cmd(0xC0); //2行目の先頭へ
      sprintf(Buf,"LongFileName.txt"); //
      lcd_str(Buf); // 開始メッセージ1行目表示

      delay_ms(1500);
      appData.state = APP_STATE_WRITE_TO_FILE;
    }
    break;



  ⑫SYS_FS_FileWrite()で"How are you?  "をファイルに書き込みが成功したら次のステートに移行します。
  case APP_STATE_WRITE_TO_FILE:
    /* Try writing to the file */
    if (SYS_FS_FileWrite( appData.fileHandle, myData, sizeof(myData) ) == -1)
    {
      /* Write was not successful. Close the file
      * and error out.*/
      SYS_FS_FileClose(appData.fileHandle);
      appData.state = APP_STATE_ERROR;
    }
    else
    {
      /* We are done writing. Close the file */
      appData.state = APP_STATE_CLOSE_FILE;
    }
    break;

  

  ⑬ファイルを閉じて、液晶の1行目に"Writed"を、2行目に"and Closed"を表示します。 その後ファイルを読み取り専用モードで開き
   1バイトづつファイルエンドまで読み出します。 この読み出したファイル先頭16バイトを液晶の1行目に表示します。2行目には
   何回目(XX回目)の呼び出しであるのか"Number=XX"と云った形式で表示します。その後アイドルステートに移行します。
  case APP_STATE_CLOSE_FILE:
    /* Close the file */
    SYS_FS_FileClose(appData.fileHandle);

    lcd_cmd(0x80); //1目の先頭へ
    sprintf(Buf,"Writed ");//
    lcd_str(Buf); //液晶表示
    lcd_cmd(0xC0); //2行目の先頭へ
    sprintf(Buf," and Closed "); //
    lcd_str(Buf); // 開始メッセージ1行目表示

    delay_ms(1500);   

    SYS_FS_HANDLE fileHandle2 = SYS_FS_FileOpen("/mnt/myDrive1/LongFileName.txt", (SYS_FS_FILE_OPEN_READ));

    do
    {
      SYS_FS_FileRead(fileHandle2, tempBuf, 1); //1バイトづつ読出し
      eof = SYS_FS_FileEOF(fileHandle2); //EOFチェック
      if(eof != true)
      {
      tempBuf2[ix] = tempBuf[0];
      ix++;
      }
    }while(eof != true); //ファイルエンドでない場合は、繰り返し読む

    SYS_FS_FileClose(fileHandle2); //ファイルを閉じる

    Num++;

    lcd_cmd(0x80); //1目の先頭へ
    sprintf(tempBuf3,"%s ",tempBuf2);
    lcd_str(tempBuf3);
    lcd_cmd(0xC0); //2目の先頭へ
    sprintf(Buf,"Number=%d ",Num); //液晶に"Write OK !" を表示
    lcd_str(Buf);

    /* The test was successful. Lets idle. */
    appData.state = APP_STATE_IDLE;
    break;


  ⑭ このアイドルステートでは、USBデバイスが抜かれた場合には再アタッチを待つステートに移行するようにします。
  case APP_STATE_IDLE:
    /* The application comes here when the demo has completed
    * successfully. Provide LED indication. Wait for device detach
    * and if detached, wait for attach. */

    if(appData.deviceIsConnected == false) //USBメモリを抜くと 挿入検出待ちモードに移行する。
    {
      appData.state = APP_STATE_WAIT_FOR_DEVICE_ATTACH;
    }
    break;




以下、app.c