RDA5830を使った携帯用ラジオの作成

無線

DSPラジオモジュールRDA5830を使った、FM/AM携帯ラジオの作成ををご紹介します。小ささにこだわったので操作性は少し犠牲になっていますが、感度良好でよく聞こえます。

幅50mm、高さ80mm、厚さ15mmのブリキ缶の中にラジオ基板、電池ケースを収めています。
ブリキ缶は、雑貨屋さんで購入したムーミンがデザインされたものを使いました。一般的なタバコより少し小さく、手のひらに収まるサイズです。通勤用、防災用におひとつ如何でしょう?

作成記事を動画にまとめたものもアップしています。よかったら、ごらんください。https://www.youtube.com/watch?v=H1quykj5dbA

画像に alt 属性が指定されていません。ファイル名: IMG_5996_2.jpg画像に alt 属性が指定されていません。ファイル名: IMG_5994_2.jpg

受信範囲は、AMが522kHz~1692kHz、FMが76.0MHz~108.0MHzです。最近は、AM放送がFM(90.0~94.9MHz)で補完放送されているので、AMの受信機能は要らないかと思いましたが、NHK第一放送が聴きたかったので、RDA5830を使ってAM放送も受信可能にしました。
機能としては、基本的な周波数の設定、プリセット局がAM、FMとも6局、音量調整、バンド(AM、FM)切換えになります。プリセット局は、プログラム中で書いています。

この記事は、ラジオの動作を保証するものではありません。また、作成中、動作中の事故の責任は負いかねます。配線の確認などを確実に行い、自己責任でお楽しみください。よろしくお願いします!

部品

使う部品は、以下のとおりです。参考に、購入先のリンクを付けておきます。(リンクは参考例です。私がよく利用させて頂いているところで、紹介したところ以外からも購入可能だと思います。)

この他に、配線用のすずメッキ線、絶縁電線などが必要になります。
AMアンテナはバーアンテナが普通ですが、ケース内に収めるためマイクロインダクタを使ってみました。FM用のアンテナは、イヤフォンを兼用しています。(3μHくらいのインダクタなども良いかもしれません。色々、試してみてください。)

部品配置図(回路)

LCDモジュールと、ラジオモジュールを配置するところは、ピンソケットで表示しています。部品の向きなどは、組み立て(部品配置、配線)の図を参考にしてください。
電源スイッチの下と右にあるホールは、電源を供給する場所です。プラスとマイナスのラインに接続してください。また、実際の部品と、図中のイメージが少し異なるところがありますが、ご容赦ください。

ブリキ缶の収納高さがあるので、ピンソケットを多用すると蓋が閉まらなくなる可能性があります。この記事では、ラジオモジュールは直付け、LCDモジュールは丸ピンソケットを使っています。また、LCDモジュールのピンは少し長いので、1.5mm程度切っています。

画像に alt 属性が指定されていません。ファイル名: front.png
基板表面
(赤線は表面配線、青線は裏面配線、オレンジ線は被覆表面配線)
画像に alt 属性が指定されていません。ファイル名: rear.png
裏面配線
 

RDA5830のピン配置は、下図(左)のとおりです。今回使ったDIP変換基板は、ピン配置が90°変わるので下図(右)の様になります。
詳細については、AitendoさんのHPをご確認ください。

画像に alt 属性が指定されていません。ファイル名: RDA5830.jpg画像に alt 属性が指定されていません。ファイル名: RDA5830_DIP.jpg

テスターなどで、確実に配線されているか確認するべし!

組立て(部品配置、配線)

ユニバーサル基板に部品を半田付けして、裏面配線をしてゆきます。LCDモジュールは、ラジオモジュールの上に来ます。最終的に組立てるときは、LCDモジュールの裏面とラジオモジュールの間を絶縁してください。
回路にI2Cのプルアップ用抵抗は配線してませんので、LCDモジュールの裏面にあるプルアップ抵抗用のジャンパーをショートしてください。

画像に alt 属性が指定されていません。ファイル名: IMG_5975.jpg画像に alt 属性が指定されていません。ファイル名: IMG_5976.jpg

