以前の記事では、RDA5807 FMラジオモジュールの基本的な使い方を紹介しました。
RDA5807でFMラジオ(2)固定選局(Aruduino)
RDA5807でFMラジオを作る(Aruduino)
今回はその応用編として、
- ATtiny202
- RDA5807 FMラジオモジュール
- AQM1602 I2C液晶
この3つを組み合わせ、実際に使えるFMラジオを製作してみました。
ATtiny202はプログラムメモリがわずか2KBしかありません。そのため一般的なライブラリをそのまま使用すると、すぐに容量不足になってしまいます。
そこで今回は、自作の軽量ライブラリを利用しながら、できるだけメモリを節約する工夫を取り入れています。その結果、放送局の切り替えや音量変更、LCDへの局名表示まで実現することができました。
「2KBしかないマイコンでもここまでできる」という一例として紹介したいと思います。
使用したライブラリ
今回は、I2C通信とAQM1602液晶には、自作の軽量ライブラリを使用しています。
#include <SoftI2C_VPORT.h>
#include <AQM1602_SoftI2C.h>どちらもATtiny202向けにメモリ使用量をできるだけ抑えたライブラリです。
一般的なライブラリに比べると機能は限定されていますが、その分フラッシュメモリの消費が非常に少なく、小容量マイコンでも安心して利用できます。
ATtiny202では、このような軽量ライブラリを利用することで、残りのメモリをアプリケーションに使えるようになります。
PROGMEMを使って局名を保存
ATtiny202ではRAMも非常に少ないため、文字列はできるだけフラッシュメモリ(PROGMEM)へ保存しています。
例えばFM局名は次のように定義しています。(自分の地域に合わせて設定し直してください)
const char ch_name0[] PROGMEM = "TOKYO FM 80.0";
const char ch_name1[] PROGMEM = "J-WAVE 81.3";さらにポインタ配列もPROGMEMへ置くことで、RAMをほとんど消費せずに局名一覧を管理できます。
表示するときだけ
strcpy_P(buf, (char*)pgm_read_word(&(ch_name[ch])));で取得した局名をLCDへ表示しています。
ボタンを押すだけで
- TOKYO FM
- J-WAVE
- NHK FM
- TBSラジオ
- 文化放送
- ニッポン放送
を順番に切り替えられるようになっています。
液晶には現在受信している局名も表示されるため、使い勝手も十分実用的です。
音量変更機能も追加
ATtiny202でも、もう一つのボタンで音量調整機能を追加しました。
set_volume(vm1);RDA5807の音量レジスタを書き換えるだけなので、比較的少ないコード量で実装できます。
液晶には
VOL 1
VOL 2
VOL 3
VOL 4
というように現在の音量も表示できるため、小さなラジオとして十分実用的になりました。
2KBのATtiny202でもここまでできる
今回のプログラムでは、
- FM受信
- 放送局切り替え
- LCD表示
- 音量調整
- ボタン操作
- 自作SoftI2Cライブラリ
これらすべてをATtiny202の2KBという限られたフラッシュメモリの中へ収めることができました。
小容量マイコンだからといって「何もできない」のではなく、ライブラリやプログラムを工夫することで、実用的なアプリケーションを十分作れることが分かりました。
回路図
今回作成した回路図です

