Code: Select all
/* This code incorporates the code in <Servo_LD3015_Sweep_pot_control> together with the period
* measurement by interrupt from <Tachometer_PWM_Arduino_codeV2c>
* Pulse width linearly related to ign frequency with negative slope
* Calibration with poteniometer input to give a 0 to 200 usec addition to the pulse width
* Count1 & 2 used to determine whether there has been an ign pulse within the 100ms loop time
* No pulse in that time means engine speed less than 500, ie stopped
* This code makes no provision for count overflow, which since counts 1 & 2 are defined as long
* would take 23,860 hrs
* Neither does it allow for Time1,2,3 & 4 overflow, which will occur after 35.7 mins (2,147,483,648/(1,000,000*60)
* Tests showed "jitter" in the output pulse from V1 of the ISR code. This version attempts to correct that
* by incorporating the pulse output within the ISR, it worked!
* See Excelspreadsheet <PWM & Servo test results> Classic Cars/Ethel Refurb/Tachometer
* Moving average of period over 3 values
* THIS CODE ASUMES THAT THE DISPLAY ANGLE BETWEEN 0 AND 6000 RPM IS 254.5 DEGREES. THE CONSTANTS C1, 2 & 3
* TAKE THIS INTO ACCOUNT. THEY WILL NEED TO BE CHANGED IF THE ANGLE ASSUMPTION IS WRONG
* IT ALSO ASSUMES THAT THE SIGNAL FROM THE ECU IS 3 X THE SHAFT FREQUENCY
*/
int InterruptPin = 2; // Input signal ign frequency from engine ECU
int AdjustPin = 4; // Analog A2 (= PB4 or pin 2 on ATiny85) for software fine tuning/calibration
int OutputPin = 0; // Output pin for control pulse pin 5 on ATtiny85
long Time1 = 0; // Time1 & Time2 used to measure period in usecs
long Time2 = 0; // Will overflow after 35.76 mins
long Time3 = 0; // Time3 & 4 used within ISR to fix ouput pulse interval
long Time4 = 0;
long Period = 0; // period calculated as <Time2 - Time1> in usecs
long count1 = 0; // Counter incremented in ISR checked in mainloop for no increase, ie no pulse
long count2 = 0;
int Pulseinterval; // Pulseinterval used to fix pulse output times
long C1 = 2464; // Define constants to calc demand from period
long C2 = 6996364;
long C3 = 666;
long Adj = 512; // The input from the adjustment poteniometer. Set to mid point to start
long AdjDash = 25; // Adj' = Adj * 50 / 1023, again set to mid point to start
int startup =0; // Flag to go through initial sweep just once
int dt; // time for pulse width in initial sweep
int demand; // Pulse time for normal operation
int Averagep1 =0; // Average values used in the moving window averaging
int Averagep2 =0;
int Averagep3 =0;
int Averagedemand = 0;
void setup()
{
pinMode(InterruptPin, INPUT); // ECU trigger signal connected to interrupt, pin 2
pinMode(AdjustPin, INPUT); // Adjustment signal connected to Analog pin A0
pinMode(OutputPin, OUTPUT);
attachInterrupt(digitalPinToInterrupt(InterruptPin), Pmeasure, RISING); // internal pull up R keeps pin 2 HIGH, button press takes it low on release
// it goes high, this rising edge used to trigger interrupt call // Pmeasurep is the name of the ISR, (Interrupt Service Routine) which is defined // as the last statement of this sketch
}
void loop() // The loop runs startup routine and then reads calibration pot and checks for no interrupts i.e. engine stopped
{
if(startup == 0) // This section up to <startup = 1> sweeps the pointer through full range and back yo zero
{
delay(1500);
for (dt = 2500; dt >= 500; dt -= 15) // goes from 0 degrees to 270 degrees in steps of 1.35 degree
{ // 270 degrees correspond to pulse width from 2500 to 500 usec
digitalWrite(OutputPin, HIGH);
delayMicroseconds(dt);
digitalWrite(OutputPin, LOW);
delay(13);
}
delay(100);
for (dt = 500; dt <= 2500; dt += 15) // goes from 270 degrees to 0 degrees
{
digitalWrite(OutputPin, HIGH);
delayMicroseconds(dt);
digitalWrite(OutputPin, LOW);
delay(13);
}
startup = 1; //End of initial pointer sweep to max and return to zero
}
else // Initial sweep just run once at startup. Following section controls pointer
{
Adj = analogRead(AdjustPin); // read the output from the adjustment potentiometer
AdjDash = Adj * C3;
AdjDash = AdjDash/Period; // Scale the value of the adjustment input to a 0 - 100 range to suit pulse time
if (count1 == count2) // Count2 has not changed (in ISR) thus no interrupt received.
{ // hence the period between pulses is greater than at leasr 60ms i.e. engine stopped
digitalWrite(OutputPin, HIGH); // so set tacho to zero
delayMicroseconds(2500);
digitalWrite(OutputPin, LOW);
}
else
{
count1 = count2;
}
delay(60); // Slowest engine speed is 500 rpm, i.e. a pulse frequency of 25Hz, or period of 40 msec
} // so ensure loop time exceeds this so that there is normally at least one interrupt
} // within the loop
void Pmeasure() // ISR, include moving window averaging of pulse width and pulse output
{
Time2 = micros(); // Time2 (& Time1) in usec
Time3 = Time2/1000; // Time3 & 4 in msec
Period = Time2 - Time1; // Period is the time between successive interrupts usecs
Time1 = Time2; // Reset Time1 in order to measure the next period
count2 = count2 + 1; // Incremented count2 is tested in <loop>, no increment means no interrupt
Pulseinterval = (Time3 - Time4);
if(Pulseinterval > 19) // ensure that ouput pulses are at least 19msec apart
{
demand = C1-C2/Period; // Pulse width = 2500-6.42*frequency; frequency = 1000000/period (usec)
Averagep3 = Averagep2;
Averagep2 = Averagep1;
Averagep1 = demand;
Averagedemand = (Averagep1 + Averagep2 + Averagep3)/3;
digitalWrite(OutputPin, HIGH);
delayMicroseconds(Averagedemand+AdjDash);
digitalWrite(OutputPin, LOW);
Time4 = Time3; // Reset Time4 to time the next ouput pulse
}
}