ATtiny202(2KB)でFMラジオを作る!RDA5807とAQM1602を組み合わせた超小型FMラジオ

ATtiny202(2KB)で製作したFMラジオを紹介するアイキャッチ画像。RDA5807 FMラジオモジュールとAQM1602液晶を組み合わせた電子工作をイメージしたデザイン。ATtiny202(2KB)でFMラジオを作る!RDA5807とAQM1602を組み合わせた超小型FM
スポンサーリンク

以前の記事では、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という限られたフラッシュメモリの中へ収めることができました。

小容量マイコンだからといって「何もできない」のではなく、ライブラリやプログラムを工夫することで、実用的なアプリケーションを十分作れることが分かりました。


回路図

今回作成した回路図です

attiny202 rda5807  aqm1602  接続図

プログラム

以下が今回作成したプログラムです。

#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をオススメします)

動作映像

実際に動かしてみたときの画像

ATtiny402ならさらに余裕がある

今回のプログラムはATtiny202でも動作しましたが、実際に開発してみるとATtiny402の使いやすさも実感しました。

ATtiny402はプログラムメモリが4KBあるため、ATtiny202の約2倍の容量があります。

この余裕のおかげで、

  • 機能追加
  • エラー処理
  • 表示内容の充実
  • プログラムの見やすさ

などをあまり容量を気にせず実装できます。

ATtiny202は「2KBでどこまでできるか」を楽しむマイコンですが、実際の製作ではATtiny402の方が余裕を持って開発できるため、非常に扱いやすいと感じました。

まとめ

今回はATtiny202・RDA5807・AQM1602を組み合わせたFMラジオを製作してみました。

2KBという非常に限られたメモリでも、自作の軽量ライブラリやPROGMEMを活用することで、局名表示・放送局切り替え・音量調整まで実現することができました。

小容量マイコンは制約が多い反面、「いかにメモリを節約するか」を考えながらプログラムを組む楽しさがあります。

一方で、同じシリーズのATtiny402なら4KBのフラッシュメモリがあるため、さらに多くの機能を追加したり、プログラムを分かりやすく整理したりすることができます。

ATtiny202は限界に挑戦する楽しさがあり、ATtiny402は実用的な作品を作りやすいマイコンです。用途に応じて使い分けることで、より幅広い電子工作を楽しめるでしょう。

タイトルとURLをコピーしました