ブリキ缶に収めるため、ユニバーサル基板の不要部分を切って、成形します。切った跡は、やすりなどで綺麗に仕上げます。

画像に alt 属性が指定されていません。ファイル名: IMG_5979.jpg
18列で切る
画像に alt 属性が指定されていません。ファイル名: IMG_5980.jpg
成形後(上部の角も丸めてください)

基板の裏面配線の様子です。電池ボックスのリード線は、長さを合わせて図中のプラス、マイナスのところに半田付けしてください。

基板裏面

アンテナ用のインダクタはまだ付けていないよ!

マイコン(PIC)

このラジオの心臓部は、PIC12F1822というマイコンです。8ピンの小さなものですが、高性能なのに百数十円で買える憎いやつです。
プログラムリストは長いので、最後に記載します。

画像に alt 属性が指定されていません。ファイル名: IMG_5977.jpgPIC12F1822-I/P
・電源電圧:1.8~5.5V
・コアサイズ:8bit
・命令長:14bit
・クロック:32MHz
・プログラムメモリ:2kW
・EEPROM:256B
・RAM:128B
・GPIO:6pin
・ADC:4Ch
・DAC:1Ch
・UART/USART:1Ch
・I2C:1Ch
・SPI:1Ch
・タイマ:3Ch
・オシレータ:内蔵/外付
・パッケージ:DIP8

動作テスト

ブリキ缶に組込む前に、動作テストをします。
PICにプログラムを書込み、電池を接続して動作確認をしてください。この時、AM、FMのアンテナにインダクタなどを繋いで試し、一番良い感度のものを確認してみてください。

タクトスイッチの左がモード選択(プリセット、周波数、音量、バンド)、中央がDOWN、右がUP

うまく動かない場合はすぐに電源を切り、部品の向き、配線などを確認してね!

組立て(ブリキ缶)

動作確認で問題がなかったら、ブリキ缶に組込んでゆきます。
ブリキ缶にヘッドフォン端子用の穴を開けます。現物合わせで位置合わせを行い、ドリルで穴を開けました。ブリキは柔らかいので、ゆっくり開けてください。バリはヤスリなどで削って、怪我などしないように気を付けてください。

画像に alt 属性が指定されていません。ファイル名: IMG_5989_2.jpg画像に alt 属性が指定されていません。ファイル名: IMG_5990.jpg

次に電源用の配線を行い、ブリキ缶にラジオ基板と、電池ボックスを収めます。ブリキ缶と、ラジオ基板裏面の間の絶縁は確実に行ってください。固定に厚手の強力両面テープを使えば、一石二鳥だと思います。
ブリキ缶の下側に当たる、電池ボックスの角も少し削った方が良いかもしれません。

画像に alt 属性が指定されていません。ファイル名: %E5%90%8D%E7%A7%B0%E6%9C%AA%E8%A8%AD%E5%AE%9A2.png
電池ボックスの赤丸のところを少し削る
画像に alt 属性が指定されていません。ファイル名: IMG_5984-1.jpg
見づらいですが、+と-に電池ボックスからのラインを接続する

ブリキ缶に無事収まったら、早速電源を入れてラジオ放送を楽しみましょう!
電池が無くなるまで連続使用したことはありませんが、一日数時間使っても1週間以上動いており、電池の持ちは良いと思います。

画像に alt 属性が指定されていません。ファイル名: IMG_5993_2.jpg画像に alt 属性が指定されていません。ファイル名: IMG_5992_2.jpg

ブリキ缶の蓋をすると操作もできないし表示も見えませんが、お気に入りの放送局を選局したらしばらくは変えないと思いますので、これはこれで良いのではないかと思っています。
小型で感度も良く、お気に入りの一品です。

プログラム

プログラムは、PICの製造元であるMicrochip社のMPLAB X IDEを使いました。コンパイラーは、XC8です。ネット上の情報を参考に書いたプログラムで、決してスマートなプログラムとは言えませんがご容赦ください。m(__)m
RDA5830の使い方の詳細は、datasheetを確認してください。

MPLAB X IDE、XC8コンパイラーについては、この記事では触れていません。Google先生などに、聞いてみてください。

PICにプログラムを書込むためには、PICkitなどの書込み装置が必要になります。私は、マイクロチップPICkit4と、Pickit対応ICSP書き込みアダプターキットを組合わせて使っています。書込み装置は数種類ありますが、使用するPICの型式が対応しているか確認してから購入してください。

#define     _XTAL_FREQ  4000000 // PICの内部発振周波数 4MHz に設定
#define     CONTRAST    0x28    // LCD コントラスト設定
#define     SW1         RA5     // MODE SW
#define     SW2         RA4     // SW
#define     SW3         RA3     // SW
#define     CHIP_ADDR   0x11    // RDA5830 address

void i2cLcdWrite(char, char, char);
void LCD_dat(char);
void LCD_cmd(char);
void LCD_clr(void);
void LCD_int(void);
void LCD_str(const char *str);
void LCD_str2(const char *str);

void RDA5830_init(void);
void RDA5830_FM_Freq(int FM_freq);
void RDA5830_AM_Freq(unsigned int AM_freq);
void RDA5830_write(unsigned char regAddr, unsigned int data);
void FM_Disp(char);
void AM_Disp(char);

void i2cStart();
void i2cStop();
void i2cTxData(char); // I2C 1文字保存書込送信用

 /**********************************************
    RDA5830 - PIC12F1822  FM/AM ラジオ
    RDA5830_main.c  2022.02.07 by JH8KMR
    受信周波数 FM 76.0 - 108.0 MHz
              AM 522 - 1692kHz
************************************************/
#include <xc.h>
#include "system.h"

// config
#pragma config FOSC = INTOSC    // 内蔵オシレータ選択
#pragma config WDTE = OFF       // ウヲッチドグ無効
#pragma config PWRTE = OFF      // パワーアップタイマ無効
#pragma config MCLRE = OFF      // MCLR ピンはデジタル入出力
#pragma config CP = OFF         // コードプロテクト無効
#pragma config CPD = OFF        // データメモリの保護は無効
#pragma config BOREN = ON       // ブラウナウトリセット無効
#pragma config CLKOUTEN = OFF   // クロックアウト機能無効
#pragma config IESO = OFF       // 内部/外部スイッチオーバモード無効
#pragma config FCMEN = OFF      // クロック監視無効
#pragma config WRT = OFF        // プログラムメモリ書込保護 OFF
#pragma config PLLEN = OFF      // PLL x 4 無効
#pragma config STVREN = ON      // スタックオーバリセット有効
#pragma config BORV = LO        // ブラウンアウト電圧 LOW
#pragma config LVP = OFF        // 低電圧プログラミング無効

void itostring(char digit, unsigned int data, char *buffer);   // int整数からASCII文字に変換

unsigned int FM_Freq;
unsigned int AM_Freq;
unsigned int FM_St[6] = {8040, 8250, 8520, 9040, 9150, 7780};
unsigned int AM_St[6] = { 567,  747, 1287, 1440, 1242, 1134};
            
// main
void main() {
    __delay_ms(100);
    unsigned char FM_CH; // Fm Channel
    unsigned char AM_CH; // Fm Channel
    unsigned int vol; // 音量段階 vol

    int i;
    int sw_mode;
    char band;  //0:FM , 1:AM
    unsigned char buff[9];
    unsigned char vol_buff[8];
    unsigned char freq_buff[8];
    char FLG;
    
// PIC Config
    OSCCON = 0b01101010; // 内部クロック4MHz
    PORTA = 0x00;        // PortA clear
    TRISA = 0xFF;        // PortA すべて入力
    ANSELA = 0x00;       // すべてデジタル

// I2C 初期化
    SSP1CON1 = 0b00001000;  // I2C Master modeにする
    SSP1CON2 = 0x00;        // PowerOn初期値にする
    SSP1STAT = 0b10000000;  // スルーレート制御は無効
    SSP1ADD = 9;            // クロックの設定 100k@4MHz
    SSP1CON1bits.SSPEN = 1; // SDA と SCL 有効にする

// 初期値
    FM_CH = 0;
    AM_CH = 0;
    vol = 2;
    FLG = 99;
    sw_mode = 1;    // CH
    band = 0;       // FM
    FM_Freq = FM_St[FM_CH];
    AM_Freq = AM_St[AM_CH];
    for ( i = 0 ; i < 8 ; i++) buff[i] = 0;
    
// LCD 初期化
    LCD_int();

// RDA5830 初期化
    RDA5830_init();
    FM_Disp(FM_CH);
    __delay_ms(100); 
    RDA5830_write(0x05, 0x89A10 | vol);
    __delay_ms(100);

    while(1){
        if ( SW1 == 0 ){    // mode
            __delay_ms(300);
            sw_mode += 1;
            if ( sw_mode > 4 ) sw_mode = 1;
            FLG = 99;
        }
        switch(sw_mode){
            case 2: // freq
                for ( i = 0 ; i < 8 ; i++) freq_buff[i] = 0;
                if( FLG == 99){
                    LCD_str("Freq    ");
                    FLG = 0;
                }
                if ( SW2 == 0 ){
                    __delay_ms(300);
                    if ( band == 0 ){
                        FM_Freq += 10;
                        if ( FM_Freq > 10800 ) FM_Freq = 7600;
                        RDA5830_FM_Freq(FM_Freq);
                        itostring(5, FM_Freq, freq_buff);
                        freq_buff[4] = freq_buff[3];
                        freq_buff[3] = '.';
                        freq_buff[5] = 'M';
                        freq_buff[6] = 'H';
                        freq_buff[7] = 'z';
                        LCD_str2(freq_buff);
                    } else {
                        AM_Freq += 9;
                        if ( AM_Freq > 1692 ) AM_Freq = 522;
                        RDA5830_AM_Freq(AM_Freq);
                        itostring(5, AM_Freq, freq_buff);
                        freq_buff[5] = 'k';
                        freq_buff[6] = 'H';
                        freq_buff[7] = 'z';
                        LCD_str2(freq_buff);
                    }
                }
                if ( SW3 == 0 ){
                    __delay_ms(300);
                    if ( band == 0 ){
                        FM_Freq -= 10;
                        if ( FM_Freq < 7600 ) FM_Freq = 10800;
                        RDA5830_FM_Freq(FM_Freq);
                        itostring(5, FM_Freq, freq_buff);
                        freq_buff[4] = freq_buff[3];
                        freq_buff[3] = '.';
                        freq_buff[5] = 'M';
                        freq_buff[6] = 'H';
                        freq_buff[7] = 'z';
                        LCD_str2(freq_buff);
                    } else {
                        AM_Freq -= 9;
                        if ( AM_Freq < 522 ) AM_Freq = 1692;
                        RDA5830_AM_Freq(AM_Freq);
                        itostring(5, AM_Freq, freq_buff);
                        freq_buff[5] = 'k';
                        freq_buff[6] = 'H';
                        freq_buff[7] = 'z';
                        LCD_str2(freq_buff);
                    }
                }
                break;
             
            case 3: // vol
                for ( i = 0 ; i < 8 ; i++) vol_buff[i] = 0;
                if ( FLG == 99 ){
                    LCD_str("Vol     ");
                    itostring(2, (unsigned int)vol, vol_buff);
                    vol_buff[7] = vol_buff[1];
                    vol_buff[6] = vol_buff[0];
                    vol_buff[5] = ' ';
                    vol_buff[4] = ' ';
                    vol_buff[3] = ' ';
                    vol_buff[2] = ' ';
                    vol_buff[1] = ' ';
                    vol_buff[0] = ' ';
                    LCD_str2(vol_buff);
                    FLG = 0;
                }
                if ( SW2 == 0 ){
                    __delay_ms(300);
                    vol = vol + (vol < 15);
                    RDA5830_write(0x05, 0x89A10 | vol);
                    itostring(2, (unsigned int)vol, vol_buff);
                    vol_buff[7] = vol_buff[1];
                    vol_buff[6] = vol_buff[0];
                    vol_buff[5] = ' ';
                    vol_buff[4] = ' ';
                    vol_buff[3] = ' ';
                    vol_buff[2] = ' ';
                    vol_buff[1] = ' ';
                    vol_buff[0] = ' ';
                    LCD_str2(vol_buff);
                }
                if ( SW3 == 0 ){
                    __delay_ms(300);
                    vol -= (vol > 0 );
                    RDA5830_write(0x05, 0x89A10 | vol);
                    itostring(2, (unsigned int)vol, vol_buff);
                    vol_buff[7] = vol_buff[1];
                    vol_buff[6] = vol_buff[0];
                    vol_buff[5] = ' ';
                    vol_buff[4] = ' ';
                    vol_buff[3] = ' ';
                    vol_buff[2] = ' ';
                    vol_buff[1] = ' ';
                    vol_buff[0] = ' ';
                    LCD_str2(vol_buff);
                }
                break;
            case 1: // ch
                if ( FLG == 99 ){
                    if ( band == 0 ) FM_Disp(FM_CH);
                    if ( band == 1 ) AM_Disp(AM_CH);
                    FLG = 0;
                }
                if ( SW2 == 0 ){    // Up
                    __delay_ms(300);
                    if ( band == 0 ){
                        FM_CH += 1;
                        if(FM_CH > 5) FM_CH = 0;
                        FM_Freq = FM_St[FM_CH];
                        FM_Disp(FM_CH);
                    } else {
                        AM_CH += 1;
                        if(AM_CH > 5) AM_CH = 0;
                        AM_Freq = AM_St[AM_CH];
                        AM_Disp(AM_CH);
                    }
                }
                if ( SW3 == 0 ){    // Down
                    __delay_ms(300);
                    if ( band == 0 ){
                        FM_CH = FM_CH - (FM_CH > 0);
                        if(FM_CH < 0) FM_CH = 5;
                        FM_Freq = FM_St[FM_CH];
                        FM_Disp(FM_CH);
                    } else {
                        AM_CH = AM_CH - (AM_CH > 0);
                        if(AM_CH < 0) AM_CH = 5;
                        AM_Freq = AM_St[AM_CH];
                        AM_Disp(AM_CH);
                    }
                }
                break;
            case 4: // Band
                for ( i = 0 ; i < 8 ; i++) freq_buff[i] = 0;
                if( FLG == 99){
                    LCD_str("Band    ");
                    if ( band == 0 ) LCD_str2("      FM");
                    if ( band == 1 ) LCD_str2("      AM");
                    FLG = 0;
                }
                if ( SW2 == 0 ){
                    __delay_ms(300);
                    band = band + ( band < 1);
                    AM_Freq = AM_St[AM_CH];
                    AM_Disp(AM_CH);
                    sw_mode = 1;
                }
                if ( SW3 == 0 ){
                    __delay_ms(300);
                    band = band - ( band > 0);
                    FM_Freq = FM_St[FM_CH];
                    FM_Disp(FM_CH);
                    sw_mode = 1;
                }
                break;
            default:
                break;
        }
    }
}
//main END
/***************************************
* int整数からASCII文字に変換
****************************************/
void itostring(char digit, unsigned int data, char *buffer)
{
	char i;

	buffer += digit;                        // 最後の数字位置
	for(i=digit; i>0; i--) {                // 変換は下位から上位へ
		buffer--;                           // ポインター1
		if ( data == 0 ) *buffer = ' ';
        else *buffer = (data % 10) + '0';	 // ASCIIへ
		data = data / 10;                   // 次の桁へ
	}
}

