PIC18F14K50の基本動作から応用プログラムまでを学びます。

ホーム
16F18313
16F18325
16F18346
16F1619
Curiosity
---
---
    
12F1822
16F1455
16F1459
18F14K50
18F26J50
dsPIC
その他
DSP ラジオ BK1088

aitendo のDSP ラジオ モジュール BK1088 は、I2Cで制御できる AM FM SW ラジオです。このモジュールを使い実用性のあるFMラジオを作成しました。BK1088 は、I2Cで制御しますが、そのデータの並び順が特殊で I2C EEPROM とは異なります。具体的には、R/W ビットが2バイト目にあります。操作対象のRegADDを1ビット左にシフトし、LSBにR/Wビットを付加して送信します。

EEPROM | Start | ChipADD + W | ADD | ADD | DATA | Stop |
BK1088 | Start | ChipADD | RegADD + W | DATA | DATA | Stop |

BK1088 で、ラジオ局を受信するには、BK1088内部のレジスタに適切な値を書き込む必要があります。また、受信する周波数の変更や、出力音量の変更も、定められたレジスタの内容を書き換えることで実現できます。ICの働きを理解するために、FM放送を受信するだけの BK_Simple と、実用的なポータブルFMラジオである BK_FM の2種類のプログラムを作成しました。8ピンPIC12F1822 を使った同じ実用的なBK1088FMラジオも公開しています。

プログラム BK_Simple

BK_Simple プログラムは、放送大学 77.1MHz を受信設定するだけのプログラムです。 BK1088の初期化、I2Cデータ書込み だけで、それ以外の機能は付加してありません。どのように、DSP モジュールをコントロールするのか理解しやすいでしょう。出力する音量を制御するには、Bk1088 の05 レジスタのLSB 5ビット(bit4 - bit0)に希望する値を書き込みます。受信周波数を変更するには、Bk1088 の 03 レジスタに、バンドの最少周波数とステップ周波数から計算した周波数セット値をセットします。セット後、同じ 03 レジスタ のMSB (TuneBit)を「1」にセットします。ソースリストと見比べてください。なお、上記回路図に示されている PushSW は、使用していません。

/*********************************************************************
 * DSP RADIO BK1088
 *   受信バンド:FM
 *   音量:vol (0x00-0x1F)に設定
 *   受信周波数:inFreq に設定する
 * PIC18F14K50  MPLAB X IDE with XC8 Ver1.32
 * Copyright (c) 2014 iwamoto All Rights Reserved
 * *******************************************************************/

#include <xc.h>

//********************************************************************
// vol    : reg 05に設定する音量値  ( 0 - 31 )
// inFreq : reg 03に設定する値  ( freq - mimFreq ) / StepFreq
//           77.1MHzの場合       ( 77.1x100 - 7600 ) / 10
//*********************************************************************
#define  vol         22                       // 音量   0 - 31
#define  inFreq     (7710 - 7600) / 10        // 放送大学  11
//#define inFreq     (8000 - 7600) / 10        // FM東京  40
//#define inFreq     (8470 - 7600) / 10        // FM横浜  87

//******************* コンフィグレーション ****************************
#pragma config FOSC = IRC        //  内部クロック
#pragma config USBDIV = OFF, CPUDIV = NOCLKDIV
#pragma config IESO  = OFF, FCMEN = OFF, PLLEN  = ON
#pragma config BORV  = 30,  BOREN = OFF, PWRTEN = OFF
#pragma config WDTPS = 32768, WDTEN = OFF
#pragma config MCLRE = OFF, HFOFST = OFF, XINST  = OFF
#pragma config BBSIZ = OFF, LVP    = OFF, STVREN = ON
#pragma config CP1   = OFF, CP0  = OFF, CPD  = OFF, CPB  = OFF
#pragma config WRT1  = OFF, WRT0 = OFF, WRTB = OFF, WRTC = OFF
#pragma config EBTR1 = OFF, EBTR0 = OFF, EBTRB  = OFF
//
//******************* 定義 *******************************************
#define _XTAL_FREQ    4000000
#define i2cStart()    SSPCON2bits.SEN=1;while(SSPCON2bits.SEN)
#define i2cStop()     SSPCON2bits.PEN=1;while(SSPCON2bits.PEN)

