2021-11-07

Raspberry Pi Pico 控制 DFPlayer Mini 播放聲音

最近有一位朋友因為想學習使用 Raspberry Pi Pico 及播放歌曲用途,所以還購買了一個播放模組
因此在下亦拜託朋友買多一個播放模組學習使用

外觀

見下文
DFPlayer Mini 正面,有一個 Micro SD 卡糟

見下文
DFPlayer Mini 背面

見下文
GD3200B 是一種 音訊格式解碼晶片,其原生系列為 YX5200
可以存取 FAT16FAT32 這兩種常見的檔案格式
及能夠將 WAV, MP3, WMA 3種常見音訊格式解碼

見下文
8002 是一種 類比音訊增幅器
能夠驅動最高 3W的揚聲器

引腳

編號 引腳 方向 功能
1 VCC 接受 3.3V 至 5V 電壓
2 RX 輸入 UART 序列輸入
3 TX 輸出 UART 序列輸出
4 DAC_R 輸出 右側數碼音訊頻道
5 DAC_L 輸出 左側數碼音訊頻道
6 SPK2 輸出 3W類比揚聲器終端2
7 GND 接地
8 SPK1 輸出 3W類比揚聲器終端1
9 IO1 輸入 IO操作模式1,詳細資料請參考 IO操作模式
10 GND 接地
11 IO2 輸入 IO操作模式2,詳細資料請參考 IO操作模式
12 AD1 輸入 AD操作模式1,詳細資料請參考 AD操作模式
13 AD2 輸入 AD操作模式2,詳細資料請參考 AD操作模式
14 USB+ 讀取外接 USB 儲存裝置 D+ 線路
15 USB- 讀取外接 USB 儲存裝置 D- 線路
16 BUSY 輸出 高電壓為閒置狀態;低電壓為忙碌狀態

揚聲器

見下文
由於 DFPlayer Mini 只支援 3W或以下 的 揚聲器,因此在下從一個廢棄的 USB音箱 拆除一個 2W揚聲器

見下文
在下使用銅線焊接到 揚聲器終端
(在下使用的銅線表面有絕緣物料覆蓋,焊接及接駁前需要將絕緣物料去除才能使用)

寫入檔案

見下文
DFPlayer Mini 會自動檢查 SD卡 中的 mp3 目錄
並會由 0000 至 9999 順序讀取 檔案名首4位數字 及 辨認副檔名為mp3,例如
0000.mp3
0001.mp3
0002.mp3
.
.
.

如果只有數字,使用者一般情況下都無法記下所有檔案對應的曲目
而 DFPlayer Mini 只會處理 檔案名首4位數字,之後的檔案名並不理會
因此可以在 檔案名首4位數字 及 副檔名之間 的檔案名可以任意命名,例如
0000-music-0.mp3
0001-music-1.mp3
0002-music-2.mp3
.
.
.

載入 SD卡

見下文
DFPlayer Mini 載入 SD卡 時, LED 會亮著,然後熄滅

IO操作模式

IO操作模式 是 DFPlayer Mini 最簡單的的操作模式
透過 2支IO引腳 接地時間長短 來執行 總共4個操作
接地時間 IO1 IO2
上一個項目 下一個項目
音量減少 音量增加

線路原型
見下文
IO操作模式 的線路

接駁線路
見下文
實際接駁線路

見下文
揚聲器的終端沒有區分極性,因此無需考慮方向

見下文
見下文
使用 按壓按鈕 連接 DFPlayer Mini 的 IO1引腳 及 IO2引腳 到 接地

測試線路
見下文
使用 紅色按鈕 將 IO1引腳 及 接地 連接
使用 白色按鈕 將 IO2引腳 及 接地 連接
短按 紅色按鈕, DFPlayer Mini 播放上一個項目
短按 白色按鈕, DFPlayer Mini 播放下一個項目
長按 紅色按鈕, DFPlayer Mini 音量減少
長按 白色按鈕, DFPlayer Mini 音量增加

AD操作模式