プログラム
以下が今回作成したプログラムです。
#include <SoftI2C_VPORT.h>
#include <AQM1602_SoftI2C.h>
// -----------------------------
// RDA5807 (0x10 / 0x11)
// -----------------------------
//#define RDA_ADDR 0x11 // write only
#define RDA_ADDR_SEQ 0x10 // シーケンシャル(一括書き込み用)
#define RDA_ADDR_RAN 0x11 // ランダムアクセス(特定レジスタ用)
SoftI2C_VPORT i2c(1, 2);
uint8_t vol = 2;
int vm1 = 2;
byte Fh;
byte Fl;
int ch = 0;
int CH_MAX = 5;
AQM1602 lcd(i2c);
// -----------------------------
// RDA5807 初期化
// -----------------------------
int fm_chan[6] = { 800, 813, 825, 905, 916, 930 };
//String ch_name[6] = { "TOKYO FM", "J-WAVE", "NHK.FM", "TBS_RADIO", "BUNKA", "NIPON" };
const char ch_name0[] PROGMEM = "TOKYO FM 80.0";
const char ch_name1[] PROGMEM = "J-WAVE 81.3 ";
const char ch_name2[] PROGMEM = "NHK.FM 82.5 ";
const char ch_name3[] PROGMEM = "TBS_RAD 90.5";
const char ch_name4[] PROGMEM = "BUNKA 91.6";
const char ch_name5[] PROGMEM = "NIPON 93.0";
const char* const ch_name[] PROGMEM = {
ch_name0, ch_name1, ch_name2, ch_name3, ch_name4, ch_name5
};
void init_and_set(int mhz) {
// 1. 周波数計算 (以前の成功ロジック通り)
int chan = (int)(mhz * 10 - 760);
chan = mhz - 760;
Fh = chan >> 2;
Fl = (chan & 0x03) << 6;
Fl |= 0b00011000; // TUNE=1, BAND=10 (日本)
// 2. ボリューム設定
uint8_t vm = vol | 0b10000000; // 0x80を立てておく
// 3. 一括書き込み (0x10アドレス)
i2c.start();
i2c.write(RDA_ADDR_SEQ << 1);
// --- Reg 02h (設定) ---
i2c.write(0xC2); // DMUTE=1, 水晶利用
i2c.write(0x85); // ENABLE=1, 32.768kHz
// --- Reg 03h (周波数) ---
i2c.write(Fh);
i2c.write(Fl);
// --- Reg 04h (制御2) ---
i2c.write(0x0A); // DE=1 (50us)
i2c.write(0x00);
// --- Reg 05h (ボリューム) ---
i2c.write(0x88);
i2c.write(vm);
i2c.stop();
}
// -----------------------------
// 周波数設定(例:813 → 81.3MHz)
// -----------------------------
void set_freq1(int x) {
int chan = x - 760;
Fh = chan >> 2;
Fl = chan & 0b00000011;
Fl = Fl << 6;
Fl = Fl | 0b00011000;
i2c.start();
i2c.write(RDA_ADDR_RAN << 1);
i2c.write(0x03);
i2c.write(Fh);
i2c.write(Fl);
i2c.stop();
}
// -----------------------------
// ボリューム設定(0〜15)
// -----------------------------
void set_volume(int v) {
byte vm = v | 0b10000000;
i2c.start();
i2c.write(RDA_ADDR_RAN << 1);
i2c.write(0x05);
i2c.write(0x88);
i2c.write(0x80 | vm);
i2c.stop();
}
void setup() {
lcd.begin(0x74, 0x56);
lcd.print("J-WAVE");
delay(400); // このディレイがないと電源再投入した時うまく動きません
init_and_set(813); // J-WAVE
pinMode(PIN_PA3, INPUT_PULLUP);
pinMode(PIN_PA6, INPUT_PULLUP); // PA3 ボタン
}
void loop() {
if (digitalRead(PIN_PA3) == LOW) {
if (ch >= 6) {
ch = 0;
}
set_freq1(fm_chan[ch]);
char buf[15];
strcpy_P(buf, (char*)pgm_read_word(&(ch_name[ch])));
lcd.setCursor(0, 0);
//lcd.print(" ");
lcd.print(buf);
while (digitalRead(PIN_PA3) == LOW) {
delay(10);
}
ch++;
}
if (digitalRead(PIN_PA6) == LOW) {
if (vm1 > 4) {
vm1 = 1;
}
set_volume(vm1);
lcd.setCursor(0, 1);
lcd.print("VOM ");
lcd.setCursor(4, 1);
lcd.print(vm1);
while (digitalRead(PIN_PA6) == LOW) {
delay(10);
}
vm1++;
}
}コンパイルした時のメモリーのサイズは以下です(ギリギリです402をオススメします)
最大2048バイトのフラッシュメモリのうち、スケッチが2010バイト(98%)を使っています。
最大128バイトのRAMのうち、グローバル変数が20バイト(15%)を使っていて、ローカル変数で108バイト使うことができます。
動作映像

ATtiny402ならさらに余裕がある
今回のプログラムはATtiny202でも動作しましたが、実際に開発してみるとATtiny402の使いやすさも実感しました。
ATtiny402はプログラムメモリが4KBあるため、ATtiny202の約2倍の容量があります。
この余裕のおかげで、
- 機能追加
- エラー処理
- 表示内容の充実
- プログラムの見やすさ
などをあまり容量を気にせず実装できます。
ATtiny202は「2KBでどこまでできるか」を楽しむマイコンですが、実際の製作ではATtiny402の方が余裕を持って開発できるため、非常に扱いやすいと感じました。
まとめ
今回はATtiny202・RDA5807・AQM1602を組み合わせたFMラジオを製作してみました。
2KBという非常に限られたメモリでも、自作の軽量ライブラリやPROGMEMを活用することで、局名表示・放送局切り替え・音量調整まで実現することができました。
小容量マイコンは制約が多い反面、「いかにメモリを節約するか」を考えながらプログラムを組む楽しさがあります。
一方で、同じシリーズのATtiny402なら4KBのフラッシュメモリがあるため、さらに多くの機能を追加したり、プログラムを分かりやすく整理したりすることができます。
ATtiny202は限界に挑戦する楽しさがあり、ATtiny402は実用的な作品を作りやすいマイコンです。用途に応じて使い分けることで、より幅広い電子工作を楽しめるでしょう。