//******************* プロトタイプ ***********************************
void delay_ms (int ms);
void i2cintl(void);
void Bk1088_Write(unsigned int, unsigned int);
void i2cTxData(char data);

// ******************* main ******************************************
void main() {
    OSCCON = 0b01010010;                        //  内部クロック4Mhz
    PORTA = 0x00; PORTB = 0x00; PORTC = 0x00;
    TRISA = 0xFF; TRISB = 0xFF; TRISC = 0xFF;   //Port すべて入力
    ANSEL  = 0x00; ANSELH = 0x00;               //すべてデジタル
// I2C初期化 SCLK=100KHz ------------------------------------------
    i2cintl();
// DSP初期化 受信バンド:FM 音量:1A -----------------------------
    Bk1088_Write(0x02, 0x6281);           // power config
    Bk1088_Write(0x03, inFreq);           // channel
    Bk1088_Write(0x05, 0x3740 + vol);     // system config2
    Bk1088_Write(0x06, 0x0930);           // system config3
    Bk1088_Write(0x07, 0x0901);           // test1
    Bk1088_Write(0x14, 0x878E);           // boot config 5
    Bk1088_Write(0x1A, 0x0001);           // boot config 11
    Bk1088_Write(0x1B, 0x48D4);           // analog config 1
    Bk1088_Write(0x1D, 0x0200);           // analog config 3
    Bk1088_Write(0x1E, 0x80AA);           //
    Bk1088_Write(0x20, 0x0EF7);           //
    Bk1088_Write(0x26, 0x8400);           //
    Bk1088_Write(0x03, inFreq | 0x8000);  // TUNEビットを立てる
    delay_ms(500);                        // 安定するまで遅延
    Bk1088_Write(0x02,0x0281);            // mute off 音を出す
    while (1);
}

//********************************************************************
// mS 単位の遅延
//********************************************************************
void delay_ms (int ms){
    while(ms-- > 0)__delay_ms(1);
}
//********************************************************************
// SSPを I2C Master mode、SCL 100kHz @ 4MHz に設定
//********************************************************************
void i2cintl(void){
    SSPCON1 = 0b00001000;                // I2C Master modeにする
    SSPCON2 = 0x00;                      // PowerOn初期値にする
    SSPSTAT = 0b10000000;                // スルーレート制御はOff
    SSPADD  = 9;                        // クロックの設定 100k@4MHz
    SSPCON1bits.SSPEN = 1;               // SSP 有効にする bit5
}
//********************************************************************
// Bk1088_のレジスタに書き込む
//********************************************************************
void Bk1088_Write(unsigned int add, unsigned int data) {
    i2cStart();                           // 送信開始
    i2cTxData(0x80);                      // Chipアドレス送信
    i2cTxData(add << 1);                  // RegAdd + Write
    i2cTxData(data >> 8);                 // Register data MSB
    i2cTxData(data & 0xff);               // Register data LSB
    i2cStop();                            // 送信終了
    __delay_us(500);
}
//********************************************************************
// SSPBUF に1文字保存し送信終了を待つ
//********************************************************************
void i2cTxData(char data){
    PIR1bits.SSPIF = 0;                  // 終了フラグクリア
    SSPBUF = data;                       // データセット
    while(!PIR1bits.SSPIF);              // 送信終了待ち
}

プログラム BK_FM

BK_FM プログラムは、BK_Simple を基本に、PushSW を3ケ追加し、音量の増減、受信FM局の変更 を可能にしました。受信局は、東京地区で受信できる周波数jを書き込んであります。ほかの地区では、プログラム11行目の presetFreq を地元FM局に周波数に変更してPICに書き込んでください。 音量や、受信局を変更すると変更内容をPIC内のEEPROMに書き込んでいます。このため、電源をOFFしても、その設定は保存され、次に電源をONとしたとき前の状態が再現されます。さらに、選局ボタン(station) を長押しすると、常に homeSt でプログラム上に指定した局が受信するようプログラムされています。 また、変更動作が完了すると、省電力と雑音防止を兼ねPICはスリープ状態になり、クロックも停止します。PushSWのどれかを押すことでPICはスリープが解除され、ボタンの処理を再開します。

