LoginSignup
58

More than 5 years have passed since last update.

ESP32でI2S+DACを使う

Last updated at Posted at 2017-05-06

概要

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の状態を見ると、それには、まだまだ時間がかかりそうな気がします。

参照

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
58