たま日記

たまに書く

少し進む

M5Stackで試作していたが音を出してみるとノイズまみれ。内蔵DACは使えなさそう。本番ではコンパクトなDevKitCを使うつもりなので構成を変更。

f:id:androuet:20190913183010j:plain

部品 購入先
ESP32-DevKitC-32D 秋月
LPS33HW 【防水】気圧センサモジュール Strawberry Linux
MAX98357A搭載 I2S 3W D級アンプボード スイッチサイエンス
スピーカー 直径27mm amazon
ピアニカ吹口(ヤマハPMP32C) 近所の楽器屋さん
シリコーンチューブ 内径12mm amazon

ESP32のcore1で気圧センサの値を読み、core0で音を出してみた。I2SはMSBから送出しているらしい。なのにlittle endianなESP32のint16_tをそのままi2s_write()して音が出るのが不思議…?

気圧センサは前回と同じものを利用。シリコンチューブに穴を空けて、センサの出っ張り部分を穴に入れて、結束バンドで固定した。チューブの反対側には栓をした。I2Cのプルアップ抵抗は手元にあった2.2KΩ。はじめは10KΩだった。でもスピーカーをつなぐとI2C読み取りに失敗する。しばらく悩んで抵抗値を変えたら動いた。いくつが適切なのかよくわからない。

アンプボードのゲインは6dbに変更。9dbだと音が割れるようだ。原因はスピーカー?電源?よくわからない。スピーカーにはエンクロージャーが必要なことを初めて知った。音が小さいな?とは感じていた。大きなスピーカーをつなげたら大きな音で鳴るのだろうか?

今後の予定:

/**
 * Devkit-Cとmax98357aで音出しする
 */

#include <driver/i2s.h>
#include <Wire.h>

/** http://akiracing.com/2018/02/05/how_to_use_lps22hb/ */
////////////////////////LPS22HB////////////////////////////
#define LPS22HB_ADDRESS 0x5c//I2Cでの気圧センサのスレーブアドレス

#define CTRL_REG1 0x10//設定用のレジスタのアドレス
#define CTRL_REG1_POWER_DOWN 0x00//データ出力モード(0Hz)

////////LPS22HBの設定////////
#define CTRL_REG1_1HZ 0x10//データ出力モード(1Hz)
#define CTRL_REG1_10HZ 0x20//データ出力モード(10Hz)
#define CTRL_REG1_25HZ 0x30//データ出力モード(35Hz)
#define CTRL_REG1_50HZ 0x40//データ出力モード(50Hz)
#define CTRL_REG1_75HZ 0x50//データ出力モード(75Hz)//akiracing.com
#define CTRL_REG1_LPF_9_1HZ 0x18//データ出力モード(1Hz)//LPF, Freq/9
#define CTRL_REG1_LPF_9_10HZ 0x28//データ出力モード(10Hz)//LPF, Freq/9
#define CTRL_REG1_LPF_9_25HZ 0x38//データ出力モード(35Hz)//LPF, Freq/9
#define CTRL_REG1_LPF_9_50HZ 0x48//データ出力モード(50Hz)//LPF, Freq/9
#define CTRL_REG1_LPF_9_75HZ 0x58//データ出力モード(75Hz)//LPF, Freq/9
#define CTRL_REG1_LPF_20_1HZ 0x1C//データ出力モード(1Hz)//LPF, Freq/20
#define CTRL_REG1_LPF_20_10HZ 0x2C//データ出力モード(10Hz)//LPF, Freq/20
#define CTRL_REG1_LPF_20_25HZ 0x3C//データ出力モード(35Hz)//LPF, Freq/20
#define CTRL_REG1_LPF_20_50HZ 0x4C//データ出力モード(50Hz)//LPF, Freq/20
#define CTRL_REG1_LPF_20_75HZ 0x5C8//データ出力モード(75Hz)//LPF, Freq/20

uint8_t barometerData[3];//8ビット出力データの保存用配列
volatile int32_t pressureBIN = 0;//2進数での気圧データ格納用
volatile float pressure = 0;//10進数での気圧データ格納用

//////////////////////////////////akiracing.com///////////////

#define SAMPLE_RATE (44100)

int16_t sinTable[100];

void setup() {
  Serial.begin(115200);
  Wire.begin(21, 22);
  for (int i=0; i<100; i++) {
    sinTable[i] =  (int16_t)(32767 * sin(PI * 2 * i / 100) *0.7); // 歪?を抑えるための0.7倍
  }
  i2cWriteByte(LPS22HB_ADDRESS, CTRL_REG1, CTRL_REG1_LPF_9_75HZ);//測定を開始する//アドレスCTRL_REG1にレジスタCTRL_REG1_75HZを書き込む
  xTaskCreatePinnedToCore(play, "Task0", 4096, NULL, 1, NULL, 0);
}

