STC8H開發(八): NRF24L01無線傳輸音訊(對講機原型)
目錄
- STC8H開發(一): 在Keil5中配置和使用FwLib_STC8封裝庫(圖文詳解)
- STC8H開發(二): 在Linux VSCode中配置和使用FwLib_STC8封裝庫(圖文詳解)
- STC8H開發(三): 基於FwLib_STC8的模數轉換ADC介紹和演示用例說明
- STC8H開發(四): FwLib_STC8 封裝庫的介紹和使用注意事項
- STC8H開發(五): SPI驅動nRF24L01無線模組
- STC8H開發(六): SPI驅動ADXL345三軸加速度檢測模組
- STC8H開發(七): I2C驅動MPU6050三軸加速度+三軸角速度檢測模組
- STC8H開發(八): NRF24L01無線傳輸音訊(對講機原型)
關於PWM, DAC和音訊
PWM是脈衝寬度調製的縮寫, 因為介紹的文章很多, 自己做功課即可, 參考
大部分低端MCU不帶DAC轉換, 但是可以使用PWM模擬, 對於音訊傳輸
- 人普通談話的聲波頻率在500-2000Hz之間, 人耳可以聽到的聲波的頻率範圍在20Hz至20kHz之間
- 用於通話, 8kHz的頻寬就能達到較好的語音傳輸效果
- 通過PWM模擬DAC, 因為PWM是方波, 其頻率會引入底噪, 底噪的頻率是PWM頻率的倍數
- PWM頻率在8KHz時, 在聽感上底噪很大, 與傳輸的音訊一樣明顯, 將PWM的頻率調節到16kHz以上才能有效抑制底噪
通過MCU實現無線音訊傳輸的實現
傳送部分
接收部分
以下實現的是單聲道 8kHz 8bit 取樣的音訊訊號傳輸
傳送部分
傳送部分需要實現的是8kHz取樣, 並通過NRF24L01將每秒的8000位元組資料傳送出去.
語音輸入
語音輸入可以使用駐極體話筒加S9013放大輸入或者直接使用MAX9814. 在測試階段建議使用後者, 可以保證取樣輸入不失真, 在調通後再用駐極體話筒電路替換.
ADC音訊取樣
因為ADC取樣需要實現準確的每秒8000取樣, 所以不能用DMA方式, 在STC8H(包括STM32等其它MCU)下, 無法在DMA情況下精確調節每秒的取樣個數, 因為ADC的取樣頻率, 取樣週期和轉換週期在不同MCU中都是固定的, 所以很難正好做到8kHz的取樣. 具體的實現中有兩種方式:
1.定時器驅動採集
通過定時器設定為8kHz, 在中斷中發起ADC轉換, 是比較容易實現的. 這時候需要將ADC也實現為中斷方式, 因為ADC的轉換時間比較長, 如果在定時器中斷中做同步的ADC轉換, 容易影響主程序. 需要有定時器的中斷處理和ADC的中斷處理, 定時器的中斷處理單純用於發起轉換, ADC的中斷才用於讀出結果.
2.連續採集定時讀取
通過定時器設定為8kHz, 將ADC的採集設定為迴圈方式(中斷採集, 但是在中斷時再次發起), 在定時器中斷中僅僅讀取採集結果. 這種方式也能實現8kHz的取樣. 因為這種方式實際上會多消耗電量, 所以使用中還是使用了前一種方法.
NRF24L01傳送
NRF24L01在設定為1Mbps頻寬時實際傳輸速度能達到23k位元組每秒, 因此對於8bit 8kHz取樣的傳輸是沒問題的. 因為NRF24L01傳輸時的響應和重發機制, 在訊號不好時, 容易卡頓, 為了避免中間傳輸時間的波動影響, 在實際實現中使用了雙陣列做緩衝. 取樣到傳送之間的邏輯為
- 兩個256位元組陣列作為全域性變數, 同時定義變數指向當前寫入的陣列編號和寫入位置
- ADC中斷讀取結果時, 往當前編號的陣列和位置中寫入並移動位置, 當寫滿一個數組時, 將此陣列標記為可傳送, 並切換到下一個陣列繼續寫入
- 在主程序中, 判斷當前是否有可傳送的陣列, 如果可傳送, 則在迴圈中按32個位元組一組將資料全部發送.
因為在正常收發的訊號強度下, NRF24L01的傳送速度是比取樣速度快的, 所以基本上NRF24L01的傳送是傳送 -> 等待 -> 傳送的狀態
接收部分
接收部分要實現的是將NRF24L01接收到的資料進行儲存, 並按照8kHz的頻率, 將每個值設為PWM輸出的佔空比, 實現DAC模擬
RNF24L01接收
因為NRF24L01傳送是集中傳送, 所以在接收時也需要有緩衝, 接收的機制和傳送相似
- 兩個256位元組陣列作為全域性變數, 同時定義變數指向當前寫入的陣列編號和寫入位置
- NRF24L01通過中斷接收資料, 在接收時, 往當前編號的陣列和位置中寫入並移動位置, 當寫滿一個數組時, 將此陣列標記為可用, 並切換到下一個陣列繼續寫入
PWM模擬DAC還原
初始化一個PWM輸出, PWM週期為256對應8bit的佔空比調節範圍, 確保PWM頻率不低於16kHz. 在8kHz定時器的中斷中, 判斷當前讀取的陣列和位置, 每次讀取一個值, 並將其設定為PWM佔空比. 如果陣列不可用, 就不做任何操作, 如果此時將佔空比設為0, 會產生噪音.
音訊輸出
測時階段, 可以在PWM輸出上串聯一個200R的電阻後值連喇叭, 可以聽到輸出的音訊. 這個電阻不能太小, 測試中如果阻值小於100R, 會導致MCU供電不足反覆重啟. 在確定音訊輸出沒問題後, 可以替換為 PAM8403 音訊放大模組.
在使用 PAM8403 模組時
- 模組需要獨立供電, 測試中如果與MCU都使用USB2TTL供電, 會使MCU供電不足而導致聲音輸出異常
- 模組與MCU的輸出可以不共地, 即模組MCU的PWM輸出和地, 可以直接接入PAM8403的音訊輸入
- 因為是單聲道訊號, 所以只能用PAM8403的一個聲道, L或者R都可以
演示程式碼
- GitHub FwLib_STC8/tree/master/demo/spi/nrf24l01_audio
- Gitee FwLib_STC8/tree/master/demo/spi/nrf24l01_audio
接線說明
在測試中傳送部分使用的是 STC8H3K32S2, 接收部分使用的是 STC8H1K08, 你可以使用STC8H系列的任意一個型號
共同的連線部分(NRF24L01)
8H3K32S2/8H1K08 NRF24L01
P35(SS, Ignored) => CSN 16
P34(MOSI) => MOSI 15
P33(MISO) => MISO 14
P32(SPCLK) => CLK 13
P36(INT2) => IRQ 17
P37(IO) => CE 18
傳送部分
STC8H3K32S2 MAX9814
P11(ADC1) => MIC
3.3V => VDD
3.3V => GAIN
GND => A/R
GND => GND
ADC, 如果是STC8H3K32S2, 使用ADC取樣需要將AVcc, AGnd 和 ADC_Vref+ 正確連線
AVcc => 3.3V
AGnd => GND
ADC_Vref+ => 3.3V
P11 => Output(MAX9814) or MIC
接收部分
STC8H1K08 PAM8403
P10(PWM1P) => 200R => L or R Input
GND => _|_ Input
Ext 3.3V/5V => VCC
Ext GND => GND
注意:
- MCU的pin腳佈局不一定相同, STC8H3K32S2和STC8H1K08都是20pin的封裝, 但是pin腳佈局就不一樣
- 燒錄傳送部分和接收部分時, 注意要調換 nrf24l01.c 中的 RX_ADDRESS 和 TX_ADDRESS