/*********************************************************************
 * DSP RADIO BK1088
 *   受信バンド:FM
 *   音量:受信局は EEPROM に保存され、電源OFF時でも失われない
 *   選局を長押しすると、ホーム局 stHome に戻る
 *   受信周波数:presetFreq に設定する
 *
 * PIC18F14K50  MPLAB X IDE with XC8 Ver1.32
 * Copyright (c) 2014 iwamoto All Rights Reserved
 * *******************************************************************/

#define  presetFreq  7610,7710,7810,8000,8130,8250,8470,0

#define  long_ms     1000                     // 長押し時間 ms
#define  homeSt      1                        // 長押しで選局する局番号
#define  minFreq     7600                     // 最低周波数
#define  maxFreq     9000                     // 最高周波数
#define  stepFreq    10                       // ステップ周波数
#define  SWs         (PORTA|0b11000111)       // SW 3ケの状態
#define  VolUp       !PORTAbits.RA5           // SW 音量 大
#define  Voldn       !PORTAbits.RA4           // SW 音量 大
#define  station     !PORTAbits.RA3           // SW 選局

#include <xc.h>

//******************* コンフィグレーション ****************************
#pragma config FOSC = IRC        //  内部クロック
#pragma config USBDIV = OFF, CPUDIV = NOCLKDIV
#pragma config IESO  = OFF, FCMEN = OFF, PLLEN  = ON
#pragma config BORV  = 30,  BOREN = OFF, PWRTEN = OFF
#pragma config WDTPS = 32768, WDTEN = OFF
#pragma config MCLRE = OFF, HFOFST = OFF, XINST  = OFF
#pragma config BBSIZ = OFF, LVP    = OFF, STVREN = ON
#pragma config CP1   = OFF, CP0  = OFF, CPD  = OFF, CPB  = OFF
#pragma config WRT1  = OFF, WRT0 = OFF, WRTB = OFF, WRTC = OFF
#pragma config EBTR1 = OFF, EBTR0 = OFF, EBTRB  = OFF
//
//******************* 定義 *******************************************
#define _XTAL_FREQ    4000000
#define i2cStart()    SSPCON2bits.SEN=1;while(SSPCON2bits.SEN)
#define i2cStop()     SSPCON2bits.PEN=1;while(SSPCON2bits.PEN)

//******************* プロトタイプ ***********************************
void delay_ms (int);
void i2cintl(void);
void Bk1088_Write(unsigned int, unsigned int);
void i2cTxData(char);
void Freq_set(int);

/* ********** EEROM に設定する 初期周波数番号 *********** */
__EEPROM_DATA(1,22,0,0,0,0,0,0);               //0番地:局、1番地:音量

