概要
前回いろいろ検証しましたが、ちょっと気になったところを深堀りしてみたいと思います。
スケッチ
#include <driver/i2s.h>
#include "ESP32LiteDebugGPIO.h"
#define SAMPLING_RATE (440*256) // サンプリングレート
#define BUFFER_LEN (1024) // バッファサイズ
uint8_t soundBuffer[BUFFER_LEN]; // DMA転送バッファ
void setup() {
Serial.begin(115200);
delay(500);
Serial.printf("f= %d\n", SAMPLING_RATE);
// 再生設定
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN),
.sample_rate = SAMPLING_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S_MSB,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 2,
.dma_buf_len = BUFFER_LEN,
.use_apll = false,
.tx_desc_auto_clear = true,
.fixed_mclk = 0,
};
int step = 256;
int val = 0;
for (int i = 0; i < BUFFER_LEN; i += 4) {
soundBuffer[i] = 0;
soundBuffer[i + 1] = 256 * val / step;
soundBuffer[i + 2] = 0;
soundBuffer[i + 3] = 127 + 127 * sin(2 * PI / 256 * val);
Serial.printf(" %d\n", soundBuffer[i + 1]);
val += (256 / step);
if (256 <= val) {
val = 0;
}
}
// 再生設定実施
i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
i2s_set_pin(I2S_NUM_0, NULL); // 25, 26
//i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN); // 25, 26
//i2s_set_dac_mode(I2S_DAC_CHANNEL_RIGHT_EN); // 25
//i2s_set_dac_mode(I2S_DAC_CHANNEL_LEFT_EN ); // 26
i2s_zero_dma_buffer(I2S_NUM_0);
delay(1000);
dispGpio();
}
void loop() {
size_t transBytes;
i2s_write(I2S_NUM_0, (char*)soundBuffer, BUFFER_LEN, &transBytes, portMAX_DELAY);
}
前回検証したコードを少し手を加えて、ステレオ出力にしてみました。また、GPIOの状態をみるためにESP32LitePackライブラリのESP32LiteDebugGPIO.hを読み込んでいます。
GPIO LEVEL MODE ADC PULLUP PULLDOWN OPEN_DRAIN I/O NOTE ----------------------------------------------------------------------------- 0 HIGH INPUT PULLUP I/O 1 LOW OUTPUT I/O [O]U0TXD_OUT_IDX 2 LOW INPUT PULLDOWN I/O 3 HIGH INPUT I/O [I]U0RXD_IN_IDX 4 LOW INPUT PULLDOWN I/O 5 HIGH INPUT PULLUP I/O 6 LOW OUTPUT PULLUP I/O [O]SPICLK_OUT_IDX 7 HIGH INPUT PULLUP I/O 8 LOW OUTPUT PULLUP I/O [O]SPID_OUT_IDX 9 HIGH INPUT PULLUP I/O 10 HIGH INPUT PULLUP I/O 11 HIGH INPUT PULLUP I/O 12 LOW INPUT PULLDOWN I/O 13 LOW INPUT PULLDOWN I/O 14 HIGH INPUT PULLUP I/O 15 HIGH INPUT PULLUP I/O 16 HIGH OUTPUT PULLUP I/O [O]SPICS0_OUT_IDX 17 HIGH OUTPUT PULLUP I/O [O]SPIQ_OUT_IDX 18 HIGH INPUT I/O 19 LOW INPUT I/O 20 LOW INPUT I/O 21 HIGH INPUT I/O 22 HIGH INPUT I/O 23 LOW INPUT I/O 25 LOW INPUT ADC I/O 26 LOW INPUT ADC I/O 27 LOW INPUT I/O 32 LOW INPUT I/O 33 LOW INPUT I/O 34 LOW INPUT I 35 LOW INPUT I 36 LOW INPUT I 37 LOW INPUT I 38 LOW INPUT I 39 LOW INPUT I
んー、現状のdispGpio()だとRTC経由でADCやDACに接続していることは、わかりますがDACかADCかはもう少し深いレジスタを見ないとわからないみたいです。出力もデジタル出力は使っていないのでINPUTのままですね。

