USBメモリ 
 全ディレクトリファイル検索

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


 USBメモリの全ディレクトリにあるファイルの数 及びファイル名を検索する例を紹介します。 全ディレクトリとはルートディレクトリだけでなく
ルートディレクトリの下で多層の階層構造になっているすべてのディレクトリを指します。 再帰関数(注)が使われています。
 本サンプルは 本例は下記のフォルダにあるHarmonyのサンプルプログラムuniversal_audio_decodersの中で使われていたプログラムの
一部を参考にして作成したものです。

"C:\microchip\harmony\v2_06\apps\audio\universal_audio_decoders"


   (注)再帰関数(Recursive Function) : 関数f の定義の中で、自分自身f を呼び出している関数




<仕様>
 ・USBメモリの全ディレクトリにあるファイルの総数をカウントしてキャラクタ液晶に表示する。
 ・取り扱うファイル、ディレクトリについては以下の制約を設けること。
   ➀取り扱うファイル最大数: 60
   ➁取り扱うディレクトリの最大数: 10
   ③取り扱うディレクトリの最大深さ: 5
 ・USBメモリのルートディレクトリにあるファイルの名称をすべて表示する。
   ファイル名はディレクトリ名を含めたフルパスで表示のこと
 ・ファイル名の表示はキャラクタ液晶 及び TeraTerm(PCとの接続はUART)とする。
 ・キャラクタ液晶への ファイル数 及び各ファイル名表示は 2~3sec の間隔をあけて順次1項目づつ表示のこと 
 ・ファイルは押しボタンスイッチ(SW2)をクリックすることにより 開いているファイルを閉じて次のファイルをオープンするかたちで
  スキップできること。 
   またスキップ後、新たにオープンしたファイル名を 閉じたファイル名の代わりにキャラクタ液晶に表示のこと。
 ・また、他の押しボタンスイッチ(SW1)をクリックすることにより ファイルを オープン/クローズできること。
   尚、ファイルがオープンしたらLEDを点灯し、クローズしたら消灯すること。



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


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





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

実験で使用したUSBメモリ内の
ディレクトリとファイル構成
(PCのコマンドプロンプトによる表示)
検索結果
(Tera Termによる表示)
全ファイル数表示
プルパス付ファイル名
表示の例     
ルート
ディレクトリのファイル
サブ(子)
ディレクトリのファイル
サブ(孫)
ディレクトリのファイル




<解説>記載してある内容は要点だけです。 詳細はプロジェクトファイルを精読願います。
  (以下は、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): 512
デフォルトからの変更要領:
 □USE USB Stack? チェック追加
 □USB Host チェック追加
 □USE MSD Host Client Driver チェック追加
項目 ④File System
Harmony Framework Configuration
 >System Services  
⑤USART設定
Harmony Framework Configuration > Drivers
 > USART
⑥Timer System Service
Harmony Framework Configuration
 >System Services  
MHC
備考 デフォルトからの変更要領:
 □USE File System Services? チェック追加
 □USE File System Auto Mount Feature?
                   チェック追加
 Mdedia Type: SYS_FS_MEDIA_TYPE_MSD

(デフォルトはSYS_FS_MEDIA_TYPE_SD_CARDなので注意)
デフォルトからの変更要領:
 Use USART Driver? チェックを入れる
 USART Module ID: USART_ID_6
デフォルトからの変更要領:
 なし
(本項のシステムタイマはデフォルトでチェックが入っている。)
項目 ⑦Timer
Harmony Framework Configuration > Drivers
 > Timer
   
MHC    
備考 デフォルトからの変更要領:
 なし
(本項のタイマはシステムタイマ用のタイマでデフォルトでチェックが入っている。) 
   



■ Pin Settins

項目  ⑧ポート設定
MHC  
備考 デフォルトからの変更要領:
 RG15/Function: GPIO_OUT
        (LED用出力ポート設定)
 RC2/Function: U6TX
        (UART6のTX設定)
 RG9/Function: U6RX
        (UART6のRX設定) 
 RB1/Function: GPIO_IN、
          PULL UP(抵抗)チェック追加
        (SW2用入力ポート設定) 
 RB0/Function: GPIO_IN、
          PULL UP(抵抗)チェック追加
        (SW1用入力ポート設定) 


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


