HUB 8735 Ultra實作:microSD卡與三個MP3故事檔案測試
本次測試讓HUB 8735 Ultra從microSD卡讀取三個MP3故事, 經由AOUT類比音訊輸出,再透過XPT8871功率擴大板推動喇叭, 逐步完成水井三寶樹藝故事機的聲音系統。
三個MP3故事均可正常讀取及播放,但目前仍有些許雜聲。 初步研判主要與音訊線過長、麵包板接觸、電源干擾及接地方式有關。
一、V1.0-03測試目標
二、使用材料
| 材料 | 數量 | 用途 |
|---|---|---|
| HUB 8735 Ultra | 1 | 讀取microSD卡並解碼MP3 |
| microSD卡 | 1 | 儲存三個水井三寶故事 |
| XPT8871音訊擴大板 | 1 | 放大AOUT類比音訊 |
| 4Ω 3W或8Ω喇叭 | 1 | 播放故事語音 |
| 5V穩壓電源 | 1 | 供應XPT8871擴大板 |
| 麵包板、杜邦線 | 若干 | 原型接線與功能測試 |
| USB Type-C資料線 | 1 | 程式燒錄及序列監控 |
三、microSD卡準備
將microSD卡格式化為FAT32,再把三個MP3故事檔案直接放在根目錄:
microSD ├── 001.mp3 姻緣花故事 ├── 002.mp3 烏龜故事 └── 003.mp3 白馬故事
.mp3。
四、HUB 8735 Ultra與XPT8871接線圖
| 來源端 | XPT8871端 | 功能 |
|---|---|---|
| HUB 8735 Ultra AOUT | IN+ | 類比音訊訊號 |
| HUB 8735 Ultra GND | IN−/GND | 音訊參考地 |
| 獨立5V正極 | +5V | 擴大板電源 |
| 獨立5V負極 | 5V− | 擴大板電源地 |
| 喇叭正端 | OUT+ | BTL喇叭輸出 |
| 喇叭負端 | OUT− | BTL喇叭輸出 |
OUT−不是GND。
喇叭必須接在OUT+與OUT−之間,
不可將OUT−接到HUB 8735 Ultra的GND。
五、V1.0-03完整測試程式
/*
* ============================================================
* 專案:HUB 8735 Ultra水井三寶故事機
* 版本:V1.0-03
* 功能:microSD卡與三個MP3故事檔案測試
* ============================================================
*
* microSD卡根目錄:
* 001.mp3
* 002.mp3
* 003.mp3
*/
#include "AmebaFatFS.h"
#include "AmebaFatFSFile.h"
#define STORY_COUNT 3
// 0xAF為最大值0dB
// 初次測試降低音量,避免擴大器輸入過載
#define MP3_VOLUME 0x90
#define STORY_INTERVAL_MS 2000
AmebaFatFS fs;
const char *storyFiles[STORY_COUNT] = {
"001.mp3",
"002.mp3",
"003.mp3"
};
const char *storyNames[STORY_COUNT] = {
"姻緣花故事",
"烏龜故事",
"白馬故事"
};
String getFullPath(const char *filename)
{
return String(fs.getRootPath()) + String(filename);
}
bool checkMP3File(const char *filename)
{
String fullPath = getFullPath(filename);
File testFile;
Serial.print("[CHECK] ");
Serial.print(fullPath);
Serial.print(" ... ");
testFile = fs.open(fullPath);
if (!testFile.isOpen()) {
Serial.println("NOT FOUND");
return false;
}
Serial.print("OK, ");
Serial.print(testFile.size());
Serial.println(" bytes");
testFile.close();
return true;
}
bool checkAllStoryFiles()
{
bool allFilesReady = true;
Serial.println();
Serial.println("Checking MP3 story files...");
Serial.println("--------------------------------");
for (int i = 0; i < STORY_COUNT; i++) {
if (!checkMP3File(storyFiles[i])) {
allFilesReady = false;
}
}
Serial.println("--------------------------------");
if (allFilesReady) {
Serial.println("[PASS] All three MP3 files are ready.");
} else {
Serial.println("[FAIL] One or more MP3 files are missing.");
}
return allFilesReady;
}
bool playStory(int storyIndex)
{
if (storyIndex < 0 || storyIndex >= STORY_COUNT) {
Serial.println("[ERROR] Invalid story index.");
return false;
}
String fullPath = getFullPath(storyFiles[storyIndex]);
File mp3File;
Serial.println();
Serial.println("================================");
Serial.print("[PLAY] Story: ");
Serial.println(storyNames[storyIndex]);
Serial.print("[PLAY] File: ");
Serial.println(fullPath);
mp3File = fs.open(fullPath);
if (!mp3File.isOpen()) {
Serial.println("[ERROR] Unable to open MP3 file.");
return false;
}
Serial.print("[INFO] File size: ");
Serial.print(mp3File.size());
Serial.println(" bytes");
mp3File.setMp3DigitalVol(MP3_VOLUME);
Serial.println("[AUDIO] Playback started.");
mp3File.playMp3();
Serial.println("[AUDIO] Playback completed.");
mp3File.close();
return true;
}
void playAllStories()
{
Serial.println();
Serial.println("Starting three-story playback test...");
for (int i = 0; i < STORY_COUNT; i++) {
if (!playStory(i)) {
Serial.print("[FAIL] Story ");
Serial.print(i + 1);
Serial.println(" playback failed.");
}
if (i < STORY_COUNT - 1) {
delay(STORY_INTERVAL_MS);
}
}
Serial.println();
Serial.println("[DONE] Three-story playback test completed.");
}
void setup()
{
Serial.begin(115200);
delay(2000);
Serial.println();
Serial.println("================================================");
Serial.println("HUB 8735 Ultra Story Player");
Serial.println("Version: V1.0-03");
Serial.println("================================================");
Serial.println("[SD] Initializing microSD card...");
fs.begin();
Serial.print("[SD] Root path: ");
Serial.println(fs.getRootPath());
if (!checkAllStoryFiles()) {
Serial.println("[SYSTEM] Playback cancelled.");
fs.end();
return;
}
playAllStories();
fs.end();
Serial.println("[SYSTEM] V1.0-03 test finished.");
}
void loop()
{
// 本版本只在開機後測試一次
delay(1000);
}
六、預期序列監控結果
Arduino IDE的序列監控視窗設定為115200 baud。
正常執行時會看到:
HUB 8735 Ultra Story Player Version: V1.0-03 [SD] Initializing microSD card... [CHECK] 0:/001.mp3 ... OK [CHECK] 0:/002.mp3 ... OK [CHECK] 0:/003.mp3 ... OK [PASS] All three MP3 files are ready. [PLAY] Story: 姻緣花故事 [AUDIO] Playback started. [AUDIO] Playback completed. [PLAY] Story: 烏龜故事 [AUDIO] Playback started. [AUDIO] Playback completed. [PLAY] Story: 白馬故事 [AUDIO] Playback started. [AUDIO] Playback completed. [DONE] Three-story playback test completed.
七、實際測試結果
- microSD卡可以正常掛載。
- 三個MP3故事檔案均可找到。
- 三個故事可以依序播放。
- HUB 8735 Ultra的AOUT可以輸出MP3音訊。
- XPT8871可以驅動外接喇叭。
- 目前音訊仍有些許雜聲,需要改善接線及供電。
下一步不是立即增加功能,而是先把類比音訊、接地與電源整理好。
八、為什麼目前會有雜聲?
1. AOUT音訊線過長
AOUT屬於小訊號類比音訊。使用過長且沒有屏蔽的杜邦線, 容易接收USB、Wi-Fi、開關電源及數位GPIO產生的干擾。
建議將AOUT與GND縮短至5~10公分,並把兩條線互相纏繞。 正式版本可以改用屏蔽音訊線:
中心導線 → HUB 8735 Ultra AOUT 屏蔽層 → HUB 8735 Ultra GND
2. 音訊線與電源線交錯
AOUT到XPT8871的輸入線應遠離:
- 5V大電流電源線
- USB傳輸線
- 開關電源模組
- WS2812燈條電源線
- 伺服馬達與馬達線路
- XPT8871的喇叭輸出線
3. 多點接地形成地迴路
HUB 8735 Ultra由電腦USB供電,而擴大板由另一組5V電源供電時, 應採用單點共地,避免GND經過多條不同路徑。
GND
IN−/GND
負極
4. 5V電源紋波
XPT8871播放較大音量時,負載電流會快速變化。 如果5V電源含有開關紋波,雜訊可能直接進入擴大器。
建議在XPT8871電源端附近並聯一顆470µF電解電容, 再並聯一顆0.1µF陶瓷電容:
+5V ───┬──── XPT8871 +5V
├──── 470µF正極
└──── 0.1µF
GND ───┬──── XPT8871 GND
├──── 470µF負極
└──── 0.1µF
5. 輸入音量過高
若HUB 8735 Ultra輸出音量太大,可能讓XPT8871輸入過載, 產生破音或沙沙聲。目前程式使用:
#define MP3_VOLUME 0x90
如果仍有明顯失真,可以降低為:
#define MP3_VOLUME 0x80
如果降低音量後破音明顯改善,表示問題可能包含輸入過載, 不完全是外部電磁干擾。
九、建議的改善接線
- 將AOUT與GND線縮短到5~10公分。
- 把AOUT與GND互相纏繞,或改用屏蔽音訊線。
- 將XPT8871移到靠近喇叭的位置。
- 讓音訊線遠離USB線、開關電源及5V大電流線。
- 在XPT8871旁增加470µF及0.1µF電容。
- 使用單點共地,避免形成地迴路。
- 將MP3數位音量由0x90降低到0x80進行比較。
- 正式版本改用焊接或洞洞板,減少麵包板接觸問題。
十、從聲音播放走向樹藝AI
V1.0-03證明HUB 8735 Ultra除了可以進行影像辨識, 也能結合microSD卡、MP3解碼、類比音訊輸出與外接擴大器, 成為地方故事播放裝置。
對水井三寶而言,這不只是「播放三個MP3」, 而是讓姻緣花、烏龜與白馬開始擁有自己的聲音, 為後續樹藝作品導覽、影像辨識與AI對話建立基礎。
沒有留言:
張貼留言