第七十六章 音頻FFT實驗
本章將介紹CanMV下FFT的應用,通過將時域采集到的音頻數據通過FFT為頻域。通過本章的學習,讀者將學習到CanMV下控制FFT加速器進行FFT的使用。
本章分為如下幾個小節:
32.1 maix.FFT模塊介紹
32.2 硬件設計
32.3 程序設計
32.4 運行驗證
32.1 maix.FFT模塊介紹
Kendryte K210片上擁有一個FFT Accelerator(快速傅里葉變換加速器)可以實現以硬件的方式對FFT的基2時分運算加速,Kendryte K210上的FFT Accelerator特點如下所示:
- 支持多種運算長度,即支持64點、128點、256點以及512點運算
- 支持兩種運算模式,即FFT以及IFFT運算
- 支持可配的輸入數據寬度,即支持32bit以及64bit輸入
- 支持可配的輸入數據排列方式,即支持虛部、實部交替,純實部以及實部、虛部分離三種數據排列方式
- 支持可配的數據搬運方式,即CPU搬運和DMA搬運
在CanMV中可以使用CanMV提供的maix.FFT模塊操作Kendryte K210上的FFT Accelerator。maix.FFT模塊可以對輸入數據進行傅里葉變換并返回相應的頻率幅值,可以將時域信號轉換為頻域信號。
maix.FFT模塊提供了run()函數,用于對輸入的時域數據進行FFT,run()函數如下所示:
FFT.run(byte=None, points=64, shift=0, direction=1)
run()函數用于對輸入的時域數據進行FFT,運算過程會自動調用硬件上的FFT Accelerator,并會同時占用DMAC Channel3和DMAC Channel4。
byte指的是輸入的時域數據,需要為bytearray類型。
points指的是FFT的運算長度,可以是64、128、256或512,默認為64。
shift指的是偏移,默認為0。
direction指的是運算模式,當為1時,為FFT,當為0時,是IFFT。
run()函數會返回一個list對象,表示計算后的頻域數據,list有points個元組,每個元組都有2個元素,第一個元素為實部,第二個元素為虛部。
run()函數的使用示例如下所示:
from maix import FFTdata = bytearray(64)
res = FFT.run(data, 64)
maix.FFT模塊提供了amplitude()函數,用于計算FFT后各個頻率點的幅值,amplitude()函數如下所示:
FFT.amplitude(res)
amplitude()函數用于計算FFT后各個頻率點的幅值,從而能夠直觀地看到頻域下數據的狀態。
res指的是FFT.run()函數運算后返回的頻域數據。
amplitude()函數的使用示例如下所示:
from maix import FFTdata = bytearray(64)
res = FFT.run(data, 64)
amp = FFT.amplitude(res)
32.2 硬件設計
32.2.1 例程功能
- 獲取板載數字麥克風的音頻數據作為時域數據輸入maix.FFT模塊進行FFT得到頻域數據后,計算頻域數據各個頻率點的幅值并在LCD上進行直觀的圖像顯示
32.2.2 硬件資源 - 數字麥克風
IIS_SDIN - IO30
IIS_BCK - IO32
IIS_LRCK - IO33
32.2.3 原理圖
本章實驗內容,需要獲取板載數字麥克風的音頻數據。
DNK210開發板上的數字麥克風的連接原理圖,如下所示:
圖32.2.3.1 數字功放NS4168連接原理圖
關于該數字麥克風的使用方法,可參考MSM261S4030H0R的數據手冊——《MSM261S4030H0R.pdf》,讀者可在A盤硬件資料芯片資料下找到這份文檔。
32.3 程序設計
32.3.1 maix.FFT模塊介紹
有關maix.FFT模塊的介紹,請見第32.1小節《maix.FFT模塊介紹》。
32.3.2 程序流程圖
圖32.3.2.1 音頻FFT實驗流程圖
32.3.3 main.py代碼
main.py中的腳本代碼如下所示:
from board import board_info
from fpioa_manager import fm
from maix import GPIO
from maix import I2S
from maix import FFT
import lcd
import imagelcd.init()
img = image.Image(size=(lcd.width(), lcd.height()))SAMPLE_RATE = 38640
SAMPLE_POINTS = 1024
FFT_POINTS = 512
HIST_NUM = 50fm.register(board_info.SPK_CTRL, fm.fpioa.GPIO0)
fm.register(board_info.MIC_WS, fm.fpioa.I2S0_WS)
fm.register(board_info.MIC_SCLK, fm.fpioa.I2S0_SCLK)
fm.register(board_info.MIC_SDIN, fm.fpioa.I2S0_IN_D0)spk_ctl = GPIO(GPIO.GPIO0, GPIO.OUT)
spk_ctl.value(0)i2s_dev = I2S(I2S.DEVICE_0)
i2s_dev.channel_config(I2S.CHANNEL_0, I2S.RECEIVER, align_mode=I2S.STANDARD_MODE)
i2s_dev.set_sample_rate(SAMPLE_RATE)hist_width = int(lcd.width() / HIST_NUM)while True:data = i2s_dev.record(SAMPLE_RATE)# 對時域數據進行FFTres = FFT.run(data.to_bytes(), FFT_POINTS)# 計算頻域數據各頻率點的幅值amp = FFT.amplitude(res)img.clear()for hist in range(HIST_NUM):if amp[hist] > lcd.height():hist_height = lcd.height()else:hist_height = amp[hist]img.draw_rectangle(hist * hist_width, lcd.height() - hist_height, hist_width, hist_height, lcd.WHITE, 1, True)lcd.display(img)del datadel resdel amp
可以看到一開始是先完成分配IO、初始化LCD、GPIO、I2S,為通過I2S獲取板載數字揚聲器的音頻數據做準備。
然后便是在一個循環中不斷地通過I2S獲取音頻數據,然后將音頻數據作為時域數據輸入進行FFT運算,得到頻域數據的計算結果后,再計算頻域數據各頻率點的幅值,最后將各頻率點的幅值通過直方圖的形式在LCD上進行顯示。
32.4 運行驗證
將DNK210開發板連接CanMV IDE,點擊CanMV IDE上的“開始(運行腳本)”按鈕后,便了看到LCD上顯示了板載數字麥克風采集到音頻數據的頻譜圖,如下圖所示:
圖32.4.1 LCD顯示頻譜圖