Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Arduino dual audio frequency counter and pulse width measuring sketch.
/* DualFrequencyCounter */
// is a bit sketchy, but seems accurate enough.
// Relies on
// - Adafruit's SPI_VFD library
// - one of those fancy Samsung 20x2 VFDs
// - and the usual digitalWriteFast lib which I know you've already got.
// checked with a DSO Nano oscilloscope, not that they're all that great
// audio should probably be buffered, limited to 0-5v
// and fed through a comparator before input into pin 2 and 3
#include <SPI_VFD.h>
#include <digitalWriteFast.h>
#define TIMERLOAD 0
#define AVGLENGTH 21
#define SCREENROW0 0
#define SCREENROW1 1
#define CHANGERISE0 0
#define CHANGERISE1 1
#define CHANGEFALL0 2
#define CHANGEFALL1 3
SPI_VFD vfd(13, 11, 12); // init vfd: data, clock, strobe
volatile unsigned long lastTimeRise0, lastTimeFall0, thisTimeRise0, thisTimeFall0;
volatile unsigned long lastTimeRise1, lastTimeFall1, thisTimeRise1, thisTimeFall1;
volatile unsigned long int timerOverflowCount = 0;
byte avgCount0, avgCount1 = 0;
unsigned long avgTotal0[AVGLENGTH], avgTotal1[AVGLENGTH];
unsigned long pulseWidthArray0[AVGLENGTH], pulseWidthArray1[AVGLENGTH];
unsigned long pulseWidthHighArray0[AVGLENGTH], pulseWidthHighArray1[AVGLENGTH];
volatile byte change = 0;
void setup() {
//Serial.begin(115200); // for testing
// set up interrupts
attachInterrupt(0, changingInt0, CHANGE);
attachInterrupt(1, changingInt1, CHANGE);
// say helloooooo
vfd.begin(20, 2);
vfd.print("frequency counter");
delay(1000);
vfd.clear();
// initialize timer1
noInterrupts(); // disable all interrupts
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = TIMERLOAD; // preload timer 65536-16MHz/8/32Hz
TCCR1B |= (1 << CS10); // 8 prescaler CS11 // 1 prescaler CS10
TIMSK1 |= (1 << TOIE1); // enable timer overflow interrupt
interrupts(); // enable all interrupts
}
void printRow(unsigned long ticks, const byte screenRow) {
float hz = getHertz(ticks);
if(hz>0) {
String spacer = "";
if(int(hz)==1000) { spacer=" "; } else
if(int(hz)<10) { spacer = " "; } else
if(int(hz)<100) { spacer = " "; } else
if(int(hz)<1000) { spacer = " "; } else
if(int(hz)<10000) { spacer = " "; }
vfd.setCursor(0, screenRow);
vfd.print(spacer);
vfd.print(hz, 2);
vfd.print("hz ");
}
}
void loop() {
unsigned long ticks0 = thisTimeRise0 - lastTimeRise0;
unsigned long ticks1 = thisTimeRise1 - lastTimeRise1;
unsigned long pulseHigh0 = thisTimeRise0 - thisTimeFall0;
unsigned long pulseHigh1 = thisTimeRise1 - thisTimeFall1;
unsigned long medianPulseWidthHigh;
// chuck out any ridiculously long or short periods
if (bitRead(change, CHANGERISE0)==1 && ticks0>400 && ticks0<4000000000) {
if(avgCount0 < AVGLENGTH) {
avgTotal0[avgCount0] = ticks0; // add current period onto our array for averaging
pulseWidthHighArray0[avgCount0] = pulseHigh0;
avgCount0++;
} else {
// when array full up, print median average
unsigned long medianTicks = median(avgCount0, avgTotal0);
printRow(medianTicks, SCREENROW0);
medianPulseWidthHigh = median(avgCount0, pulseWidthHighArray0);
printPulseWidth(medianPulseWidthHigh, medianTicks, SCREENROW0);
avgCount0 = 0;
}
change &= ~(1 << CHANGERISE0);
}
if (bitRead(change, CHANGERISE1)==1 && ticks1>400 && ticks1<4000000000) {
if(avgCount1 < AVGLENGTH) {
avgTotal1[avgCount1] = ticks1;
pulseWidthHighArray1[avgCount1] = pulseHigh1;
avgCount1++;
} else {
unsigned long medianTicks = median(avgCount1, avgTotal1);
printRow(medianTicks, SCREENROW1);
medianPulseWidthHigh = median(avgCount1, pulseWidthHighArray1);
printPulseWidth(medianPulseWidthHigh, medianTicks, 1);
avgCount1 = 0;
}
change &= ~(1 << CHANGERISE1);
}
}
// has to use a floating point thing as one of the operands otherwise it'll end up 1 or 0
float getPulseWidth(unsigned long pulseWidthHigh, unsigned long pulseWidth) {
return ((double)pulseWidthHigh / pulseWidth) * 100;
}
void printPulseWidth(unsigned long pulseWidthHigh, unsigned long pulseWidth, int screenRow) {
vfd.setCursor(15, screenRow);
float pwPercent = getPulseWidth(pulseWidthHigh, pulseWidth);
if(pwPercent<100) {
vfd.print(pwPercent, 1);
vfd.print("%");
}
}
float getHertz(unsigned long ticks) {
return 1/((0.0625 * ticks) / 1000000); // (x uS in a tick, * number of ticks) / microsec in a second = period
}
ISR(TIMER1_OVF_vect) { // interrupt service routine that wraps a user defined function supplied by attachInterrupt
timerOverflowCount++;
}
void changingInt0() {
if(digitalReadFast(2)) {
lastTimeRise0 = thisTimeRise0;
thisTimeRise0 = (timerOverflowCount * 65535) + TCNT1;
change |= 1 << CHANGERISE0;
} else {
lastTimeFall0 = thisTimeFall0;
thisTimeFall0 = (timerOverflowCount * 65535) + TCNT1;
change |= 1 << CHANGEFALL0;
}
}
void changingInt1() {
if(digitalReadFast(3)) {
lastTimeRise1 = thisTimeRise1;
thisTimeRise1 = (timerOverflowCount * 65535) + TCNT1;
change |= 1 << CHANGERISE1;
} else {
lastTimeFall1 = thisTimeFall1;
thisTimeFall1 = (timerOverflowCount * 65535) + TCNT1;
change |= 1 << CHANGEFALL1;
}
}
unsigned long int median(unsigned long int n, unsigned long int x[]) {
unsigned long temp;
int i, j;
// the following two loops sort the array x in ascending order
for(i=0; i<n-1; i++) {
for(j=i+1; j<n; j++) {
if(x[j] < x[i]) {
// swap elements
temp = x[i];
x[i] = x[j];
x[j] = temp;
}
}
}
if(n%2==0) {
// if there is an even number of elements, return mean of the two elements in the middle
return((x[n/2] + x[n/2 - 1]) / 2.0);
} else {
// else return the element in the middle
return x[n/2];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment