/********************************************************************* * WAVオーディオプレーヤ * dsPIC33FJ64GP802のオーディオDACの使用例 * SDカード内のWAVファイルを再生する * * オリジナル・ソース作成:後閑氏(MPLAB X IDE使用) * * コメント追加+ソース変更:N.Ishii(MPLAB IDE V8.60使用) 2014/9/26 **********************************************************************/ //#include #include "p33FJ64GP802.h" // MPLAB IDE V8.60の場合は、xc.hは使用不可 // 通常どおり、MPUデバイス名.hをインクルードする #include "lcd_i2c_lib.h" #include "GenericTypeDefs.h" #include "FSIO.h" #include //// コンフィギュレーション設定 80MHz (MPLAB IDE V8.60の場合は、通常の書き方でないと動かなかった) /// #pragma configという記述は、MPLAB IDE V8.60には無いようである _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) /* 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)) int Pattern[512]; // 正弦波データ /* グローバル変数、定数定義 */ int i, Mode, Flag, SDFlag, EndFlag; unsigned int ptr, ptrA, ptrB; int TestFlag; // 追加 /* LCD用メッセージ */ const unsigned char StatMsg[] = "Start WAV Player"; const unsigned char FerrMsg[] = "File Open error!"; /* 関数プロトタイピング */ void DispFile(void); unsigned int Chunk(unsigned char *buf); /*********** メイン関数 **************************/ int main(void){ /* クロックの設定 7.37MHz*4=32MHz */ CLKDIVbits.PLLPRE = 0; // 7.37MHz /2=3.685(内蔵クロック使用 追記) PLLFBDbits.PLLDIV = 42; // 3.685MHz * 44 = 162 CLKDIVbits.PLLPOST = 0; // 162MHz / 2 = 81MHz -> 40.5MIPS(原文誤記で、162mHzになっていた。また40MIPSは約で、正確には、40.5MIPS) /* I/Oの初期設定 */ AD1PCFGL = 0xFFFF; // すべてデジタルにセット TRISA = 0x001C; // RA2,3 input TRISB = 0xFFD4; // RB2,3,5 Output /* スイッチ関連設定 */ CNPU2bits.CN24PUE = 1; // RB6 Pullup CNPU2bits.CN23PUE = 1; // RB7 Pullup // CNEN2bits.CN23IE = 1; // RB7 状態変化割り込み許可(CN割込みは使わないことにした) // I2Cの初期設定 /* I2C1BRG = 0x9C; // 100kHz@16MHz(原文コメントのまま)Fcy=16MHzの場合は、0x9Cの設定で計算上、約100kHz(102.6Hz)になるが、 // Fcy= 40.5MHzに設定されているので、実際は計算上、約250kHz(259.6kHz)となる。 // オシロでSCL波形を見てみたら、5.1kΩのプルアップでもかなり波形が鈍っていたので、設定値を修正して // 100kHz@40.5Mになるようにした。 */ I2C1BRG = 0x0194; // 100kHz@40.5M I2C1CON = 0x8000; // I2Cイネーブル /* SPIのピン割り付け */ RPINR20bits.SDI1R = 3; // SDI1をRP3に RPOR0bits.RP0R = 8; // SCK1をRP0に RPOR0bits.RP1R = 7; // SDO1をRP1に /* 液晶表示器初期化 */ lcd_init(); // 初期化 lcd_clear(); // 全消去 lcd_cmd(0x80); // 1行目指定 lcd_str(StatMsg); // 開始表示 /* 補助クロック初期化 */ 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割込みフラグクリア /** テスト用正弦波生成 **/ for(i=0; i<512; i++) // 正弦波テーブル生成 Pattern[i] = (int)(0x7FFF * sinf(6.28*i/32)); /* //// カードの実装確認とディレクトリ読み込み (永久待ち) while(!FSInit()); // FATの初期化 */ while(PORTAbits.RA2 != 0) // Check Mount SD Card(CD信号入力をチェック中し、未挿入の場合、赤LED点滅 追加) { LATAbits.LATA0 = 1; // Red LED ON Delay_ms(1000); LATAbits.LATA0 = 0; // Red LED OFF Delay_ms(1000); } // SD Mount(マウント完了時、緑LED1回点滅 追加) LATAbits.LATA1 = 1; // Green LED ON Delay_ms(1000); LATAbits.LATA1 = 0; // Green LED OFF Delay_ms(1000); FSInit(); // FATの初期化(このファイルシステムは、戻り値を持つが、未使用) /* 変数の初期化 */ Mode = 2; Flag = 0; SDFlag = 0; EndFlag = 0; TestFlag = 0; // 追加 // IEC1bits.CNIE = 1; // 状態変化割り込み許可(CN割込みは使わないことにした) /*************** メインループ ********************/ while(1){ /** モードで分岐 ***/ switch(Mode){ /****** テストモードの場合 ********/ case 1: while(TestFlag == 1) // テストモードの場合、SW1(MODE SW)が押されるまで、SINデータ転送(DAC)を繰返す { IEC4bits.DAC1LIE = 0; // 割り込み禁止 IEC4bits.DAC1RIE = 0; // 割り込み禁止 ptr = 0; // ポインタリセット result = 512; // 正弦波データ分解能 /// 一巡繰り返し while(ptr < result){ while(!IFS4bits.DAC1LIF); // Lchレディー待ち IFS4bits.DAC1LIF = 0; // フラグクリア DAC1LDAT = Pattern[ptr]; // 正弦波データ出力 while(!IFS4bits.DAC1RIF); // Rchレディー待ち IFS4bits.DAC1RIF = 0; // フラグクリア DAC1RDAT = Pattern[ptr]; // 正弦波データ出力 ptr++; // ポインタ更新 } if(PORTBbits.RB7 == 0) TestFlag = 0; } /// 途中で、SW1(MODE SW)が押されたら、再生モードに移行 Mode = 2; // 再生モードへ移行 while(PORTBbits.RB7 == 1); // SW1チャッタ回避 Delay_ms(30); break; /******* 最初の音楽データの出力の場合 ************/ /// WAVフォーマットの音楽データは、Lch下位, Lch上位, Rch下位, Rch上位,・・・の順に並んでいる case 2: /* 最初のファイルのサーチ(ルートだけにWAVファイルがあることが前提) */ result = FindFirst("*.*", ATTR_ARCHIVE, rptr); if(result == 0){ fptr = FSfopen(Record.filename, FS_READ); // ファイルのオープン DispFile(); // ファイル名表示 if(fptr != 0){ // 正常オープンの場合 Flag = 0; // BufferA指定 SDFlag = 0; // SD読み出しフラグクリア /** 最初のデータ読み出し ***/ count = FSfread(BufferA, 1, 4096, fptr); // 最初の読み出し ptrA = Chunk(BufferA); // WAVファイルの先頭指定 ptrB = 0; // ポインタリセット SDFlag = 1; // BufferBへも格納させる(原文誤記で'可能'になっていた) Mode = 3; // 次のモードへ IEC4bits.DAC1LIE = 1; // Lch割り込み許可 IEC4bits.DAC1RIE = 1; // Rch割り込み許可 追加 } } /** ファイルオープンできなかった場合 **/ else{ lcd_cmd(0xC0); lcd_str(FerrMsg); // メッセージ表示 } break; /******** ファイルの再生継続の場合 **************/ case 3: 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のチェック 曲送り *********/ if(PORTBbits.RB6 == 0){ // SW2オンの場合 EndFlag = 1; // 終了フラグセット } /// SW1のチェック テストモードへの移行 140915:CN割込みは使わないでメインで読込むように修正 if(PORTBbits.RB7 == 0){ // SW1オンの場合 TestFlag = 1; // テストフラグセット } if(TestFlag == 0){ // 再生モードの場合 /***** 曲終了か曲送りスイッチの処理 **/ 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(1000); // 曲間の間 Delay_ms(500); // 曲間の間 /* 連続再生のため次のファイルサーチ */ result = FindNext(rptr); // 次のファイルへ if((result==0)&&(Record.attributes==ATTR_ARCHIVE)){ // ファイルがあった場合 fptr = FSfopen(Record.filename, FS_READ); // ファイルをオープン /** 最初のデータ読み出し ***/ count = FSfread(BufferA, 1, 4096, fptr); // 最初の読み出し Flag = 0; ptrA = Chunk(BufferA); // WAVファイルの先頭指定 ptrB = 0; // バッファポインタリセット DispFile(); // ファイル名表示 /* if(EndFlag){ // スイッチ曲送りの場合 EndFlag = 0; // 終了フラグクリア } */ SDFlag = 1; // BufferBにも格納させる IEC4bits.DAC1LIE = 1; // Lch割り込み許可 IEC4bits.DAC1RIE = 1; // Rch割り込み許可 追加 } else{ /* 全ファイル終了なら最初から繰り返し */ Mode = 2; // モード2に戻る } /// スイッチ曲送りの場合の、終了フラグクリアは、ここに記述しないと /// 収録最終曲を再生中に、SKIP SWが押された場合、最初の曲のファイル名表示の後、直ぐに /// 再生完了処理されてしまい再生しないで、次の曲の再生に移ってしまう if(EndFlag){ // スイッチ曲送りの場合 EndFlag = 0; // 終了フラグクリア } } } else{ // テストモードの場合 IEC4bits.DAC1LIE = 0; // Lch割り込み禁止 IEC4bits.DAC1RIE = 0; // Rch割り込み禁止 追加 /***** 再生完了処理 *****/ /* バッファクリア */ for(i=0; i<4096; i++){ BufferA[i] = 0; BufferB[i] = 0; } FSfclose(fptr); // ファイルのクローズ Mode = 1; // モード1へ while(PORTBbits.RB7 == 0); // SW1チャッタ回避 Delay_ms(30); } } break; default : break; // どれでもない場合 } } } /********************************* * データチャンクサーチ関数 **********************************/ 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); } /******************************************** * ファイル名表示 *********************************************/ void DispFile(void){ /* lcd_cmd(0xC0); // 2行目指定 lcd_str((const unsigned char *)Record.filename); // ファイル名表示 */ /// 以下のように修正(こうしないと、前のファイル名のお尻部分が残る場合がある) // 2行目(12文字分:最大文字数)クリア lcd_cmd(0xC0); // 2行目指定 lcd_str(" "); // 2行目(12文字分)クリア // 2行目にファイル名表示 lcd_cmd(0xC0); // 2行目指定 lcd_str((const unsigned char *)Record.filename); // ファイル名表示 } /* オリジナルソースでのDAC記述(LCH割込みのみで、Rchの空き状態(DAC1STATbits.EFLL)をチェックして両chデータのFIFO転送を行う) では、時々(約1.5時間再生中に、1回の割合)次のファイルの再生が、おかしくなる(ブツ音になる)現象が発生した。 なので、DAC割込み関数は、Lchと、Rchで、2つ用意し、割込みを許可する時も、両ch許可するように修正した。 両DAC割込み関数の中では、ステータスリードは行わず、基本、FIFOへのデータ転送と、バッファフルチェックのみとした。 //////////////////////////////////////////////////////////////// // DACL割り込み処理関数 // 1回の割込み(割込み周期:22.7uS(44.1kHz))で、1ワード転送 //////////////////////////////////////////////////////////////// void __attribute__((interrupt, no_auto_psv)) _DAC1LInterrupt(void){ IFS4bits.DAC1LIF = 0; // 割り込みフラグクリア /// バッファAの場合 if(Flag == 0){ // バッファAの場合 DAC1LDAT = BufferA[ptrA+1] * 256 + BufferA[ptrA]; // 次のLchデータ出力 ptrA += 2; // ポインタA更新 while(DAC1STATbits.RFULL); // Rch空きまで待つ 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の場合 DAC1LDAT = BufferB[ptrB+1] * 256 + BufferB[ptrB]; // 次のLchデータ出力 ptrB += 2; // ポインタB更新 while(DAC1STATbits.RFULL); DAC1RDAT = BufferB[ptrB+1] * 256 + BufferB[ptrB]; // 次のRchデータ出力 ptrB += 2; // ポインタB更新 if(ptrB >= count){ // バッファ終了の場合 Flag = 0; // バッファ切り替え SDFlag = 1; // SD読み出しフラグセット ptrB = 0; // ポインタBリセット } } } */ /*************************************************************** * 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リセット } } } /* //// SW1,2は、両方共にメインループで、読込むように修正した 140915 ///////////////////////////////////////// // スイッチ割り込み処理 ////////////////////////////////////////// void __attribute__((interrupt, no_auto_psv)) _CNInterrupt(void){ IFS1bits.CNIF = 0; // 割り込みフラグクリア //// SW1の場合 if(PORTBbits.RB7 == 0){ // SW1オンの場合 if((Mode == 2) || (Mode == 3)) // モード2か3の場合 Mode = 1; // モード1にする else // モード1の場合 Mode = 2; // モード2にする while(PORTBbits.RB7 == 0); // チャッタ回避 Delay_ms(30); } } */