Skip to content

Instantly share code, notes, and snippets.

@raplin
Created May 28, 2023 22:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save raplin/eb901bf8f7c7baa1296d07bc926b87d3 to your computer and use it in GitHub Desktop.
Save raplin/eb901bf8f7c7baa1296d07bc926b87d3 to your computer and use it in GitHub Desktop.
Frequency counter for RPi that samples at 24.576Mhz and is accurate up to several Mhz, uses very little CPU (500Khz input uses 9% of one CPU core on a RPI4)
/*
RPi frequency counter
We use the RPi's I2S input (on pin 38)
Setting up I2S input:
https://learn.adafruit.com/adafruit-i2s-mems-microphone-breakout/raspberry-pi-wiring-test
Compile kernel module, modprobe it as described
(for RPI4 use 'modprobe snd-i2smic-rpi rpi_platform_generation=2')
I set up .asoundrc as
---
pcm.dmic_hw {
type hw
card sndrpii2scard
channels 2
format S32_LE
}
---
then 'arecord -l' should show a device 'sndrpii2scard'
Finally, hook the input signal (3v3 max!) up to the I2S DIN input (GPIO20) - no other signals (apart from gnd) are required..
So.. we set up a sound device that reads a 384khz stereo 32 bit I2S input; this gives a sample rate of
384khz = 24576000hz! (can use lower, e.g. 48khz or whatever)
Just pipe the i2s input into this program... i.e.
arecord -D dmic_hw -c2 -r384000 -f S32_LE -t raw -v - | TestFrequencyCounter
*/
#include <stdint.h>
#include <stdio.h>
#define SAMPLE_RATE 384000 //sample the input at 24.576mhz
#define BLOCK_SIZE 8000 //should evenly divide SAMPLE_RATE
uint32_t buf[BLOCK_SIZE*2]; //stereo 32 bit
void processSamples(void)
{
uint32_t f=0;
uint32_t state=0;
while(1){
f=0;
uint32_t count=SAMPLE_RATE/BLOCK_SIZE;
while(count--){
fread(buf,sizeof(buf),1,stdin);
uint32_t t=BLOCK_SIZE*2;
uint32_t *ptr=buf;
uint32_t s=0;
while(t--){
// This uses some performance tricks but is basically just counting +ve edges in the data
s=*ptr++;
s=s^state;
if (!s) continue;
uint32_t mask=(1<<31);
if (s < 0x10000) mask=(1<<16);
while(mask){
if (s & mask){
state=0xffffffff^state;
if (state) f++; //+ve edge
s^=(mask-1);
s&=(mask-1);
if (!s) break; //no more edges remain, get out
}
mask>>=1;
}
}
}
printf("%dHz\n",f);
}
}
int main(int argc,char **argv)
{
processSamples();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment