音声再生IC NSP2340Aを試す I2C編

NSP2340A、前回までで大体やりたいことはやれたのですが、あと一個やっておきたいのが、I2Cでの接続です。これまではUART通信で全部試していたのですが、UARTはArduino IDEのシリアルモニタを使ったデバッグ用に残しておいた方が何かと便利なので、I2C接続できるならそれに越したことはありません。というかI2Cも選べるとかすごいぞNSP2340A。

ということで、以下、I2Cでの動作確認です。

Windowsの”NSPPlayListEditor”で作成済みのプロジェクトを開き、”Project Settings”を開きます。

これまでは”UART Mode”にチェックを入れていましたが、そこを外して”I2C Mode”にチェックを入れます。アドレスはお好みで。”Update firmware via UART”はそのままです(チェックON)。

それから、先の検証で有効にしていた”Auto Sleep”ですが、今回は”Reset Status”を”Disable”にしています。これで、起動時に勝手に有効にはならず、”AUTO_SLEEP_EN”コマンドを発行すればオートスリープが有効になります。使おうと思えば使える状態、ということですね。

設定が済んだら、これまでの手順通り、リビルド&書き込みです。ちなみに、今回は作成済みのプロジェクトをリビルドしていますが、もちろん最初からI2C用に設定してプロジェクトをつくっても大丈夫です。

マイコン(ATtiny1616)側のプログラムは以下のようにしました。

#include <Wire.h>

#define PIN_SW_CH1  PIN_PA6
#define PIN_SW_CH2  PIN_PA7

#define ON  LOW
#define OFF HIGH

#define AUDIO_ADDR 0x20
#define AUDIO_CH1 1
#define AUDIO_CH2 2
#define AUDIO_VOLUME 48

uint8_t sw_ch1 = OFF;
uint8_t sw_ch2 = OFF;
uint8_t prev_sw_ch1 = OFF;
uint8_t prev_sw_ch2 = OFF;

uint16_t num_sound = 1;
bool is_bgm_playing = false;

uint8_t send_cmd(const uint8_t *cmd, uint8_t len) {
  Wire.beginTransmission(AUDIO_ADDR);
  for (uint8_t i = 0; i < len; i++) {
    Wire.write(cmd[i]);
  }
  Wire.endTransmission();

  delay(5);

  Wire.requestFrom(AUDIO_ADDR, 1);
  if (Wire.available()){
    return Wire.read();
  }
  return 0x00;
}

uint8_t calc_checksum(const uint8_t *data, uint8_t len){
  uint16_t sum = 0;
  for(uint8_t i=0; i<len; i++){
    sum += data[i];
  }
  return (uint8_t)(~sum);
}

uint8_t power_down(){
  uint8_t cmd[2];
  cmd[0] = 0x82;
  cmd[1] = calc_checksum(cmd, sizeof(cmd)-1); // cmd[0]~cmd[1]までの2byteのチェックサムを計算
  return send_cmd(cmd, sizeof(cmd));
}

uint8_t power_up(){
  // スリープを復帰させるためだけのコマンドは存在しないので、音量設定で代替
  return set_volume(AUDIO_VOLUME);
}

uint8_t set_auto_sleep_enable(){
  uint8_t cmd[2];
  cmd[0] = 0x83;
  cmd[1] = calc_checksum(cmd, sizeof(cmd)-1); // cmd[0]~cmd[1]までの2byteのチェックサムを計算
  return send_cmd(cmd, sizeof(cmd));
}

uint8_t set_auto_sleep_disable(){
  uint8_t cmd[2];
  cmd[0] = 0x84;
  cmd[1] = calc_checksum(cmd, sizeof(cmd)-1); // cmd[0]~cmd[1]までの2byteのチェックサムを計算
  return send_cmd(cmd, sizeof(cmd));
}

uint8_t set_volume(uint8_t vol){
  if(vol > 128){
    vol = 128;
  }
  uint8_t cmd[3];
  cmd[0] = 0xF0;
  cmd[1] = vol;
  cmd[2] = calc_checksum(cmd, sizeof(cmd)-1); // cmd[0]~cmd[1]までの2byteのチェックサムを計算
  return send_cmd(cmd, sizeof(cmd));
}

uint8_t play_sound(uint8_t ch, uint16_t num_sound){
  uint8_t cmd[5];
  cmd[0] = 0xA0;
  cmd[1] = ch;
  cmd[2] = highByte(num_sound);
  cmd[3] = lowByte(num_sound);
  cmd[4] = calc_checksum(cmd, sizeof(cmd)-1); // cmd[0]~cmd[3]までの4byteのチェックサムを計算
  return send_cmd(cmd, sizeof(cmd));
}

uint8_t stop_sound(uint8_t ch){
  uint8_t cmd[3];
  cmd[0] = 0xA1;
  cmd[1] = ch;
  cmd[2] = calc_checksum(cmd, sizeof(cmd)-1); // cmd[0]~cmd[1]までの2byteのチェックサムを計算
  return send_cmd(cmd, sizeof(cmd));
}

uint8_t pause_sound(uint8_t ch){
  uint8_t cmd[3];
  cmd[0] = 0xA2;
  cmd[1] = ch;
  cmd[2] = calc_checksum(cmd, sizeof(cmd)-1); // cmd[0]~cmd[1]までの2byteのチェックサムを計算
  return send_cmd(cmd, sizeof(cmd));
}