AD操作模式 是 DFPlayer Mini 較進階的的操作模式
透過 2支AD引腳 連接的10種不同電阻值 來執行 總共20個操作
電阻值 AD1 AD2
Segment 1 Segment 5
3KΩ Segment 2 Segment 6
6.2KΩ Segment 3 Segment 7
9.1KΩ Segment 4 Segment 8
15KΩ 短接地為上一個項目;長接地為音量減少 Segment 9
24KΩ 短接地為下一個項目;長接地為音量增加 Segment 10
33KΩ 播放或暫停 Segment 11
51KΩ 循環播放 Segment 12
100KΩ 切換播放來源:USB、SD、SPI Segment 13
200KΩ 切換播放模式:全部、目錄、項目、隨機 Segment 14

線路原型1
見下文
AD操作模式 的線路

線路原型2
見下文
由於 AD操作模式 所使用大部分電阻值都不是常見的電阻值,要符合指定電阻值是極不實際

見下文
雖然電阻值只是參考值,不需要絕對值,但又不知誤差範圍,因此在下改為使用電位器來調整電阻值來待替電阻器

接駁線路
見下文
實際接駁線路

見下文
見下文
使用 100KΩ電位器

測試線路
見下文
在下使用 萬用標 接駁到 電位器 來調整 15KΩ 、 24KΩ 及 33KΩ 測試

控制操作模式

DFPlayer Mini 除了使用 IO 及 AD引腳 控制播放器,還可以通過 序列訊號 讓 微控制器 操作
而 DFPlayer Mini 使用比較簡單的 通用非同步收發傳輸 (Universal Asynchronous Receiver Transmitter (UART)) 傳送訊號
但傳送的訊號並不簡單,而是一組頗繁複指令群

排序 指令 指令值 描述
1 開始 0x7E 傳送開始指令
2 版本 0xFF 查詢版本資料,符合才可以運作
3 長度 0x06 版本、長度、功能、狀態、參數(高)、參數(低) 總共 6個指令
4 控制 ? 控制 DFPlayer 執行指定操作,詳細資料參考 控制指令
5 查詢 ? 查詢狀態資料:0 為不傳回;1 為傳回
如果需要傳回資料,微控制器的 RX引腳 要連接到 TX引腳 以接收資料
詳細資料參考 查詢指令
6 參數(高) ? 參數 的 第15至第8位元資料
7 參數(低) ? 參數 的 第7至第0位元資料
8* 校驗和(高) ? 校驗和 的 第15至第8位元資料
9* 校驗和(低) ? 校驗和 的 第7至第0位元資料
10 結束 0xEF 傳送結束指令

* 校驗和 (Checksum) 是用作確保提交的資料一致
DFPlayer 的 校驗和 計算是一條簡單的方程式
校驗和 = -(版本 + 長度 + 功能 + 狀態 + 參數(高) + 參數(低)) & 0xFFFF

再將 校驗和 分開為 高位元組 及 低位元組
校驗和(高) = (校驗和 & 0xFF00) >> 8
校驗和(低) = 校驗和 & 0x00FF

控制指令

功能 功能值 參數
下一個項目 0x01
上一個項目 0x02
指定項目 0x03 0 至 9999
音量增加 0x04
音量減少 0x05
指定音量 0x06 0 至 30
指定EQ 0x07 0 = Normal
1 = Pop
2 = Rock
3 = Jazz
4 = Classic
5 = Bass
播放模式 0x08 0 = 全部循環播放
1 = 目錄循環播放
2 = 項目循環播放
3 = 隨機播放
播放來源 0x09 0 = USB
1 = SD
2 = Aux
3 = Sleep
4 = Flash
關機 0x0A
重設 0x0C
播放 0x0D
暫停 0x0E
全部循環播放 0x11
停止 0x16

如果需要發送 指定音量 的指令,先計算 校驗和
-(0xFF + 0x06 + 0x06 + 0x00 + 0x00 + 0x00) & 0xFFFF
# 校驗和 = 0xFEF5
# 校驗和(高) = 0xFE
# 校驗和(低) = 0xF5

因此整個訊號為
0x7E 0xFF 0x06 0x06 0x00 0x00 0x00 0xFE 0xF5 0xEF

共10組位元組訊號

查詢指令