int nobress = 4149000; // 息を吹いていない場合の数値
int lower = nobress - 5000; // シリアルプロッタの下限
int upper = nobress + 10000; // シリアルプロッタの上限

float vol = 0;

void loop() {
  barometer(); //気圧データの取得
  Serial.print(lower); Serial.print(" "); //4120000 - 4130000
  Serial.print(upper); Serial.print(" "); //4140000
  Serial.println(pressureBIN);

  float v = 0;
  if (pressureBIN < lower + 8000) {
    v = 0;
  } else {
    v = (pressureBIN - (lower + 8000)) / (upper - (lower + 8000.0));
    if (v > 1) v = 1;
  }
  vol = v;

  delay(1000/75);  
}

void barometer() {
  i2cRead(LPS22HB_ADDRESS, 0x28, 3, barometerData); //0x28から,3バイト分をbarometerDataにいれる
  pressureBIN = (uint32_t)barometerData[2] << 16 | (uint16_t)barometerData[1] << 8 | barometerData[0]; //barometerData[2]を左に16シフトし(<<),barometerData[1]を左に8シフトし(<<),barometerData[0]を足し合わせる(|)
  pressure = pressureBIN / 4096.000000;//[hPa]に変換
}

void i2cRead(uint8_t Address, uint8_t Register, uint8_t NBytes, volatile uint8_t* Data) {//指定したアドレスのデータを読む関数
  Wire.beginTransmission(Address);//指定したアドレスと通信を始める
  Wire.write(Register);//レジスタを書き込む
  Wire.endTransmission(false);//通信を終了する

  Wire.requestFrom(Address, NBytes);//スレーブからNByteのデータを要求する
  uint8_t index = 0;
  while (Wire.available()) {
    Data[index++] = Wire.read();//データを読み込む
  }
  Wire.endTransmission(true);
  
  //Serial.print(index); Serial.print(" ");
}

void i2cWriteByte(uint8_t Address, uint8_t Register, volatile uint8_t Data) {//指定したアドレスにデータを書き込む関数
  Wire.beginTransmission(Address);//指定したアドレスと通信を始める
  Wire.write(Register);//指定するレジスタを書き込む
  Wire.write(Data);//データを書き込む
  Wire.endTransmission(true);//通信を終了する
}


void I2S_Init() {
  i2s_config_t i2s_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
    .sample_rate = SAMPLE_RATE,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
    .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
    .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
    .intr_alloc_flags = 0,
    .dma_buf_count = 16,
    .dma_buf_len = 60
  };

  i2s_pin_config_t pin_config = {
    .bck_io_num = 26,   // BCLK
    .ws_io_num = 25, //23,    // LRC
    .data_out_num = 23,// 25, // DIN
    .data_in_num = -1   // Not used
  };
  i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL);
  i2s_set_pin((i2s_port_t)0, &pin_config);
  i2s_set_clk((i2s_port_t)0, SAMPLE_RATE, (i2s_bits_per_sample_t)16, (i2s_channel_t)2);
}

int DAC_Write_int16(int16_t val) {
  uint16_t d[2]; // 0:RIGHT 1:LEFT
  size_t written;
  d[1] = d[1] = val;
  //i2s_push_sample((i2s_port_t)0, (const char *)d, 100);
  i2s_write((i2s_port_t)0, (const void *)d, (size_t)4, &written, 100);
}

int DAC_Write_char(int8_t *addr) {
  int8_t sample[4];
  sample[0] = *(addr+0);
  sample[1] = *(addr+1);
  sample[2] = *(addr+0);
  sample[3] = *(addr+1);
  size_t written;
  i2s_write((i2s_port_t)0, (const void *)sample, (size_t)4, &written, 100);
}

float volume = 0;
void play(void* param) {
  I2S_Init();
  int count = 0;
  while (1) {
    for (int i=0; i<50000; i++) {
      volume = volume * 0.99 + vol * 0.01; // 滑らかに変化させる
      
      // 正弦波
      DAC_Write_int16(sinTable[count] * volume); // 配列要素が0.7倍してある
/*
      int16_t sample = sinTable[count];
      DAC_Write_char((int8_t *)&sample);
      DAC_Write_int16(sinTable[count]);
*/
      // 矩形波
      //DAC_Write_int16((int16_t)((count < 50 ? -32767 : 32767) * volume * 0.7));

      // のこぎり波
      //DAC_Write_int16((int16_t)((-30000 + count * 600) * volume * 0.7));

      // 三角波
      //DAC_Write_int16((int16_t)((count<50 ? -30000 + count * 1200 : 30000 - (count-50) * 1200) * volume * 0.7));

      count+=1;
      if (count ==100) count = 0;
    }
    vTaskDelay(1);  // wdt対策
  }
}