了解如何在Raspberry Pi Pico上以高達500 kHz的頻率采樣并根據(jù)捕獲的數(shù)據(jù)計算快速傅立葉變換。
硬件部件:
Raspberry Pi Pico× 1個
在此項目中,我們將使用一些特殊功能以極快的速度從Raspberry Pi Pico的模數(shù)轉(zhuǎn)換器(ADC)捕獲數(shù)據(jù),然后對數(shù)據(jù)進行快速傅立葉變換。這是許多項目的常見任務(wù),例如涉及音頻處理或廣播的項目。
很可能您已經(jīng)有了一個想要從中收集數(shù)據(jù)的傳感器。就我而言,我有一個麥克風連接到Pico的A0輸入。如果您只是來這里學習,則可以使模擬輸入懸空而無任何連接。
您可以在GitHub上找到完整程序。
1.背景
Raspberry Pi Pico如此有用的一個主要原因是其眾多的硬件功能使處理器從執(zhí)行常規(guī)I / O任務(wù)中解放出來。在我們的例子中,我們將使用Pico的直接內(nèi)存訪問(DMA)模塊。這是一項硬件功能,可以自動化任務(wù),涉及以極快的速度將大量數(shù)據(jù)進出內(nèi)存到IO。
可以將DMA模塊配置為在準備好樣本后立即將它們從ADC中取出。以最快的速度,您可以在高達0.5 MHz的頻率下采樣!
收集所有這些數(shù)據(jù)后,您可能需要對其進行一些處理。一個常見的任務(wù)是將您的信息從時域轉(zhuǎn)換到頻域以進行進一步處理。就我而言,我有一個麥克風,我想從該麥克風收集音頻樣本,然后計算樣本中包含的最大頻率分量。最常用的算法是快速傅立葉變換。
2. ADC采樣代碼
如果您還沒有這樣做,我強烈建議您在GitHub上克隆Raspberry Pi的pico-examples庫。這是我用來開始的所有采樣代碼的地方。以下代碼的很大一部分來自此存儲庫中的dma_capture示例。
我將介紹軟件的一些關(guān)鍵要素,以解釋發(fā)生了什么。您可以在“代碼”部分找到完整的程序。
// set sample rate
adc_set_clkdiv(CLOCK_DIV);
這條線確定ADC收集樣本的速度。“ clkdiv”是指時鐘分頻,它使您可以分割48 MHz基本時鐘,以更低的速率進行采樣。目前,一個樣本需要96個循環(huán)來收集。這樣得出的最大采樣率是每秒48、000、000個循環(huán)/每個樣本96個循環(huán)=每秒500、000個樣本。
為了降低采樣速度,可以增加時鐘分頻。將CLOCK_DIV設(shè)置為960將使每個樣本的循環(huán)數(shù)增加10倍,從而每秒產(chǎn)生50000個樣本。您猜到了,將CLOCK_DIV設(shè)置為9600可以得到每秒5 000個樣本。
void sample(uint8_t *capture_buf) {
adc_fifo_drain();
adc_run(false);
dma_channel_configure(dma_chan, &cfg,
capture_buf, // dst
&adc_hw-》fifo, // src
NSAMP, // transfer count
true // start immediately
);
gpio_put(LED_PIN, 1);
adc_run(true);
dma_channel_wait_for_finish_blocking(dma_chan);
gpio_put(LED_PIN, 0);
}
該功能實際上是從ADC收集樣本。處理器復位ADC,排空緩沖區(qū),然后開始采樣。它還將在采樣期間打開LED,以便您查看正在發(fā)生的情況。
3. FFT代碼
// get NSAMP samples at FSAMP
sample(cap_buf);
// fill fourier transform input while subtracting DC component
uint64_t sum = 0;
for (int i=0;i《NSAMP;i++) {sum+=cap_buf[i];}
float avg = (float)sum/NSAMP;
for (int i=0;i《NSAMP;i++) {fft_in[i]=(float)cap_buf[i]-avg;}
上面的這一部分用ADC的采樣填充cap_buf數(shù)組,然后對其進行預處理以進行傅立葉變換庫。對于許多應(yīng)用程序,在對數(shù)據(jù)進行傅里葉變換之前,先從數(shù)據(jù)序列中減去平均值是有利的。否則,任何直流電平(信號偏移會超過零)將導致輸出頻點接近零而具有巨大的幅度。我使用的庫KISS FFT期望信號具有浮點類型,因此我在減去均值的同時也轉(zhuǎn)換了樣本。
// compute fast fourier transform
kiss_fftr(cfg , fft_in, fft_out);
// compute power and calculate max freq component
float max_power = 0;
int max_idx = 0;
// any frequency bin over NSAMP/2 is aliased (nyquist sampling theorum)
for (int i = 0; i 《 NSAMP/2; i++) {
float power = fft_out[i].r*fft_out[i].r+fft_out[i].i*fft_out[i].i;
if (power》max_power) {
max_power=power;
max_idx = i;
}
}
float max_freq = freqs[max_idx];
printf(“Greatest Frequency Component: %0.1f Hz\n”,max_freq);
下一部分將計算FFT,然后計算輸出數(shù)據(jù)中的最大頻率分量。FFT的輸出是復數(shù)值,因此要獲得可用的功率值,可以取復結(jié)果的大小。
還要注意的是,與其循環(huán)遍歷FFT的所有NSAMP輸出值,我們僅對NSAMP / 2進行裝箱。由于奈奎斯特采樣定理,任何大于采樣率1/2的頻率都將被混疊在一起,因此這些bin對我們沒有用。這是信號處理的基本結(jié)果,如果您不熟悉,則值得進一步研究!
就音頻而言,人耳通常可以聽到高達20 kHz左右的頻率。我使用的CLOCK_DIV值為960,產(chǎn)生的采樣率為50 kHz。因此,我可以捕獲的最大非混疊頻率為25 kHz,這應(yīng)該綽綽有余!
// BE CAREFUL: anything over about 9000 here will cause things
// to silently break. The code will compile and upload, but due
// to memory issues nothing will work properly
#define NSAMP 1000
需要指出的最后一點代碼是NSAMP或收集的樣本數(shù)。在信號處理中,在較高和較低數(shù)量的樣本之間存在一個基本的權(quán)衡。更多的樣本將花費更長的時間來收集和處理,但是會產(chǎn)生更高分辨率的傅里葉變換。更少的樣本將導致更短的采樣周期和更快的處理,但是您的傅立葉變換將更加精細。
對于Pico,我發(fā)現(xiàn)分配過多的內(nèi)存會導致難以調(diào)試的失敗。如果您將NSAMP設(shè)置得太大,您的Pico將沒有足夠的內(nèi)存來分配給保存樣本的陣列。該代碼仍然可以編譯和上傳,但是您可能會得到一些奇怪的行為。在我的示例中,將NSAMP保持在9000以下似乎很好。
3.編譯和上傳
如果尚未下載,請下載Raspberry Pi Pico入門。這是一個堅實的資源,可為您提供設(shè)置構(gòu)建系統(tǒng),編譯C / C ++代碼并將其上傳到Pico所需的一切。
以下所有說明均適用于macOS / Linux,但我想Windows上的CMake也有類似的過程。
要編譯我的代碼,請先在GitHub上下載我的存儲庫。
導航到adc_fft目錄
創(chuàng)建一個名為“ build”的目錄
在其中導航,然后鍵入“ cmake 。./”
輸入“ make”,如果正確安裝了Pico構(gòu)建系統(tǒng),則所有內(nèi)容均應(yīng)編譯
將您的Pico放入引導加載程序模式,然后將adc_fft.uf2文件拖放到出現(xiàn)的驅(qū)動器中
那應(yīng)該是全部!您可以通過USB監(jiān)視程序的輸出。它將在從A0采樣的數(shù)據(jù)中輸出最大頻率分量,并且LED應(yīng)該快速閃爍。
責任編輯:pj
-
處理器
+關(guān)注
關(guān)注
68文章
18921瀏覽量
227184 -
adc
+關(guān)注
關(guān)注
97文章
6293瀏覽量
542400 -
模數(shù)轉(zhuǎn)換器
+關(guān)注
關(guān)注
26文章
3039瀏覽量
126537
發(fā)布評論請先 登錄
相關(guān)推薦
評論