/***************************************************************** * akc6951ライブラリ・ソースファイル * DSP MODULE:aitendo M6951(原典先にならいakc6951とした。) * * 原典参考先:諏訪工房さん * * 2018/7/11 N.Ishii * * <更新履歴> * 2018/7/28 * V1a:ワイドFMを受信出来るようにした。 * 2020/8/6 * V1b: * (1) #include "akc6951_lib_V1a.h"→ #include "akc6951_lib_V1b.h" * (2) SCLが400kになっていた件に伴うコメント修正等 ******************************************************************/ #include //#include "akc6951_lib_V1a.h" #include "akc6951_lib_V1b.h" /********************* * mS 単位の遅延 **********************/ void delay_ms (int ms){ while(ms-- > 0)__delay_ms(1); } /***************************************************************************** * SSPを I2C Master mode、SCL 100kHz @ 4MHz に設定 * ↑- SSPを I2C Master mode、SCL 400kHz @ 16MHz に設定の誤記 200806 * BRGにセットする値:SSPADD=9 * SCL速度=_XTAL_FREQ / (SSPADD + 1) * 4 * = 16000000 / (9 + 1) * 4 = 16000000 / 40= 400kHzになる。 200806 *******************************************************************************/ void i2cintl(void){ SSPCON1 = 0b00001000; // I2C Master modeにする SSPCON2 = 0x00; // PowerOn初期値にする // SSPSTAT = 0b10000000; // スルーレート制御はOff(400kでoffにしても動いてはいました。) SSPSTAT = 0b00000000; // スルーレート制御はOn:400kの場合はonにするのが常道のようなので修正しました。 SSPADD = 9; // クロックの設定 100k@4MHz //// 下記: 200806 // ↑- これは、改修前の、Fosc=4Mの時の話 // Fosc=16Mに改版後、気が付かないでいた。 // クロックの設定 400k@16MHzの誤記である。 SSPCON1bits.SSPEN = 1; // SSP 有効にする bit5 } /************************************************************** * M6951のレジスタに、1バイト書き込む *************************************************************/ void akc6951_write_byte(char regadr, char data) { i2cStart(); // 送信開始 i2cTxData(DEVADDR +MW); // Chipアドレス+ライトビット送信 i2cTxData(regadr); // RegAdd送信 i2cTxData(data); // データ送信 i2cStop(); // 送信終了 __delay_ms(1); } /************************************************************** * レジスタ書込み開始アドレスを指定し、 * そこからM6951のレジスタに、nバイト・ブロック書込みを行う。 **************************************************************/ void akc6951_write_byte_block(char regadr, char *data, int nbytes) { i2cStart(); // 送信開始 i2cTxData(DEVADDR +MW); // Chipアドレス+ライトビット送信 i2cTxData(regadr); // RegAdd送信 while(nbytes--){ i2cTxData(*data++); // データ・ブロック送信 } i2cStop(); // 送信終了 __delay_ms(1); } /******************************************************** * M6951のレジスタから、1バイト読み込む *********************************************************/ char akc6951_read_byte(char regadr) { char data; i2cStart(); // 送信開始 i2cTxData(DEVADDR +MW); // Chipアドレス+ライトビット送信 i2cTxData(regadr); // RegAdd送信 i2cStop(); i2cStart(); // リスタートに相当 i2cTxData(DEVADDR +MR); // Chipアドレス+リードビット送信 data = i2c_Read(); // 1バイト読込 i2c_NAck(); // NACK送信 i2cStop(); return data; } /************************************************************** * レジスタ読込み開始アドレスを指定し、 * M6951のレジスタから、nバイト・ブロック読込みを行う。 **************************************************************/ void akc6951_read_byte_block(char regadr, char *data, int nbytes) { i2cStart(); // 送信開始 i2cTxData(DEVADDR +MW); // Chipアドレス+ライトビット送信 i2cTxData(regadr); // RegAdd送信 i2cStop(); i2cStart(); // リスタートに相当 i2cTxData(DEVADDR +MR); // Chipアドレス+リードビット送信 while(nbytes--){ *data++ = i2c_Read(); if(nbytes){ i2c_Ack(); } else{ i2c_NAck(); } } i2cStop(); } /******************************************** * SSPBUF に1文字保存し送信終了を待つ ********************************************/ void i2cTxData(char data){ PIR1bits.SSPIF = 0; // 終了フラグクリア SSPBUF = data; // データセット while(!PIR1bits.SSPIF); // 送信終了待ち } /******************************************************************** * SSPBUF から1文字受信 ********************************************************************/ unsigned char i2c_Read( void ){ SSPCON2bits.RCEN = 1; // データ受信を指示 while ( !SSPSTATbits.BF ); // 8ビット受信の完了を待つ return SSPBUF; // 受信データで復帰 } /**************************************** * akc6951_initialize *****************************************/ void akc6951_initialize(void) { /// get default value(バッファを介して書込んでいるので最初に全レジスタ読込) akc6951_read_byte_block(0, RegVal, 14); // Reg0-13まで、14バイト分RegVal[0]〜に読込 akc6951_read_byte_block(20, RegVal+20, 8); // Reg20-27まで、8バイト分RegVal[20]〜に読込 /// set initial parameter→ 各レジスタの設定ビットを他のビットをマスク(defult値を保持)して決める。 PowerOn(); // Reg0-b7:power_onビットを'1':ONにセット FMmode(); // Reg0-b6:fm_enビットを'1':FM MODEにセット SeekUp(); // Reg0-b3:seekupビットを'1':順方向シークにセット MuteOff(); // Reg0-b2:muteビットを'0':mute off AMBand(MW2); // 522-1620KHz→ Reg1-b2-0:fmbandビットマスク Reg1-b7-3:ambandビットを上位にシフトしてセット // MW2:522-1620KHz, 9KHz Step (Japan) // FMBand(FM4); // 76-90MHz→ Reg1-b3-7:ambandビットマスク Reg1-b2-0:ambandビットを上位にシフトしてセット // FM4:76-90MHz (Japan) FMBand(FM2); // 76-108MHz→ Reg1-b3-7:ambandビットマスク Reg1-b2-0:ambandビットを上位にシフトしてセット // FM2:76-108MHz 180728 OSC32K(); // Reg2-b6:Ref_32k_modeビットを'1':水晶f=32.768kHzにセット AM3KStep(); // Reg2-b5:Mode3kビットを'1':AMモード 3kHzステップにセット SetChannel((77800-30000)/25); // 77.8MHz 77800KHz=25KHz*x+30000KHz /* UsrChanStart((76000-30000)/25/32); // 76MHz UsrChanStop((90000-30000)/25/32); // 90MHz */ UsrChanStart((76000-30000)/25/32); // 76MHz UsrChanStop((108000-30000)/25/32); // 108MHz //Volume(63); RadioMode(); // Reg6-b1:lineビットを'0':ラジオモードにセット('1'でライン入力モード) DualSPK(); // Reg6-b0:phase_invビットを'0':デュアルspにセット //SingleSPK(); NormalOPR(); // Reg7-b7-6:rsvビットを'0':通常動作にセット('0'でデバッグモード) De50(); // Reg7-b5:deビットを'1':ディエンファシス=50uSにセット(日本) BaseBoostOff(); // Reg7-b4:bbenビットを'0':低域ブースト無し StereoMode(); // Reg7-b3-2:'00'オートステレオ/Reg7-b3:'1'ステレオにセット FMBandWidth(BW50K); // Reg7-b7-2マスク後、b1-0:bwに、2をセット VolCtrlByI2C(); // Reg9-b3:pd_adc_volビット'1'i2cにセット // たぶん音量制御に、I2Cを使うという意味 SelXTL(); // Reg9-b2:osc_enビット'1'水晶使用にセット LowPowerOn(); // Reg9-b0:lv_enビット'1'低電力消費モードにセット FMSeekStep(STEP50K); // Reg11-b7-6,3-0マスク後、b5-4:spaceに、01をセット fmシークステップ=50k ADCEnable(); // Reg12-b7:pd_adcビットを'0':adc許可にセット RFEnable(); // Reg12-b5:pd_rxビットを'0':analog+rx許可にセット TuneLED(); // Reg13-b6:st_ledビットを'0':同調LEDにセット // tunedピン(4pin:LED-Anode)にLEDを接続 // 電流制限抵抗不要 →モジュールにled実装されている VolOut(VL0DB); // Reg13-b7-4,1-0マスク後、b3-2:vol_preに、00をセット 音量レベル=0db /// write parameter→ ここでまとめて、まず、Reg1-13まで13バイト分書込む akc6951_write_byte_block(1, RegVal+1, 13); // write PowerON, FMmode→ 最後に、Reg0に、1バイト書き込む akc6951_write_byte(0, RegVal[0]); } /*************************************************** * FM受信周波数設定 ***************************************************/ int akc6951_FM_tunning(signed long freq) { int channel; unsigned int to; // set request channel channel = (int)((freq - 30000L)/25L); // Lは、x1000のマクロで、30000000/25000 と同じ意味 // 最大受信f/ステップf // Reg2[12:8]:上位5bit channel tuneビット→ freq=25kHz * channel + 30MHz (fm時) // Reg3[7:0]:下位8bit channel tuneビット SetChannel(channel); // akc6951_write_byte_block(2, RegVal+2, 2); // Register2,Register3 // void akc6951_write_byte_block(char regadr, char *data, int nbytes) /// trigger tunning FMmode(); // select FM // Reg0-b6:fm_en '1'でFMモード/'0'でAMモード(1セットなので他ビットを変えない為にOR ResetTune(); // Reg0-b5:tuneビットをリセット(チュウニング・プロセスをリセット) // 0セットなので他ビットを変えない為にAND /// ---- ここまでで、Reg0のfm_enと、tuneビットの値を得る。FMモード/チュウニング・プロセスをリセット------ akc6951_write_byte(0, RegVal[0]); // ここで初めてRegister0に上設定値を書き込む(単純バイトI2C書き込み) TrigTune(); // Reg0-b5:tuneビットをセット(チュウニング・プロセスをセット) akc6951_write_byte(0, RegVal[0]); // Register0 // wait for tunning complete to = 0xffff; while(to--){ // read tunning complete status RegVal[20] = akc6951_read_byte(20); // Reg20リードオンリーをバイト読み。 if(STC) break; // Reg20-b6:stc(シークorTUNEプロセスビットが、'1'completeかチェック } // judge tune result if(!to) return -1; // リードをffffh回繰返してもerrorの場合 return 0; // '0':complete } /*************************************************** * AM受信周波数設定 ***************************************************/ int akc6951_AM_tunning(signed long freq) { int channel; unsigned int to; // set request channel channel = (int)(freq/3L); // AM 3KHz Step SetChannel(channel); akc6951_write_byte_block(2, RegVal+2, 2); // Register2,Register3 // triiger tunning AMmode(); // select AM ResetTune(); akc6951_write_byte(0, RegVal[0]); // Register0 TrigTune(); akc6951_write_byte(0, RegVal[0]); // Register0 // wait for tunning complete to = 0xffff; while(to--){ // read tunning complete status RegVal[20] = akc6951_read_byte(20); if(STC) break; } // judge tune result if(!to) return -1; return 0; } /**************************************** * akc6951_音量設定 *****************************************/ void akc6951_VOL_setting(unsigned char vol) { // set volume Volume(vol); // Reg6-b7-2:volumes Reg6-b1-0をマスクして指定ビット位置に音量値セット akc6951_write_byte(6, RegVal[6]); // Register6に音量値を書込む } /**************************************** * akc6951_SP設定 *****************************************/ void akc6951_SelectSpeaker(char sw) { // select speaker if(sw){ DualSPK(); }else{ SingleSPK(); // Reg6-b0:phase inv:'1'シングルsp } akc6951_write_byte(6, RegVal[6]); // Register6 }