● 実験テーマ169

 
「ESP32S3+SDカード+液晶+MP3デコーダIC_MP3_Player製作記録_その1」
 (
まずは、MP3デコーダIC:VS1011単体のSIN TEST(サイン波を鳴らすテスト)を試した)

■ 2026.2.25
  ・
MP3デコーダIC:VS1011単体のSIN TESTについてCopilotに聞いてみた。

  <Copilotの回答>
   ・VS1011のサイン波テスト(Sine Test)は、SPI通信が正しく確立されているか、アナログ回路(アンプ・ヘッドホン)が生きているかを切り分けるのに最適です。
   ・以下の手順で、ライブラリを使わずに実装してみてください。
    (1) サイン波テストの有効化手順
      ※ VS1011のサイン波テストは、特定のビットを立ててから、特殊な8バイトのシーケンスをSDIに送ることで起動します。

        ① テスト許可:
         ・SCI_MODE レジスタの SM_TESTS ビット(第5ビット:0x20)を 1 にします。

        ② テスト開始コマンド:
         ・SDI(XDCSをLowにする)から以下の8バイトを送信します。
          0x53 0xEF 0x6E [周波数] 0x00 0x00 0x00 0x00

        ③ 終了コマンド:
         ・SDIから以下の8バイトを送信します。
          0x45 0x78 0x69 0x74 0x00 0x00 0x00 0x00

    (2) サイン波テストのコード例 

void sineTest(uint8_t pitch) {
  // 1. テストモードを許可 (SCI_MODEの5番目のビットを立てる)
  writeRegister(SCI_MODE, 0x0820); // SM_SDINEW | SM_TESTS

  while (!digitalRead(VS_DREQ));

  // 2. サイン波開始シーケンス (SDI)
  SPI.beginTransaction(settingsInitial);
  digitalWrite(VS_XDCS, LOW);

  SPI.transfer(0x53); // Special sequence 1
  SPI.transfer(0xEF); // Special sequence 2
  SPI.transfer(0x6E); // Special sequence 3
  SPI.transfer(pitch); // 周波数設定 (例: 0x24)
  for(int i=0; i<4; i++) SPI.transfer(0x00);

  digitalWrite(VS_XDCS, HIGH);
  PI.endTransaction();

  Serial.println("Sine Test Started...");
  delay(3000); // 3秒間鳴らす

  // 3. サイン波終了シーケンス (SDI)
  SPI.beginTransaction(settingsInitial);
  digitalWrite(VS_XDCS, LOW);

  SPI.transfer(0x45);
  SPI.transfer(0x78);
  SPI.transfer(0x69);
  SPI.transfer(0x74);
  for(int i=0; i<4; i++) SPI.transfer(0x00);

  digitalWrite(VS_XDCS, HIGH);
  SPI.endTransaction();

  Serial.println("Sine Test Stopped.");
}

    (3) 周波数(pitch)の目安
      ※ pitch に渡す値によって音が変わります。
         0x24: 約 441Hz
         0x44: 約 1000Hz (1kHz)

    (4) 注意点
      ① DREQの確認: コマンドを送る前には必ず DREQ がHighであることを確認してください。
      ② 音量: 事前に SCI_VOL を適正な値(0x2020など)に設定しておかないと、音が聞こえないか、逆に最大音量で鳴る可能性があります。

      ※ まずはこのテストで「ピー」という音が鳴ることを目指しましょう。


■ 2026.3.5
  <続テスト第1弾:MP3デコーダIC:VS1011のみを動かすArduinoスケッチを検討:ESP32S3_VS1011_SINE_TEST.ino>
   ・一応スケッチ作成しコンパイルしてみたが、次のエラーが2個所残った。
   → redefinition of 'SPISettings settings'(「SPISettings設定」の再定義エラー)

  <この件、Copilotに聞いてみた>
   Q1:ESP32S3と、VS1011をSPI接続し、SCK転送周波数を設定する際、コマンドレジスタへの書込み(SCI)は低速(1Mz程度)
      データバッファへの書込み(SDI)は高速(4MHz程度)にする必要はありますか?
      必要がある場合、以下のように
      SPISettings settings(1000000, MSBFIRST, SPI_MODE0);
      SPISettings settings(4000000, MSBFIRST, SPI_MODE0);
      条件でSCK転送周波数の設定を変える書き方を、教えて下さい。

   A1:ESP32-S3とVS1011を接続する場合、ご指摘の通り通信の種類によって周波数を切り替える必要があります。

      (1) 周波数を分ける必要性
        ※ VS1011の仕様(データシート)に基ずき、以下の制約があるためです。
         ① SCI (コマンド): 内部クロック(CLKI)の1/6以下の周波数である必要があります。
            初期化直後は外部水晶(12.288MHz等)そのままの速度で動くため、約2.0MHz以下(安全のため1MHz程度)に設定します。
 
         ② SDI (データ): 内部クロックの1/4以下の周波数が上限です。
            初期化でレジスタ(SCI_CLOCKF)を書き換えてクロック倍増(PLL)を有効にした後は、約4MHz〜6MHz程度まで高速化可能です。

      (2) コードの書き方(再定義エラーの回避)
        ※ redefinition エラーは、同じスコープ内で同じ変数名(settings)を二度宣言しようとしているために発生します。
           用途ごとに変数名を変えるか、引数として直接渡すことで解決できます。

         ① 推奨:用途別に設定値を定義しておく方法
            あらかじめ異なる名前で定義しておき、通信の直前で使い分けます。