■app.h に青字部分を追加します。

➀取り扱うディレクトリの最大深さを5に、取り扱うディレクトリの最大数を10に、取り扱うファイル最大数を80に設定します。
  #define TRAVERSE_DEPTH 5
  #define DISK_MAX_DIRS 10
  #define DISK_MAX_FILES 80



➁ファイルを最大64バイトのフルパス付ファイルで扱います。 このフルパスのファイルと Harmonyのファイルが標準で持っている
 ファイルの特性を保持しているSYS_FS_FSTAT構造体をメンバーにもつDISK_FILE_NODE構造体を宣言します。 プログラムの
 なかではこのDISK_FILE_NODE構造体のインスタンスをファイルとして扱います。
  typedef struct{
    SYS_FS_FSTAT fstat;
    char path[64];
  }DISK_FILE_NODE;

  typedef struct{
    char path[64]; // at most 64 bytes long
  }DISK_FILE_PATH;



以下、 app.h


■ app.c に青字部を追加します。

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



➁所要の変数、ファイルハンドル、構造体のインスタンスなどの宣言 及び定義を行います。
  int delay_Clock = 200000000; //システムクロック:200MHz
  char Buf[32];
  char Buf2[32]; //液晶上段、下段のバッファを分ける //下段が16文字を超えると上段の表示に影響がある
  int BtnEvent[2];

  int totalFiles;
  int ysPlay;

  SYS_FS_HANDLE ys_fileHandle;
  SYS_FS_HANDLE ys_dirHandle;
  SYS_FS_FSTAT ys_dirstat;

  static DISK_FILE_NODE rootNode;
  static DISK_FILE_PATH FilesTable[DISK_MAX_FILES];
  char str_FileName[64]; //ファイル名
  int Num_File = 1;


③USBの接続状態に係る列挙名、USBメモリ読み込み状態に係る列挙名、ボタンスイッチの状態に係る列挙名などを
  定義します。
  enum{ //USBの状態に係る列挙名
      USB_STATE_OPEN_HOST_LAYER,
      USB_STATE_WAIT_FOR_HOST_ENABLE,
      USB_STATE_DEVICE_CONNECTED,
      USB_STATE_WAIT_FOR_DEVICE_ATTACH,
      USB_STATE_UNMOUNT_DISK
     }UsbState;

  enum{ //USBディスクの状態に係る列挙名
      DISK_STATE_INIT,
      DISK_STATE_SCAN_FINISHED,
      DISK_STATE_OPEN_FIRST_FILE,
      DISK_STATE_SCANNING,
      DISK_STATE_RUNNING,
      DISK_STATE_REMOVED,
      DISK_STATE_NO_FILES,
     }DiskState;


  enum {
      APP_OPE_PAUSE_REPLAY = 1,
      APP_OPE_SKIP,
     }AppBtn;

  enum
   { 
    PLAYER_CMD_STOP,
    PLAYER_CMD_PLAY,
    PLAYER_CMD_NEXT_FILE,
   }PlayerCommand;


④関数のプロトタイプ宣言をおこないます。
  //関数のプロトタイプ宣言
  void DISK_NextTrack(void);
  void DISK_CloseTruck (SYS_FS_HANDLE fileHandle);
  void DISK_OpenTrack ( const char *fname );
  bool ys_FileType83(char *myFileName);
  SYS_FS_RESULT DISK_FS_ReadDirFlat(char *lfname, uint8_t *dir_count, SYS_FS_FSTAT * dir_table, bool isRoot);
  void DISK_TraverseAllFiles(DISK_FILE_NODE node, bool isRoot,uint8_t depth);
  void APP_SYSFSEventHandler(SYS_FS_EVENT event, void * eventData, uintptr_t context);
  USB_HOST_EVENT_RESPONSE APP_USBHostEventHandler (USB_HOST_EVENT event, void * eventData, uintptr_t context);
  void USB_Connection_Tasks(void);
  void DISK_Tasks(void);
  void APP_PlayerTask(void);
  bool APP_PlayerCommand (int cmd);
  void APP_OnButtonEvent(uint8_t button, bool bButtonClosed, int32_t repeatCount);
  void APP_BtnTask(void);



