了解如何在Raspberry Pi Pico上以高达500 kHz的频率采样并根据捕获的数据计算快速傅立叶变换。
硬件部件:
Raspberry Pi Pico× 1个
在此项目中,我们将使用一些特殊功能以极快的速度从Raspberry Pi Pico的模数转换器(ADC)捕获数据,然后对数据进行快速傅立叶变换。这是许多项目的常见任务,例如涉及音频处理或广播的项目。
很可能您已经有了一个想要从中收集数据的传感器。就我而言,我有一个麦克风连接到Pico的A0输入。如果您只是来这里学习,则可以使模拟输入悬空而无任何连接。
您可以在GitHub上找到完整程序。
1.背景
Raspberry Pi Pico如此有用的一个主要原因是其众多的硬件功能使处理器从执行常规I / O任务中解放出来。在我们的例子中,我们将使用Pico的直接内存访问(DMA)模块。这是一项硬件功能,可以自动化任务,涉及以极快的速度将大量数据进出内存到IO。
可以将DMA模块配置为在准备好样本后立即将它们从ADC中取出。以最快的速度,您可以在高达0.5 MHz的频率下采样!
收集所有这些数据后,您可能需要对其进行一些处理。一个常见的任务是将您的信息从时域转换到频域以进行进一步处理。就我而言,我有一个麦克风,我想从该麦克风收集音频样本,然后计算样本中包含的最大频率分量。最常用的算法是快速傅立叶变换。
2. ADC采样代码
如果您还没有这样做,我强烈建议您在GitHub上克隆Raspberry Pi的pico-examples库。这是我用来开始的所有采样代码的地方。以下代码的很大一部分来自此存储库中的dma_capture示例。
我将介绍软件的一些关键要素,以解释发生了什么。您可以在“代码”部分找到完整的程序。
// set sample rate
adc_set_clkdiv(CLOCK_DIV);
这条线确定ADC收集样本的速度。“ clkdiv”是指时钟分频,它使您可以分割48 MHz基本时钟,以更低的速率进行采样。目前,一个样本需要96个循环来收集。这样得出的最大采样率是每秒48、000、000个循环/每个样本96个循环=每秒500、000个样本。
为了降低采样速度,可以增加时钟分频。将CLOCK_DIV设置为960将使每个样本的循环数增加10倍,从而每秒产生50000个样本。您猜到了,将CLOCK_DIV设置为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,排空缓冲区,然后开始采样。它还将在采样期间打开LED,以便您查看正在发生的情况。
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数组,然后对其进行预处理以进行傅立叶变换库。对于许多应用程序,在对数据进行傅里叶变换之前,先从数据序列中减去平均值是有利的。否则,任何直流电平(信号偏移会超过零)将导致输出频点接近零而具有巨大的幅度。我使用的库KISS FFT期望信号具有浮点类型,因此我在减去均值的同时也转换了样本。
// 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,然后计算输出数据中的最大频率分量。FFT的输出是复数值,因此要获得可用的功率值,可以取复结果的大小。
还要注意的是,与其循环遍历FFT的所有NSAMP输出值,我们仅对NSAMP / 2进行装箱。由于奈奎斯特采样定理,任何大于采样率1/2的频率都将被混叠在一起,因此这些bin对我们没有用。这是信号处理的基本结果,如果您不熟悉,则值得进一步研究!
就音频而言,人耳通常可以听到高达20 kHz左右的频率。我使用的CLOCK_DIV值为960,产生的采样率为50 kHz。因此,我可以捕获的最大非混叠频率为25 kHz,这应该绰绰有余!
// 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或收集的样本数。在信号处理中,在较高和较低数量的样本之间存在一个基本的权衡。更多的样本将花费更长的时间来收集和处理,但是会产生更高分辨率的傅里叶变换。更少的样本将导致更短的采样周期和更快的处理,但是您的傅立叶变换将更加精细。
对于Pico,我发现分配过多的内存会导致难以调试的失败。如果您将NSAMP设置得太大,您的Pico将没有足够的内存来分配给保存样本的阵列。该代码仍然可以编译和上传,但是您可能会得到一些奇怪的行为。在我的示例中,将NSAMP保持在9000以下似乎很好。
3.编译和上传
如果尚未下载,请下载Raspberry Pi Pico入门。这是一个坚实的资源,可为您提供设置构建系统,编译C / C ++代码并将其上传到Pico所需的一切。
以下所有说明均适用于macOS / Linux,但我想Windows上的CMake也有类似的过程。
要编译我的代码,请先在GitHub上下载我的存储库。
导航到adc_fft目录
创建一个名为“ build”的目录
在其中导航,然后键入“ cmake 。./”
输入“ make”,如果正确安装了Pico构建系统,则所有内容均应编译
将您的Pico放入引导加载程序模式,然后将adc_fft.uf2文件拖放到出现的驱动器中
那应该是全部!您可以通过USB监视程序的输出。它将在从A0采样的数据中输出最大频率分量,并且LED应该快速闪烁。
责任编辑:pj
-
处理器
+关注
关注
68文章
19156浏览量
229079 -
adc
+关注
关注
98文章
6430浏览量
544058 -
模数转换器
+关注
关注
26文章
3129浏览量
126735
发布评论请先 登录
相关推荐
评论