狀態 狀態值 參數
音量 0x43
EQ 0x44
播放模式 0x45
版本 0x46
USB 中項目的總數 0x47
SD 中項目的總數 0x48
已選擇 USB 項目的位置 0x4B
已選擇 SD 項目的位置 0x4C

查詢指令 與 控制指令 相同,但查詢的訊號必須為 0x01
例如要查詢音量,指令應為
0x7E 0xFF 0x06 0x43 0x01 0x00 0x00 0xFE 0xB7 0xEF
# 已計算校驗和

如果成功,會傳回一堆位元組資料,由於在下使用 CircuitPython 的 busio.uart 類別,會傳回 bytes 資料
例如查詢音量資料,會傳回
b'~\xff\x06A\x00\x00\x00\xfe\xba\xef~\xff\x06A\x00\x00\x00\xfe\xba\xef~\xff\x06A\x00\x00\x00\xfe\xba\xef~\xff\x06C\x00\x00\x1e\xfe\x9a\xef'

當中的 ~ (波紋號) 其實就是 \x7e ,而 \x06A 及 \x06C 其實分別是 \x06\x41 及 \x06\x43
即是傳回的訊號是
\x7E\xFF\x06\x41\x00\x00\x00\xFE\xBA\xEF
\x7E\xFF\x06\x41\x00\x00\x00\xFE\xBA\xEF
\x7E\xFF\x06\x41\x00\x00\x00\xFE\xBA\xEF
\x7E\xFF\x06\x43\x00\x00\x1E\xFE\x9A\xEF

閣下應該會發現傳回的訊號與指令組非常相似,當中的 \x43 其實就是 查詢音量 的指令,之後的 \x00\x1E 就是音量值
因此要將傳回查詢資料,便需要檢查傳回的位元組陣列中,第3位元組 是否與 查詢指令 相同
如果相同,便將 第5位元組 及 第6位元組 傳回,就是要查詢的資料,即是
# 傳回 = (data[5] << 16) | data[6]
(注意:陣列由 0 開始)

收發訊號

控制操作模式 使用 UART 來收發訊號,DFPlayer Mini 的 RX引腳 為接收訊號,而 TX引腳 為發送訊號
如果只是單方使用 微控制器 向 DFPlayer Mini 發送控制訊號,只需要將 微控制器 的 TX引腳 接駁到 DFPlayer Mini 的 RX引腳 便可以
如果 微控制器 還需要向 DFPlayer Mini 查詢訊號,便需要將 微控制器 的 RX引腳 連接到 DFPlayer Mini 的 TX引腳

由於 UART 只需一條訊號線便可以發送訊號,沒有時脈來區分高或低訊號
因此需要透過特定速度來讓收發訊號的裝置來確認內容
電子訊號速度用 鮑率 (Baudrate) 來判別
DFPlayer Mini 的 鮑率為 9600 ,即是 每1秒傳送9600位元 的速度

編程

由於 MicroPython 及 CircuitPython 已經內建 UART 的函式庫,不需要自己建立一套 UART 函式庫便可以立即操作
使用 MicroPython 的語法:
import machine

id = 0
tx = 0
rx = 1
baudrate = 9600
bits = 8
parity = None

uart = machine.UART(id, baudrate, bits, parity, tx, rx)
data = [] # some byte array
uart.write("".join(map(chr, data)))

使用 CircuitPyhon 的語法:
import board, busio

tx = board.GP0
rx = baord.GP1
baudrate = 9600
bits = 8
parity = None

uart = busio.UART(tx, rx, baudrate = baudrate, bits = bits, parity = parity)
data = [] # some byte array
uart.write(data)
在下使用 CircuitPython 亦製作一個簡單的函式庫讓 Raspberry Pi Pico 控制 DFPlayer Mini
import busio, time, board

class DFPlayerMini:

	def __init__(self, tx, rx):
		self.__uart(tx, rx, baudrate = 9600, bits = 8, parity = None)

	def sendControl(self, command, parameterH, parameterL):
		byteArray = [0x7E, 0xFF, 0x06, command, 0x01, parameterH, parameterL, 0x00, 0x00, 0xEF]
        checksum = -(commands[1] + commands[2] + commands[3] + commands[4] + commands[5] + commands[6]) & 0xFFFF
        commands[7] = (checksum & 0xFF00) >> 8
        commands[8] = checksum & 0x00FF
		self.__uart.write(bytes(byteArray))
        time.sleep(0.1)