⑤delay_us( )、delay_ms( ) という NOPを使った 1μsec 、1msec単位の遅延関数をつくります。HarmonyではSYS_Tasks ( )の中で
  長い時間の遅延をつかうとその遅延時間だけタスクがそこで止まってしまいますので注意して使う必要があります。
  void delay_us(volatile unsigned int usec) //1μsec遅延
  {
    volatile int count;

    count = (int)(delay_Clock/20000000)*usec;

  ……
  ……


⑥UARTでTeraTermに文字を送信する関数です。
  void Write_Byte(char chr) //1バイト送信関数
  {
    PLIB_USART_TransmitterByteSend(USART_ID_6, chr); //送信バッファーに1バイト書込み・送信
    // U6TXREG = chr; ////送信バッファーに1バイト書込み・送信

    while (!PLIB_USART_TransmitterIsEmpty(USART_ID_6)); //送信バッファーが空になるまで待つ

  ……
  ……


⑦ファイルテーブルFilesTable[ ]の管理番号をインクリメントして次のファイルをオープンする関数です
  void DISK_NextTrack(void) //次のファイル曲へ
  {
    Num_File++;
    if(Num_File > (totalFiles -1))Num_File = 1;
    DISK_OpenTrack(FilesTable[Num_File].path); //ファイルオープン
  }


⑧ファイルをクローズして、LEDを消灯する関数です。
  void DISK_CloseTruck (SYS_FS_HANDLE fileHandle)
  {
    SYS_FS_FileClose ( fileHandle );
    ysPlay = 0;
    LATGbits.LATG15 = 0; //LED1 消灯
  }


⑨ファイルをオープンして LEDを点灯後、ファイル名を取得してキャラクタ液晶に表示しています。
  void DISK_OpenTrack ( const char *fname )
  {
    char tempBuf[16];

    ys_fileHandle = SYS_FS_FileOpen(fname, (SYS_FS_FILE_OPEN_READ_PLUS)); //読み込みモードで、ファイルオープン、
    ysPlay = 1;
    LATGbits.LATG15 = 1; //LED1 点灯



    SYS_FS_FileNameGet(ys_fileHandle, str_FileName, 32 ); //ファイル名取得

    lcd_cmd(0x80); //1目の先頭へ
    sprintf(Buf,"%s ", str_FileName);//
    lcd_str(Buf); //液晶表示
    lcd_cmd(0xC0); //2行目の先頭へ

    sprintf(Buf,"%s ", str_FileName + 16);//str_FileName のポインタを16バイト進める
    lcd_str(Buf); // 開始メッセージ1行目表示

  }



⑩ファイル名からファイルが8.3形式のファイルか ロングファイル名のファイルかを判別する関数です。
 Harmonyの場合 ロングファイル名のファイル名を8.3形式で表示すると先頭から7番目が ~ (0x7E、チルダ)になります。
 このことを利用して判別しています。
  bool ys_FileType83(char *myFileName) //8.3形式ファイルチェック //8.3形式ならtrue
  {
    bool Flag;
    
    if(myFileName[6] != 0x7E) Flag = true; //~が含まれない → 8.3形式ファイル名
    else Flag = false; //~が含まれる → ロングファイル名 //例:MOONRI~1.mp3

    return Flag;
  }




⑪ファイルの数とカウント、及びファイル名のリスト作成をおこなっています。SYS_FS_DirRead()関数でディレクトリ・ファイルのステータスを
 読み込みます。 読み込みは順次1つの内部のディレクトリとファイルが1つづつ読み込まれます。 属性がディレクトリかファイル判定、
 して、処理を分けます。 ファイルの場合は更に 8.3形式のファイルか ロングファイル名ファイルかを分別します。
  ファイルテーブルFilesTable[ ].pathにフルパス付のファイル名を読み出していきます。
 全部読みだしたか否かの判定は (ys_dirstat.lfname[0] == '\0') && (ys_dirstat.fname[0] == '\0') すなわち両方のファイル名形式が
 共にNULLになったことでおこなっています。

  


//ファイル名リスト作成
SYS_FS_RESULT DISK_FS_ReadDirFlat(char *lfname, uint8_t *dir_count, SYS_FS_FSTAT * dir_table, bool isRoot)
{
SYS_FS_RESULT ret;
bool Flag83;
char longFileName[300];

ys_dirHandle = SYS_FS_DirOpen(lfname); //lfname: フルパスのディレクトリ名 //ルートディレクトリは0

if(ys_dirHandle == SYS_FS_HANDLE_INVALID)
{
return SYS_FS_RES_FAILURE;
}
*dir_count = 0;

//このdo ループで トータルのファイル数をカウントする。
do
{
if(totalFiles < DISK_MAX_FILES) //DISK_MAX_FILES = 80
{
ys_dirstat.lfname = longFileName;
ys_dirstat.lfsize = 300;

ret = SYS_FS_DirRead(ys_dirHandle,&ys_dirstat); //ディレクトリ・ファイルのステータスを読み込む
//読み込度に 順次異なるディレクトリorファイルを読み込む
//諸元は構造体SYS_FS_FSTATのオブジェクトys_dirstatによる
if ((ys_dirstat.lfname[0] == '\0') && (ys_dirstat.fname[0] == '\0')) //ディレクトリの終端に達した場合、ここで抜ける
{
break;
}
if(ret!= SYS_FS_RES_FAILURE) //読み込み成功なら
{

if(ys_dirstat.fattrib != SYS_FS_ATTR_DIR) // 属性がディレクトリでない場合 //SYS_FS_ATTR_DIR: ディレクトリ
{
Flag83 = ys_FileType83(ys_dirstat.fname);
// 8.3形式ファイルの場合
if(Flag83 == true)
{
strcpy(FilesTable[totalFiles].path, lfname); //ファイル名fnameを FilesTable[totalFiles].pathにコピー

if(!isRoot) //ルートでない場合
{
strcat(FilesTable[totalFiles].path, "/"); //パスの末尾に / を追加
}
strcat(FilesTable[totalFiles].path, ys_dirstat.fname);

(totalFiles)++;


}
// ロングファイル名のファイルの場合
else if(Flag83 != true)
{
strcpy(FilesTable[totalFiles].path, lfname); //ファイル名fnameを FilesTable[totalFiles].pathにコピー

if(!isRoot) //ルートでない場合
{
strcat(FilesTable[totalFiles].path, "/"); //パスの末尾に / を追加
}
strcat(FilesTable[totalFiles].path, ys_dirstat.lfname);

(totalFiles)++;

}

}
// 属性がディレクトリの場合
else if(ys_dirstat.fattrib == SYS_FS_ATTR_DIR && ys_dirstat.fname[0] != '.') // Skip ".\" and "..\" directories
{

if(*dir_count < DISK_MAX_DIRS)
{
dir_table[*dir_count]= ys_dirstat;
(*dir_count)++;
}
}

}
else
{
ret = SYS_FS_RES_FAILURE;
break;
}
}
}while(ret==SYS_FS_RES_SUCCESS);

SYS_FS_DirClose(ys_dirHandle);
return ret;

}


⑪DISK_TraverseAllFiles( )関数は、再帰関数です。 下から4行目にDISK_TraverseAllFiles( )があらわれて 自分自身を
 呼び出しています。
  上の層のディレクトリから順次検索してゆきます。 DISK_FS_ReadDirFlat()関数で 1つのディレクトリの
 下にあるファイル数、ファイル名を探索します。 検索は1つのディレクトリのサブディレクトリの1つ、更にその下のサブ
 ディレクトリ…… といった順番に検索されそのディレクトリにサブディレクトリが無くなると 1つ上の層のディレクトリ検索に
 移行するといった検索順序となっています。
   for(i = 0; i < totalDir; i++){ …… の部分で この層にある各ディレクトリを管理して、 この層の全部のディレクトリを順次
  自らのDISK_TraverseAllFiles( )関数を呼び出しています。 

 
void DISK_TraverseAllFiles(DISK_FILE_NODE node, bool isRoot,uint8_t depth)  //全ファイル詳細調査関数    //再帰関数
{
if(depth > TRAVERSE_DEPTH) //ディレクトリの最大深さ //5
{
return;
}
int i;
SYS_FS_RESULT ret;

uint8_t totalDir = 0;
SYS_FS_FSTAT dirTable[DISK_MAX_DIRS];

ret = DISK_FS_ReadDirFlat(node.path, &totalDir, dirTable, isRoot); //

if(ret == SYS_FS_RES_FAILURE)
{
return;
}

DISK_FILE_NODE child_node;

for(i = 0; i < totalDir; i++)
{

child_node.fstat = dirTable[i];

strcpy(child_node.path, node.path);
if(!isRoot)
{
strcat(child_node.path, "/");
}

strcat(child_node.path, child_node.fstat.fname);

//-----内部のここで呼ばれている
DISK_TraverseAllFiles(child_node, false, depth+1); //再帰

}

return;
}



⑫APP_SYSFSEventHandler( )とAPP_USBHostEventHandler( )はUSBマウントに係るコールバック関数で HaarmonyではUSB接続の状態を
 チェックする際に必ず使用されます。 USB接続が完了するとSYS_FS_EVENT eventがSYS_FS_EVENT_MOUNTになります。
  USB_Connection_Tasks();は 無限循環タスクのAPP_Tasks ( )から常に呼ばれるようにしておき、USBディスクがタスク実行中に
 抜かれた場合もプログラムがハングしないようにしておきます。 

        

 
void APP_SYSFSEventHandler(SYS_FS_EVENT event, void * eventData, uintptr_t context) //USBマウントに係るコールバック関数 { switch(event) { case SYS_FS_EVENT_MOUNT: //マウント完了の場合 UsbState = USB_STATE_DEVICE_CONNECTED; //USBメモリ接続完了 break; case SYS_FS_EVENT_UNMOUNT: //未マウントの場合 UsbState = USB_STATE_UNMOUNT_DISK; UsbState = USB_STATE_WAIT_FOR_DEVICE_ATTACH; ys_fileHandle = SYS_FS_HANDLE_INVALID; totalFiles = 0; break; default: break; } } 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)USB_HOST_EVENT_RESPONSE_NONE; } void USB_Connection_Tasks(void) //USB接続状態 { switch(UsbState) { case USB_STATE_OPEN_HOST_LAYER: /* Set the event handler and enable the bus */ SYS_FS_EventHandlerSet(APP_SYSFSEventHandler, (uintptr_t)NULL); //コールバック関数APP_SYSFSEventHandler()セット USB_HOST_EventHandlerSet(APP_USBHostEventHandler, 0); //コールバック関数APP_USBHostEventHandler()セット // enable, open, bus 0 USB_HOST_RESULT ret = USB_HOST_BusEnable(0); if(ret == USB_HOST_RESULT_FALSE) return; UsbState = USB_STATE_WAIT_FOR_HOST_ENABLE; break; case USB_STATE_WAIT_FOR_HOST_ENABLE: //ホストの準備完了待ち /* Check if the host operation has been enabled */ if(USB_HOST_BusIsEnabled(0)) { /* This means host operation is enabled. We can * move on to the next state */ UsbState = USB_STATE_WAIT_FOR_DEVICE_ATTACH; } break; case USB_STATE_WAIT_FOR_DEVICE_ATTACH: //デバイスの接続待ち /* Wait for device attach. The state machine will move * to the next state when the attach event * is received. */ break; default: break; } }

    

(13)  DISK_Tasks( )関数は無限循環タスクのAPP_Tasks ( )から常に呼ばれています。 USBの接続状態を監視して USBディスクが
   挿入されたらUSBの初期化などをおこないます。
    PICが起動したり、USBディスクが挿入された場合 DISK_TraverseAllFiles( )関数で ルートディレクトリ内のファイルを検索します。
   検索した結果として、ファイルの総数 やファイルテーブルFilesTable[ ]から読み出したファイル名をキャラクタ液晶に表示します。

          

   
void DISK_Tasks(void) { int i; USB_Connection_Tasks(); if(UsbState == USB_STATE_DEVICE_CONNECTED) //USBデバイス接続状態 { //ディスクが抜かれていた場合 → ディスクをスキャンしてリスト作成 if (DiskState == DISK_STATE_REMOVED ) //ファイルなし //ファイルシステムアンマウント //ディスク抜き出し状態 { DiskState = DISK_STATE_INIT; //ディスクをスキャンしてリスト作成 } } else // { if ( DiskState != DISK_STATE_REMOVED ) //ディスク抜き出し状態ではない状態 { DiskState = DISK_STATE_REMOVED; } } switch (DiskState) { //PIC起動後 最初に呼ばれる  --> ディスクの全スキャン (ファイルの フルパス付きファイル名リストテーブルを作成する) case DISK_STATE_INIT: DiskState = DISK_STATE_SCANNING; break; case DISK_STATE_SCANNING: //スキャン実行 DISK_TraverseAllFiles(rootNode); //ルートディレクトリの全ファイルを調査する if(totalFiles == 0) { // No File DiskState = DISK_STATE_NO_FILES; } else { DiskState = DISK_STATE_SCAN_FINISHED; lcd_cmd(0x80); //1目の先頭へ sprintf(Buf,"TotalFile=%d ", (totalFiles - 1)); //FilesTable[0].pathは、System Volume Information lcd_str(Buf); // 開始メッセージ1行目表示 lcd_cmd(0xC0); //2行目の先頭へ sprintf(Buf," "); // lcd_str(Buf); // 開始メッセージ1行目表示 delay_ms(3000); //全ファイル表示 for キャラクタ液晶 & UART for (i = 1; i < totalFiles; i++ ) //FilesTable[0].pathはSystem Volume Information { lcd_cmd(0x80); //1目の先頭へ sprintf(Buf,"%s ", FilesTable[i].path); // lcd_str(Buf); // 開始メッセージ1行目表示 WriteString(Buf); //UARTへ送信 WriteString("\r\n"); //改行 lcd_cmd(0xC0); //2行目の先頭へ sprintf(Buf2,"%s ", FilesTable[i].path + 16); //FilesTable[i]のポインタを16バイト進める lcd_str(Buf2); // 開始メッセージ1行目表示 delay_ms(2000); } } break; case DISK_STATE_SCAN_FINISHED: //スキャン終了 DiskState = DISK_STATE_OPEN_FIRST_FILE; break; case DISK_STATE_OPEN_FIRST_FILE: //最初に呼ばれるファイル Num_File = 1; //ファイルテーブル No = 1 を設定 //No = 0 にはファイルではないもの(System Volume Information)が入っている DISK_OpenTrack(FilesTable[Num_File].path); //ファイルオープン DiskState = DISK_STATE_RUNNING; break; case DISK_STATE_RUNNING: //ファイルを繰り返し実行 break; default: break; } }



(14) ボタンスイッチからの信号により ファイルオープン、クローズ、及びスキップする操作に係る関数です。

  bool APP_PlayerCommand (int cmd)
  {
    switch (cmd)
    {
      case PLAYER_CMD_STOP:
         DISK_CloseTruck(ys_fileHandle);
      break;
  ......
  ......


以下、app.c




■ system_interrupt.c に青字部分を追加します。

➀ ファイルオープン、クローズ、スキップを操作するボタンスイッチのイベントフラグの変数です。
  extern int BtnEvent[2];


➁ 上記ボタンスイッチがクリックされたことを検出しています。
  //Button1 RB0
  if(PORTBbits.RB0 == 0) BtnEvent[0] = 1; //入力変化検出 //ファイル open/close

  //Button2 RB1
  else if(PORTBbits.RB1 == 0) BtnEvent[1] = 1; //入力変化検出 //次のファイル




以下、 system_interrupt.c