uint8_t resume_sound(uint8_t ch){
  uint8_t cmd[3];
  cmd[0] = 0xA3;
  cmd[1] = ch;
  cmd[2] = calc_checksum(cmd, sizeof(cmd)-1); // cmd[0]~cmd[1]までの2byteのチェックサムを計算
  return send_cmd(cmd, sizeof(cmd));
}

/////////////////////////////////////////////////////////////////////////////////////////////

void setup(){
  // 省電力化のため、不要な部分を停止。動作周波数はArduino IDEの方のClock指定で1MHzに落とす
  ADC0.CTRLA &= ~ADC_ENABLE_bm; // ADC(アナログデジタル変換)を停止
  SPI0.CTRLA &= ~SPI_ENABLE_bm; // SPI停止

  Serial.begin(9600); // PB3がRX、PB2がTXになる。Arduino IDEのシリアルモニタ接続用
  Wire.begin();

  pinMode(PIN_SW_CH1, INPUT_PULLUP);
  pinMode(PIN_SW_CH2, INPUT_PULLUP);

  // 省電力化のため、未使用ピンはすべてプルアップする
  pinMode(PIN_PA1, INPUT_PULLUP);
  pinMode(PIN_PA2, INPUT_PULLUP);
  pinMode(PIN_PA3, INPUT_PULLUP);
  pinMode(PIN_PA4, INPUT_PULLUP);
  pinMode(PIN_PA5, INPUT_PULLUP);
  pinMode(PIN_PB4, INPUT_PULLUP);
  pinMode(PIN_PB5, INPUT_PULLUP);
  pinMode(PIN_PC0, INPUT_PULLUP);
  pinMode(PIN_PC1, INPUT_PULLUP);
  pinMode(PIN_PC2, INPUT_PULLUP);
  pinMode(PIN_PC3, INPUT_PULLUP);

  // 起動直後のみ、通信が安定しないのでリトライ処理を入れる。5回まで
  for(uint8_t i=0;i<5;i++){
    delay(500);
    if(set_volume(AUDIO_VOLUME) == 0xA5){
      break; // コマンド正常受付
    }
  }
}

void loop(){
  sw_ch1 = digitalRead(PIN_SW_CH1);
  sw_ch2 = digitalRead(PIN_SW_CH2);

  if(prev_sw_ch1 == OFF && sw_ch1 == ON){
    play_sound(AUDIO_CH1, num_sound);
    Serial.print("Play Sound: ");
    Serial.println(num_sound);
    num_sound++;
    if(num_sound >= 5){
      num_sound = 1;
    }
  }

  if(prev_sw_ch2 == OFF && sw_ch2 == ON){
    if(is_bgm_playing){
      stop_sound(AUDIO_CH2);
      Serial.println("Stop BGM.");
    }else{
      play_sound(AUDIO_CH2, 5);
      Serial.println("Play BGM.");
    }
    is_bgm_playing = !is_bgm_playing;
  }

  prev_sw_ch1 = sw_ch1;
  prev_sw_ch2 = sw_ch2;

  delay(10);
}

ありがたいことにコマンド自体はUART通信と共通なので、基本的には通信の部分だけを書き換えています。それから、I2Cを使用するので、setup()関数内でのI2Cの電力カットは削除して、Wire.begin()を追加。あとは、シリアルモニタの動作確認用に音声の再生時にログ出力するようにしたのと、今後の検証用にpower_down()/power_up()関数を追加しています。今回は使いませんけれど。

配線は以下のようになります。ATtiny1616の場合、デフォルトではB0がSCL、B1がSDAになります。一方、NSP2340AではBP00(←UARTではRX)がSCL、BP01(←UARTではTX)がSDAになりますので、B0とBP00、B1とBP01を接続します。

記事作成後の追記:
この記事の作成中は特にトラブルなく動いてしまったので省略してしまいましたが、ATtiny1616とNSP2430Aブレークアウトボードキットを組み合わせる場合は、I2C通信用の外付けのプルアップ抵抗を入れておく方がベターです。具体的には、SCLとSDAのそれぞれのラインについて、ラインの途中から外付け抵抗を挟んでプラス電源(Vcc)と繋ぎます。抵抗の大きさは3.3kΩぐらいで多分OK。これを入れておかないと、通信が不安定になって音声が再生されたりされなかったりします(←後日のものづくりのときにハマりました)。

元々UART通信で使っていたピン(B2, B3)をPC側の書き込み機と接続します。ではでは動作確認。

問題なしです。

Arduino IDEのシリアルモニタにも問題なくログが出力されています。開発するならI2C接続の方が便利かな。

最後に念の為、I2C接続版で、ボタン電池(LR44) x 3個で動くかのチェック。

大丈夫でした。

ということで、I2C接続でも問題なく使用できました。UART版に比べて、I2CをOFFにしない分、ごく僅かに消費電流が上がるかもしれませんが、本当にごく僅かだと思うので、開発する分にはデバッグのしやすいI2C接続をメインで使うのが良いかなあと思いました。少なくとも自分の場合。

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメント一覧 (1件)

コメントする

目次