第三十二章 音訊FFT實驗
1)實驗平臺:正點原子DNK210開發板
2)章節摘自【正點原子】DNK210使用指南 - CanMV版 V1.0
3)購買連結:https://detail.tmall.com/item.htm?&id=782801398750
4)全套實驗原始碼+手冊+影片下載地址:http://www.openedv.com/docs/boards/k210/ATK-DNK210.html
5)正點原子官方B站:https://space.bilibili.com/394620890
6)正點原子K210技術交流企鵝群:605557868
本章將介紹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,運算過程會自動呼叫硬體上的FFTAccelerator,並會同時佔用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 FFT
data = bytearray(64)
res = FFT.run(data, 64)
maix.FFT模組提供了amplitude()函式,用於計算FFT後各個頻率點的幅值,amplitude()函式如下所示:
FFT.amplitude(res)
amplitude()函式用於計算FFT後各個頻率點的幅值,從而能夠直觀地看到頻域下資料的狀態。
res指的是FFT.run()函式運算後返回的頻域資料。
amplitude()函式的使用示例如下所示:
from maix import FFT
data = 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 image
lcd.init()
img = image.Image(size=(lcd.width(), lcd.height()))
SAMPLE_RATE = 38640
SAMPLE_POINTS = 1024
FFT_POINTS = 512
HIST_NUM = 50
fm.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)
# 對時域資料進行FFT
res= 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 data
del res
del amp
可以看到一開始是先完成分配IO、初始化LCD、GPIO、I2S,為透過I2S獲取板載數字揚聲器的音訊資料做準備。
然後便是在一個迴圈中不斷地透過I2S獲取音訊資料,然後將音訊資料作為時域資料輸入進行FFT運算,得到頻域資料的計算結果後,再計算頻域資料各頻率點的幅值,最後將各頻率點的幅值透過直方圖的形式在LCD上進行顯示。
32.4 執行驗證
將DNK210開發板連線CanMV IDE,點選CanMV IDE上的“開始(執行指令碼)”按鈕後,便了看到LCD上顯示了板載數字麥克風採集到音訊資料的頻譜圖,如下圖所示:
圖32.4.1 LCD顯示頻譜圖