// ******************* main ******************************************
void main() {
    int FM_FREQ[] = {presetFreq};             // 周波数プリセット
    char st;                                  // 受信中の局番号
    signed char vol;                          // 音量
    int SwTime;                               // SW を押している時間
    char dummy;                               // 作業変数

    OSCCON = 0b01010010;                      // 内部クロック4Mhz
    PORTA = 0x00; PORTB = 0x00; PORTC = 0x00; // 全ポート 0
    TRISA = 0xFF; TRISB = 0xFF; TRISC = 0xFF; // PortC すべて入力
    ANSEL = 0x00; ANSELH = 0x00;              // すべてデジタル
    WPUA  = 0xFF; WPUB   = 0xFF;              // 弱プルアップするピンを選択
    INTCON2bits.nRABPU = 0;                   // 弱プルアップ有効
    IOCA = 0b00111000;                        // PORTA 状態変化割り込み
    INTCONbits.RABIE = 1;

// I2C初期化 SCLK=100KHz ------------------------------------------
    i2cintl();
// DSP初期化 受信バンド:FM 音量:22 -----------------------------
    st = eeprom_read(0);                      // 起動時受信局番号
    vol = eeprom_read(1);                     // 起動時音量
    Bk1088_Write(0x02, 0x6281);               // power config
    Bk1088_Write(0x05, 0x3740 + vol);         // system config2
    Bk1088_Write(0x06, 0x0930);               // system config3
    Bk1088_Write(0x07, 0x0901);               // test1
    Bk1088_Write(0x14, 0x878E);               // boot config 5
    Bk1088_Write(0x1A, 0x0001);               // boot config 11
    Bk1088_Write(0x1B, 0x48D4);               // analog config 1
    Bk1088_Write(0x1D, 0x0200);               // analog config 3
    Bk1088_Write(0x1E, 0x80AA);               //
    Bk1088_Write(0x20, 0x0EF7);               //
    Bk1088_Write(0x26, 0x8400);               //
    delay_ms(500);                            // 遅延
    Freq_set(FM_FREQ[st]);                    // 指定する局の周波数をセット
    Bk1088_Write(0x02,0x0281);                // mute off 音を出す
    //********************************************************************
    while(1){
        while(SWs == 0xFF);                   // SW が押されるのを待つ
        delay_ms(5);                          // チャタリング防止
        if(station){ // -------- 局を変更 ------------------------
            st++;                             // 局番号を更新する
            if(FM_FREQ[st]==0)st=0;           // 局番号の範囲確認
            Freq_set(FM_FREQ[st]);            // 指定する局の周波数をセット
            eeprom_write(0, st);              // EEPROM 0番地 書込み
        }
        if(VolUp){ // -------- 音量 大 ------------------------
            vol++;                            // 音量を上げる
            if(vol > 28)vol = 28;             // 上限の確認
            Bk1088_Write(0x05,0x3740 + vol);  // 音量をセット
            eeprom_write(1, vol);             // EEPROM 1番地 書込み
        }
        if(Voldn){ // -------- 音量 小 ----------------
            vol--;                            // 音量を上げる
            if(vol <  0)vol = 0;              // 下限の確認
            Bk1088_Write(0x05,0x3740 + vol);  // 音量をセット
            eeprom_write(1, vol);             // EEPROM 1番地 書込み
        }
        SwTime = 0;
        while(SWs != 0xFF){                   // SW が押されているなら
            delay_ms(1);                      // 離れるのを待つ
            if(!station)continue;             // 選局 SW なら以下を実施
            if(SwTime < long_ms){             // 長押しより 短いなら
                SwTime++;                     //   長押し時間を増やす
            }else if(SwTime == long_ms){      // 長押し指定時間なら
                SwTime++;                     //   長押し時間を増やし
                st = homeSt;                  //   ホーム局を
                Freq_set(FM_FREQ[homeSt]);    //   選局する
                eeprom_write(0, st);          //   EEPROM 0番地 書込み
            }                                 // SW が離れるのを待つ
        }
        delay_ms(5);                          // チャタリング防止
        dummy=PORTA;                          // 状態変化割込み準備
        INTCONbits.RABIF = 0;                 // 状態変化割込み準備
        NOP();
        SLEEP();                              // SW が押されるまでスリープ
        NOP();
   }
}

//********************************************************************
// mS 単位の遅延
//********************************************************************
void delay_ms (int ms){
    while(ms-- > 0)__delay_ms(1);
}
//********************************************************************
// SSPを I2C Master mode、SCL 100kHz @ 4MHz に設定
//********************************************************************
void i2cintl(void){
    SSPCON1 = 0b00001000;                     // I2C Master modeにする
    SSPCON2 = 0x00;                           // PowerOn初期値にする
    SSPSTAT = 0b10000000;                     // スルーレート制御はOff
    SSPADD  = 9;                              // クロックの設定 100k@4MHz
    SSPCON1bits.SSPEN = 1;                    // SSP 有効にする bit5
}
//********************************************************************
// Bk1088_のレジスタに書き込む
//********************************************************************
void Bk1088_Write(unsigned int add, unsigned int data) {
    i2cStart();                               // 送信開始
    i2cTxData(0x80);                          // Chipアドレス送信
    i2cTxData(add << 1);                      // RegAdd + Write
    i2cTxData(data >> 8);                     // Register data MSB
    i2cTxData(data & 0xff);                   // Register data LSB
    i2cStop();                                // 送信終了
    __delay_us(500);
}
//********************************************************************
// SSPBUF に1文字保存し送信終了を待つ
//********************************************************************
void i2cTxData(char data){
    PIR1bits.SSPIF = 0;                       // 終了フラグクリア
    SSPBUF = data;                            // データセット
    while(!PIR1bits.SSPIF);                   // 送信終了待ち
}
//*****************************************************************/
// 新しい周波数をセットする
// 引数は、freq
//*****************************************************************/
void Freq_set(int freq){
    signed int reg_val;
    reg_val=(freq - minFreq)/stepFreq;
    Bk1088_Write(0x0003, reg_val);            // 周波数セット
    Bk1088_Write(0x0003, reg_val | 0x8000);   // TUNEビットを立てる
}