reSpeaker XVF3800 I2C経由でのオーディオ再生とボリューム制御
このコードは、XIAO ESP32S3がI2Sインターフェースを通じて外部AIC3104オーディオコーデックチップを使用して440Hzのトーンを再生するものです。オーディオ出力をヘッドフォン(HPLOUT)またはライン出力(LOP)に送信し、シリアルモニターで+または-を入力することでボリュームを調整できます。トーンはサイン波を使用してコード内で生成されます(ファイルからではありません)。また、mを入力することで出力モード(ヘッドフォンまたはライン出力)を切り替えることができます。AIC3104はI2Cを使用してボリュームと出力パワーのレジスタを設定することで制御されます。

動作原理
コードは、I2Cインターフェースを介してAIC3104オーディオコーデックを初期化することから始まり、DACボリュームやヘッドフォン/ライン出力レベルなどの主要な出力パラメータを設定します。次に、AudioToolsライブラリのSineWaveGeneratorコンポーネントを使用してサイン波信号(具体的には440Hzトーン)を生成します。このオーディオ信号はI2Sインターフェースを介してストリーミングされ、ESP32S3からAIC3104チップにデジタルオーディオデータを送信するように設定されています。サイン波は、I2Sストリームへのオーディオデータのリアルタイム転送を処理するStreamCopyオブジェクトを使用してコーデックに継続的に供給されます。一方、ユーザーはシリアルモニターを介してシステムと対話でき、ボリュームレベルと**出力モード(ヘッドフォンまたはライン出力)**のオンザフライ制御が可能です。
シリアルコマンド
システムはシリアルモニターに入力されたシンプルなシリアルコマンドに応答します。+キーを押すとボリュームが1レベル上がり、-キーを押すと下がります。mキーを押すと、出力モードがヘッドフォン出力(HPLOUT)とライン出力(LOP)の間で切り替わります。これらのコマンドにより、コードを変更したり再アップロードしたりすることなく、リアルタイムでオーディオ出力のテストと調整が可能です。
コード
#include <Wire.h>
#include "AudioTools.h"
// AIC3104 I2C address
#define AIC3104_ADDR 0x18
// Register addresses
#define AIC3104_PAGE_CTRL        0x00
#define AIC3104_LEFT_DAC_VOLUME  0x2B
#define AIC3104_RIGHT_DAC_VOLUME 0x2C
#define AIC3104_HPLOUT_LEVEL     0x33
#define AIC3104_HPROUT_LEVEL     0x41
#define AIC3104_LEFT_LOP_LEVEL   0x56
#define AIC3104_RIGHT_LOP_LEVEL  0x5D
// Audio: 16kHz, stereo, 16-bit
AudioInfo info(16000, 2, 16);
SineWaveGenerator<int16_t> sineWave(32000);
GeneratedSoundStream<int16_t> sound(sineWave);
I2SStream out;
StreamCopy copier(out, sound);
// Volume: range 0–17 (0–8 = DAC, 9–17 = analog boost)
int volume = 8;
bool useHPOUT = true; // true = use HPLOUT, false = use LOP
void aic3104_reg_write(uint8_t reg, uint8_t val) {
  Wire.beginTransmission(AIC3104_ADDR);
  Wire.write(reg);
  Wire.write(val);
  Wire.endTransmission();
}
void setupAIC3104() {
  Wire.begin();
  aic3104_reg_write(AIC3104_PAGE_CTRL, 0x00);
  // Set default 0dB DAC volume
  aic3104_reg_write(AIC3104_LEFT_DAC_VOLUME, 0x00);
  aic3104_reg_write(AIC3104_RIGHT_DAC_VOLUME, 0x00);
  // Set output to 0dB, unmuted, powered up
  aic3104_reg_write(AIC3104_HPLOUT_LEVEL, 0x0D);
  aic3104_reg_write(AIC3104_HPROUT_LEVEL, 0x0D);
  aic3104_reg_write(AIC3104_LEFT_LOP_LEVEL, 0x0B);
  aic3104_reg_write(AIC3104_RIGHT_LOP_LEVEL, 0x0B);
}
void setVolume(int vol) {
  vol = constrain(vol, 0, 17);
  volume = vol;
  if (vol <= 8) {
    // DAC attenuation
    uint8_t dacVal = vol * 9; // 0dB to -72dB
    aic3104_reg_write(AIC3104_LEFT_DAC_VOLUME, dacVal);
    aic3104_reg_write(AIC3104_RIGHT_DAC_VOLUME, dacVal);
    // Output level fixed to 0dB
    aic3104_reg_write(AIC3104_HPLOUT_LEVEL, 0x0D);
    aic3104_reg_write(AIC3104_HPROUT_LEVEL, 0x0D);
    aic3104_reg_write(AIC3104_LEFT_LOP_LEVEL, 0x0B);
    aic3104_reg_write(AIC3104_RIGHT_LOP_LEVEL, 0x0B);
  } else {
    // DAC at 0dB
    aic3104_reg_write(AIC3104_LEFT_DAC_VOLUME, 0x00);
    aic3104_reg_write(AIC3104_RIGHT_DAC_VOLUME, 0x00);
    // Boost output gain via HPLOUT or LOP
    uint8_t gain = (vol - 8); // from +1 to +9 dB
    uint8_t outVal = (gain << 4) | 0x0B; // Set gain and power/mute bits
    if (useHPOUT) {
      aic3104_reg_write(AIC3104_HPLOUT_LEVEL, outVal);
      aic3104_reg_write(AIC3104_HPROUT_LEVEL, outVal);
    } else {
      aic3104_reg_write(AIC3104_LEFT_LOP_LEVEL, outVal);
      aic3104_reg_write(AIC3104_RIGHT_LOP_LEVEL, outVal);
    }
  }
  // Debug info
  Serial.print("Volume set to ");
  Serial.print(volume);
  Serial.print(" (");
  if (vol <= 8) Serial.print("-" + String(volume * 1) + " dB)");
  else Serial.print("+" + String((vol - 8)) + " dB)");
  Serial.println();
}
void setup() {
  Serial.begin(115200);
  while (!Serial);
  setupAIC3104();
  setVolume(volume);
  auto config = out.defaultConfig(TX_MODE);
  config.copyFrom(info);
  config.pin_bck = 8;
  config.pin_ws = 7;
  config.pin_data = 44;
  config.is_master = true;
  out.begin(config);
  sineWave.begin(info, N_A4); // 440Hz tone
}
void loop() {
  copier.copy();
  if (Serial.available()) {
    char c = Serial.read();
    if (c == '+' && volume < 17) {
      setVolume(volume + 1);
    } else if (c == '-' && volume > 0) {
      setVolume(volume - 1);
    } else if (c == 'm') {
      useHPOUT = !useHPOUT;
      setVolume(volume);
      Serial.print("Switched to ");
      Serial.println(useHPOUT ? "HPLOUT (headphone)" : "LOP (line out)");
    }
  }
}
技術サポート & 製品ディスカッション
私たちの製品をお選びいただき、ありがとうございます!私たちは、お客様の製品体験が可能な限りスムーズになるよう、さまざまなサポートを提供しています。異なる好みやニーズに対応するため、複数のコミュニケーションチャンネルを用意しています。