M5StickCを簡易オシロスコープにする その2 事前調査(DAC)

概要

前回は事前調査としてADCの速度を調べました。今回は直接はオシロスコープには関係ないですがDACの速度も調べてみたいと思います。高速で動くのであればDDSにもなりそうですね。

スケッチ

#include <driver/i2s.h>

#define SAMPLING_RATE (16000)       // サンプリングレート
#define BUFFER_LEN    (512)         // バッファサイズ
uint8_t soundBuffer[BUFFER_LEN];    // DMA転送バッファ

void setup() {
  // 再生設定
  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_ONLY_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 val = 0;
  for (int i = 0; i < BUFFER_LEN; i += 2) {
    soundBuffer[i] = 0;
    soundBuffer[i + 1] = val;
    val++;
    if (256 <= val) {
      val = 0;
    }
  }

  // 再生設定実施
  i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
  i2s_set_pin(I2S_NUM_0, NULL);
  i2s_zero_dma_buffer(I2S_NUM_0);
}

void loop() {
  size_t transBytes;
  i2s_write(I2S_NUM_0, (char*)soundBuffer, BUFFER_LEN, &transBytes, portMAX_DELAY);
}

ベースはこんな感じにしてみました。256段階のDAC出力なので、0(0V)から255(3.3V)までを変化させるのこぎり波を出してみたいと思います。

DACは8ビット精度なのですがI2Sの制限として、16ビットとして送信する必要があります。なので512個の配列を用意して、奇数のデータは0で偶数に0から255を詰め込んでいます。

サンプリングレートを16000にしていますので、256で割った62.5Hzぐらいののこぎり波が出力されれば成功です。

出力結果です。62.3Hzぐらいなので大丈夫そうですね。出力電圧は3.24Vになっていましたが、この数値は安いオシロスコープの値なのでそれほど信用できません。。。

PulseViewは3.3V/divに対応してほしい、、、5V/vivの下が2V/vivなんですよね。

100,000sps(390Hz)

余裕ですね。

500,000sps(1,953Hz)

まだまだです。

1,000,000sps(3,906Hz) NG

周波数が増えていません。上限がありますね。

600,000sps(2,343Hz)

いけました。

700,000sps(2,734Hz) NG

だめでした。6と7の境界線ってことは。。。

655,350sps(2,561Hz)

いけました。

660,000sps(2,578Hz)

あれ、もっといけますね。

670,000sps(2,617Hz) NG

だめになった。

661,498sps(2,583Hz)

ここが限界でした。64,000sps(2,500Hz)ぐらいまでで使うのが数値的にわかりやすいそうですね。

640,000sps(20,000Hz) 32point

256段階から32段階に減らしました。思ったより波形が汚いですね、、、

640,000sps(40,000Hz) 16point

ちょっと微妙ですね。

640,000sps(80,000Hz) 8point

んー。

640,000sps(160,000Hz) 4point

波形がおかしくなった!

のこぎり波はここまでにして、サイン波を検証します。

640,000sps(2,500Hz) 256point Sin

256段階のサイン波は流石にきれいです。

640,000sps(5,000Hz) 128point Sin

まだ大丈夫ですね。

640,000sps(10,000Hz) 64point Sin

ちょっと荒れてきました。

640,000sps(20,000Hz) 32point Sin

んー、これはダメな気がします。

ちょっと不安になったので、他のオシロスコープで確かめます。やっぱり波形が荒れていますね。

640,000sps(40,000Hz) 16point Sin

さすがにこれはサイン波じゃないですよね?

100,000sps(6,250Hz) 16point Sin

周波数を落としても波形は同じです。

あれ、この波形はマイクでおかしかったときの波形に似ていますね、、、ということはステレオ系の設定が影響している可能性があります。

I2S_CHANNEL_FMT_ONLY_LEFTからI2S_CHANNEL_FMT_ALL_LEFTに変更。おっ、きれいになりました。ただし、周波数が倍になっています。これはステレオとしてデータを入れないといけないので1サンプリング4バイトになったからだと思います。

改修版スケッチ

#include <driver/i2s.h>

#define SAMPLING_RATE (100000)      // サンプリングレート
#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_ALL_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 = 16;
  int val = 0;
  for (int i = 0; i < BUFFER_LEN; i += 4) {
    soundBuffer[i] = 0;
    soundBuffer[i + 1] = 127 + 127 * sin(2 * PI / 256 * val);
    soundBuffer[i + 2] = 0;
    soundBuffer[i + 2] = 0;
    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);
  i2s_zero_dma_buffer(I2S_NUM_0);
}

void loop() {
  size_t transBytes;
  i2s_write(I2S_NUM_0, (char*)soundBuffer, BUFFER_LEN, &transBytes, portMAX_DELAY);
}

サイン波に改造されていますが、I2S_CHANNEL_FMT_ALL_LEFT出力です。

100,000sps(6,250Hz) 16point Sin

きれいになりました!

640,000sps(40,000Hz) 16point Sin

これぐらいまでがきれいなサイン波の限界かな?

640,000sps(80,000Hz) 8point Sin

んー、これはぎりぎりサイン波でしょうか?

640,000sps(160,000Hz) 4point Sin

周波数カウントでは拾えますが、これだったら矩形波でいいよね?

640,000sps(80,000Hz) 8point のこぎり波

のこぎり波リベンジ! きれいになりました。

640,000sps(160,000Hz) 4point のこぎり波

これが限界ですね。

まとめ

I2SでのDDSはサイン波が80KHz、のこぎり波が160KHzまでいけました。I2Sのマイクと同じくDAC出力もステレオ設定で送信しないとデータがおかしくなる問題がありました。

スピーカーをDAC出力で送信するときにも音質に影響を与えているはずです。

コメントする

メールアドレスが公開されることはありません。

管理者承認後にページに追加されます。公開されたくない相談はその旨本文に記載するかTwitterなどでDM投げてください。またスパム対策として、日本語が含まれない投稿は無視されますのでご注意ください。