Huge rewrite of the schedule code to add all 4 required schedules

This commit is contained in:
Josh Stewart 2013-09-05 10:27:16 +10:00
parent 251886d8aa
commit d86597d700
3 changed files with 164 additions and 86 deletions

View File

@ -17,7 +17,6 @@ Need to calculate the req_fuel figure here, preferably in pre-processor macro
#define pinTPS 8 //TPS input pin
#define pinTrigger 2 //The CAS pin
#define pinMAP 0 //MAP sensor pin
#define triggerFilterTime 100 // The shortest time (in uS) that pulses will be accepted (Used for debounce filtering)
//**************************************************************************************************
#include "globals.h"
@ -42,6 +41,7 @@ int req_fuel_uS = configPage1.reqFuel * 1000; //Convert to uS and an int. This i
// These aren't really configuration options, more so a description of how the hardware is setup. These are things that will be defined in the recommended hardware setup
int triggerActualTeeth = configPage2.triggerTeeth - configPage2.triggerMissingTeeth; //The number of physical teeth on the wheel. Doing this here saves us a calculation each time in the interrupt
int triggerToothAngle = 360 / configPage2.triggerTeeth; //The number of degrees that passes from tooth to tooth
int triggerFilterTime = 100; // The shortest time (in uS) that pulses will be accepted (Used for debounce filtering)
volatile int toothCurrentCount = 0; //The current number of teeth (Onec sync has been achieved, this can never actually be 0
volatile unsigned long toothLastToothTime = 0; //The time (micros()) that the last tooth was registered
@ -123,7 +123,7 @@ void setup() {
//Setup the dummy fuel and ignition tables
dummyFuelTable(&fuelTable);
dummyIgnitionTable(&ignitionTable);
initialiseScheduler();
initialiseSchedulers();
counter = 0;
configPage2.triggerTeeth = 12; //TESTING ONLY!
@ -214,7 +214,7 @@ void loop()
//This may potentially be called a number of times as we get closer and closer to the opening time
if (injectorStartAngle > crankAngle)
{
setSchedule1(openInjector,
setFuelSchedule1(openInjector,
(injectorStartAngle - crankAngle) * timePerDegree,
currentStatus.PW,
closeInjector
@ -223,7 +223,7 @@ void loop()
//Likewise for the ignition
if (ignitionStartAngle > crankAngle)
{
setSchedule2(beginCoilCharge,
setIgnitionSchedule1(beginCoilCharge,
(ignitionStartAngle - crankAngle) * timePerDegree,
configPage2.dwellRun,
endCoilCharge
@ -278,17 +278,24 @@ void trigger()
if ( (curTime - toothLastToothTime) < triggerFilterTime) { interrupts(); return; } //Debounce check. Pulses should never be less than 100uS, so if they are it means a false trigger. (A 36-1 wheel at 8000pm will have triggers approx. every 200uS)
toothCurrentCount++; //Increment the tooth counter
//Serial.println("Got trigger");
//Begin the missing tooth detection
//If the time between the current tooth and the last is greater than 1.5x the time between the last tooth and the tooth before that, we make the assertion that we must be at the first tooth after the gap
//if ( (curTime - toothLastToothTime) > (1.5 * (toothLastToothTime - toothLastMinusOneToothTime))) { toothCurrentCount = 1; }
//if ( (curTime - toothLastToothTime) > ((3 * (toothLastToothTime - toothLastMinusOneToothTime))>>1)) { toothCurrentCount = 1; } //Same as above, but uses bitshift instead of multiplying by 1.5
if (toothCurrentCount > triggerActualTeeth)
if ( (curTime - toothLastToothTime) > ((3 * (toothLastToothTime - toothLastMinusOneToothTime))>>1)) //Same as above, but uses bitshift instead of multiplying by 1.5
{
toothCurrentCount = 1;
toothOneMinusOneTime = toothOneTime;
toothOneTime = curTime;
}
//TESTING METHOD
/*
if (toothCurrentCount > triggerActualTeeth) }
{
toothCurrentCount = 1;
toothOneMinusOneTime = toothOneTime;
toothOneTime = curTime;
} //For testing ONLY
}
*/
toothLastMinusOneToothTime = toothLastToothTime;
toothLastToothTime = curTime;

View File

@ -26,18 +26,22 @@ See page 136 of the processors datasheet: http://www.atmel.com/Images/doc2549.pd
#include <avr/interrupt.h>
#include <avr/io.h>
//#define clockspeed 16000000
void initialiseSchedulers();
void setFuelSchedule1(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)());
void setFuelSchedule2(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)());
void setIgnitionSchedule1(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)());
void setIgnitionSchedule2(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)());
int schedule1Status; //Value=0 means do nothing, value=1 means call the startCallback, value=2 means call the endCallback
int schedule2Status; //As above, for 2nd scheduler
unsigned long schedule1Duration; //How long (uS) after calling the start callback to we call the end callback
unsigned long schedule2Duration;
void (*schedule1StartCallback)(); //Start Callback function for schedule1
void (*schedule2StartCallback)();
void (*schedule1EndCallback)(); //End Callback function for schedule1
void (*schedule2EndCallback)();
enum ScheduleStatus {OFF, PENDING, RUNNING}; //The 3 statuses that a schedule can have
struct Schedule {
unsigned long duration;
ScheduleStatus Status;
void (*StartCallback)(); //Start Callback function for schedule
void (*EndCallback)(); //Start Callback function for schedule
};
void initialiseScheduler();
void setSchedule1(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)());
void setSchedule2(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)());
Schedule fuelSchedule1;
Schedule fuelSchedule2;
Schedule ignitionSchedule1;
Schedule ignitionSchedule2;

View File

@ -1,98 +1,165 @@
void initialiseScheduler()
void initialiseSchedulers()
{
// Much help in this from http://arduinomega.blogspot.com.au/2011/05/timer2-and-overflow-interrupt-lets-get.html
//Schedule 1, which is uses timer 3
TCCR3B = 0x00; //Disbale Timer2 while we set it up
//Fuel Schedules, which uses timer 3
TCCR3B = 0x00; //Disbale Timer3 while we set it up
TCNT3 = 0; //Reset Timer Count
TIFR3 = 0x00; //Timer2 INT Flag Reg: Clear Timer Overflow Flag
TIMSK3 = 0x01; //Timer2 INT Reg: Timer2 Overflow Interrupt Enable
TCCR3A = 0x00; //Timer2 Control Reg A: Wave Gen Mode normal
TCCR3B = (1 << CS12); //Timer2 Control Reg B: Timer Prescaler set to 256. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg
//Schedule 2, which is uses timer 4
TCCR4B = 0x00; //Disbale Timer2 while we set it up
TCNT4 = 0; //Reset Timer Count
TIFR4 = 0x00; //Timer2 INT Flag Reg: Clear Timer Overflow Flag
TIMSK4 = 0x01; //Timer2 INT Reg: Timer2 Overflow Interrupt Enable
TCCR4A = 0x00; //Timer2 Control Reg A: Wave Gen Mode normal
TCCR4B = (1 << CS12); //Timer2 Control Reg B: Timer Prescaler set to 256. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg
schedule1Status = 0;
schedule2Status = 0;
TIFR3 = 0x00; //Timer3 INT Flag Reg: Clear Timer Overflow Flag
TCCR3A = 0x00; //Timer3 Control Reg A: Wave Gen Mode normal
TCCR3B = (1 << CS12); //Timer3 Control Reg B: Timer Prescaler set to 256. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg
fuelSchedule1.Status = OFF;
fuelSchedule2.Status = OFF;
//Ignition Schedules, which uses timer 5
TCCR5B = 0x00; //Disbale Timer3 while we set it up
TCNT5 = 0; //Reset Timer Count
TIFR5 = 0x00; //Timer3 INT Flag Reg: Clear Timer Overflow Flag
TCCR5A = 0x00; //Timer3 Control Reg A: Wave Gen Mode normal
TCCR5B = (1 << CS12); //Timer3 Control Reg B: Timer Prescaler set to 256. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg
ignitionSchedule1.Status = OFF;
ignitionSchedule2.Status = OFF;
}
/*
This turns schedule 1 on, gives it callback functions and resets the relevant timer based on the time in the future that this should be triggered
These 4 function turn a schedule on, provides the time to start and the duration and gives it callback functions.
All 4 functions operate the same, just on different schedules
Args:
startCallback: The function to be called once the timeout1 is reached
timeout1: The number of uS in the future that the callback should be triggered
duration: The number of uS before endCallback is called
startCallback: The function to be called once the timeout is reached
timeout: The number of uS in the future that the callback should be triggered
duration: The number of uS after startCallback is called before endCallback is called
endCallback: This function is called once the duration time has been reached
*/
void setSchedule1(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)())
void setFuelSchedule1(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)())
{
if(fuelSchedule1.Status == RUNNING) { return; } //Check that we're not already part way through a schedule
//We need to calculate the value to reset the timer to (preload) in order to achieve the desired overflow time
//As the timer is ticking every 16uS (Time per Tick = (Prescale)*(1/Frequency))
//TODO: Need to add check for timeout > 1048576 ????
if(schedule1Status == 2) { return; } //Check that we're note already part way through a schedule
TCNT3 = 65536 - (timeout / 16); //Each tick occurs every 16uS with a 256 prescaler so divide the timeout by 16 to get ther required number of ticks. Subtract this from the total number of tick (65536 for 16-bit timer)
schedule1Duration = duration;
schedule1StartCallback = startCallback; //Name the start callback function
schedule1EndCallback = endCallback; //Name the start callback function
schedule1Status = 1; //Turn this schedule on and set it
unsigned int absoluteTimeout = TCNT3 + (timeout / 16); //Each tick occurs every 16uS with the 256 prescaler, so divide the timeout by 16 to get ther required number of ticks. Add this to the current tick count to get the target time. This will automatically overflow as required
OCR3A = absoluteTimeout;
fuelSchedule1.duration = duration;
fuelSchedule1.StartCallback = startCallback; //Name the start callback function
fuelSchedule1.EndCallback = endCallback; //Name the start callback function
fuelSchedule1.Status = PENDING; //Turn this schedule on
TIMSK3 |= (1 << OCIE3A); //Turn on the A compare unit (ie turn on the interrupt)
}
void setFuelSchedule2(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)())
{
if(fuelSchedule2.Status == RUNNING) { return; } //Check that we're not already part way through a schedule
//We need to calculate the value to reset the timer to (preload) in order to achieve the desired overflow time
//As the timer is ticking every 16uS (Time per Tick = (Prescale)*(1/Frequency))
unsigned int absoluteTimeout = TCNT3 + (timeout / 16); //Each tick occurs every 16uS with the 256 prescaler, so divide the timeout by 16 to get ther required number of ticks. Add this to the current tick count to get the target time. This will automatically overflow as required
OCR3B = absoluteTimeout; //Use the B copmare unit of timer 3
fuelSchedule2.duration = duration;
fuelSchedule2.StartCallback = startCallback; //Name the start callback function
fuelSchedule2.EndCallback = endCallback; //Name the start callback function
fuelSchedule2.Status = PENDING; //Turn this schedule on
TIMSK3 |= (1 << OCIE3B); //Turn on the B compare unit (ie turn on the interrupt)
}
//Ignition schedulers use Timer 5
void setIgnitionSchedule1(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)())
{
if(ignitionSchedule1.Status == RUNNING) { return; } //Check that we're not already part way through a schedule
//We need to calculate the value to reset the timer to (preload) in order to achieve the desired overflow time
//As the timer is ticking every 16uS (Time per Tick = (Prescale)*(1/Frequency))
unsigned int absoluteTimeout = TCNT5 + (timeout / 16); //Each tick occurs every 16uS with the 256 prescaler, so divide the timeout by 16 to get ther required number of ticks. Add this to the current tick count to get the target time. This will automatically overflow as required
OCR5A = absoluteTimeout;
ignitionSchedule1.duration = duration;
ignitionSchedule1.StartCallback = startCallback; //Name the start callback function
ignitionSchedule1.EndCallback = endCallback; //Name the start callback function
ignitionSchedule1.Status = PENDING; //Turn this schedule on
TIMSK5 |= (1 << OCIE5A); //Turn on the A compare unit (ie turn on the interrupt)
}
void setIgnitionSchedule2(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)())
{
if(ignitionSchedule2.Status == RUNNING) { return; } //Check that we're not already part way through a schedule
//We need to calculate the value to reset the timer to (preload) in order to achieve the desired overflow time
//As the timer is ticking every 16uS (Time per Tick = (Prescale)*(1/Frequency))
unsigned int absoluteTimeout = TCNT5 + (timeout / 16); //Each tick occurs every 16uS with the 256 prescaler, so divide the timeout by 16 to get ther required number of ticks. Add this to the current tick count to get the target time. This will automatically overflow as required
OCR5B = absoluteTimeout;
ignitionSchedule2.duration = duration;
ignitionSchedule2.StartCallback = startCallback; //Name the start callback function
ignitionSchedule2.EndCallback = endCallback; //Name the start callback function
ignitionSchedule2.Status = PENDING; //Turn this schedule on
TIMSK5 |= (1 << OCIE5B); //Turn on the A compare unit (ie turn on the interrupt)
}
//As above, but for schedule2
void setSchedule2(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)())
//Timer3A (fuel schedule 1) Compare Vector
//This function gets called when either the start time or the duration time are reached
//This calls the relevant callback function (startCallback or endCallback) depending on the status of the schedule.
//If the startCallback function is called, we put the scheduler into RUNNING state
ISR(TIMER3_COMPA_vect) //fuelSchedule1
{
//TODO: Need to add check for timeout > 1048576 ????
if(schedule2Status == 2) { return; } //Check that we're note already part way through a schedule
TCNT4 = 65536 - (timeout / 16); //Each tick occurs every 16uS with a 256 prescaler so divide the timeout by 16 to get ther required number of ticks. Subtract this from the total number of tick (65536 for 16-bit timer)
schedule2Duration = duration;
schedule2StartCallback = startCallback; //Name the callback function
schedule2EndCallback = endCallback; //Name the callback function
schedule2Status = 1; //Turn this schedule on
if (fuelSchedule1.Status == PENDING) //Check to see if this schedule is turn on
{
fuelSchedule1.StartCallback();
fuelSchedule1.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
unsigned int absoluteTimeout = TCNT3 + (fuelSchedule1.duration / 16);
OCR3A = absoluteTimeout;
}
else if (fuelSchedule1.Status == RUNNING)
{
fuelSchedule1.EndCallback();
fuelSchedule1.Status = OFF; //Turn off the schedule
TIMSK3 &= ~(1 << OCIE3A); //Turn off this output compare unit (This simply writes 0 to the OCIE3A bit of TIMSK3)
}
//TIFR3 = 0x00; //Timer3 INT Flag Reg: Clear Timer Overflow Flag. I'm not 100% sure this is necessary, but better to be safe
}
//Timer3 (schedule 1) Overflow Interrupt Vector
//This needs to call the callback function if one has been provided and rest the timer
ISR(TIMER3_OVF_vect)
ISR(TIMER3_COMPB_vect) //fuelSchedule2
{
if (schedule1Status == 1) //Check to see if this schedule is turn on
if (fuelSchedule2.Status == PENDING) //Check to see if this schedule is turn on
{
schedule1StartCallback(); //Replace with user provided callback
schedule1Status = 2; //Turn off the callback
TCNT3 = 65536 - (schedule1Duration / 16);
fuelSchedule2.StartCallback();
fuelSchedule2.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
unsigned int absoluteTimeout = TCNT3 + (fuelSchedule2.duration / 16);
OCR3B = absoluteTimeout;
}
else if (schedule1Status == 2)
else if (fuelSchedule2.Status == RUNNING)
{
schedule1EndCallback();
schedule1Status = 0; //Turn off the callback
TCNT3 = 0; //Reset Timer to 0 out of 255
fuelSchedule2.EndCallback();
fuelSchedule2.Status = OFF; //Turn off the schedule
TIMSK3 &= ~(1 << OCIE3B); //Turn off this output compare unit (This simply writes 0 to the OCIE3A bit of TIMSK3)
}
TIFR3 = 0x00; //Timer2 INT Flag Reg: Clear Timer Overflow Flag
//TIFR3 = 0x00; //Timer3 INT Flag Reg: Clear Timer Overflow Flag. I'm not 100% sure this is necessary, but better to be safe
}
//AS above for schedule2
ISR(TIMER4_OVF_vect)
ISR(TIMER5_COMPA_vect) //ignitionSchedule1
{
if (schedule2Status == 1) //A value of 1 means call the start callback
if (ignitionSchedule1.Status == PENDING) //Check to see if this schedule is turn on
{
schedule2StartCallback();
schedule2Status = 2; //Set to call the end callback on the next run
TCNT4 = 65536 - (schedule2Duration / 16);
ignitionSchedule1.StartCallback();
ignitionSchedule1.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
unsigned int absoluteTimeout = TCNT5 + (ignitionSchedule1.duration / 16);
OCR5A = absoluteTimeout;
}
else if (schedule2Status == 2)
else if (ignitionSchedule1.Status == RUNNING)
{
schedule2EndCallback();
schedule2Status = 0; //Turn off the callback
TCNT4 = 0; //Reset Timer to 0 out of 255
ignitionSchedule1.EndCallback();
ignitionSchedule1.Status = OFF; //Turn off the schedule
TIMSK5 &= ~(1 << OCIE5A); //Turn off this output compare unit (This simply writes 0 to the OCIE3A bit of TIMSK3)
}
TIFR4 = 0x00; //Timer2 INT Flag Reg: Clear Timer Overflow Flag
//TIFR3 = 0x00; //Timer3 INT Flag Reg: Clear Timer Overflow Flag. I'm not 100% sure this is necessary, but better to be safe
}
ISR(TIMER5_COMPB_vect) //ignitionSchedule2
{
if (ignitionSchedule2.Status == PENDING) //Check to see if this schedule is turn on
{
ignitionSchedule2.StartCallback();
ignitionSchedule2.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
unsigned int absoluteTimeout = TCNT5 + (ignitionSchedule2.duration / 16);
OCR5B = absoluteTimeout;
}
else if (ignitionSchedule2.Status == RUNNING)
{
ignitionSchedule2.EndCallback();
ignitionSchedule2.Status = OFF; //Turn off the schedule
TIMSK5 &= ~(1 << OCIE5B); //Turn off this output compare unit (This simply writes 0 to the OCIE3A bit of TIMSK3)
}
//TIFR3 = 0x00; //Timer3 INT Flag Reg: Clear Timer Overflow Flag. I'm not 100% sure this is necessary, but better to be safe
}