Learn how to use the Raspberry Pi Pico to sample at up to 500 kHz and perform a Fast Fourier Transform on the recorded data.
In this project, we'll leverage some unique capabilities to gather data from the Raspberry Pi Pico's analogue to digital converter (ADC) at a very high rate and then do a Fast Fourier Transform on it. Many tasks, such as those involving audio processing or radio, need this job.
If you're reading this, you probably already have a sensor in mind that you'd like to gather data from. In my situation, I've connected a microphone to the Pico's A0 input. If you're only looking to learn, you may keep the analogue input open and unconnected.
The Raspberry Pi Pico's multitude of hardware capabilities spare the CPU from performing regular I/O chores, which is one of the reasons why it's so helpful. We'll utilise the Pico's Direct Memory Access (DMA) module in this situation. This is a hardware feature that allows you to automate operations like moving huge volumes of data from memory to IO at a high pace.
The DMA module may be set up to automatically grab samples from the ADC as soon as they are ready. You can sample at up to 0.5 MHz at its fastest!
After you've gathered all of this information, you'll probably want to process it. Converting your data from the time domain to the frequency domain for additional processing is a typical operation. In my situation, I have a microphone from which I want to gather audio samples and then calculate the samples' highest frequency component. The Fast Fourier Transform is the most often used algorithm for this.
Code for ADC Sampling
Image credit: Alex Wulff |
I strongly advise you to clone Raspberry Pi's pico-examples library on GitHub if you haven't previously. This is where I obtained all of my first sample code from. The dma_capture example in this repository provided a large chunk of the code used below.
To clarify what's going on, I'll go through several essential features of my software. The complete programme may be found in the Code section.
// set sample rate
adc_set_clkdiv(CLOCK_DIV);
You can increase clock divisions to sample at a slower rate. When CLOCK DIV is set to 960, the number of cycles each sample is multiplied by ten, resulting in 50, 000 samples per second. When you set CLOCK DIV to 9600, you get 5, 000 samples per second.
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);
The samples from the ADC are collected by this function. The CPU starts sampling after resetting the ADC and draining its buffer. During the sample time, it will also turn on the LED so you can see what's going on.
FFT Code
// 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;}
// 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);
Also, rather of cycling over all of the FFT's NSAMP output values, we'll only bin NSAMP/2. Any frequencies larger than 1/2 the sample rate will be aliased together due to the Nyquist Sampling Theorem, therefore these bins are useless to us. If you're unfamiliar with signal processing, this is a basic finding worth studying more!
The human ear can typically perceive frequencies up to about 20 kHz in audio. I'm using a CLOCK DIV of 960, which corresponds to a sample rate of 50 kHz. As a result, the highest unaliased frequency I can capture is 25 kHz, which should be plenty!
// 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
Uploading and Compiling
All of the methods here are for macOS/Linux, although I'm sure CMake on Windows has a similar approach.
- To compile my code, clone my GitHub repository first.
- Go to the adc fft directory and open it.
- Create a "build" directory.
- Navigate to that folder and type "cmake../" If you installed the Pico build system correctly, everything should compile.
- Put your Pico in bootloader mode, then drag & drop the adc fft.uf2 file into the disc that appears on the screen.
That ought to be it! The program's output may be monitored via USB. It will output the data sampled from A0 with the highest frequency component, and the LED should flash fast.
In my situation, I attached a microphone to the analogue pin and used a speaker to feed the microphone tones to ensure that my code was accurate.
Credit To Author: Alex Wulff
Posts You May like:
- Using CircuitPython for RP2040
- How to Setup Pico RP2040 on Windows
- Using micro-ROS on the Raspberry Pi Pico
- LED Tricks Using The Raspberry Pi Pico
- The RP2040 Raspberry Pi Pico Meets LoRa
- Pico supports SD cards and FatFS
- How to connect a Raspberry Pi Pico to LoRaWAN
- 50 Raspberry Pi Hacks & Tips You Should Know
- How to Install Wi-Fi and Internet on a Raspberry Pi Pico
- Easiest Way to to Run DC Motor with Raspberry Pi Pico
- Drag-and-Drop Programming For The Raspberry Pi Pico
- Make Pico DIY Project Without Any Soldering or Breadboard
- Detailed Comparison of Arduino Nano RP2040 Connect Vs Raspberry Pi Pico
- Make a USB Microphone Using the Raspberry Pi Pico
- The RP2040 is Now Available for $1
- Pico LoRa Expansion is Finally Out! Supports 868MHz
- CircuitPython 6.3.0 is Now Available with Improvement & Fixes
- Pico DIY Projects! Must Try 17 Different Pico HATs & Expansions