JavaScript が無効です。有効にしてください。
ななよんのホームページ
トップ > 電子工作 > 作品紹介 > 【KT0913】8ピンPICでラジオを作る【12LF1822】

【KT0913】8ピンPICでラジオを作る【12LF1822】

最終更新日さいしゅうこうしんび:2024-02-07

 最近、秋月電子通商で新商品として AM/FM ラジオ IC の KT0913 が出ました。SDR 方式のため外付け部品が少なく、I2C で制御できます。この KT0913 と 8 ピン PIC の PIC12LF1822 を使って AM/FM ラジオ受信機を作ってみました。KT0913 にはボリュームやボタンで選局・音量調節を行える機能もありますが、今回は I2C で制御します。

仕様の検討

 KT0913 は 5 V では定格オーバになるため、3.3 V で動かします。PIC の LF タイプは 5 V には対応していない(低電圧版)ものの消費電力が少ないため、電池駆動を想定してこの組み合わせで作ってみます。

 周波数等の表示には 8 文字 2 行キャラクタ LCD の AQM0802 を使います。電池 2 本で動くようにしたかったのですが、この LCD が 3.3 V 用なので、3 端子レギュレータで降圧します。また、現在はバックライト付きのほうが安いので、バックライト付きを使用し、ボタン操作してから数秒間バックライトが点灯するようにします。この LCD も I2C 制御なので、2 本の信号線でラジオ IC と LCD が動かせられます。

 出力はスピーカを鳴らせるようにし、小型化のためモノラルとします。パワーアンプには NJM2073D を使用したため、14 V まで動作します(NJM2073D は 15 V までだが、3 端子レギュレータの入力電圧の都合)。NJM2073D はステレオですが、BTL 動作も可能なので、今回はモノラルということで BTL の回路で使用します。

 操作はボタン 3 つで、1 つがモード切り替え、残り 2 つがアップとダウンです。モード切り替えのボタンを押しながらアップダウンボタンを押すと選局です。モード切り替えボタンは押さずにアップダウンで音量調節です。モード切り替えボタンを押してアップダウンを押さないで離すと、AM と FM が切り替わります。

 電源は DC ジャックとしますが、DC プラグ付きのバッテリスナップを使うことで、電池駆動も可能です。

 PIC12LF1822 は 8 ピンですが、2 本が電源端子なので、I/O として使えるのは 6 本です。そのうち 1 本は /MCLR と共用のため、入力専用となります。2 本が I2C、1 本がバックライト制御、残りの 3 本にボタンを接続します。

 ボリューム、バンド・周波数は PIC 内蔵の EEPROM に保存し、電源を切っても、次に使うときに設定が保持されるようにします。ボタン操作 10 秒後に保存するようにします。なお、EEPROM は寿命があるので(とはいっても実用上問題ないとは思いますが)注意してください。気になるようでしたら書き込み後に値をチェック(ベリファイ)し、書き込めていなかったら別の領域(番地)に書き込むようにすればいいと思います。

回路

 回路図を以下に示します。特に工夫した箇所はありません。各 IC データシートのサンプル回路そのままで組み合わせただけですね。ボタンは PIC 内蔵のプルアップを使うため、プル抵抗はありません。ボリュームがありませんが、KT0913 は I2C でボリュームのコントロールが可能なため、ソフトウェア制御にします。LCD バックライトは電流食いなので、トランジスタで制御しています。

回路図

 I2C バスにプルアップ抵抗がありませんが、KT0913 に 47 kΩ のプルアップが内蔵されています。AQM0802 モジュールには 10 kΩ のプルアップがあり、有効か無効かがはんだジャンパで選べますが、有効にしています。並列の合成抵抗が形成されますが、問題なく動作しました。また、KT0913 の ENABLE ピンにはプルダウン抵抗が内蔵されています。今回は電源スイッチで入り切りをするようにしたので、ENABLE ピンは常時 VDD に接続しています。

 KT0913 に接続した水晶発振子のコンデンサは、24 pF が指定されていましたが、入手困難なので 22 pF で代用しています。

 音声出力は、今回はモノラルのため、ソフトウェアでモノラルに設定して 1 本のみ使用しています。ステレオで製作される場合は、LOUT が左チャンネル、ROUT が右チャンネルになります。

 AM のアンテナは、普通はバーアンテナを使いますが、ブレッドボードでの実験時のみ普通の 470 μH のマイクロインダクタを使ってみました。感度は劣りますが、ブレッドボードでは使いやすいです。FM のアンテナは単なる線で問題ありません。ケースに組み込むときはロッドアンテナのほうが見た目はいいでしょう。

 当初、NJM2073 の電源は回路図通りに三端子レギュレータの IN 側の電源を使っていましたが、ユニバーサル基板で回路を構築する際に配線を誤ったため、3.3 V に変更しました。3.3 V でも動作するので問題ありません。特に音量が小さいと感じることもありません。


広告

プログラミング

 プログラムは部品が届く前に一度書いてみましたが、初めて使うデバイスが一発で動くということもないと思うのでブレッドボードで回路を構築し、プログラムのデバッグを行いながら作ってみます。

ブレッドボードで動作確認の様子

 当初は MCC で設定を行い、生成された関数で処理を書いていたのですが、すぐにプログラムメモリがいっぱいになってしまったので、ちまちまとレジスタを叩くプログラムに変更しました。F1 ファミリはレジスタが多いので大変です。

 I2C 通信ができませんでした。I2C 関連の while 文で止まっているようで、次の処理が実行されませんでした。レジスタ設定をいじっているうちに動くようになりましたが、何が原因だったのかは覚えていません。

 タイマ 0 は、1 ms 間隔で割り込みが入るようにし、割り込み関数内で変数をインクリメントさせ、Arduino の millis() のような機能を実装しています。長押しの判定、最終操作の 10 秒後に設定を EEPROM に保存させる機能や、LCD バックライトを消灯させる処理に使います。タイマ 1、2 は使用していません。

 KT0913 のレジスタアドレスはマクロ定義し、プログラミングしやすいように工夫しました。

プログラム全体

#include <xc.h>

// コンフィギュレーションビット
#pragma config FOSC = INTOSC
#pragma config WDTE = OFF
#pragma config PWRTE = OFF
#pragma config MCLRE = OFF
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = ON
#pragma config CLKOUTEN = OFF
#pragma config IESO = ON
#pragma config FCMEN = ON
#pragma config WRT = OFF
#pragma config PLLEN = OFF
#pragma config STVREN = ON
#pragma config BORV = LO
#pragma config LVP = OFF

// クロック周波数:4 MHz
#define _XTAL_FREQ 4000000

// KT0913 のアドレス
#define RADIO_ADDRESS   0b01101010
// AQM0802 のアドレス
#define LCD_ADDRESS     0x7C

// KT0913 レジスタ
#define CHIP_ID         0x01    // ASCII 文字「KT」
#define SEEK            0x02    // FM チャンネル間隔・右ミュート・左ミュート
#define TUNE            0x03    // FM チューナ有効化・周波数設定(50 kHz 単位)
#define VOLUME          0x04    // ミュート、低音、カップリングコンデンサ設定(ポップ音軽減)
#define DSPCFGA         0x05    // ステレオ/モノラル・ディエンファシス・ブレンド
#define LOCFGA          0x0A    // AFC 無効化
#define LOCFGC          0x0C    // FM 周波数範囲設定?
#define RXCFG           0x0F    // スタンバイモード・音量(32 段階)
#define STATUSA         0x12    // ステータスレジスタ
#define STATUSB         0x13    // ステータスレジスタ
#define STATUSC         0x14    // ステータスレジスタ
#define AMSYSCFG        0x16    // バンド切り替え・クロック設定、他
#define AMCHAN          0x17    // AM 周波数設定
#define AMCALI          0x18    // チップ内蔵キャパシタ?
#define GPIOCFG         0x1D    // VOL、CH ピンのモード設定
#define AMDSP           0x22    // AM 帯域幅・左チャンネル反転?
#define AMSTATUSA       0x24    // ステータスレジスタ
#define AMSTATUSB       0x25    // ステータスレジスタ
#define SOFTMUTE        0x2E
#define USERSTARTCH     0x2F
#define USERSTARTNUM    0x30
#define USERCHANNUM     0x31
#define AMCFG           0x33
#define AMCFG2          0x34
#define VOLGUARD        0x3A
#define AFC             0x3C

