DSPラジオモジュールRDA5830を使った、FM/AM携帯ラジオの作成ををご紹介します。小ささにこだわったので操作性は少し犠牲になっていますが、感度良好でよく聞こえます。
幅50mm、高さ80mm、厚さ15mmのブリキ缶の中にラジオ基板、電池ケースを収めています。
ブリキ缶は、雑貨屋さんで購入したムーミンがデザインされたものを使いました。一般的なタバコより少し小さく、手のひらに収まるサイズです。通勤用、防災用におひとつ如何でしょう?
作成記事を動画にまとめたものもアップしています。よかったら、ごらんください。https://www.youtube.com/watch?v=H1quykj5dbA
受信範囲は、AMが522kHz~1692kHz、FMが76.0MHz~108.0MHzです。最近は、AM放送がFM(90.0~94.9MHz)で補完放送されているので、AMの受信機能は要らないかと思いましたが、NHK第一放送が聴きたかったので、RDA5830を使ってAM放送も受信可能にしました。
機能としては、基本的な周波数の設定、プリセット局がAM、FMとも6局、音量調整、バンド(AM、FM)切換えになります。プリセット局は、プログラム中で書いています。
この記事は、ラジオの動作を保証するものではありません。また、作成中、動作中の事故の責任は負いかねます。配線の確認などを確実に行い、自己責任でお楽しみください。よろしくお願いします!
部品
使う部品は、以下のとおりです。参考に、購入先のリンクを付けておきます。(リンクは参考例です。私がよく利用させて頂いているところで、紹介したところ以外からも購入可能だと思います。)
- マイコン:PIC12F1822
- 8pin 丸ピンICソケット
- カーボン抵抗:1/4W 10KΩ x 3
- 電解コンデンサ:47μF 35v x 2
- タクトスイッチ x 3
- スライドスイッチ:電源用
- 電池ボックス:単5×2本
- I2C接続小型LCDモジュール
- 丸ピンICソケット(シングル6P)
- 3.5mmφ オーディオジャック(基板取付用)
- RDA5830ラジオモジュール
- ラジオモジュールDIP化基板
- マイクロインダクタ:AMアンテナ用 330μH
- セラミックコンデンサ 0.1μF 250V
- ユニバーサル基板 72 x 47.5mm
この他に、配線用のすずメッキ線、絶縁電線などが必要になります。
AMアンテナはバーアンテナが普通ですが、ケース内に収めるためマイクロインダクタを使ってみました。FM用のアンテナは、イヤフォンを兼用しています。(3μHくらいのインダクタなども良いかもしれません。色々、試してみてください。)
部品配置図(回路)
LCDモジュールと、ラジオモジュールを配置するところは、ピンソケットで表示しています。部品の向きなどは、組み立て(部品配置、配線)の図を参考にしてください。
電源スイッチの下と右にあるホールは、電源を供給する場所です。プラスとマイナスのラインに接続してください。また、実際の部品と、図中のイメージが少し異なるところがありますが、ご容赦ください。
ブリキ缶の収納高さがあるので、ピンソケットを多用すると蓋が閉まらなくなる可能性があります。この記事では、ラジオモジュールは直付け、LCDモジュールは丸ピンソケットを使っています。また、LCDモジュールのピンは少し長いので、1.5mm程度切っています。
基板表面 (赤線は表面配線、青線は裏面配線、オレンジ線は被覆表面配線) | 裏面配線 |
RDA5830のピン配置は、下図(左)のとおりです。今回使ったDIP変換基板は、ピン配置が90°変わるので下図(右)の様になります。
詳細については、AitendoさんのHPをご確認ください。
テスターなどで、確実に配線されているか確認するべし!
組立て(部品配置、配線)
ユニバーサル基板に部品を半田付けして、裏面配線をしてゆきます。LCDモジュールは、ラジオモジュールの上に来ます。最終的に組立てるときは、LCDモジュールの裏面とラジオモジュールの間を絶縁してください。
回路にI2Cのプルアップ用抵抗は配線してませんので、LCDモジュールの裏面にあるプルアップ抵抗用のジャンパーをショートしてください。
ブリキ缶に収めるため、ユニバーサル基板の不要部分を切って、成形します。切った跡は、やすりなどで綺麗に仕上げます。
18列で切る | 成形後(上部の角も丸めてください) |
基板の裏面配線の様子です。電池ボックスのリード線は、長さを合わせて図中のプラス、マイナスのところに半田付けしてください。
アンテナ用のインダクタはまだ付けていないよ!
マイコン(PIC)
このラジオの心臓部は、PIC12F1822というマイコンです。8ピンの小さなものですが、高性能なのに百数十円で買える憎いやつです。
プログラムリストは長いので、最後に記載します。
PIC12F1822-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のアンテナにインダクタなどを繋いで試し、一番良い感度のものを確認してみてください。
うまく動かない場合はすぐに電源を切り、部品の向き、配線などを確認してね!
組立て(ブリキ缶)
動作確認で問題がなかったら、ブリキ缶に組込んでゆきます。
ブリキ缶にヘッドフォン端子用の穴を開けます。現物合わせで位置合わせを行い、ドリルで穴を開けました。ブリキは柔らかいので、ゆっくり開けてください。バリはヤスリなどで削って、怪我などしないように気を付けてください。
次に電源用の配線を行い、ブリキ缶にラジオ基板と、電池ボックスを収めます。ブリキ缶と、ラジオ基板裏面の間の絶縁は確実に行ってください。固定に厚手の強力両面テープを使えば、一石二鳥だと思います。
ブリキ缶の下側に当たる、電池ボックスの角も少し削った方が良いかもしれません。
電池ボックスの赤丸のところを少し削る | 見づらいですが、+と-に電池ボックスからのラインを接続する |
ブリキ缶に無事収まったら、早速電源を入れてラジオ放送を楽しみましょう!
電池が無くなるまで連続使用したことはありませんが、一日数時間使っても1週間以上動いており、電池の持ちは良いと思います。
ブリキ缶の蓋をすると操作もできないし表示も見えませんが、お気に入りの放送局を選局したらしばらくは変えないと思いますので、これはこれで良いのではないかと思っています。
小型で感度も良く、お気に入りの一品です。
プログラム
プログラムは、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);
}
ご不明な点、ご意見などありましたら、お問い合わせフォームからご連絡ください。
コメント