#include <SPI.h>

// 1. 異なる変数名で定義する
SPISettings settingsSCI(1000000, MSBFIRST, SPI_MODE0); // コマンド用(1MHz)
SPISettings settingsSDI(4000000, MSBFIRST, SPI_MODE0); // データ用(4MHz)

void writeCommand(uint8_t addr, uint16_t data) {
  // 2. 通信開始時に適切な設定を指定する
  SPI.beginTransaction(settingsSCI);
  digitalWrite(XCS_PIN, LOW);
  // ... 送信処理 ...
  digitalWrite(XCS_PIN, HIGH);
  SPI.endTransaction();
}

void writeData(uint8_t data) {
  // 3. データ送信時は高速な設定を指定する
  SPI.beginTransaction(settingsSDI);
  digitalWrite(XDCS_PIN, LOW);
  SPI.transfer(data);
  digitalWrite(XDCS_PIN, HIGH);
  SPI.endTransaction();
}

補足:なぜ beginTransaction を使うのか
・ESP32-S3のようなマルチタスク動作をする環境では、SPI.beginTransaction() を呼ぶことで、
 その通信間だけ指定した周波数・モードにハードウェアが自動的に再設定されます。
 これにより、他のデバイスとSPIバスを共有している場合でも安全に速度を切り替えられます。

  <もう1件、Copilotに聞いてみた>
   Q2:PLL設定値の計算確認お願いします。以下のように書きました。

/// SCI_CLOCKF設定
/// Fx'tal= 12.288MHz
/// ダブラーを有効にするので、SCI_CLOCKFに設定する値= XTALI[Hz]/2000に、0x8000を加算する。
/// つまり、SCI_CLOCKFに設定する値= 0x8000 + (XTALI[Hz]/2000)= 32768 + (12288000/2000)= 32768 + (6144)= 38912(0x9800)
writeRegister(SCI_CLOCKF, 0x9800);
delay(10);
while (!digitalRead(DREQ));

   A2:結論から申し上げますと、VS1011におけるPLLの設定値「0x9800」および計算プロセスは正解です。
      12.288MHzの水晶(XTALI)を使用し、内部クロックを2倍(24.576MHz)にする設定として完璧です。

     (1) 設定のポイントと注意点
      ① 計算の整合性
       ・公式どおり 0x8000 (倍率設定ビット) + 6144 (12288000/2000) で 0x9800 となり、計算ミスはありません。
      ② 周波数の上限:
       ・VS1011の最大動作クロックは 25MHz 前後です。2倍設定(約24.6MHz)は、
        チップの能力を最大限に引き出しつつ、安全に動作する最適な値です。
      ③ SPI速度への影響
       ・この設定(0x9800)を書き込んだ後は、内部クロックが24.576MHzに上がります。
      ④ SCI (コマンド): 24.576 / 6 = 最大 4.09MHz まで
         SDI (データ): 24.576 / 4 = 最大 6.14MHz まで
         理論上はここまで上げられますが、ご予定の「SDIを4MHz」にする設定は非常に安定して動作するはずです。
 
    (2) 1点だけアドバイス
      ① while (!digitalRead(DREQ)); で待機を入れているのは非常に良い実装です。
         VS1011はPLL設定を書き換えた直後、内部クロックが切り替わるまでわずかな間、反応できなくなります
         delay(10) も入っているので確実ですが、もし動作が不安定になる場合は、
         writeRegister の直後に DREQ が HIGH になるのをしっかり待つのがコツです。

   → 明日スケッチ修正してコンパイルエラー無くす。