void FM_Disp(char ch_num){
    LCD_clr();
    RDA5830_FM_Freq(FM_Freq);
    switch(ch_num){
        case 0:
            LCD_str("AIR-G   "); LCD_str2(" 80.4MHz"); break;
        case 1:
            LCD_str("NorthWav"); LCD_str2(" 82.5MHz"); break;
        case 2:
            LCD_str("NHK-FM  "); LCD_str2(" 85.2MHz"); break;
        case 3:
            LCD_str("STV-FM  "); LCD_str2(" 90.4MHz"); break;
        case 4:
            LCD_str("HBC-FM  "); LCD_str2(" 91.5MHz"); break;
        case 5:
            LCD_str("e-niwa  "); LCD_str2(" 77.8MHz"); break;
        default: break;
    }
}

void AM_Disp(char ch_num){
    LCD_clr();
    RDA5830_AM_Freq(AM_Freq);
    switch(ch_num){
        case 0:
            LCD_str("NHK #1  "); LCD_str2("  567kHz"); break;
        case 1:
            LCD_str("NHK #2  "); LCD_str2("  747kHz"); break;
        case 2:
            LCD_str("HBC     "); LCD_str2(" 1287kHz"); break;
        case 3:
            LCD_str("STV     "); LCD_str2(" 1440kHz"); break;
        case 4:
            LCD_str("Nippon-H"); LCD_str2(" 1242kHz"); break;
        case 5:
            LCD_str("Bunka-H "); LCD_str2(" 1134kHz"); break;
        default: break;
    }
}

