This is how I did it.
A lot of it is dealing with the quadrature meter and the calibration pot - which you don't need.
Worked fine.
Allan
/* aircored meter revcounter afh jan 2016 version 1.0
This code runs on an arduino pro mini.
The engine alternator energise pin is ac coupled to a simple amplifier
which is thewn applied to an arduino digital input pin.
The impulse is timed, obtained from the ripple on
the alternator connection. The inverse of this ,
scaled for alternator drive ratio and number of poles, gives
the rpm. The scaling can be changed by the reading of
a potentiomer, so is easily adjustable on site.
The value given is scaled to the meter range, eg 0..270 degrees,
and cos and sin lookup tables give the drive voltage to each coil.
This sent as a pwm signal, of value 0..255 to each coil, with
appropriate phase reversals for the 4 possible quadrants.
The sw allows for up to 360 degrees,
During normal operation the on-board LED flahes.
out of range coditions : revs above 1.3 * maxrevs are displayed as 1.3 * maxrevs.
: no signal - the revcounter sweeps.
Debugging : a serial output displays at 57600 baud , per line:
rpm/10, scaling, meter angle, coil 0 current, coil 1 current
0-? 0-1023 0-359 0-255 0-255
afh January 2016.
*/
// Declarations...........
// constants won't change.
const int ledPin = 13; // the number of the LED pin
const int potinput = A2; // input pin from calibration pot
const int pulsepin = 4; // input pin from alternator
// pins to which drive coils on the meter are connected
//sin coil
const int coil_1_plus = 5;
const int coil_1_minus = 3;
//cos coil
const int coil_0_plus = 6;
const int coil_0_minus = 9;
// now sin and cos lookup tables. 255 == 1 for driving pwm outputs
// in 1 degree intervals 0..89
const unsigned char sinA [] =
{0, 4, 8, 13, 17, 22, 26, 31, 35, 39, 44, 48, 53, 57, 61, 65,
70, 74, 78, 83, 87, 91, 95, 99, 103, 107, 111, 115, 119, 123,
127, 131, 135, 138, 142, 146, 149, 153, 156, 160, 163, 167,
170, 173, 177, 180, 183, 186, 189, 192, 195, 198, 200, 203,
206, 208, 211, 213, 216, 218, 220, 223, 225, 227, 229, 231,
232, 234, 236, 238, 239, 241, 242, 243, 245, 246, 247, 248,
249, 250, 251, 251, 252, 253, 253, 254, 254, 254, 254, 254, 255};
const unsigned char cosA [] =
{255, 254, 254, 254, 254, 254, 253, 253, 252, 251, 251, 250,
249, 248, 247, 246, 245, 243, 242, 241, 239, 238, 236, 234,
232, 231, 229, 227, 225, 223, 220, 218, 216, 213, 211, 208,
206, 203, 200, 198, 195, 192, 189, 186, 183, 180, 177, 173,
170, 167, 163, 160, 156, 153, 149, 146, 142, 138, 135, 131,
127, 123, 119, 115, 111, 107, 103, 99, 95, 91, 87, 83, 78,
74, 70, 65, 61, 57, 53, 48, 44, 39, 35, 31, 26, 22, 17, 13,
8, 4, 0};
// to date me ... cos and sin from excel, formatted with aedit!
// project specific values
const unsigned long interval =20; // interval at which to update reading /mS
const unsigned int maxrevs = 4000; // for this engine /rpm
const unsigned int maxangle = 270; // for typical meter /degrees
const unsigned int offset = 35; // for meter zero
const unsigned int maxD = 20; // for averaging
unsigned long debounce[maxD];
unsigned long ave;
const unsigned long zerorevs = 0;
unsigned char i,j; //as counter
// Variables :
unsigned long pulselow ;
unsigned long pulsehigh ;
unsigned long pulselen ;
unsigned int potval ;
unsigned long freq;
unsigned long rpm;
int angle ; // 0..360
//CODE STARTS HERE!
void setup() { // setting pin directions - all unassigned are inputs
pinMode(ledPin, OUTPUT);
// and now the coils
pinMode(coil_0_minus, OUTPUT);
pinMode(coil_0_plus , OUTPUT);
pinMode(coil_1_minus, OUTPUT);
pinMode(coil_1_plus , OUTPUT);
Serial.begin(115200); // for debugging monitor.
j=0;
k=0;
up = true;
}
// main do forever loop
void loop() {
// : read the pulse input, derive rpm,
// : debounce the value, and drive the meter.
// : finally send a load of debug data over the serial link.
unsigned long currentMillis = millis();
unsigned long previousMillis ;
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
potval = analogRead(potinput); // get calibration value 0..1023
// now obtain pulse width - note I measure both high and low
// parts to deal with asymmetry of input pulsetrain
pulselow = pulseIn( pulsepin, LOW,20000); // measure pulsewidth
pulsehigh = pulseIn( pulsepin, HIGH,20000); // measure pulsewidth
pulselen = pulselow + pulsehigh; // total pulsewidth
freq = 4000000 / pulselen ; // gives freq in Hz * 4
//and scale noting that typically 500hz = 2000rpm
rpm = (potval * freq) / 400; // and calibrate
// such that pot halfway is about right
if (rpm > 10000) rpm= zerorevs; // no input signal - offset value
// - timeout from pulseIn gives a large value
if (rpm >(1.3 * maxrevs )) rpm = 1.3*maxrevs ; // handle overscale
// now debounce routine - average of last maxD readings
if (++i>=maxD) i=0;
debounce [i] = rpm; // circular buffer
ave = 0;
for(j=0;j<maxD;j++) {
ave += debounce[j];
}
rpm = ave/maxD;
// now convert rpm to angle on meter
angle = (rpm * maxangle) / maxrevs ; // eg for full scale 4000 rpm
// = 270 degrees deflection on meter
if (angle < offset){
angle = angle + 360 - offset;
}
else
angle = angle - offset ; // deals with the displayed zero
// position offset point
angle = angle % 360; // so we don't get silly values!
//now drive aircored meter via sin/cos lookup tables with pwm function
unsigned char coil_0_val;
unsigned char coil_1_val;
// for logging
// 0..90
if ((angle >= 0) && (angle < 90)) { //first quadrant
analogWrite( coil_0_minus , 0);
analogWrite( coil_1_minus , 0);
analogWrite( coil_0_plus , coil_0_val = sinA[angle]);
analogWrite( coil_1_plus , coil_1_val = cosA[angle]);
}
// 90 .. 180
if ((angle >= 90) && (angle <180)) { //second quadrant
analogWrite( coil_0_minus , 0);
analogWrite( coil_1_plus , 0);
analogWrite( coil_0_plus , coil_0_val = cosA[angle - 90]);
analogWrite( coil_1_minus , coil_1_val = sinA[angle - 90]);
}
// 180..270
if ((angle >= 180) && (angle < 270)) { //third quadrant
analogWrite( coil_0_plus , 0);
analogWrite( coil_1_plus , 0);
analogWrite( coil_0_minus ,coil_0_val = sinA[angle - 180]);
analogWrite( coil_1_minus , coil_1_val = cosA[angle - 180]);
}
// 270-360
if ((angle >= 270) && (angle < 360)) { //fourth quadrant
analogWrite( coil_0_plus , 0);
analogWrite( coil_1_minus , 0);
analogWrite( coil_0_minus , coil_0_val = cosA[angle - 270]);
analogWrite( coil_1_plus , coil_1_val = sinA[angle - 270]);
}
// and finally print out debug values.
Serial.print(rpm/10);
Serial.write(" ");
Serial.print(potval);
Serial.write(" ");
Serial.print(angle);
Serial.write(" ");
Serial.print(coil_0_val);
Serial.write(" ");
Serial.print(coil_1_val);
Serial.write("\n");
}
}