■ 2026.3.6
  <続テスト第1弾:MP3デコーダIC:VS1011のみを動かすArduinoスケッチを検討:ESP32S3_VS1011_SINE_TEST.ino>
   ・気になってた事
 
/// SPI2(FSPI)の名前を「SPI2」に統一して定義
SPIClass SPI2(FSPI);  // クラスとは、オブジェクト(実体)を生成するためのテンプレート

/// 実行関数の中では、SPI2を使用→ この考えで良いはず
/***********************************************
* レジスタへの設定データ書込み関数
* XCS使用:インストラクションレジスタ選択
************************************************/
void writeRegister(unsigned char address, unsigned int value) {
  while (!digitalRead(DREQ)); // DREQがHighになるまで待機(digitalRead()は標準関数)

  SPI2.beginTransaction(settings);
  digitalWrite(XCS, LOW); // SCIセレクト(digitalWrite()は標準関数)

  SPI2.transfer(0x02); // Write命令
  SPI2.transfer(address); // レジスタアドレス
  SPI2.transfer(value >> 8); // データ上位8bit
  SPI2.transfer(value & 0xFF); // データ下位8bit

  digitalWrite(XCS, HIGH);
  SPI2.endTransaction();
}

-------------------------------------------------------------------------------------

   ・昨晩のCopilotからのアドバイスを参考に、スケッチを修正した。
     ① writeRegister(unsigned char address, unsigned int value);これを
       ・writeCommand(unsigned char address, unsigned int value);
       ・writeData(unsigned char data); // 今回はMP3データ未使用なので使わない
       に修正

     ② // SPI設定: 初期は低速(1MHz)で開始
        SPISettings settings(1000000, MSBFIRST, SPI_MODE0);-> SPISettings settingsSCI(1000000, MSBFIRST, SPI_MODE0); // コマンド用(1MHz)

       // CLOCKF設定後は高速(4MHz)に切り替え可能
       SPISettings settings(4000000, MSBFIRST, SPI_MODE0); ->SPISettings settingsSDI(4000000, MSBFIRST, SPI_MODE0); // データ用(4MHz)

       に修正

     ③ 以下に修正

/***********************************************
* レジスタへの設定データ書込み関数
* XCS使用:インストラクションレジスタ(SCI)選択
************************************************/
void writeCommand(unsigned char address, unsigned int value) {
  while (!digitalRead(DREQ)); // DREQがHighになるまで待機(digitalRead()は標準関数)

  SPI2.beginTransaction(settingsSCI);
  digitalWrite(XCS, LOW); // SCIセレクト(digitalWrite()は標準関数)

  SPI2.transfer(0x02); // Write命令
  SPI2.transfer(address); // レジスタアドレス
  SPI2.transfer(value >> 8); // データ上位8bit
  SPI2.transfer(value & 0xFF); // データ下位8bit

  digitalWrite(XCS, HIGH);
  SPI2.endTransaction();
}

/***********************************************
* 送信バッファへの送信データ書込み関数
* XDCS使用:データレジスタ(SDI)選択
************************************************/
void writeData(unsigned char data) {
  while (!digitalRead(DREQ)); // DREQがHighになるまで待機

  SPI2.beginTransaction(settingsSDI);
  digitalWrite(XDCS, LOW); // SDIセレクト
  SPI2.transfer(data); // データ送信
  digitalWrite(XDCS, HIGH);
  SPI2.endTransaction();
}

→ コンパイル結果
  これでエラー無しになった!!!

最大1310720バイトのフラッシュメモリのうち、スケッチが317731バイト(24%)を使っています。
最大327680バイトのRAMのうち、グローバル変数が20792バイト(6%)を使っていて、ローカル変数で306888バイト使うことができます。

--------------------------------------------------------------------------------------------------------------------------

・ハード・ジャンパー線の配線を済ませた。
 液晶とSDカードの結線は、そのままにしといた。

 動作チェックは明日予定
 


■ 2026.3.7
  <続テスト第1弾:MP3デコーダIC:VS1011のみを動かすArduinoスケッチを検討:ESP32S3_VS1011_SINE_TEST.ino>
   (1) 動作チェック開始
    ① VCC - GND ショートチェック OK
    ② HEX書込み~ RUNで確認 
     ・音が出ない。シリアルモニタに以下が出ているので走ってはいる。

entry 0x403c88b8
Sine Test Started...
Sine Test Stopped.

     ・何故か、VS1011eモジュール基板のコネクタ入口に供給されているVccを実測すると2.83Vしかない。しかもマイナス。
     → イージー結線テレコミスだった。
        コネクタの1番ピンの見方が逆だった。結線をやり直し

   <音量レジスタ仕様確認>
   
   0xLL         0xRR
 ------------------------------------------------