// バンド定義
#define FM 0
#define AM 1

// 初期値設定
#define DEFAULT_BAND    FM                      // バンド
#define DEFAULT_AM_FREQ 1422                    // AM 周波数(1422 kHz はラジオ日本)
#define DEFAULT_FM_FREQ 92.4 *1000000/50000     // FM 周波数(92.4 の部分を MHz 値に変更・ラジオ日本)
#define DEFAULT_VOLUME  12                      // 音量

// 経過ミリ秒
uint32_t millis;
// 設定情報
uint8_t band;     // バンド
uint16_t am_freq; // AM 周波数
uint16_t fm_freq; // FM 周波数
uint8_t volume;   // 音量
// 画面表示
uint8_t upperText[8];
uint8_t lowerText[8];

// ======== 割り込み関数 ========
void __interrupt() tmrint(void) {
    if(TMR0IF) {
        millis++;   // 変数インクリメント
        TMR0IF = 0; // クリア
        TMR0 = 131; // 初期値設定
    }
}

// ======== EEPROM 関連関数 ========
// 1 バイト書き込み
void eeprom_write(uint8_t address, uint8_t data) {
    GIE = 0;    // 一旦割り込み無効化
    // アドレス指定
    EEADR = address;
    EEDAT = data;
    // 書き込み有効化
    WREN = 1;
    // アンロックシーケンス
    EECON2 = 0x55;
    EECON2 = 0xAA;
    WR = 1;
    // 書き込み完了待ち
    while(WR);
    // 書き込み無効化
    WREN = 0;
    // 割り込み再開
    GIE = 1;
}
// 1 バイト読み出し
uint8_t eeprom_read(uint8_t adderss) {
    // アドレス指定
    EEADRH = 0;
    EEADRL = adderss;
    // 読み出し
    EECON1 = 0b00000001;
    // データを返す
    return EEDATL;
}

// ======== I2C 関連関数 ========
void i2c_send(uint8_t data) {
    SSP1IF = 0;
    SSP1BUF = data;
    while(!SSP1IF);
}
// 1 バイト I2C 送信
void i2c_write1byte(uint8_t address, uint8_t reg, uint8_t data) {
    // スタートコンディション送信・待ち
    SEN = 1;
    while(SEN);
    // スレーブアドレス送信
    i2c_send(address);
    // レジスタアドレス送信
    i2c_send(reg);
    // データ送信
    i2c_send(data);
    // ストップコンディション送信・待ち
    PEN = 1;
    while(PEN);
}
// 2 バイト I2C 送信
void i2c_write2byte(uint8_t address, uint8_t reg, uint16_t data) {
    // スタートコンディション送信・待ち
    SEN = 1;
    while(SEN);
    // スレーブアドレス送信
    i2c_send(address);
    // レジスタアドレス送信
    i2c_send(reg);
    // 1 バイト目データ送信
    i2c_send((uint8_t)(data>>8));
    // 2 バイト目データ送信
    i2c_send((uint8_t)data);
    // ストップコンディション送信・待ち
    PEN = 1;
    while(PEN);
}

