RDA5807M　Arduinoで制御　SEEK機能編　DSP　FMラジオ

#はじめに
* RDA5807では、特定周波数の受信には成功したので、SEEK機能に挑戦。
* モジュールは、Aliexpressにて調達してみた。1枚40円。送料込みで4枚で250円。安すぎ。
* SEEK機能での選局だが、単純な形であればそれほど難しくなかった。

#使用したもの
* RDA5807Mモジュール　(Aliexpress)。
* IC（RDA5807M)のデータシート
* AN230(Silicon Labs)
* Arduino Uno互換機(ELEGOO)

#モジュールについて
* Aliexpressで入手したモジュールだが、Aitendoさんで「取り扱い終了版」となっているものと同じように見える。また、AMAZONで売っているものとも同じ外見。
* ピン配置は、AITENDOさんの「RDA5807-M」と同じ（「RDA5807-M2」とは異なる）。
![RDA5807M_Ali.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482773/1047a2d3-ad3d-90ba-c89b-492944aba4b5.jpeg)

#配線
* 本モジュールについては、作例も多いので割愛。[この記事](https://qiita.com/Nabeshin/items/c8c826e8751e5405f8a4)や[このページ](http://www.theorycircuit.com/arduino-rda5807m-fm-receiver/)をご参照ください。

#Slaveアドレスについて
* ここでは 0X11（個別レジスタ書込/読込用）を使います。普通のコードで読み書きできます。

#コード作成のポイント
* RDA5807はSi4702の上位互換のようなICなので、Si4702のProgramming Guide(AN230)が参考になる。SEEKの場合、Table 14を参照すればよい。具体的には以下を設定する。
    * SKMODE：SEEKを周波数帯の端で止めるか、１周して続けるか（Wrap）。→　Wrap
    * SEEKUP:SEEK方向を上にするか、下にするか。→　上にする。
    * SEEK bit：SEEKを開始するときに1にする。
    * その他の設定はそのまま。BAND、SPACEを特定周波数を受信するときと同様に設定する。
* SEEK機能はこれだけでOK。これで何かしら放送を受信するはず。ただし、ICから情報を読み取らないと、何を受信しているかがわからない。

#情報の読み取り
* 必要な情報はレジスタ0Ahと0Bhに格納されるので、これらのレジスタを読みだす必要がある。ここでは、シリアルモニタに表示することにするが、LCDに表示するのも容易。
    * 受信している周波数（CHAN）: 0Ah.[9:0]
    * 受信完了フラグ（STC）： 0Ah.[14]
    * SEEK失敗フラグ（SEEKFAIL）： 0Ah.[13]
    * 受信強度（RSSI）： 0Bh.[15:9]
    * 局かどうか（STATION）： 0Bh.[8]
* これらをレジスタから読み込んで、適当に表示をすればよい。

#プログラムの構造
* 上記の情報は、選局（SEEK）が完了（STC==1)してから読み出すべきだが、選局は直ぐ終わる場合も、時間がかかる場合もあり、情報を読み出すタイミングがわからない。早く読み出しすぎると、受信が完了していない時点の周波数を読んでしまう。
* なので、一定時間ごと（0.5秒）に0Ahを読み込み、STCフラグが1になればそこで、読み取りを終わらせることにした。
* なお、本ICのファミリーである5807FPには、STC==1になるとパルスを出すGPIOピンがあり、このピンで割り込みをかけることもできるようになっている。
* コードはloop()は使わず、setup()の中で処理しています。
* プログラムは、1回選局するとそこで終わりになります。プログラムを続けて走らせると、前回に選局した局からSEEKを開始します。

#選局基準（SEEKTH）
* レジスタ05h.[11:8]に選局の基準となるSEEKTHが定められている。大きすぎると、弱小局を拾わないような気がしたので、少し小さくしています（詳細はよくわかりません）。

#動作コード
* 76 → 108MHzの間でSEEKし、最初に見つけた局の周波数などをシリアルモニタに表示し、そこで終了する簡単なコードです。
* 選局終了後、もう一度走らせると、前回選局した周波数からまた、SEEKを開始します。上限まで行って見つからないと、下限からSEEKを続けます。詳細はコードとデータシート見てください。

```
#include <Wire.h>

int buffer1;
int buffer2;
int buffer3;
int buffer4;

void setup()
{
delay(200);
Wire.begin();

Wire.beginTransmission(0X11);
   Wire.write(0X03);
   Wire.write(0b00000000); //  CHAN NA 
   Wire.write(0b00001000); // TUNE0, BAND10, SPACE00
Wire.endTransmission();

Wire.beginTransmission(0X11);
   Wire.write(0X05);
   Wire.write(0x84); //SeekTh 0100  
   Wire.write(0x8F); //VOL1111
Wire.endTransmission();

Wire.beginTransmission(0X11);
   Wire.write(0X02);
   Wire.write(0xc3); //seek up, seek on  
   Wire.write(0x05); //seekmode0(wrap)
Wire.endTransmission();

delay(100);

boolean stc=0;

while (stc==0)
{
Wire.begin();
  Wire.beginTransmission(0X11);
  Wire.write(0X0a);
  Wire.endTransmission(false);
  Wire.requestFrom(0x11,4); //reading 5807
  Wire.endTransmission(true);
  
  buffer1= Wire.read(); //0AH
  buffer2= Wire.read(); //0AL
  buffer3= Wire.read(); //0BH
  buffer4= Wire.read(); //0BL

int freq= 760 + 512*bitRead(buffer1,1)+ 256*bitRead(buffer1,0)+ buffer2;
stc=bitRead(buffer1,6);
boolean seekfail=bitRead(buffer1,5);
byte rssi = buffer3>>1;
boolean station = bitRead(buffer3,0);

Serial.begin(9600);   
   Serial.print("STC:");
   Serial.print(stc);
   Serial.print("  SeekFail:");
   Serial.println(seekfail);
   Serial.print("Freq*10:");
   Serial.print(freq,DEC);
   Serial.print("  RSSI:");
   Serial.print(rssi,DEC);
   Serial.print("  Station:");
   Serial.println(station);

delay(500);
}
}

void loop() {
  // put your main code here, to run repeatedly:
}

```
#おまけ
* 天井から垂らした電線をアンテナにしていますが、当方の環境（東京）では、主要な局はちゃんと選局できています。どこの局かわからない周波数を選局するときもありますが…
* 割込みによるプログラムも試してみたかったので、RDA5807FPも使ってみました。5807FPについては、乾電池駆動の箱ラジオを作成したので、後日公開予定です。