左チャンネルの音量 | 右チャンネルの音量
------------------------------------------------

    ※ いずれのチャンネルも
       0x00:最大音量(0dB)
        |
        |(-0.5dBステップ)
        |
       0xFE:最小音量(-127dB)

    → SIN TESTでの音量設定は、0x2020
       左右共、-32dB

    → テスト結果:オシロで実測:約0.5Vp-p(負荷オープン時)
              大昔のデータを見ると、0x08(-8dB)でやってて、この時は、実測:約2Vp-pなので合ってる。

              また出力周波数実測:周期=0.67mSなので1.5kHzで正しい。



              ただ音が出ない時の方が多い。また出ても止まらない。
              resetの問題か後で調査。

              波形自体は綺麗で、聞いた感じもノイズ無しで良好

    → 結線修正(電源供給極性逆を直す。その他信号線も)後、時々は動いたが、
       VS1011へのVcc=3.3V供給が、プラス、マイナス逆だった状態で一度通電しているので、
       VS1011にダメージを与えた可能性もあるので、交換して試してみたい。
       運よく15年前にVS1011を初めて扱った時にも同じような現象(時々音が鳴らない)が起き、VS1011モジュールの交換品(当時700円)を
       秋月に発注してあった。それが手元にとってあった。1回しか使ってないと思われる。(とても綺麗)

    → 交換して症状が変わるか試してみた。
    → 交換しても同じ。モジュールではない。元に戻した。

-----------------------------------------------------------------------------------------------------------------

   ・Copilotのアドバイスの中のトラブル対策に次ような記述があった。
    「最初だけ鳴って止まる」 → バッファフル状態の処理(DREQのチェック)がうまくいっていない可能性があります。

   <データシート:7.4 データ・リクエスト端子DREQ>
    ・DREQ端子/信号は,VS1011eの内蔵FIFOがデータ受信可能であることを示します。

     ※ DREQが"H"ならば,最低32バイトのSDIデータまたはSCIコマンド1個の入力が可能です。
       この条件が満たされない時、DREQは"L"になります.この時送り手は新たなデータ転送を開始してはいけません。

     ※ 32バイトの安全領域があるため,32バイトまでならDREQの状態をチェックすることなく連続して送ることが出来ます。
       これは低速なマイクロコントローラによるVS1011eの制御をより容易にします.

     注意-1:DREQはバイトの転送途中などを含め不特定のタイミングでレベルが変化します。
           したがってDREQは、次のバイト・データを送出できるかどうかを決めるためだけに使用してください。
          すでに開始したバイト転送を中断してはいけません。

     注意-2:VS1002以前のVS10XX製品において,DREQはSDI専用でした。VS1011eでは、DREQはSCIのステータス表示としても使用されます。

------------------------------------------------------------------------------------------------------------------

   <Copilotに聞いてみた>
    Q1.今晩は。コンパイルエラーが無くなったので、スケッチを書込んで動作確認を開始しました。
       最初の結果ですが、以下です。
        ① シリアルモニターにSine Test Started... Sine Test Stopped.と表示はしている。
        ② 最初、音が出なかったが、配線ミスを発見し直したら鳴ったが、以降時々鳴らない現象が起き、
           今は全く鳴らなくなった。(Sine Test Started...が表示後、暫くして"ポツ"と鳴るだけ。)

        ③ 鳴った時のサイン波形を自作オシロで確認した所、もくろみ通り、1.5kHzで綺麗に出ていた。ノイズも気にならないレベル。
           出力オープン時のレベルは、約0.5Vp-p(負荷オープン時)であった。
          SIN TESTでの音量設定は、0x2020にしたので、左右共、-32dBなので合ってそう。

      DREQのチェックが上手く行ってないのかな?
       後質問ですが、SINE TEST関数の中で、スペシャルコマンドを以下のようにDREQチェック無しに8バイト
       送ってますが、これは32バイト以下だからOKと理解してます。正しいでしょうか?
       この部分は、SPI2.transfer(0x53);のように、void writeData(unsigned char data)関数を使う必要はないということで
       よいでしょうか?

void writeData(unsigned char data) {
  while (!digitalRead(DREQ)); // DREQがHighになるまで待機

  SPI2.beginTransaction(settingsSDI);
  digitalWrite(XDCS, LOW); // SDIセレクト
  SPI2.transfer(data); // データ送信
  digitalWrite(XDCS, HIGH);
  SPI2.endTransaction();
}  