データ出力です。
| GPIO | データ | 波形 |
| 25 | データ2 | 正弦波 |
| 26 | データ1 | ノコギリ波 |
データは最初にノコギリ波、次に正弦波を入れてあります。
I2S_CHANNEL_FMT_ALL_RIGHT

両方とも正弦波になりました。つまり右チャンネルは正弦波。
I2S_CHANNEL_FMT_ALL_LEFT

当たり前ですがノコギリ波になりました。
I2S_CHANNEL_FMT_ONLY_RIGHT

ステレオデータではなく、モノラルデータなので両方ノコギリ波にしてみました。あれ、、、ステレオで出力されている、、、
I2S_CHANNEL_FMT_ALL_RIGHTとI2S_CHANNEL_FMT_ONLY_RIGHTはデータ形式の差であって、両方ともステレオ出力されちゃうのね。。。
I2S_CHANNEL_FMT_ONLY_LEFT

I2S_CHANNEL_FMT_ONLY_RIGHTとI2S_CHANNEL_FMT_ONLY_LEFTの差がないように思えるけれど、、、
まとめ
| GPIO | データ | ステレオ |
| 25 | データ2 | 右(RIGHT) |
| 26 | データ1 | 左(LEFT) |
GPIO的には25が右、26が左ですがデータは左、右の順番で設定しないとだけみたいです。そしてI2SでのDAC出力は常にステレオになります。片チャンネルだけ出力することはできませんでした!
そして、モノラル転送をすると波形がおかしくなるので、常にステレオモードで転送をおすすめします。つまりI2S_CHANNEL_FMT_RIGHT_LEFTで、どちらのチャンネルにどのデータを出力するのかを明示的に指定しないといけません。
ATOMなどでGROVE端子に出ているGPIO26に、I2S経由のDAC出力をすると、I2Cで利用している25にも出力が出ちゃうのでI2Cが使えなくなります!
モノラル設定でも両チャンネルに出力されちゃうのはバグな気もしますが、現状はこの動作なので気をつけて使う必要がありますね。。。Arduinoで使われているESP-IDFのバージョンが古すぎるので、現行バージョンのESP-IDFだと修正されているかもしれません。。。早く4系に更新されないかな、、、
追記(2020/09/25)
コメント欄でi2s_set_dac_mode()関数を教えていただきました!
| 初期化方法 | 出力GPIO |
| i2s_set_pin(I2S_NUM_0, NULL) | 25, 26 |
| i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN) | 25, 26 |
| i2s_set_dac_mode(I2S_DAC_CHANNEL_RIGHT_EN) | 25 |
| i2s_set_dac_mode(I2S_DAC_CHANNEL_LEFT_EN ) | 26 |
上記の初期化方法があるとのことです。おそらくDACのときにはi2s_set_pin()関数ではなく、i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN)で初期化したほうが良さそうですね。




コメント
ESP-IDFのi2s_set_pin関数のドキュメントによると
if *pin is set as NULL, this function will initialize both of the built-in DAC channels by default. if you don’t want this to happen and you want to initialize only one of the DAC channels, you can call i2s_set_dac_mode instead.
と言うことなのでi2s_set_dac_mode関数を使えば良いのではないでしょうか?
https://docs.espressif.com/projects/esp-idf/en/v3.2.3/api-reference/peripherals/i2s.html#_CPPv411i2s_set_pin10i2s_port_tPK16i2s_pin_config_t
情報ありがとうございます
この関数で指定できました!
とてもためになる研究記事の公開をありがとうございます。
この記事で書いているDACは「built-in DAC」のことですよね。
DACを「内蔵DAC」と修正すると私のような初心者にはわかりやすいかもしれません。
「おそらくDACのときにはi2s_set_pin()関数ではなく、」
「おそらく内蔵DACのときにはi2s_set_pin()関数ではなく、」
細かい指摘ですみません。