2010-01-25

Arduinoで秋月電子通商のAM/FMラジオモジュールを制御する

秋月電子通商のラジオモジュールをArduinoで制御します。Arduinoではない素のATMEGA48で作ったものをArduinoに移植しました。

overviewwiretop

ArduinoのI2C通信はうまく使いこなせなかったので、自前のI2C関数を使用しました。12番ピンがSCL、13番ピンがSDAになっています。

モジュールからバーアンテナまでの距離は15cmくらいにしてください。長すぎると電波が減衰して音が小さくなります。短すぎるとデジタルノイズが乗ります。

使い方

RS-232Cで本機を接続します。ホストのPCは9600bps、8ビット、ノンパリティ、ストップ1ビット、でターミナルを開きます。

下の図はPuTTYを使用する場合の例です。接続先の指定で「シリアル」を選択し、「開く」をクリックします。

putty

ターミナルが開いたら、まず「?[Enter]」と入力して、 「 RADIO 1.0」と表示されれば、通信は成功しています。

?[Enter]
 RADIO 1.0

周波数の設定

周波数の数値を入力して、そのまま[Enter]を押します。

AM954kHzに設定する場合。kHz数をそのまま入力します。

954[Enter]

FM82.5MHzに設定する場合、MHz数を100倍して入力します。

8250[Enter]

2000未満の数値を入力した場合、AMの周波数。それ以上の数値の場合、FMの周波数の設定となります。
周波数を設定しただけでは、音は鳴りません。後述のバンドの選択をすることで、放送が聴けます。

バンドの選択

AM放送を選択するコマンドは、amです。

am[Enter]

FM放送を選択するコマンドは、fmです。

fm[Enter]

このどちらかのコマンドを実行すると、ミュート状態が解除され、先に設定した周波数の放送が聴けます。

ミュート

消音するには、muteコマンドを実行します。

mute[Enter]

再度、放送を聴くには、amコマンドまたはfmコマンドを実行します。

スケッチ

static const int I2C_SCL_PIN = 12;
static const int I2C_SDA_PIN = 13;

void wait10us()
{
  delayMicroseconds(10);
}

void wait10ms()
{
  delayMicroseconds(10000);
}
void wait100ms()
{
  int i;
  for (i = 0;i < 10; i++) {
    wait10ms();
  }
}
void wait1s()
{
  int i;
  for (i = 0;i < 100; i++) {
    wait10ms();
  }
}

// i2c

void i2c_cl_0()
{
  pinMode(I2C_SCL_PIN, OUTPUT);
}

void i2c_cl_1()
{
  pinMode(I2C_SCL_PIN, INPUT);
}

void i2c_da_0()
{
  pinMode(I2C_SDA_PIN, OUTPUT);
}

void i2c_da_1()
{
  pinMode(I2C_SDA_PIN, INPUT);
}

int i2c_get_da()
{
  return digitalRead(I2C_SDA_PIN);
}

void i2c_start()
{
  i2c_da_0();
  wait10us();
  i2c_cl_0();
  wait10us();
}

void i2c_stop()
{
  i2c_cl_1();
  wait10us();
  i2c_da_1();
  wait10us();
}

void i2c_repeat()
{
  i2c_cl_1();
  wait10us();
  i2c_da_0();
  wait10us();
  i2c_cl_0();
  wait10us();
}

bool i2c_write(int c)
{
  int i;
  bool nack;

  wait10us();

  for (i = 0; i < 8; i++) {
    if (c & 0x80) {
      i2c_da_1();
    } else {
      i2c_da_0();
    }
    c <<= 1;
    wait10us();
    i2c_cl_1();
    wait10us();
    i2c_cl_0();
    wait10us();
  }

  i2c_da_1();
  wait10us();

  i2c_cl_1();
  wait10us();
  nack = i2c_get_da();
  i2c_cl_0();

  return nack;
}

int i2c_read(bool nack)
{
  int i, c;

  i2c_da_1();
  wait10us();

  c = 0;

  for (i = 0; i < 8; i++) {
    i2c_cl_1();
    wait10us();
    c <<= 1;
    if (i2c_get_da()) {
      c |= 1;
    }
    i2c_cl_0();
    wait10us();
  }

  if (nack) {
    i2c_da_1();
  } else {
    i2c_da_0();
  }
  wait10us();
  i2c_cl_1();
  wait10us();
  i2c_cl_0();
  wait10us();

  return c;
}

// ns9542

void ns9542_write(int a, int c)
{
  i2c_start();
  i2c_write(0xc8);
  i2c_write(a);
  i2c_write(c);
  i2c_stop();
}

int ns9542_read(int a)
{
  int c;
  i2c_start();
  i2c_write(0xc8);
  i2c_write(a);
  i2c_repeat();
  i2c_write(0xc9);
  c = i2c_read(true);
  i2c_stop();
  return c;
}

