/******************************************************************************* * ソース名:WAVPlayer_V2a.C * * 以前作った、WAVPlayerのハードを基本そのまま利用し、液晶を * I2C制御のキャラクタ液晶(2行表示)から、SPI制御の超小型GLCD * (21文字*6行表示:128*48dot)に置換える。 * * V2からの、変更点: * @ ハード的には、HOME SWを、BACK SKIP SWに置換える。 * A 現状の、SKIP SWによる順送り・順スクロール機能の他に、 *    BACK SKIP SWによる逆送り・逆スクロール機能を *    ソフト変更により実現する。 * * <最終結果:2016.12.15> * 表示は別として、再生のみは、両モードの混在で動かせた。 * モードによって、スクロール方法・マーカー・リストの表示方法が異なるため * 途中で異なるモードにした場合、表示が上手く合わない。 * 自分の技量不足もあるが、スクロールの考え方を根本から変えないと * いけない気がする。 * * 今回は逃げとして、以下の簡単な方法にしてみた。 * 現在の、HOME SWを、MODE SWとして使う。 * そして、電源ON時に、1度だけこのSWの状態を読込み、 * 押されてない場合は、順送り(順方向ループ再生)モード、 * 押されている場合は、逆送り(逆方向ループ再生)モード * として、そのモード専用に動かすことにした。 * * ファイルシステム:Microchip社製 File System→ FAT16(FAT32互換) * 超小型SPI制御 128x48dot モノクロ液晶: AQM1248A "Xiamen Zottler Electronics" * * MPU: dsPIC33FJ64GP802 * * N.Ishii 2015.11.19 * * 一度諦めたが、再チャレンジ * N.Ishii 2016.10.4 * 3度目のチャレンジ * N.Ishii 2016.12.15 **********************************************************************************/ #include "p33FJ64GP802.h" #include "AQM1248A_Lib.h" #include "GenericTypeDefs.h" #include "FSIO.h" //// コンフィギュレーション設定 80MHz _FOSCSEL(FNOSC_FRCPLL & IESO_OFF); // Oscillator Mode (Internal Fast RC (FRC) w/ PLL) // Internal External Switch Over Mode (Start-up device with user-selected oscillator source) _FOSC(FCKSM_CSDCMD & IOL1WAY_OFF & OSCIOFNC_ON & POSCMD_NONE); // Clock Switching and Monitor (Both Clock Switching and Fail-Safe Clock Monitor are disabled) // Peripheral Pin Select Configuration (Allow Multiple Re-configurations) // OSC2 Pin Function (OSC2 pin has digital I/O function) // Primary Oscillator Source (Primary Oscillator Disabled) _FWDT(WDTPOST_PS32768 & WDTPRE_PR128 & WINDIS_OFF & FWDTEN_OFF); // Watchdog Timer Postscaler (1:32,768) // WDT Prescaler (1:128) // Watchdog Timer Window (Watchdog Timer in Non-Window mode) // Watchdog Timer Enable (Watchdog timer enabled/disabled by user software) _FPOR(ALTI2C_OFF & FPWRT_PWR64); // Alternate I2C pins (I2C mapped to SDA1/SCL1 pins) // POR Timer Value (64ms) _FICD(JTAGEN_OFF & ICS_PGD2); // JTAG Port Enable (JTAG is Disabled) // Comm Channel Select (Communicate on PGC2/EMUC2 and PGD1/EMUD1) _FBS(BWRP_WRPROTECT_OFF & BSS_NO_FLASH & RBS_NO_RAM) // Boot Segment Write Protect (Boot Segment may be written) // Boot Segment Program Flash Code Protection (No Boot program Flash segment) // Boot Segment RAM Protection (No Boot RAM) _FSS(SWRP_WRPROTECT_OFF & SSS_NO_FLASH & RSS_NO_RAM) // Secure Segment Program Write Protect (Secure segment may be written) // Secure Segment Program Flash Code Protection (No Secure Segment) // Secure Segment Data RAM Protection (No Secure RAM) _FGS(GWRP_OFF & GSS_OFF) // General Code Segment Write Protect (User program memory is not write-protected) // General Segment Code Protection (User program memory is not code-protected) /// メッセージ・テーブル char str_Number[] = "xxx"; char str_Total[] = "xxx"; /// MDDファイル用構造体のポインタ変数 FSFILE *fptr; size_t result, count; SearchRec Record; SearchRec *rptr = &Record; __attribute__((far)) unsigned char BufferA[4096]; // バッファA __attribute__((far)) unsigned char BufferB[4096]; // バッファB __attribute__((far)) char FileName[100][13]; // 2次元配列: 最大、100ファイルで、1ファイル→13文字(8文字+'.WAV'+NULL)構成 /// グローバル変数、定数定義 int i, y, Flag, SDFlag, EndFlag, B_SkipFlag; unsigned int ptr, ptrA, ptrB; int FileNo; int Number; int sq; int FN_Top; // 画面先頭に表示するファイル名バッファのポインタ int LastFileNo; int b_case; /// プロトタイプ unsigned int Chunk(unsigned char *buf); void StoreFiles_and_DispFiles(void); void DispNumber(void); void Scroll_Disp(void); void itostring(char digit, unsigned int data, char *buffer); /******* メインルーチン ***********/ int main(void) { /// クロックの設定 7.37MHz*4=32MHz CLKDIVbits.PLLPRE = 0; // 7.37MHz /2=3.685(内蔵クロック使用 追記) PLLFBDbits.PLLDIV = 42; // 3.685MHz * 44 = 162MHz CLKDIVbits.PLLPOST = 0; // 162MHz / 2 = 81MHz -> 40.5MIPS(約40MIPS) /// Initialize Port AD1PCFGL = 0xFFFF; // すべてデジタルにセット TRISA = 0x001C; // RA0(SPI_CS),1,4 is Out, Other is Input TRISB = 0xFCD4; // RB9:SPI_SDI, RB8:SPI_SCK, RB5:SPI_CS is Out) /// スイッチ関連設定 CNPU2bits.CN24PUE = 1; // RB6 Pullup CNPU2bits.CN23PUE = 1; // RB7 Pullup LATAbits.LATA1 = 1; // Green LED OFF /// SPI信号(超小型GLCD用)のイニシャル・レベル設定 SPI_CS = 1; SPI_SCK = 1; SPI_SDI = 1; /// SPI(SDカード用)のピン割り付け RPINR20bits.SDI1R = 3; // SDI1をRP3に RPOR0bits.RP0R = 8; // SCK1をRP0に RPOR0bits.RP1R = 7; // SDO1をRP1に //// WAV Player V2 タイトル表示 LCD_int (); // Grafic LCD 初期設定 LCD_clr (); // 表示クリア LCD_ROMstr3x("WAV",0,0); // 3倍文字列表示 LCD_ROMstr3x("PLAYER2",3,0); // 3倍文字列表示 /* 補助クロック初期化 */ ACLKCONbits.SELACLK = 1; // Select SOSC 11MHz ACLKCONbits.AOSCMD = 1; // HS mode ACLKCONbits.APSTSCLR = 7; // 1/1 ACLKCONbits.ASRCSEL = 0; // Select SOSC /// <オーディオDACの初期化> /// DMAなしでのオーディオDAC動作の設定になっている /// FIFOがフルでない時(FIFOが空いた時)は、必ず両方のチャンネルの割込みが発生するように設定している /// デフォルトで挿入するデータを、0x0000として、割込み発生時にデータが何もない時には自動的に無音状態になるように設定されている DAC1CONbits.FORM = 1; // signed int DAC1CONbits.DACFDIV = 0; // No divide:補助オシレータ周波数設定= 25.6MHz(DACCLKレート= Fs x 256= 44.1k x 256= 25.6MHz) DAC1STATbits.LOEN = 1; // Left Out Enable DAC1STATbits.LITYPE = 0; // Interrupt not ful DAC1STATbits.ROEN = 1; // Right Out Enable DAC1STATbits.RITYPE = 1; // Interrupt not full DAC1DFLT = 0; // default out DAC1LDAT = 0; // out off DAC1RDAT = 0; // out off DAC1CONbits.DACEN = 1; // DAC Enable:両チャンネルのDAC割込みが発生 IFS4bits.DAC1LIF = 0; // Left Flag Clear:LCH割込みフラグクリア IFS4bits.DAC1RIF = 0; // Right Flag Clear:RCH割込みフラグクリア /// カードの実装確認とディレクトリ読み込み (永久待ち) while(PORTAbits.RA2 != 0) // Check Mount SD Card(CD信号入力をチェックし未挿入の場合、緑LED点滅) { LATAbits.LATA1 = 1; // 緑LED ON Delay_ms(1000); LATAbits.LATA1 = 0; // 緑LED OFF Delay_ms(1000); } /// SD Mount(マウント完了時、緑LED点灯) LATAbits.LATA1 = 1; // Green LED ON Delay_ms(2000); LCD_clr (); /// P_ON時の、MODE SW 読込み LCD_posyx(0, 0); if(PORTBbits.RB7 == 0){ B_SkipFlag= 1; LCD_ROMstr("Back Skip Mode"); } else{ B_SkipFlag= 0; LCD_ROMstr("Front Skip Mode"); } Delay_ms(2000); LCD_clr (); FSInit(); // FATの初期化(このファイルシステムは、戻り値を持つが、未使用) /// 変数の初期化 sq = 0; b_case = 0; /************* メインループ ****************/ while(1) { switch (sq) { case 0: ///// 最初のファイル発見と、ファイルリスト表示〜最初のファイルオープン FileNo = 0; FN_Top = 1; fptr = 0; Flag = 0; SDFlag = 0; EndFlag = 0; y = 5; /// 最初のファイルのサーチ(ルートだけにWAVファイルがあることが前提) result = FindFirst("*.*", ATTR_ARCHIVE, rptr); if(result == 0){ /// ファイルが発見できた場合の処理 StoreFiles_and_DispFiles(); // FileNameバッファへの、SD内音楽ファイル名の格納(最大100ファイル)と、 // 初期ファイル名一覧表示処理(最大6ファイル) /// 最初の曲番号表示 DispNumber(); /// 最初のファイルを開く fptr = FSfopen(FileName[FileNo], FS_READ); if(fptr != 0){ // 正常オープンの場合 LCD_posyx(0,0); LCD_dat(0x8c); // 最初ファイル名の頭に、'●'を表示 Flag = 0; // BufferA指定 SDFlag = 0; // SD読み出しフラグクリア /** 最初のデータ読み出し ***/ count = FSfread(BufferA, 1, 4096, fptr); // 最初の読み出し ptrA = Chunk(BufferA); // WAVファイルの先頭指定 ptrB = 0; // ポインタリセット SDFlag = 1; // BufferBへも格納させる sq= 1; IEC4bits.DAC1LIE = 1; // Lch割り込み許可 IEC4bits.DAC1RIE = 1; // Rch割り込み許可 追加 } } else{ LCD_posyx(0,0); LCD_ROMstr("File Open error!"); } break; /******** ファイルの再生継続と、スクロール表示 **************/ case 1: if(SDFlag){ // 読み出しフラグオンの場合 SDFlag = 0; // 読み出しフラグクリア if(Flag == 0){ // バッファ切り替えフラグ確認 /* 4kバイト単位 バッファB側に格納 */ count = FSfread(BufferB, 1, 4096, fptr); // バッファBに読み出し ptrB = 0; // ポインタBリセット } else{ // バッファA側に格納の場合 count = FSfread(BufferA, 1, 4096, fptr); // バッファAに読み出す ptrA = 0; // ポインタAリセット } /// SW2:Skip SWのチェック 曲送り if(PORTBbits.RB6 == 0){ // SW2オンの場合 EndFlag = 1; // 終了フラグセット } /***** 曲終了か曲送りスイッチの処理 **/ if((count == 0) || (EndFlag)){ // ファイル終了か終了フラグオンの場合 IEC4bits.DAC1LIE = 0; // Lch割り込み禁止 IEC4bits.DAC1RIE = 0; // Rch割り込み禁止 追加 /***** 再生完了処理 *****/ /* バッファクリア */ for(i=0; i<4096; i++){ BufferA[i] = 0; BufferB[i] = 0; } FSfclose(fptr); // ファイルのクローズ Delay_ms(500); // 曲間の間 /* 連続再生のため次のファイルオープン */ if(B_SkipFlag == 0){ // Skip SW(順送り)ONの場合:151118 追加 FileNo++; } else{ /// Back Skip SW の場合 if(FileNo == 0){ // 最初の曲再生時に、Back Skip SW onの場合 FileNo= LastFileNo; // 最後のファイル番号を指定(再生のための操作) b_case= 1; // Scroll_Disp() の中の、バック処理ケースNoセット } else{ // FileNoがラストから手前5曲分の範囲で、B_SKIP SWが押された場合 //(例:全12曲の場合、LastFileNo= 11なので、LastFileNo-4(FileNo=7)〜 -0(FileNo=11)の範囲) // (SWを押された時点(FileNo--する前)の、FileNoで押されたリスト上の位置を判断) 161214 if((FileNo >= LastFileNo-4) && (FileNo <= LastFileNo)) b_case= 2; // FileNoがラストから手前6曲目から、初期画面の2番目の曲までの範囲で、B_SKIP SWが押された場合 //(例:全12曲の場合、LastFileNo= 11なので、LastFileNo-5(FileNo=6)〜 FileNo=1の範囲) if((FileNo >= 1) && (FileNo <= LastFileNo - 5)) b_case= 3; /// SWを押された時点(FileNo--する前)の、FileNoで押されたリスト上の位置を判断し、 /// 各バックスキップ用case処理(表示関係)の、case番号を取得後、次の再生ファイル番号にする。 161214 FileNo--; } } fptr= FSfopen(FileName[FileNo], FS_READ); if((fptr != 0) && (FileNo < 100)){ // 100曲以内でかつ、正常オープンの場合 /** 最初のデータ読み出し ***/ count = FSfread(BufferA, 1, 4096, fptr); // 最初の読み出し Flag = 0; ptrA = Chunk(BufferA); // WAVファイルの先頭指定 ptrB = 0; // バッファポインタリセット Scroll_Disp(); // ファイルリスト・スクロール表示 SDFlag = 1; // BufferBにも格納させる IEC4bits.DAC1LIE = 1; // Lch割り込み許可 IEC4bits.DAC1RIE = 1; // Rch割り込み許可 } else{ // オープン失敗か100曲を超えた場合 //(次のファイルが無い=全ファイル再生終了の場合も相当) sq = 0; FileNo = 0; FN_Top = 1; fptr = 0; } /// スイッチ曲送りの場合の、終了フラグクリアは、ここに記述しないと /// 収録最終曲を再生中に、SKIP SWが押された場合、最初の曲のファイル名表示の後、直ぐに /// 再生完了処理されてしまい再生しないで、次の曲の再生に移ってしまう if(EndFlag){ // スイッチ曲送りの場合 EndFlag = 0; // 終了フラグクリア } } } break; default : break; // どれでもない場合 } } } /************************************************************************* * スクロール表示と、マーカー表示処理 * バック・スクロール追加:151118 **************************************************************************/ void Scroll_Disp(void){ int i; if(B_SkipFlag == 0){ // フロント・スクロールの場合 if(FileNo < 6){ LCD_posyx(FileNo,0); LCD_dat(0x8c); // 現在のファイル名の頭に、'●'を表示 /// 曲番号表示 DispNumber(); if(FileNo > 0){ // FileNo= 1〜 5 LCD_posyx(FileNo-1,0); LCD_dat(0x20); // 1つ前のマーカーを消す } } else{ // FileNo= 6以上 if(FileNo < 100){ LCD_clr(); // 全画面消去 i= 0; //// 6曲分のファイル名の表示(FileNoは、スクロール毎に、1つ上にずれる) while(i < 6) { /// ファイルリスト表示(X=2(12dot),Y(i)の位置にファイル名を表示後、Y=Y+1(i=i+1)する) LCD_posyx(i, 12); LCD_ROMstr(FileName[FN_Top]); FN_Top++; i++; } /// FN_Topは、1回目、7で抜けるので、2回目のために、-5しておけばよい FN_Top = FN_Top - 5; LCD_posyx(5,0); LCD_dat(0x8c); // 最終行のファイル名の頭に、'●'を表示 /// トータル・曲数(ファイル数)の再表示 LCD_posyx(0, 108); LCD_ROMstr("Tot"); LCD_posyx(1, 108); LCD_ROMstr(str_Total); /// 曲番号表示 DispNumber(); } } } else{ // バック・スクロールの場合 switch(b_case){ case 0 : // 何もしない break; case 1 : // 1曲目に、B_SKIP SWが押された場合 LCD_clr(); // 全画面消去 i= 5; while((i >= 0) && (i < 6)) { /// 最終曲から上に6曲分ファイルリスト表示(X=2(12dot),Y(i)の位置にファイル名を表示) LCD_posyx(i, 12); LCD_ROMstr(FileName[LastFileNo]); LastFileNo--; i--; } LastFileNo= LastFileNo + 6; // 元の値に戻すために6を足す。(例: LastFileNo=11の時、LastFileNo=5で抜けるので6を足す) LCD_posyx(5,0); LCD_dat(0x8c); // 最終行のファイル名の頭に、'●'を表示 /// トータル・曲数(ファイル数)の再表示 LCD_posyx(0, 108); LCD_ROMstr("Tot"); LCD_posyx(1, 108); LCD_ROMstr(str_Total); /// 曲番号表示 DispNumber(); break; case 2 : // FileNoがラストから手前5曲分の範囲で、B_SKIP SWが押された場合 LCD_posyx(y,0); LCD_dat(0x20); // カレントのマーカーを消す LCD_posyx(y-1,0); LCD_dat(0x8c); // 1つ前のファイル名の頭に、'●'を表示 y--; if(y == 0) y= 5; /// 曲番号表示 DispNumber(); break; case 3 : // FileNoがラストから手前6曲目から、初期画面の2番目の曲までの範囲で、B_SKIP SWが押された場合 LCD_clr(); // 全画面消去 i= 0; while(i < 6) { /// 頭から6曲分ファイルリスト表示(X=2(12dot),Y(i)の位置にファイル名を表示) LCD_posyx(i, 12); LCD_ROMstr(FileName[FileNo]); // この時点で、FileNoは、一つ上にずれている。(バックスクロール表示) FileNo++; i++; } FileNo= FileNo - 6; // 元の値に戻す。(次の再生ファイル番号に戻す。) LCD_posyx(0,0); LCD_dat(0x8c); // 最上行のファイル名の頭に、'●'を表示 /// トータル・曲数(ファイル数)の再表示 LCD_posyx(0, 108); LCD_ROMstr("Tot"); LCD_posyx(1, 108); LCD_ROMstr(str_Total); /// 曲番号表示 DispNumber(); break; default: break; } b_case= 0; } } /************************************************************************ * 次の2つの機能がある * @ SDに収録のWAVファイル名を、ファイル名バッファに最大100ァイル名 *   格納する * A 初期ファイルリストとして、最大6曲分リスト表示する *************************************************************************/ void StoreFiles_and_DispFiles(void){ int i, j; LCD_clr(); // 全画面消去 /// ファイル名の表示 i = 0; // ポインタリセット j = 0; /// 最初のファイル名の表示と格納 while(Record.filename[j] != 0){ // 名称最後か? FileName[i][j] = Record.filename[j]; // 格納 j++; } /// 最初のファイル名表示(X=2(12dot),Y=0の位置にファイル名を表示後、Y=Y+1する点に注意) LCD_posyx(i++, 12); LCD_ROMstr(Record.filename); /// すべてのファイル名の表示と格納 while(FindNext(rptr) == 0){ // 次のファイルサーチ //// ファイル発見できれば表示し格納 j = 0; // 文字列ポインタリセット if(Record.attributes==ATTR_ARCHIVE) // 発見できたか? while(Record.filename[j] != 0){ // 名称の最後か? FileName[i][j] = Record.filename[j];// 格納 j++; } /// 初期ファイルリストとして、1画面、最大6曲分リスト表示する if(i<=5){ LCD_posyx(i, 12); LCD_ROMstr(Record.filename); } i++; if(i>=100) // 格納ファイルが100個目を超えたか? break; // 終了して抜ける } /// トータル・曲数(ファイル数)の表示 LCD_posyx(0, 108); LCD_ROMstr("Tot"); itostring(3, i, str_Total); LCD_posyx(1, 108); LCD_ROMstr(str_Total); LastFileNo= i - 1; // ※ LastFileNo(グローバル変数)を用意し、これをバック・スクロールの時、使う。 } /*********************** * 曲番号を表示する ************************/ void DispNumber(void){ LCD_posyx(4, 108); LCD_ROMstr("Num"); Number = FileNo + 1; itostring(3, Number, str_Number); LCD_posyx(5, 108); LCD_ROMstr(str_Number); } /********************************* * データチャンクサーチ関数 **********************************/ unsigned int Chunk(unsigned char *buf){ unsigned local; local = 36; while(!((buf[local]=='d')&&(buf[local+1]=='a')&&(buf[local+2]=='t')&&(buf[local+3]=='a'))) local++; return(local+8); } /*************************************************************** * LCH_DACL割り込み処理関数 * 1回の割込み(割込み周期:22.7uS(44.1kHz))で、1ワード転送 ****************************************************************/ void __attribute__((interrupt, no_auto_psv)) _DAC1LInterrupt(void){ IFS4bits.DAC1LIF = 0; // LCH 割り込みフラグクリア /******** バッファAの場合 **********/ if(Flag == 0){ // バッファAの場合 DAC1LDAT = BufferA[ptrA+1] * 256 + BufferA[ptrA]; // 次のLchデータ出力 ptrA += 2; // ポインタA更新 if(ptrA >= count){ // バッファ終了の場合 Flag = 1; // バッファ切り替え SDFlag = 1; // SD読み出しフラグセット ptrA = 0; // ポインタAリセット } } /********** バッファBの場合 ***********/ else{ // バッファBの場合 DAC1LDAT = BufferB[ptrB+1] * 256 + BufferB[ptrB]; // 次のLchデータ出力 ptrB += 2; // ポインタB更新 if(ptrB >= count){ // バッファ終了の場合 Flag = 0; // バッファ切り替え SDFlag = 1; // SD読み出しフラグセット ptrB = 0; // ポインタBリセット } } } /*************************************************************** * RCH_DACL割り込み処理関数) * 1回の割込み(割込み周期:22.7uS(44.1kHz))で、1ワード転送 ****************************************************************/ void __attribute__((interrupt, no_auto_psv)) _DAC1RInterrupt(void){ IFS4bits.DAC1RIF = 0; // RCH 割り込みフラグクリア /******** バッファAの場合 **********/ if(Flag == 0){ // バッファAの場合 DAC1RDAT = BufferA[ptrA+1] * 256 + BufferA[ptrA]; // 次のRchデータ出力 ptrA += 2; // ポインタA更新 if(ptrA >= count){ // バッファ終了の場合 Flag = 1; // バッファ切り替え SDFlag = 1; // SD読み出しフラグセット ptrA = 0; // ポインタAリセット } } /********** バッファBの場合 ***********/ else{ // バッファBの場合 DAC1RDAT = BufferB[ptrB+1] * 256 + BufferB[ptrB]; // 次のRchデータ出力 ptrB += 2; // ポインタB更新 if(ptrB >= count){ // バッファ終了の場合 Flag = 0; // バッファ切り替え SDFlag = 1; // SD読み出しフラグセット ptrB = 0; // ポインタBリセット } } } /***************************************** * 数値から文字列に変換 *****************************************/ void itostring(char digit, unsigned int data, char *buffer){ char i; buffer += digit; // 文字列の最後 for(i=digit; i>0; i--) { // 最下位桁から上位へ buffer--; // ポインター1 *buffer = (data % 10) + '0'; // その桁数値を文字にして格納 data = data / 10; // 桁-1 } }