dfPlayerMini = DFPlayerMini(board.GP0, board.GP1)
dfPlayerMini.sendControl(0x01, 0x00, 0x00) # 控制 DFPlayer Mini 播放下一個項目
線路原型
見下文
在下使用 按壓按鈕 監察訊號狀態
如果使用 DM1微開關 ,便可以省卻 電阻器

接駁線路
見下文
見下文
雖然在下在原型中標示使用 10KΩ電阻器,但使用 1KΩ或以上的電阻器都可以

測試線路
見下文
使用控制操作模式可以將短按及長按的操作分離

載入 USB儲存裝置

由於普遍 USB儲存裝置都是 USB Type-A 插頭,要讓 DFPlayer Mini 連接到 USB儲存裝置,最好先製作 USB Type-A 的插孔

USB Type-A 引腳
編號 引腳
1 VCC
2 D-
3 D+
4 GND

USB Type-A 轉接器
見下文
在下自製的 USB Type-A 轉接器

見下文
連接 USB儲存裝置

(在下將印刷電路板上的文字標籤相反)

線路原型
見下文
在下使用 引腳座元件 待替 USB元件

接駁線路
見下文
見下文
由於在下通常只借用 USB轉接器 的電源引腳及接地引腳,很少使用所有 USB引腳
在辨認線路時經常弄錯 USB 的 D+引腳 及 D-引腳 (加上自己設計 USB轉接器 的電路板時亦弄錯標籤)
因此在下接駁到 USB 的線路是按照 USB 官方線路的顏色接駁,被免再次弄錯

測試線路
見下文
在下將 USB 及 SD 同時連接到 DFPlayer Mini
透過切換 播放來源 後再選取播放的項目
但切換到指定 播放來源 後,播放項目到達盡頭後,不會自動切換 播放來源 ,而是在當前 播放來源 循環

補充資料

見下文
由於在下喜歡使用耳筒收聽,因此購買一個傳統的 3.5mm耳筒插孔

見下文
線路原型,只是不使用揚聲器,將 DAC_L 及 DAC_R 接駁到 3.5mm耳筒插孔 的 左聲道 及 右聲道
(習慣上,白線 為 左聲道,紅線 為 右聲道)

見下文
見下文
實際線路,如果只是測試用途,DAC_L 及 DAC_R 接反其實影響不大
另外接駁到 3.5mm耳筒插孔 還要接地,可以減少雜音

由於只是由 揚聲器 改為 耳筒,因此在下沒有拍攝影片效果,與揚聲器沒有太大分別

總結

由於 DFPlayer Mini 已經發佈了一段長時間,只有 Arduino 的實作參與資料,官方亦只提供支援 Arduino 的函式庫
而 Raspberry Pi Pico 發佈至今只有 1年左右,在下在網絡上找不到相關的函式庫
不過 DFPlayer Mini 官方的文件亦不清晰,在下唯有先了解支援 Arduino 的函式庫後
反覆測試來製作支援 Raspberry Pi Pico 的函式庫

雖然 DFPlayer Mini 有提供 USB 線路,但同樣在網絡上找不到相關的實作
碰巧由於在下想方便地從電腦 USB 取電學習電子知識,因此曾經製作 USB轉接器,在這次實作中可以測試 USB儲存裝置
不過由於在下設計不良,最初接駁錯誤,導致燒毀了一支 USB
幸好並不貴重,只是用來製作 Linux Live USB ,亦沒有保存重要資料

由於 IO操作模式 及 AD操作模式 都有一些操作會區分 短接地 及 長接地
但並沒有明確數據資料定義 接地時間 才算 短接地 或 長接地
因此在下用 time.sleep 以 100毫秒 遞增來測試 接地時間
最後發現當 接地時間 大於 775毫秒 便是 長接地,小於或等於 便是 短接地
如果認為 控制操作模式 過於複雜,可以使用 IO操作模式 或 AD操作模式 配合 接地時間 來達到基本操作效果


參考資料

沒有留言 :

張貼留言