void ns9542_imf_adjust()
{
  int bF, imf, fhm, g_fhm;
  bF = 0;
  g_fhm = 0xf0;
  ns9542_write(0x15, 0x0e);
  ns9542_write(0x3d, 0x27);
  for (fhm = 0; fhm < 4; fhm++) {
    bF = 0;
    for (imf = 0; imf < 3; imf++) {
      ns9542_write(0x37, fhm);
      ns9542_write(0x16, 22 + imf);
      wait10ms();
      if ((ns9542_read(0x70) & 0x0c) == 0x0c) {
        bF++;
        if (imf == 1 && g_fhm == 0xf0) {
          g_fhm = fhm;
        }
      }
    }
    if (bF == 3) {
      g_fhm = fhm;
      break;
    }
  }
  ns9542_write(0x37, 0x80 | g_fhm);
  ns9542_write(0x16, 23);
  ns9542_write(0x3d, 0x37);
  wait100ms();
}

void ns9542_best_iml(int iml)
{
  ns9542_write(0x32, 0x00);
  while (iml < 16) {
    ns9542_write(0x17, 0xc0 | iml);
    wait10ms();
    if (!(ns9542_read(0x70) & 0x08)) {
      break;
    }
    iml++;
  }
  iml--;
  ns9542_write(0x17, 0xc0 | iml);
  ns9542_write(0x32, 0x80);
  wait10ms();
  ns9542_write(0xfe, 0x0a);
  wait10ms();
  wait10ms();
}

void ns9542_find_pg(int ialgn, int *fine_phase, int *fine_gain, int *result_pg)
{
  int i, j;
  for (i = 0; i < 16; i++) {
    ns9542_write(0x15, 0x0a | (ialgn << 4));
    ns9542_write(0x15, 0x0b | (ialgn << 4));
    if (ns9542_read(0x05) & 0x08) {
      for (j = 0; j < 20; j++) {
        if (!(ns9542_read(0x05) & 0x08)) {
          int g = ns9542_read(0x65);
          int p = ns9542_read(0x66);
          if (g >= 103 && g <= 138 && 2 >= p && p <= 14) {
            *fine_gain = g;
            *fine_phase = p;
            *result_pg = 1;
            return;
          }
        }
        wait10ms();
      }
    }
  }
  *result_pg = 0;
}

void ns9542_table_write(int *fine_p, int *fine_g)
{
  int i, j, k, result;
  result = 0;
  for (i = 0; i < 4; i++) {
    ns9542_write(0x38, fine_g[i]);
    ns9542_write(0x39, fine_p[i] << 4);
    for (j = 0; j < 10; j++) {
      ns9542_write(0x15, 0x0e | (i << 4));
      ns9542_write(0x15, 0x03 | (i << 4));
      if (ns9542_read(0x05) & 0x08) {
        wait100ms();
        for (k = 0; k < 10; k++) {
          if (!(ns9542_read(0x05) & 0x08)) {
            result++;
            goto L1;
          }
          wait10ms();
        }
        break;
      }
    }
L1:;
    if (result != i + 1) {
      break;
    }
  }
}

void ns9542_dsp_align_body()
{
  int iml, imf, ialgn, cnt, fp, fg;
  int fine_p[5] = { 0, 0, 0, 0, 0 };
  int fine_g[5] = { 0, 0, 0, 0, 0 };
  iml = 5;
  for (ialgn = 0; ialgn < 4; ialgn++) {
    ns9542_write(0x15, 0x0a | (ialgn << 4));
    wait100ms();
    wait100ms();
    ns9542_best_iml(iml);
    imf = 0;
    cnt = 0;
    fp = 0;
    fg = 0;
    for (cnt = 0; cnt < 5; cnt++) {
      int fine_phase, fine_gain, result_pg;
      ns9542_find_pg(ialgn, &fine_phase, &fine_gain, &result_pg);
      if (result_pg == 0) {
        return;
      }
      fp = fp + fine_phase;
      fg = fg + fine_gain;
      if (cnt == 2 && ialgn < 2) {
        cnt++;
        break;
      }
    }
    fine_p[ialgn] = fp / cnt;
    fine_g[ialgn] = fg / cnt;
  }
  ns9542_table_write(fine_p, fine_g);
}

void ns9542_mute(bool mute)
{
  if (mute) {
    ns9542_write(0x00, ns9542_read(0x00) | 0x02);
  } else {
    ns9542_write(0x00, ns9542_read(0x00) & ~0x02);
  }
}

