ESP32でI2S+DACを使う

  • 20
    いいね
  • 0
    コメント

概要

ESP32にはI2Sインターフェースが内蔵されており、音声の入出力が可能です。ESP32のI2SはDMA経由で動作し、低いCPUの負荷で動作します。8bitのDACも内蔵されており、I2Sに接続して使用できます。
I2Sのライブラリ、サンプルプログラムは esp-idf開発環境[1]で提供されています。使用方法もProgramming Guide[2]に説明してありますが、そこに書いてある方法では、内蔵DACから音声を出力することができませんでした。試行錯誤の結果、若干修正することで出力できるようなったので、その修正内容を紹介します。 
esp-idfのドキュメントの更新が追い付いていないだけで、いずれ解決される問題ではないかと思いますが、現時点(2017年5月)では、この情報は、提供されていないようなので、紹介します。

ドキュメントにあるI2S+DACの使い方

ドキュメント、Programming GuideのI2Sのページ[3]にあるI2SとDACを組み合わせて使う方法を以下に示します。大変簡単です。

#include "driver/i2s.h"
#include "freertos/queue.h"

static const int i2s_num = 0; // i2s port number

static const i2s_config_t i2s_config = {
     .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN,
     .sample_rate = 44100,
     .bits_per_sample = 8, /* must be 8 for built-in DAC */
     .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
     .communication_format = I2S_COMM_FORMAT_I2S_MSB,
     .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // high interrupt priority
     .dma_buf_count = 8,
     .dma_buf_len = 64
};

...

    i2s_driver_install(i2s_num, &i2s_config, 0, NULL);   //install and start i2s driver

    i2s_set_pin(i2s_num, NULL); //for internal DAC

    i2s_set_sample_rates(i2s_num, 22050); //set sample rates

    i2s_driver_uninstall(i2s_num); //stop & destroy i2s driver

補足すると、

  • i2s_driver_install()で設定した値と違わなければ、i2s_set_sample_rates()は不要。
  • i2s_driver_uninstall()も、特にメモリーを解放したいというようなことが無ければ不要。
  • データの書き込みは、1サンプルづつ i2s_push_sample() か、まとめてi2s_write_bytes()で行う。

その問題点

ドキュメントどおり作成したプログラムでは、DACの出力から、ちゃんとした音声が出力されません。
私は、次のような現象に遭遇しました。

  • i2s_set_sample_rates()で、エラーになります。メッセージは"Invalid bits per sample"
  • esp-idf/components/driver/i2s.cを改造し、上記のエラーが出ないようにしても、
    • DAC1から、なんか波形は出るが、いろいろ変
    • DAC2からは、ノイズしか出力されない

対策

試行錯誤の結果、わかった対策は、以下の通りです。

  • .bits_per_sample は 16, "must be 8 for built-in DAC" と書いてあるけど16
  • i2s_push_sample(), i2s_write_bytes()でデータを書き込む場合も1サンプルあたり 16bit x 2 = 32bitづつ書き込む。但しDACは8bitなので、下位8bitは無視される
  • .dma_buf_countは大目に取る。参考にしたプログラム[4]では14

当初、 .dma_bufの数など2つあれば十分、余裕を持たせて4程度にし、.dma_buf_lenを1024で試したが、うまくいきませんでした。参考にしたプログラム[4]で14となっていたので、同じにしたところ急にうまくいくようになりました。14がマジックナンバーというわけでもなく16でも問題無く動作しました。

デモ・プログラム

実際に、I2S+内蔵DACで動作するプログラムの例として、bluetooth speakerを作成しました。Tw_Mhageさんの「ESP32でBluetoothイヤホンを作ってみた」に触発され、esp-idfのサンプルプログラム a2dp_sinkを改造しています。
サンプリングレート44.1kHz, 12bit stereoに決め打ちです。環境によっては、うまく動かないかもしれませんが、サンプルプログラムとして参考になれば幸いです。

動作の様子をYouTube, ソースプログラムをgithubで公開しています。

必要な回路は、下の図程度です。GPIO25,GPIO26にDAC1,DAC2の信号が出力されます。これは固定されていて変更できません。

最後に

ESP32での音声出力は、前からやりたかったことなので、出来て嬉しいです。次は、音声入力に挑戦したいと考えています。音声入出力の機能がArduinoライブラリなどに、まとめられて、簡単に使えるようになるのが理想ですが、現状のIDFやesp32-arduinoの状態を見ると、それには、まだまだ時間がかかりそうな気がします。

参照