/******************************************************************************* * ソース名:UL024TF_Touch_MP3_Player_V1b.C * * <V1aからの変更・追加点> * @ アルバムジャケット・イメージの表示が出来る様にした。 *   本来は、ID3v2タグの、アートワーク情報を検索して描画する方法を *   とるべきだが、タグの規格で、JPEG及び、PNGにしか対応してない。 *   ともにデコーダソフトが必要になるので、今の私には難しすぎる。 *   簡易的に、SDに、MP3ファイルの他に、それに対応する曲番をファイル名 *   に入れたBMPイメージファイルを用意して、それを再生前に読み出し *   表示することにした。 * * A 上記に伴い、イメージ表示エリア(100x100dotにした)を確保するため *   Skipと、Homeボタンの位置を下に移動した。 *   ボタンの座標データも変更した。 * * ファイルシステム:Microchip MMD File System V1.2.1→ FAT16(FAT32互換) * * 2.4inch カラーQVGA液晶(aitendo): UL024TF * MPEG Audio Codec LSI: VS1011e(EXT CLK:12.288MHz) * * Condition: * 8MHz External X'tal Oscillator, 20x PLL (8MHzx20= 160MHz) * Fcy=160MHz/2=80MHz, Tcy=12.5ns * * CPU: PIC32MX340F256H * * N.Ishii 2016.9.5 **********************************************************************************/ #include /* PIC32 peripheral library */ #include "colorlcd_lib32k.h" #include #include // コンフィギュレーション設定 // CPU=80MHz Peri=80MHz,HS+PLL,Divider=1/2,PLL=x20,WDT=Off #pragma config FNOSC=PRIPLL, POSCMOD=HS, FPLLIDIV=DIV_2 #pragma config FPLLMUL=MUL_20, FPBDIV=DIV_1, FPLLODIV=DIV_1 #pragma config FWDTEN=OFF, ICESEL=ICS_PGx2 // 漢字メッセージ・テーブル const short MsgMusc[] = {0x8B,0xC8,0x96,0xBC,0x81,0x46,0x00,0x00}; // "曲名:" const short MsgBand[] = {0x89,0x89,0x91,0x74,0x81,0x46,0x00,0x00}; // "演奏:" const short MsgTitl[] = {0x8B,0xC8,0x96,0xBC,0x82,0xC8,0x82,0xB5,0x00,0x00}; // "曲名なし" const short MsgNone[] = {0x89,0x89,0x91,0x74,0x8E,0xD2,0x95,0x73,0x96,0xBE,0x00,0x00}; // "演奏者不明" /// タッチボタン座標データ short Button[4][2] = {{357,890}, // [0]:ボタン-1:SKIP {150,930}, // [1]:ボタン-2:HOME {181,766}, // [2]:ボタン-3:VOL+ {117,988} // [3]:ボタン-4:VOL- }; char str_N[]= "Nxxx"; // 曲番表示用 char str_T[]= "/xxx"; // トータル曲数表示用 char str_V[]= "Vxx"; // 音量表示用 char str_IMAGE[]= "IMAGExxx.BMP"; // ジャケットイメージ表示用 short Count; char MsgScrn[] = "X=xxxx Y=xxxx"; // 座標表示 /// ファイルシステム用変数 FSFILE *fptr; size_t result; SearchRec Record; SearchRec *rptr = &Record; char FileName[256][13]; // 2次元配列: 最大、256ファイルで、1ファイル→13文字(8文字+'.MP3'+NULL)構成 unsigned char DataBuf[1024]; // ファイルヘッダ部の読取りバッファ #define MaxTag 512 unsigned short ImageBuffer[10000]; // 16bitBMPデータバッファ:3600ワード= 100x100 /// VS1011用変数 short Volume, BASS; unsigned char Buffer[256]; /// グローバル変数 short FileNo; short FN_Top; short Total; short i; short SW; char ChgFlag; short NameSize, locate; short temp; /// プロトタイプ void DispFiles(void); void SetupScreen(void); short Getscreen(unsigned int chn); short CheckBtn(short x, short y); void TouchFunc(void); void ScrollDisp(void); void HomeFunc(void); void DispIDTag(void); void DispJacketImage(short Ndot, short Str_X, short Str_Y); void itostring(char digit, unsigned short data, char *buffer); /******* メインルーチン ***********/ int main(void) { //システム最適設定 SYSTEMConfigPerformance(80000000); mJTAGPortEnable(DEBUG_JTAGPORT_OFF); // JTAGを無効化 //I/O設定 AD1PCFG = 0xFFFF; // 全てデジタルに(タッチ用設定は、AN4(X-)、AN3(Y+)なので、0xFFE7) TRISE = 0x0000; // LCDデータバス(DB0-DB7)オール出力 TRISD = 0xFFA7; // LCD_CS,RESET:全て出力・デバッグLED:出力・SW2-5:入力、他未使用ピンは入力設定 TRISB = 0x4FC7; // VS1011_CS,RESET,DCS:出力、DREQ:入力・SD_CS:出力、LCD_WR,LCD_RS:出力、他未使用ピンは入力設定 // (今回は、VS1011は未使用だが、将来使う予定なので、その設定にしておく) TRISF = 0xFFFC; // 今回Fポートは未使用だが、RF0,RF1のみ出力設定にした LATDbits.LATD3 = 1; // デバッグLED消灯 /// 液晶表示器の初期化 Glcd_Init(); Glcd_Clear(BLACK); Glcd_Str(0, 0, "Touch MP3 Player V1b", CYAN, BLACK); delay_ms(1000); Glcd_Clear(BLACK); /// VS1011eの初期化 VS1011_Init(); // Initialize VS1011 Port+ SPI2(CKP=4M)+ SDI_TestMode+ CLK Doubler Volume = 0x08; // Set Volume = -8[dB] SetVolume(Volume, Volume); BASS = 0; // Set Bass Boost = off SetBassBoost(BASS, 15); delay_ms(1000); // Start VS1011e Sine Test // VS1011_SineTest(); // Start Sin_5kHz /// 変数初期化 Count= 0; SetupScreen(); // ADC設定変更 FileNo= 0; FN_Top= 1; SW= -1; ChgFlag= 0; /// カード挿入まで待ちファイル有無確認(CDの無いUL024TF使用のため、MDD_MediaDetect()の戻り値を強制的に、'1':挿入にしている。) while(fptr == 0){ Glcd_Clear(BLACK); // 画面消去 if(!MDD_MediaDetect()){ // カード挿入中か? Glcd_Str(0, 0, "Wait Card Insert!", CYAN, BLACK); // 挿入待ちメッセージ(今回はここは常に実行しない) } else{ // 常にカード挿入状態を実行する。 /*** ファイルオープンとファイル名表示 ***/ Glcd_Str(0, 1, "Find Card OK!", CYAN, BLACK); FSInit(); // FS初期化 /* 最初のファイルのサーチ */ result = FindFirst("*.MP3", ATTR_ARCHIVE, rptr); if(result != 0){ // ファイルありか? Glcd_Str(0, 1, "Find No File?", CYAN, BLACK); // ファイル無しメッセージ } else break; // ファイル発見で抜ける } delay_ms(500); // 0.5秒ごとに繰り返し } //// ファイルが発見できた場合の処理 DispFiles(); // ファイル名一覧表示処理 Glcd_Char(16, 11, 0xE9, GREEN, BLACK); // ボタン-1:Skip 表示(キャラクタ) Glcd_Char(25, 11, 0xE9, YELLOW, BLACK); // ボタン-2:Home 表示(キャラクタ) Glcd_Char(25, 0, 0xEB, CYAN, BLACK); // ボタン-1:VOL+ 表示(キャラクタ) Glcd_Char(25, 16, 0xED, CYAN, BLACK); // ボタン-1:VOL- 表示(キャラクタ) itostring(2, 64-Volume, str_V+1); Glcd_Str(21, 0, str_V, WHITE, BLACK); // 'Vxx'表示 Glcd_Char(0, 0, 0xE4, GREEN, BLACK); // '●'を選択初期位置(先頭)に表示); /// メイン ループ while(1) { /// 曲番表示 itostring(3, FileNo+1, str_N+1); Glcd_Str(16, 16, str_N, WHITE, BLACK); // 'Nxxx'表示 /// ジャケットイメージ表示(イメージファイルがない場合は無視して何もしない) // 表示前に、イメージ表示エリアを全クリア for(i= 0; i < 10000; i++) ImageBuffer[i]= BLACK; DispJacketImage(100, 305, 34); // イメージファイルを開く itostring(3, FileNo+1, str_IMAGE+5); // ファイル名の曲番部分を文字に変換して、全体の文字列と結合 fptr = FSfopen(str_IMAGE, "r"); // File Open (Read Mode) if(fptr != 0){ // ファイルオープンに成功の場合、以下を実行 FSfread(ImageBuffer,1,138,fptr); // ヘッダ情報部の、規定バイト数:138バイト分は、読み飛ばす FSfread(ImageBuffer,2,10000,fptr); // BMPデータを、バッファへ全て(10000ワード)転送 DispJacketImage(100, 305, 34); // ジャケットイメージを描画する FSfclose(fptr); // イメージ描画が終了したら、ファイルをクローズ } /// 選択されたファイルの再生処理 fptr = FSfopen(FileName[FileNo], "r"); // ファイルを開く if (fptr != 0) { // Success File Open ? // 尚、ファイル名が無い(0)をオープンしようとするとオープンエラー:fptr == 0になる。 result = FSfread(DataBuf, 1, 1024, fptr); // ファイルヘッダ部 1KB読み込み FSrewind(fptr); // ファイルの最初にポインタを戻す DispIDTag(); // タグ情報を検索し、曲名と演奏者名を液晶に表示 // Out of Music Data As Far as EOF of File do{ /// 更新フラグチェックと制御(音量ボタン) if(ChgFlag){ // 更新ありか? ChgFlag = 0; // フラグクリア SetVolume(Volume, Volume); // 音量設定 } MP3_XDCS_IO = 0; /// File Read (Unit 256Byte) result = FSfread(Buffer, 1, 256, fptr); for( i= 0; i 60)&&(Yset < 1000)){ /// 確実なタッチの検出 if((abs(Xset - Xold) < 15) && abs(Yset - Yold) < 15){ Count++; if(Count >= 3){ Count = 0; SW= CheckBtn(Xset, Yset); // 押されたボタン判断 /// 音量ボタンチェック if(SW == 2){ // ボタン2:音量アップ if(Volume > 0) { // 最高0(0dB): 64〜0の範囲で設定 Volume--; itostring(2, 64-Volume, str_V+1); // 'Vxx'表示:表示は、最小0〜最大64の表示に変換 Glcd_Str(21, 0, str_V, WHITE, BLACK); } ChgFlag = 1; // 変更フラグオン SW= -1; } if(SW == 3){ // ボタン3:音量ダウン if(Volume < 64) { // 0〜64の範囲で設定 Volume++; itostring(2, 64-Volume, str_V+1); // 'Vxx'表示:表示は、最小0〜最大64の表示に変換 Glcd_Str(21, 0, str_V, WHITE, BLACK); } ChgFlag = 1; // 変更フラグオン SW= -1; } } } else Count = 0; Xold = Xset; Yold = Yset; } else SW= -1; } /***************************************************** * 選択マーカー移動表示+スクロール処理 ******************************************************/ void ScrollDisp(void) { short i; FileNo++; // 次の行へ if(FileNo < 12) { Glcd_Char(0, FileNo-1, 0x20, BLACK, BLACK); // その前に表示されている、選択キャラクタ'●'をクリアする Glcd_Char(0, FileNo, 0xE4, GREEN, BLACK); // '●'を選択初期位置(先頭)に表示 } else{ //// スクロール表示 for(i= 0; i < 12; i++) Glcd_Str(2, i, " ", BLACK, BLACK); // ファイル名のみクリア i= 0; /// 12曲分のファイル名の表示(スクロール毎に、1つ上にずれる) while(i < 12) { Glcd_Str(2, i, FileName[FN_Top],CYAN, BLACK); FN_Top++; i++; } /// FN_Topは、1回目、13で抜けるので、2回目のために、-11しておけばよい FN_Top = FN_Top - 11; Glcd_Char(0, FileNo, 0x20, BLACK, BLACK);// 現在位置の、選択キャラクタ'●'をクリアする } } /***************************************************************** * HOMEボタンをタッチした時と、オープンエラー時の処理(同じ処理) ******************************************************************/ void HomeFunc(void) { if(FileNo < 12) Glcd_Char(0, FileNo, 0x20, BLACK, BLACK);// 現在位置の、選択キャラクタ'●'をクリアする else Glcd_Char(0, 11, 0x20, BLACK, BLACK);// スクロール領域の場合、最終行の選択キャラクタ'●'をクリアする /// ファイル名のみクリア for(i= 0; i < 12; i++) Glcd_Str(2, i, " ", BLACK, BLACK); /// 初期ファイルリスト表示に戻す。 if(Total >= 12) { // トータル曲数が、13曲以上の場合 for(i= 0; i < 12; i++) Glcd_Str(2, i, FileName[i], CYAN, BLACK); } else { // トータル曲数が、13曲未満の場合 for(i= 0; i < Total; i++) Glcd_Str(2, i, FileName[i], CYAN, BLACK); } FileNo = 0; FN_Top= 1; Glcd_Char(0, FileNo, 0xE4, GREEN, BLACK); // '●'を選択初期位置(先頭)に表示 } /******************************** * 座標取得実行関数 ********************************/ short Getscreen(unsigned int chn) { short i; AD1CON1bits.ADON = 0; AD1CON1bits.DONE = 0; // 手動の場合は変換開始前に、DONEビットをクリアする必要あり(自動クリアしない) AD1CHS = chn; // Select Channel AD1CON1bits.ADON = 1; AD1CON1bits.SAMP = 1; for(i=0; i<100; i++) // Wait Aquisition 11usec Nop(); AD1CON1bits.SAMP = 0; // A/D convert start while(!AD1CON1bits.DONE); // wait conversion return(ADC1BUF0); // return result } /**************************************** * タッチスクリーン用設定変更 ****************************************/ void SetupScreen(void) { /* ADCの設定 */ AD1CON1bits.ADON = 0; AD1CON1 = 0x0000; // ADオフ、手動制御、10bit分解能 AD1CON2 = 0; // AVdd, AVss, SCANなし,MUXAのみ AD1CON3 = 0x0304; // AD用クロック周期:Tad = 10*Tcy= 10*12.5= 125nS/サンプル時間:3Tad= 3*125= 375nS // Tad= 2* 周辺用クロック(Tpbclk=Tcy)* (ADCS(7:0)設定+1)= 2* 12.5* (4+1) AD1CHS = 0x00000000; // チャネル選択(※ S/Hチャンネル設定は上位16bitでする) // AD1PCFG = 0xFFE7; // AN4(X-)、AN3(Y+)→ ここでは、これは必要ない。 AD1CSSL = 0; // 自動スキャンなし AD1CON1bits.ADON = 1; // AD有効化 } /********************************************************** * タッチボタン判定処理 * タッチポイントの座標から、押されたボタンの種別No.を取得 * し、その値を返す ***********************************************************/ short CheckBtn(short x, short y) { short i, btn; /* /// タッチ座標位置表示(デバッグ用) itostring(4, x, MsgScrn+2); itostring(4, y, MsgScrn+9); Glcd_Str(3, 16, MsgScrn, GREEN, BLACK); */ /// ボタンチェック btn = -1; for(i=0; i<4; i++){ if((x > Button[i][0]-15)&&(x < Button[i][0]+15)){ if((y > Button[i][1]-15)&&(y < Button[i][1]+15)) btn = i; } } return(btn); } /*************************************** * 整数からASCII文字に変換 ****************************************/ void itostring(char digit, unsigned short data, char *buffer) { char i; buffer += digit; // 最後の数字位置 for(i=digit; i>0; i--) { // 変換は下位から上位へ buffer--; // ポインター1 *buffer = (data % 10) + '0'; // ASCIIへ data = data / 10; // 次の桁へ } } /*************************************************** * ファイルリストの表示 * 最大256ファイル ****************************************************/ void DispFiles(void){ short i, j; Glcd_Clear(BLACK); // 全画面消去 /// ファイル名の表示 i = 0; j = 0; /// 最初のファイル名の表示と格納 while(Record.filename[j] != 0){ // 名称最後か? FileName[i][j] = Record.filename[j]; // 格納 j++; } Glcd_Str(2, i++, Record.filename, CYAN, BLACK); // 最初のファイル名の表示 /// すべてのファイル名の表示と格納 while(FindNext(rptr) == 0){ // 次のファイルサーチ //// ファイル発見できれば表示し格納 j = 0; // 文字列ポインタリセット if(Record.attributes==ATTR_ARCHIVE) // 発見できたか? while(Record.filename[j] != 0){ // 名称の最後か? FileName[i][j] = Record.filename[j];// 格納 j++; } /// 初期ファイルリストとして、1画面、最大12曲分リスト表示する if(i<=11) Glcd_Str(2, i, Record.filename, CYAN, BLACK); i++; if(i>=256) // 格納ファイルが256個目を超えたか? break; // 終了して抜ける } Total= i; /// トータル・曲数(ファイル数)の表示 itostring(3, Total, str_T+1); Glcd_Str(20, 16, str_T, WHITE, BLACK); } /************************************************* * タグ情報を検索し、曲名と演奏者名を液晶に表示 **************************************************/ void DispIDTag(void) { unsigned short i, j, KanjiFlag; // 半角、漢字混在フラグ /// IDタグ表示エリアをクリア(漢字のスペースコード+キャラクタのスペースコードで埋める) for(i= 3; i < 24 ; i++) KanjiCode(14, i, 0x81, 0x40, BLACK, BLACK); for(i= 3; i < 24 ; i++) KanjiCode(16, i, 0x81, 0x40, BLACK, BLACK); Glcd_Str(3, 13, " ", BLACK, BLACK); Glcd_Str(3, 15, " ", BLACK, BLACK); /// 表題表示 Kanji_Str(14, 0, MsgMusc, MAGENTA, BLACK); Kanji_Str(16, 0, MsgBand, MAGENTA, BLACK); //// 曲名サーチ (TIT2) locate = 0; // 文字列インデックス for(i=0; i 42) // 曲名:漢字で21文字max(21*2=42byteに制限) NameSize = 42; /// 表示制御 KanjiFlag = 0; // 混在フラグオフ j= 3; for(i=0; i 42) // 曲名:漢字で21文字max(21*2=42byteに制限) NameSize = 42; /* 表示制御 */ KanjiFlag = 0; // 漢字混在フラグオフ j= 3; for(i=0; i Str_X-Ndot; Xpos--){ Glcd_Pixel(Xpos, Ypos, ImageBuffer[ptr--]); } } }