// ======== LCD 制御関数 ========
// LCD にコマンド送信関数
void lcd_writeCommand(uint8_t command) {
    i2c_write1byte(LCD_ADDRESS, 0x00, command); // RS = 0
    __delay_ms(2);
}
// LCD にデータ送信関数
void lcd_writeData(uint8_t data) {
    i2c_write1byte(LCD_ADDRESS, 0x40, data);    // RS = 1
    __delay_ms(1);
}
// LCD 表示関数
void display(uint8_t upper[8], uint8_t lower[8]) {
    lcd_writeCommand(0x01);
    lcd_writeCommand(0x80 + 0x00);   // DDRAM(文字位置)セット:1 行目
    for(uint8_t i=0; i<8; i++) {
        lcd_writeData(upper[i]);
    }
    lcd_writeCommand(0x80 + 0x40);   // DDRAM(文字位置)セット:2 行目
    for(uint8_t i=0; i<8; i++) {
        lcd_writeData(lower[i]);
    }
}
// 通常画面を表示する関数
void changeSettings(void) {
    // 表示
    upperText[4] = ' ';
    upperText[6] = 'H';
    upperText[7] = 'z';
    lowerText[1] = 'M';
    lowerText[2] = ' ';
    lowerText[3] = 'V';
    lowerText[4] = 'O';
    lowerText[5] = 'L';
    if(band == FM) {
        // FM なら
        if(fm_freq <= 2000) {
            // FM 周波数の整数部分が 2 桁
            upperText[0] = (fm_freq*5/1000)%10 + '0';
            upperText[1] = (fm_freq*5/100)%10 + '0';
            upperText[2] = '.';
            upperText[3] = (fm_freq*5/10)%10 + '0';
        } else {
            // FM 周波数の整数部分が 3 桁
            upperText[0] = (fm_freq*5/10000)%10 + '0';
            upperText[1] = (fm_freq*5/1000)%10 + '0';
            upperText[2] = (fm_freq*5/100)%10 + '0';
            upperText[3] = '.';
            upperText[4] = (fm_freq*5/10)%10 + '0';
        }
        upperText[5] = 'M';
        lowerText[0] = 'F';
    } else {
        // AM なら
        upperText[0] = (am_freq/1000)%10 + '0';
        upperText[1] = (am_freq/100)%10 + '0';
        upperText[2] = (am_freq/10)%10 + '0';
        upperText[3] = am_freq%10 + '0';
        upperText[5] = 'k';
        lowerText[0] = 'A';
    }
    lowerText[6] = (volume/10)%10 + '0';
    lowerText[7] = volume%10 + '0';
    display(upperText, lowerText);
    
    // 設定反映
    if(band == FM) {
        i2c_write2byte(RADIO_ADDRESS, AMSYSCFG, 0b0000000000000010);
        i2c_write2byte(RADIO_ADDRESS, TUNE, 0b1000000000000000 + fm_freq);      // 15 ビット目をセットしないと FM が入らない
    } else {
        i2c_write2byte(RADIO_ADDRESS, AMSYSCFG, 0b1000000000000010);
        i2c_write2byte(RADIO_ADDRESS, AMCHAN, 0b0000000000000000 + am_freq);
    }
    
    i2c_write2byte(RADIO_ADDRESS, RXCFG, 0b1000100000000000 + volume);
    
    __delay_ms(50);
}