void ns9542_tune_am9(int freq) // freq = kHz
{
  unsigned short psy;

  psy = freq;

  ns9542_write(0x00, 0x23);

  wait10ms();
  wait10ms();

  ns9542_write(0x04, 0x80);
  ns9542_write(0x0c, 0xf0);

  ns9542_write(0x10, 0x10);

  ns9542_write(0x02, psy & 0xff);
  ns9542_write(0x03, psy >> 8);

  ns9542_write(0x00, 0x21);
}

void ns9542_tune_fm(int freq) // freq = MHz * 100
{
  unsigned short psy;

  psy = freq / 5;

  ns9542_write(0x00, 0x03);

  ns9542_write(0x10, 0x10);

  ns9542_write(0x02, psy & 0xff);
  ns9542_write(0x03, psy >> 8);

  ns9542_write(0x00, 0x01);
}

void ns9542_reset()
{
  ns9542_write(0xfe, 0xaa);
}

void ns9542_power_on()
{
  static unsigned char power_on[] = {
    0x01, 0x30,
    0x0c, 0x80,
    0x0e, 0x34,
    0x15, 0xc4,
    0x20, 0x3c,
    0x21, 0x03,
    0x22, 0x0a,
    0x23, 0x0a,
    0x30, 0xff,
    0x3d, 0x07,
    0x40, 0x1a,
    0x41, 0x9a,
    0x50, 0xe1,
    0x54, 0xb0,
    0x55, 0x36,
    0x5c, 0xc8,
    0x5d, 0x61,
    0x5e, 0x88,
    0x5f, 0xa5,
    0x71, 0x2c,
    0x72, 0x06,
  };
  int i;
  for (i = 0; i < sizeof(power_on); i += 2) {
    ns9542_write(power_on[i], power_on[i + 1]);
  }

  ns9542_write(0x00, ns9542_read(0x00) | 0x03);
}

void ns9542_dsp_alignment()
{
  ns9542_write(0x0e, ns9542_read(0x0e) & ~0x60 | 0x40);
  ns9542_write(0x01, 0x08);
  ns9542_write(0x15, 0x0c);
  ns9542_write(0x16, 0x17);
  ns9542_write(0x37, 0x82);
  ns9542_write(0x3d, 0x37);
  wait100ms();

  ns9542_imf_adjust();
  ns9542_dsp_align_body();

  ns9542_write(0x01, 0x38);
  ns9542_write(0x0e, ns9542_read(0x0e) & ~0x60 | 0x20);
  ns9542_write(0x15, 0xc0);
  ns9542_write(0x17, 0x20);
  ns9542_write(0x32, 0x00);
  ns9542_write(0x37, 0x01);
}

boolean isdigit(int c)
{
  return c >= '0' && c <= '9';
}


int uart_get()
{
  return Serial.read();
}

int uart_put(int c)
{
  Serial.write(c);
}

void uart_puts(char const *p)
{
  while (*p) {
    uart_put(*p);
    p++;
  }
}

int freq_am = 594; // 594kHz
int freq_fm = 8250; // 82.5MHz
unsigned char buffer[16];
int length;

void setup()
{                
  pinMode(I2C_SCL_PIN, INPUT);
  pinMode(I2C_SDA_PIN, INPUT);
  digitalWrite(I2C_SCL_PIN, 0);
  digitalWrite(I2C_SDA_PIN, 0);

  ns9542_reset();
  ns9542_power_on();
  ns9542_dsp_alignment();

  Serial.begin(9600);  // opens serial port, sets data rate to 9600 bps
  length = 0;

  uart_put('\r');
  uart_put('\n');
}

// the loop() method runs over and over again,
// as long as the Arduino has power

void loop()                     
{
  int c = uart_get();
  if (c >= 0) {
    if (c == '\r') {
      uart_put('\r');
      uart_put('\n');
      if (length > 0) {
        if (isdigit(buffer[0])) {
          int i, freq = 0;
          for (i = 0; i < length && isdigit(buffer[i]); i++) {
            freq = freq * 10 + (buffer[i] - '0');
          }
          if (freq < 2000) {
            freq_am = freq;
          } else {
            freq_fm = freq;
          }
          goto done;
        }
        if (length == 1) {
          if (buffer[0] == '?') {
            uart_puts(" RADIO 1.0\r\n");
            goto done;
          }
        }
        if (length == 2) {
          if (buffer[0] == 'a' && buffer[1] == 'm') {
            ns9542_tune_am9(freq_am);
            goto done;
          }
          if (buffer[0] == 'f' && buffer[1] == 'm') {
            ns9542_tune_fm(freq_fm);
            goto done;
          }
        }
        if (length == 4) {
          if (buffer[0] == 'm' && buffer[1] == 'u' && buffer[2] == 't' && buffer[3] == 'e') {
            ns9542_mute(true);
            goto done;
          }
        }
      }
done:;
      length = 0;
    } else {
      uart_put(c);
      if (length < sizeof(buffer)) {
        buffer[length++] = c;
      }
    }
  }
}