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を点灯し、クローズしたら消灯すること。
<外観> PIC32MZ評価ボード(→購入方法)を使った実験品の外観です。
<動作結果>(→ 動画 : 1080pのHD動画を見ることができます。)
実験で使用したUSBメモリ内の ディレクトリとファイル構成 (PCのコマンドプロンプトによる表示) |
![]() |
|
検索結果 (Tera Termによる表示) |
![]() |
|
全ファイル数表示 |
||
プルパス付ファイル名 表示の例 |
ルート ディレクトリのファイル |
|
サブ(子) ディレクトリのファイル |
||
サブ(孫) ディレクトリのファイル |
||
<解説>記載してある内容は要点だけです。 詳細はプロジェクトファイルを精読願います。
(以下は、Harmony v2.04 をもとに作成しています。最新のバージョンは異なることがあるかもしれませんので注意してください。)
■MHCの設定
■Options
■ Pin Settins
■キャラクタ液晶表示ライブラリの追加要領
キャラクタ液晶表示のライブラリ 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になったことでおこなっています。
|
⑪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) //全ファイル詳細調査関数 //再帰関数 |
⑫APP_SYSFSEventHandler( )とAPP_USBHostEventHandler( )はUSBマウントに係るコールバック関数で HaarmonyではUSB接続の状態を
チェックする際に必ず使用されます。 USB接続が完了するとSYS_FS_EVENT eventがSYS_FS_EVENT_MOUNTになります。
USB_Connection_Tasks();は 無限循環タスクのAPP_Tasks ( )から常に呼ばれるようにしておき、USBディスクがタスク実行中に
抜かれた場合もプログラムがハングしないようにしておきます。
|
(13) DISK_Tasks( )関数は無限循環タスクのAPP_Tasks ( )から常に呼ばれています。 USBの接続状態を監視して USBディスクが
挿入されたらUSBの初期化などをおこないます。
PICが起動したり、USBディスクが挿入された場合 DISK_TraverseAllFiles( )関数で ルートディレクトリ内のファイルを検索します。
検索した結果として、ファイルの総数 やファイルテーブルFilesTable[ ]から読み出したファイル名をキャラクタ液晶に表示します。
|
(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