// ======== メイン関数 ========
void main(void) {
    // レジスタ設定
    OSCCON = 0b01101010;        // 内蔵クロック 4 MHz
    // ピン設定
    TRISA = 0b00111110;         // RA5-RA3 が入力、RA0 は出力 I2C ピンは入力にする
    ANSELA = 0;                 // すべてデジタルに(アナログは使用しない)
    WPUA = 0b00111000;          // 入力ピンはすべてプルアップ
    OPTION_REG = 0b00000010;    // プルアップ有効、タイマ 0 プリスケーラ 1:8
    // I2C 設定
    SSP1CON1 = 0b00101000;      // MSSP:I2C マスタモード
    SSP1ADD = 9;
    SSP1STAT = 0b10000000;
    // タイマ・割り込み設定
    INTCON = 0b11100000;        // タイマ 0 割り込み使用
    TMR0 = 131;                 // タイマ 0 初期値

    // LCD 初期化
    __delay_ms(40);
    lcd_writeCommand(0x38);
    lcd_writeCommand(0x39);
    lcd_writeCommand(0x14);
    lcd_writeCommand(0x7E);
    lcd_writeCommand(0x55);
    lcd_writeCommand(0x6C);
    lcd_writeCommand(0x38);
    lcd_writeCommand(0x0C);
    lcd_writeCommand(0x01);
    
    // 設定読み出し
    band = eeprom_read(0); // バンド
    am_freq = eeprom_read(1)*256 + eeprom_read(2); // AM 周波数
    fm_freq = eeprom_read(3)*256 + eeprom_read(4); // FM 周波数
    volume = eeprom_read(5);  // 音量
    
    // 初回起動(0xFF)なら、初期値を代入、初期値はマクロ定義
    if(band == 0xFF) {
        band = DEFAULT_BAND;
        am_freq = DEFAULT_AM_FREQ;
        fm_freq = DEFAULT_FM_FREQ;
        volume = DEFAULT_VOLUME;
    }
    
    __delay_ms(600);    // ラジオ起動待ち
    
    // ラジオ初期設定
    i2c_write2byte(RADIO_ADDRESS, VOLUME, 0b1110000010110000);                  // カプコン 10 uF
    i2c_write2byte(RADIO_ADDRESS, DSPCFGA, 0b1001000000000000);                 // //モノラル
    i2c_write2byte(RADIO_ADDRESS, LOCFGC, 0b0000000000101100);                  // FM 周波数範囲拡張
    i2c_write2byte(RADIO_ADDRESS, AMDSP, 0b1010111111000100);                   // AM 帯域幅 6 kHz(音質が改善された)
    
    changeSettings();   // 表示
    
    while (1) {
        // 経過ミリ秒と、ボタン状態を条件式にしているのは、ボタンが押された場合に処理をスキップさせるため
        // 5 秒後バックライト OFF
        millis = 0;
        while(millis < 5000 && RA5 && RA4 && RA3);
        LATA0 = 0;
                
        // 10 秒後設定保存
        while(millis < 10000 && RA5 && RA4 && RA3);
        if(millis >= 10000) {
            eeprom_write(0, band);
            eeprom_write(1, (uint8_t)(am_freq>>8));
            eeprom_write(2, (uint8_t)am_freq);
            eeprom_write(3, (uint8_t)(fm_freq>>8));
            eeprom_write(4, (uint8_t)fm_freq);
            eeprom_write(5, volume);
        }
        
        millis = 0; // ここでゼロにしないと EEPROM に何度も書き込まれてしまう
        
        // ボタンが押されるのを待つ
        while(RA5 && RA4 && RA3);
        
        if(!RA5) {
            // モードボタンが押された=選局またはバンド切り替え
            LATA0 = 1;
            uint8_t btnflg = 0;   // アップダウンが押されたら 1 にする
            
            while(!RA5) {
                // モードボタンが押されている間
                if(!RA4) {
                    // アップボタン押された                    
                    if(band == FM) {
                        // FM なら FM 周波数を上限までアップ
                        if(fm_freq < 2199) fm_freq+=2;
                    } else {
                        // AM なら AM 周波数を上限までアップ
                        if(am_freq < 1710) am_freq++;
                    }
                    changeSettings();
                    
                    millis = 0; // 経過ミリ秒カウント開始
                    while(!RA4) {
                        // ボタンが離されるまで待つ
                        if(millis > 2000) {
                            // もっと長押しで加速
                            if(band == FM) {
                                if(fm_freq < 2190) fm_freq+=10;
                            } else {
                                if(am_freq < 1700) am_freq+=10;
                            }
                            changeSettings();
                        } else if(millis > 500) {
                            // 長押しでどんどんアップ
                            if(band == FM) {
                                if(fm_freq < 2199) fm_freq+=2;
                            } else {
                                if(am_freq < 1710) am_freq++;
                            }
                            changeSettings();
                        }
                    }
                    
                    btnflg = 1;
                    __delay_ms(100);    // チャタリング対策
                }
                
                if(!RA3) {
                    // ダウンボタン押された 以下、アップと同様の処理                    
                    if(band == FM) {
                        if(fm_freq > 641) fm_freq-=2;
                    } else {
                        if(am_freq > 200) am_freq--;
                    }
                    changeSettings();
                    
                    millis = 0;
                    while(!RA3) {
                        if(millis > 2000) {
                            if(band == FM) {
                                if(fm_freq > 650) fm_freq-=10;
                            } else {
                                if(am_freq > 210) am_freq-=10;
                            }
                            changeSettings();
                            __delay_ms(10);
                        } else if(millis > 500) {
                            if(band == FM) {
                                if(fm_freq > 641) fm_freq-=2;
                            } else {
                                if(am_freq > 200) am_freq--;
                            }
                            changeSettings();
                        }
                    }
                    
                    btnflg = 1;
                    __delay_ms(100);
                }
                __delay_ms(100);
            }
            
            if(btnflg == 0) {
                // アップダウンが押されなかったら、バンド切り替え
                if(band == FM) {
                    band = AM;
                } else {
                    band = FM;
                }
                changeSettings();
                __delay_ms(100);
                while(!RA5);
            }
        } else {
            // モードボタンは押されていない=ボリュム調整
            LATA0 = 1;
            while(!(RA4 && RA3)) {
                if(!RA4) {
                    // アップボタン押された                    
                    if(volume < 31) volume++;
                    changeSettings();
                    __delay_ms(300);
                }

                if(!RA3) {
                    // ダウンボタン押された                    
                    if(volume > 0) volume--;
                    changeSettings();
                    __delay_ms(300);
                }
            }
        }
        
        __delay_ms(200);    // チャタリング対策
    }
}

組み立て

 ユニバーサル基板上に回路を組みました。メイン基板には C タイプの基板を使用しましたが、部品密度が高く、ミスやジャンパも多かったので、もう少し大きい基板を使うのがいいと思います。LCD とボタンの基板はC基板ヲ切って使いました。アップダウンのボタンは逆のほうがよかったかもしれません。

 タカチ社製 SW-125 プラスチックケースに組み込んでみます。ケースの加工は苦手なので、スイッチの部分失敗してしまいましたが、ケースがもったいないので穴を拡張してそのまま使いました。見た目が悪いのと、タクトスイッチの高さが少し短い(押しづらい)ですが、動作するのでヨシとします。👈😼

完成したラジオ

考察

 バーアンテナの使い方がよくわかっていないためか、AM の感度が悪すぎるものの、FM の感度は文句なしです。横浜に住んでいるので、鶴見区の三ツ池公園付近から出ているラジオ日本のワイド FM(92.4 MHz)はとてもよく入りました。埼玉の NACK5(79.5 MHz)もアンテナを伸ばしたら少しノイズ交じりですが聴こえました。AM もラジオ日本は隣の市(川崎の多摩川土手)に送信所があるのだがそれすら……聴こえるには聴こえるのですが、ノイズの中にこもった音声が混ざっている、といった感じです。今後の課題とします。

 また、電源を入れると、音量ボタンを押さないと鳴らないことがあります。たまに、全く鳴らないこともあります。原因がわからないので、とりあえず放置しています。

 ソフトウェアに関しては、PIC で I2C と内蔵 EEPROM を使うのが初めてなので、少し大変でした。また、MCC は使わず手動でレジスタをいじったので、とても勉強になりました。長押しすると選局が加速する機能も、初めて作ってみたので勉強になりました。

 今回は実用性よりも勉強ということで作ってみましたが、FM の感度は良くそこそこ実用的なので、プリント基板で作ってもいいかもしれません。


戻る
広告

右メニュー

【表示がおかしい・崩れる場合】PC では Shift と F5 を同時押し。スマホではキャッシュを削除してみてください。



とうサイトでは Cookieくっきー使用しようします。くわしくは上記じょうきの「プライバシーポリシー・免責事項等めんせきじこうとう」もご確認かくにんください。



上へ戻る