---------------------------------------------------------------------------------------------------------------

   A1.要点のみ整理
     1. コード内のタイポ(打ち間違い)の可能性
      ・提示いただいたコードの「サイン波開始シーケンス」の中で、1箇所だけ SPI2 ではなく SPI になっている 部分があります。

// 開始シーケンスの中のここです
for(int i=0; i<4; i++) SPI.transfer(0x00);

     2. SCI_MODEの設定値         
      ・writeCommand(SCI_MODE, 0x0820); とされていますが、
       VS1011において 0x0800 (SM_SDINEW) は通常デフォルトで有効(またはチップによっては非対応)な場合があります。
       もしこれで鳴らない場合は、一度シンプルに 0x0020 (SM_TESTSのみ) で試してみるのも手です。

     3.明日への切り分けアドバイス
      (1) もし明日テスター等で確認される際は、以下の順で見てみてください:
        ① DREQピン: Sine Test Started... と表示された後、
           ずっと High を維持していますか?(もしLowのままだと、その後の終了シーケンスの while で止まってしまいます)

        ② 配線の緩み: ブレッドボードは「特定の周波数で共振して接触不良が起きる」ことがあります。
           サイン波(振動)が出た瞬間に接触が変わるケースも稀にあります。

        ③ 転送の整合性: 8バイト送る際、もし SPI2.transfer が途中で化けると、
           VS1011は「あと4バイト来るはずだ」と待ち続けてしまい、次のコマンドを受け付けなくなります。

      ※ 自作オシロで1.5kHzが見えたということは、「正解」まであと一歩です。
         タイポの修正と、明示的なXRESETの操作で安定する可能性が高いです!応援しております。

      ※ DREQの極性: もし修正後も「ポツ」と鳴るだけで止まる場合は、
         DREQがLowのままになっていないか(=VS1011がフリーズしていないか)をテスターで確認してみてください。

      ※ 最後に、もし明日試してもうまくいかない場合に備えて、1点だけ「魔法の1バイト」のヒントを置いておきます。

      ※ VS1011でデータ送信(SDI)が途切れたりフリーズしたりした際、
         ダミーデータとして 0x00 を1バイト送るだけで内部状態がリセットされ、DREQが復帰することがあります。
         もし行き詰まったら思い出してください。
 


■ 2026.3.8
  <続テスト第1弾:MP3デコーダIC:VS1011のみを動かすArduinoスケッチを検討:ESP32S3_VS1011_SINE_TEST.ino>
   (1)  昨日の対策を試した
     ① サイン波開始シーケンスの中で、1箇所だけ SPI2 ではなく SPI になっている 部分があります。

/// サイン波開始シーケンス (SDI)
SPI2.beginTransaction(settingsSDI);
digitalWrite(XDCS, LOW); // XDCS:データバッファ選択

SPI2.transfer(0x53); // Special sequence 1
SPI2.transfer(0xEF); // Special sequence 2
SPI2.transfer(0x6E); // Special sequence 3
SPI2.transfer(pitch); // 周波数設定
for(int i=0; i<4; i++) SPI.transfer(0x00); ← ここ-> for(int i=0; i<4; i++) SPI2.transfer(0x00);に修正してみる。

 delay(3000); // 3秒間鳴らす

 ※ for(int i=0; i<4; i++) SPI.transfer(0x00); ← ここ-> for(int i=0; i<4; i++) SPI2.transfer(0x00);に修正してみる。

 /// サイン波終了シーケンス (SDI)
SPI2.beginTransaction(settingsSDI);
digitalWrite(XDCS, LOW);

SPI2.transfer(0x45);
SPI2.transfer(0x78);
SPI2.transfer(0x69);
SPI2.transfer(0x74);
for(int i=0; i<4; i++) SPI2.transfer(0x00); ← ここはOK

       なので、3秒間鳴るはずが鳴らず、終了シーケンスのみ正常に実行され一瞬「ポツ」と鳴るだけで止まってたと考えられる。
       これでやっとESP32S3_VS1011_SINE_TEST.inoを動かすことが出来た!!

       → ビデオ撮った:video001.mp3/ 1.68MB 5秒


<最終回路図>

・こちらからどうそ→ ESP32S3_MP3_PLAYER_回路図 (SD+LCD+VS1011接続)

<最終スケッチ>

こちらから、どうぞ→ ESP32S3_VS1011_SINE_TEST.ino
  


← 実験テーマ1に戻る   TOP PAGEに戻る     実験テーマ170へ →