void RDA5830_init(void){
    RDA5830_write(0x02, 0xC001);
    __delay_ms(500);
    RDA5830_write(0x05, 0x89A1);
    RDA5830_write(0x14, 0x00F1);
    RDA5830_write(0x15, 0x10A0);
    RDA5830_write(0x1A, 0x0542);
    RDA5830_write(0x21, 0x7F80);
    RDA5830_write(0x23, 0x50A4);
    RDA5830_write(0x75, 0x47C0);
    RDA5830_write(0x79, 0xD893);
}

// FM freq set
void RDA5830_FM_Freq(int FM_freq){
    int    CHAN;
    
    CHAN = (FM_freq/10) - 760;
    RDA5830_write(0x40, 0x0000);
    RDA5830_write(0x03, CHAN << 6 | 0b011000);
}

// AM Freq set
void RDA5830_AM_Freq(unsigned int AM_freq){
    RDA5830_write(0x40, 0x0002);
    RDA5830_write(0x72, AM_freq);
}

void RDA5830_write(unsigned char regAddr, unsigned int data)
{
    i2cStart();
    i2cTxData(CHIP_ADDR << 1);
    i2cTxData(regAddr);
    i2cTxData(data >> 8);
    i2cTxData(data);
    i2cStop();
}
#include <xc.h>
#include "system.h"

void i2cLcdWrite(char addr, char cont, char data){
    i2cStart();
    i2cTxData(addr);
    i2cTxData(cont);
    i2cTxData(data);
    i2cStop();
}

void LCD_dat(char chr){
    i2cLcdWrite(0x7C, 0x40, chr);
    __delay_us(50);
}

void LCD_cmd(char cmd){
    i2cLcdWrite(0x7C, 0x00, cmd);
    if(cmd & 0xFC)
        __delay_us(50);
    else
        __delay_ms(2);
}

void LCD_clr(void){
    LCD_cmd(0x01);
}

void LCD_int(void){
    __delay_ms(100);
    LCD_cmd(0x38);
    LCD_cmd(0x39);
    LCD_cmd(0x14);
    //CONTRAST
    LCD_cmd(0x70 + (CONTRAST & 0x0F));
    LCD_cmd(0x5C + (CONTRAST >> 4));
    LCD_cmd(0x6B); 
    __delay_ms(200);
    LCD_cmd(0x38);
    LCD_cmd(0x0C);
    LCD_cmd(0x01);
}

void LCD_str(const char *str){
    LCD_cmd(0x80); // 1行目
    while(*str) LCD_dat(*str++); 
}

void LCD_str2(const char *str){
    LCD_cmd(0xC0); // 2行目
    while(*str) LCD_dat(*str++);
}
#include <xc.h>
#include "system.h"

void i2cStart(){
    SSP1IF = 0;
    SSP1CON2bits.SEN = 1;
    while (SSP1IF == 0) {}
    SSP1IF = 0;
}

void i2cStop(){
    SSP1IF = 0;
    SSP1CON2bits.PEN = 1;
    while (SSP1IF == 0) {}
    SSP1IF = 0;
}

void i2cTxData(char data){
    PIR1bits.SSP1IF = 0;
    SSP1BUF = data;
    while(!PIR1bits.SSP1IF);
}

ご不明な点、ご意見などありましたら、お問い合わせフォームからご連絡ください。

コメント

PAGE TOP