Last active
October 2, 2017 14:44
-
-
Save equaliser/7075202 to your computer and use it in GitHub Desktop.
Arduino dual audio frequency counter and pulse width measuring sketch.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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
hi equalizer,
Looking at your code and I like lot's of it. Just few questions:
regards,
Vincent