/******************************************************************** * プロジェクト名: OLED_MP3_PLAYER_TEST * PIC32MXトレーニング基板使用 * * ファイルシステム:MMD File System→ FAT16(FAT32互換) * 1.5インチカラーOLED: Adafruit_1.5INCH_RGBOLED * 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 * * デバッグ年月日: 2018/2/19 N.Ishii *********************************************************************/ #include // PIC32 peripheral library #include "SSD1351_OLEDlibPIC32MX_2.h" #include #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 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}; // "演奏者不明" /// ファイルシステム用変数 FSFILE *fptr; size_t result; SearchRec Record; SearchRec *rptr = &Record; unsigned short ImageBuffer[10000]; // 16bitBMPデータバッファ:10000ワード= 100x100 char FileName[256][13]; // 2次元配列: 最大、256ファイルで、1ファイル→13文字(8文字+'.MP3'+NULL)構成 180210 char str_IMAGE[]= "IMAGExxx.BMP"; // ジャケットイメージ表示用 180210 short FileNo= 0; short temp; short Total; /// IDTAG読込み関連追加 #define MaxTag 512 unsigned char DataBuf[1024]; // IDTAG読込み用 short NameSize, locate; /// VS1011e関連 int Volume, BASS, BASSFreq, TREB, TREBFreq; // 180216 unsigned char Buffer[256]; char ChgFlag = 0; char BACK_SW_ON_Flag = 0; /// プロトタイプ void DispJacketImage(short Ndot, short Str_X, short Str_Y); void GoBackSD_SPI_Set(void); void GoBackOLED_SPI_Set(void); void DispIDTag(void); void SaveFilesName(void); void itostring(char digit, unsigned short data, char *buffer); int main(void) { short i,temp; /* システム最適設定 */ SYSTEMConfigPerformance(80000000); mJTAGPortEnable(DEBUG_JTAGPORT_OFF); // JTAGを無効化 /* I/O初期設定 */ AD1PCFG = 0xFFFF; // デジタルに TRISE = 0x0000; // LED-RE0 〜 RE7 TRISF = 0x0034; // VS1011_SDI, VS1011_SDO, VS1011_SCK, VOL_UP_SW, VOL_DWN_SW, LED-RF1, LED-RF0 TRISC = 0xF000; // OSC,SOSC TRISD = 0x0F00; // RD7:OLED_RST, RD3(RD4 NGで変更180125), OLED_CS, SW2〜SW5(未使用) TRISB = 0x40CF; // RB3:SD_CD, RB4:OLED_DC, RB5:SD_CS,VS1011e制御信号, SKIP_SW, BASS_SW TRISG = 0x0280; // RG6:SCK(SD/OLED共用),RG7:SD_SDI, RG8:SDO(SD/OLED共用), MAIN_RTN_SW, EEPROM_SDA.SCL /// LEDモジュールの全セグメントOFF LATE = 0; LATFbits.LATF0 = 0; LATFbits.LATF1 = 0; /// スイッチ状態変化割込み許可 mCNOpen(CN_ON, CN3_ENABLE | CN4_ENABLE | CN17_ENABLE | CN18_ENABLE, CN5_PULLUP_ENABLE); // NEXT_SW, BACK_SW, VOL_UP_SW, VOL_DWN_SW, SD-CD_PULLIP /// 状態合わせ temp = PORTB; temp = PORTF; /// OLEDの初期化 OLEDInit(); // OLEDコントローラー初期化(初期化の末尾で全面クリアしている。) /// VS1011eの初期化 VS1011_Init(); // MP3デコーダ初期化 // MP3デコーダ初期設定 Volume = 8; // 音量の初期設定 SetVolume(Volume, Volume); // 初期値制御 BASS = 5; // 音質の初期設定 BASSFreq = 15; // 低音強調 TREB = 0; // 高音フラット TREBFreq = 0x08; // 8kHz SetBoost(BASS, BASSFreq, TREB, TREBFreq); // 初期値制御 delay_ms(1000); // 最初だけはこの待ち(DREQがHiになるまで)が必要のようだ。これが無いとテストトーン鳴らない。 // Start VS1011e Sine Test:デバッグ用 // VS1011_SineTest(); // Start Sin_5kHz /// カード挿入まで待ち、SDカード初期化を実施 GoBackSD_SPI_Set(); while(FSInit() != 1){ // Check Mount SD Card and Format:戻り値:'1'=カード実装されフォーマット済/'0'=カード未実装 // メッセージ表示にするとSPI設定チェンジが頻繁になるのとタイミングが難しく誤動作する時があったので簡易的に、LED点滅に変更した。 LATFbits.LATF0= !LATFbits.LATF0; // 挿入待ちの合図 delay_ms(500); } LATFbits.LATF0= 0; /// カード挿入状態であれば以下を実行する。 GoBackOLED_SPI_Set(); OLED_Str(0, 0, "Find Card OK!", CYAN, BLACK); delay_ms(1000); OLED_Str(0, 0, " ", BLACK, BLACK); /// SDに格納されている、MAX256個の、MP3ファイル名の格納処理と、最初から、16曲分のファイル名リストを表示 SaveFilesName(); /// 最初から、16曲分のファイル名リストを表示後のクリア delay_ms(2000); OLED_Clear(BLACK); // 全画面クリア GoBackSD_SPI_Set(); /// 最初のBMPファイルのサーチ */ result = FindFirst("*.BMP", ATTR_ARCHIVE, rptr); if(result != 0){ // ファイルありか? GoBackOLED_SPI_Set(); OLED_Str(0, 1, "Find No File?", CYAN, BLACK); // ファイル無しメッセージ while(1); // stop } /// 最初のBMPファイルのサーチ成功の場合、最初の、BMP File Open (Read Mode) itostring(3, FileNo+1, str_IMAGE+5); // BMPファイル名の曲番部分を文字に変換して、全体の文字列と結合 fptr = FSfopen(str_IMAGE, "r"); // File Open (Read Mode) mCNClearIntFlag(); // CN割り込みフラグクリア ConfigIntCN(CHANGE_INT_ON | CHANGE_INT_PRI_2); // CN割り込み有効化 /// 割り込み許可 INTEnableSystemMultiVectoredInt(); /// メイン ループ while(1) { if(fptr != 0){ // BMPファイルオープンに成功の場合、以下を実行 FSfread(ImageBuffer,1,138,fptr); // ヘッダ情報部の、規定バイト数:138バイト分は、読み飛ばす FSfread(ImageBuffer,2,10000,fptr); // BMPデータを、バッファへ全て(10000ワード)転送 GoBackOLED_SPI_Set(); DispJacketImage(100, 114, 1); // ジャケットイメージを描画する GoBackSD_SPI_Set(); FSfclose(fptr); // イメージ描画が終了したら、BMPファイルをクローズ } else { //// BMPファイルオープンに失敗(曲ファイルに対応するイメージが無い場合) /// イメージファイルがない場合は茶色のジャケットを表示 if(FileNo != Total) { //下記処理は、収録最終曲までの範囲で行う。最終曲の次になったらスキップして最初からの繰返し処理になる。 for(i= 0; i < 10000; i++) ImageBuffer[i]= BROWN; GoBackOLED_SPI_Set(); DispJacketImage(100, 114, 1); /// そして中央に、"NO IMAGE"と表示 OLED_Str(4, 6, "NO IMAGE", WHITE, BROWN); GoBackSD_SPI_Set(); } } /// MP3ファイル・ヘッダ部の、タグ情報読込み処理 fptr = FSfopen(FileName[FileNo], "r"); // MP3ファイルを開く if (fptr != 0) { // Success MP3 File Open ? // 尚、ファイル名が無い(0)をオープンしようとするとオープンエラー:fptr == 0になる。 result = FSfread(DataBuf, 1, 1024, fptr); // ファイルヘッダ部 1KB読み込み FSrewind(fptr); // ファイルの最初にポインタを戻す GoBackOLED_SPI_Set(); DispIDTag(); // タグ情報を検索し、演奏者名と曲名をOLEDに表示 GoBackSD_SPI_Set(); //// ファイルのEOFまで音声データ連続出力 do{ /// 更新フラグチェックと制御 if(ChgFlag){ // 更新ありか? ChgFlag = 0; // フラグクリア SetVolume(Volume, Volume); // 音量設定 } //// 音楽データの再生 INTDisableInterrupts(); // SPI処理中は割り込み禁止 MP3_XDCS_IO = 0; // データ用CSセット /// 256バイト単位でファイルリード result = FSfread(Buffer, 1, 256, fptr); // 256バイト読み出し for(i= 0; i 0) Volume--; // 0dBmax ChgFlag = 1; // 変更フラグオン } if (PORTFbits.RF5 == 0) { // VOL_DWN_SW ON ? if (Volume < 64) Volume++; ChgFlag = 1; // 変更フラグオン } if (PORTBbits.RB1 == 0) { // NEXT_SW ON ?(skip) result = 0; // 強制的に、メインで、MP3ファイルリード終了(EOFの手前まで読んだ)にする。 } if (PORTBbits.RB2 == 0) { // BACK_SW ON ?(skip) BACK_SW_ON_Flag = 1; result = 0; // 強制的に、メインで、MP3ファイルリード終了(EOFの手前まで読んだ)にする。 } delay_ms(1); // チャッタリング回避用 temp = PORTB; // 状態合わせ temp = PORTF; mCNClearIntFlag(); //割り込みフラグクリア } /*********************************************************** * 16bitBMPカラーイメージ表示関数(アルバムジャケット表示用) * 正方形サイズ前提(100x100dot) * 表示は、右上原点(114,1)より画素描画を繰り返す * * <引数の説明> * @ Ndot:表示ドット数(例:100) * A Str_X:ドット描画開始X座標 * B Str_Y:ドット描画開始Y座標 ************************************************************/ void DispJacketImage(short Ndot, short Str_X, short Str_Y) { short Xpos, Ypos, ptr; /// BMPフォーマットのデータは、左下から右上に向かって配列されているので /// この順で描画すると、上下逆様の画像表示になってしまう。 /// 右上の画素(例:ptr=9999, Xpos= 114, Ypos= 1)から下に向かって描画する必要がある。 ptr= (Ndot * Ndot)-1; for(Ypos= Str_Y; Ypos < Str_Y+(Ndot-1); Ypos++){ for(Xpos=Str_X; Xpos > Str_X-Ndot; Xpos--){ OLED_Pixel(Xpos, Ypos, ImageBuffer[ptr--]); } } } /*********************************************************** * OLED-SPIアクセス後に、SD-SPIアクセスする時に、 * SD-SPIポート設定(SPI2モジュール使用)に戻す関数 ************************************************************/ void GoBackSD_SPI_Set(void) { SPI2CONbits.ON= 1; // OLEDアクセス後なので、SPI2モジュールを有効化 } /******************************************************************* * SD-SPIアクセス後に、OLED-SPIアクセスする時に、 * OLED-SPIポート設定(単純ポートによるソフトウエアSPI)に戻す関数 ********************************************************************/ void GoBackOLED_SPI_Set(void) { SPI2CONbits.ON= 0; // SPI2モジュールを無効化 TRISGbits.TRISG6 = 0; // SCK2ピンを、RG6ポート出力に戻す。 TRISGbits.TRISG7 = 1; // SDI2ピンを、RG7ポート入力に戻す。 TRISGbits.TRISG8 = 0; // SDO2ピンを、RG8ポート出力に戻す。 } /********************************************************************************************** * 演奏者名・曲名表示 * 半角のみの場合は、MAX 16文字x2行表示 * 漢字の場合は、MAX 9文字×2行表示 ***********************************************************************************************/ void DispIDTag(void){ unsigned short i, KanjiFlag; // 半角、漢字混在フラグ //// 演奏者名サーチ (TPE2)or(TPE1) locate = 0; for(i=0; i 36) // 漢字で18文字(2行分)max(18*2=36byteに制限) NameSize = 36; /// 表示制御 KanjiFlag = 0; // 漢字混在フラグオフ for(i=0; i 36) // 曲名:漢字で18文字(2行分)max(18*2=36byteに制限) NameSize = 36; /// 表示制御 KanjiFlag = 0; // 混在フラグオフ for(i=0; i=256) // 格納ファイルが256個目を超えたか? break; // 終了して抜ける } Total= i; // トータル曲数変数へ退避 GoBackOLED_SPI_Set(); } /*************************************** * 整数から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; // 次の桁へ } }