speeduino/speeduino/scheduler.ino

786 lines
39 KiB
C++

/*
Speeduino - Simple engine management for the Arduino Mega 2560 platform
Copyright (C) Josh Stewart
A full copy of the license may be found in the projects root directory
*/
#include "scheduler.h"
#include "globals.h"
void initialiseSchedulers()
{
nullSchedule.Status = OFF;
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
// Much help in this from http://arduinomega.blogspot.com.au/2011/05/timer2-and-overflow-interrupt-lets-get.html
//Fuel Schedules, which uses timer 3
TCCR3B = 0x00; //Disable Timer3 while we set it up
TCNT3 = 0; //Reset Timer Count
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
//TCCR3B = 0x03; //Timer3 Control Reg B: Timer Prescaler set to 64. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg
//Ignition Schedules, which uses timer 5
TCCR5B = 0x00; //Disable Timer5 while we set it up
TCNT5 = 0; //Reset Timer Count
TIFR5 = 0x00; //Timer5 INT Flag Reg: Clear Timer Overflow Flag
TCCR5A = 0x00; //Timer5 Control Reg A: Wave Gen Mode normal
//TCCR5B = (1 << CS12); //Timer5 Control Reg B: Timer Prescaler set to 256. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg
TCCR5B = 0x03; //aka Divisor = 64 = 490.1Hz
//The remaining Schedules (Schedules 4 for fuel and ignition) use Timer4
TCCR4B = 0x00; //Disable Timer4 while we set it up
TCNT4 = 0; //Reset Timer Count
TIFR4 = 0x00; //Timer4 INT Flag Reg: Clear Timer Overflow Flag
TCCR4A = 0x00; //Timer4 Control Reg A: Wave Gen Mode normal
TCCR4B = (1 << CS12); //Timer4 Control Reg B: aka Divisor = 256 = 122.5HzTimer Prescaler set to 256. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg
#elif defined (CORE_TEENSY)
//FlexTimer 0 is used for 4 ignition and 4 injection schedules. There are 8 channels on this module, so no other timers are needed
FTM0_MODE |= FTM_MODE_WPDIS; // Write Protection Disable
FTM0_MODE |= FTM_MODE_FTMEN; //Flex Timer module enable
FTM0_MODE |= FTM_MODE_INIT;
FTM0_SC = 0x00; // Set this to zero before changing the modulus
FTM0_CNTIN = 0x0000; //Shouldn't be needed, but just in case
FTM0_CNT = 0x0000; // Reset the count to zero
FTM0_MOD = 0xFFFF; // max modulus = 65535
//FlexTimer 1 is used for schedules on channel 5+. Currently only channel 5 is used, but will likely be expanded later
FTM1_MODE |= FTM_MODE_WPDIS; // Write Protection Disable
FTM1_MODE |= FTM_MODE_FTMEN; //Flex Timer module enable
FTM1_MODE |= FTM_MODE_INIT;
FTM1_SC = 0x00; // Set this to zero before changing the modulus
FTM1_CNTIN = 0x0000; //Shouldn't be needed, but just in case
FTM1_CNT = 0x0000; // Reset the count to zero
FTM1_MOD = 0xFFFF; // max modulus = 65535
/*
* Enable the clock for FTM0/1
* 00 No clock selected. Disables the FTM counter.
* 01 System clock
* 10 Fixed frequency clock
* 11 External clock
*/
FTM0_SC |= FTM_SC_CLKS(0b1);
FTM1_SC |= FTM_SC_CLKS(0b1);
/*
* Set Prescaler
* This is the slowest that the timer can be clocked (Without used the slow timer, which is too slow). It results in ticks of 2.13333uS on the teensy 3.5:
* 60000000 Hz = F_BUS
* 128 * 1000000uS / F_BUS = 2.133uS
*
* 000 = Divide by 1
* 001 Divide by 2
* 010 Divide by 4
* 011 Divide by 8
* 100 Divide by 16
* 101 Divide by 32
* 110 Divide by 64
* 111 Divide by 128
*/
FTM0_SC |= FTM_SC_PS(0b111);
FTM1_SC |= FTM_SC_PS(0b111);
//Setup the channels (See Pg 1014 of K64 DS).
//The are probably not needed as power on state should be 0
//FTM0_C0SC &= ~FTM_CSC_ELSB;
//FTM0_C0SC &= ~FTM_CSC_ELSA;
//FTM0_C0SC &= ~FTM_CSC_DMA;
FTM0_C0SC &= ~FTM_CSC_MSB; //According to Pg 965 of the K64 datasheet, this should not be needed as MSB is reset to 0 upon reset, but the channel interrupt fails to fire without it
FTM0_C0SC |= FTM_CSC_MSA; //Enable Compare mode
FTM0_C0SC |= FTM_CSC_CHIE; //Enable channel compare interrupt
FTM0_C1SC &= ~FTM_CSC_MSB; //According to Pg 965 of the datasheet, this should not be needed as MSB is reset to 0 upon reset, but the channel interrupt fails to fire without it
FTM0_C1SC |= FTM_CSC_MSA; //Enable Compare mode
FTM0_C1SC |= FTM_CSC_CHIE; //Enable channel compare interrupt
FTM0_C2SC &= ~FTM_CSC_MSB; //According to Pg 965 of the datasheet, this should not be needed as MSB is reset to 0 upon reset, but the channel interrupt fails to fire without it
FTM0_C2SC |= FTM_CSC_MSA; //Enable Compare mode
FTM0_C2SC |= FTM_CSC_CHIE; //Enable channel compare interrupt
FTM0_C3SC &= ~FTM_CSC_MSB; //According to Pg 965 of the datasheet, this should not be needed as MSB is reset to 0 upon reset, but the channel interrupt fails to fire without it
FTM0_C3SC |= FTM_CSC_MSA; //Enable Compare mode
FTM0_C3SC |= FTM_CSC_CHIE; //Enable channel compare interrupt
FTM0_C4SC &= ~FTM_CSC_MSB; //According to Pg 965 of the datasheet, this should not be needed as MSB is reset to 0 upon reset, but the channel interrupt fails to fire without it
FTM0_C4SC |= FTM_CSC_MSA; //Enable Compare mode
FTM0_C4SC |= FTM_CSC_CHIE; //Enable channel compare interrupt
FTM0_C5SC &= ~FTM_CSC_MSB; //According to Pg 965 of the datasheet, this should not be needed as MSB is reset to 0 upon reset, but the channel interrupt fails to fire without it
FTM0_C5SC |= FTM_CSC_MSA; //Enable Compare mode
FTM0_C5SC |= FTM_CSC_CHIE; //Enable channel compare interrupt
FTM0_C6SC &= ~FTM_CSC_MSB; //According to Pg 965 of the datasheet, this should not be needed as MSB is reset to 0 upon reset, but the channel interrupt fails to fire without it
FTM0_C6SC |= FTM_CSC_MSA; //Enable Compare mode
FTM0_C6SC |= FTM_CSC_CHIE; //Enable channel compare interrupt
FTM0_C7SC &= ~FTM_CSC_MSB; //According to Pg 965 of the datasheet, this should not be needed as MSB is reset to 0 upon reset, but the channel interrupt fails to fire without it
FTM0_C7SC |= FTM_CSC_MSA; //Enable Compare mode
FTM0_C7SC |= FTM_CSC_CHIE; //Enable channel compare interrupt
//Do the same, but on flex timer 3 (Used for channels 5-8)
FTM3_C0SC &= ~FTM_CSC_MSB; //According to Pg 965 of the K64 datasheet, this should not be needed as MSB is reset to 0 upon reset, but the channel interrupt fails to fire without it
FTM3_C0SC |= FTM_CSC_MSA; //Enable Compare mode
FTM3_C0SC |= FTM_CSC_CHIE; //Enable channel compare interrupt
FTM3_C1SC &= ~FTM_CSC_MSB; //According to Pg 965 of the K64 datasheet, this should not be needed as MSB is reset to 0 upon reset, but the channel interrupt fails to fire without it
FTM3_C1SC |= FTM_CSC_MSA; //Enable Compare mode
FTM3_C1SC |= FTM_CSC_CHIE; //Enable channel compare interrupt
FTM3_C2SC &= ~FTM_CSC_MSB; //According to Pg 965 of the K64 datasheet, this should not be needed as MSB is reset to 0 upon reset, but the channel interrupt fails to fire without it
FTM3_C2SC |= FTM_CSC_MSA; //Enable Compare mode
FTM3_C2SC |= FTM_CSC_CHIE; //Enable channel compare interrupt
FTM3_C3SC &= ~FTM_CSC_MSB; //According to Pg 965 of the K64 datasheet, this should not be needed as MSB is reset to 0 upon reset, but the channel interrupt fails to fire without it
FTM3_C3SC |= FTM_CSC_MSA; //Enable Compare mode
FTM3_C3SC |= FTM_CSC_CHIE; //Enable channel compare interrupt
FTM3_C4SC &= ~FTM_CSC_MSB; //According to Pg 965 of the K64 datasheet, this should not be needed as MSB is reset to 0 upon reset, but the channel interrupt fails to fire without it
FTM3_C4SC |= FTM_CSC_MSA; //Enable Compare mode
FTM3_C4SC |= FTM_CSC_CHIE; //Enable channel compare interrupt
FTM3_C5SC &= ~FTM_CSC_MSB; //According to Pg 965 of the K64 datasheet, this should not be needed as MSB is reset to 0 upon reset, but the channel interrupt fails to fire without it
FTM3_C5SC |= FTM_CSC_MSA; //Enable Compare mode
FTM3_C5SC |= FTM_CSC_CHIE; //Enable channel compare interrupt
FTM3_C6SC &= ~FTM_CSC_MSB; //According to Pg 965 of the K64 datasheet, this should not be needed as MSB is reset to 0 upon reset, but the channel interrupt fails to fire without it
FTM3_C6SC |= FTM_CSC_MSA; //Enable Compare mode
FTM3_C6SC |= FTM_CSC_CHIE; //Enable channel compare interrupt
FTM3_C7SC &= ~FTM_CSC_MSB; //According to Pg 965 of the K64 datasheet, this should not be needed as MSB is reset to 0 upon reset, but the channel interrupt fails to fire without it
FTM3_C7SC |= FTM_CSC_MSA; //Enable Compare mode
FTM3_C7SC |= FTM_CSC_CHIE; //Enable channel compare interrupt
// enable IRQ Interrupt
NVIC_ENABLE_IRQ(IRQ_FTM0);
NVIC_ENABLE_IRQ(IRQ_FTM1);
#elif defined(CORE_STM32)
//see https://github.com/rogerclarkmelbourne/Arduino_STM32/blob/754bc2969921f1ef262bd69e7faca80b19db7524/STM32F1/system/libmaple/include/libmaple/timer.h#L444
(TIMER1->regs).bas->PSC = (CYCLES_PER_MICROSECOND << 1) - 1; //2us resolution
(TIMER2->regs).bas->PSC = (CYCLES_PER_MICROSECOND << 1) - 1; //2us resolution
(TIMER3->regs).bas->PSC = (CYCLES_PER_MICROSECOND << 1) - 1; //2us resolution
// Alternative 2us resolution:
//TimerX.setPrescaleFactor(CYCLES_PER_MICROSECOND * 2U);
Timer2.setMode(TIMER_CH1, TIMER_OUTPUT_COMPARE);
Timer2.setMode(TIMER_CH2, TIMER_OUTPUT_COMPARE);
Timer2.setMode(TIMER_CH3, TIMER_OUTPUT_COMPARE);
Timer2.setMode(TIMER_CH4, TIMER_OUTPUT_COMPARE);
Timer3.setMode(TIMER_CH1, TIMER_OUTPUT_COMPARE);
Timer3.setMode(TIMER_CH2, TIMER_OUTPUT_COMPARE);
Timer3.setMode(TIMER_CH3, TIMER_OUTPUT_COMPARE);
Timer3.setMode(TIMER_CH4, TIMER_OUTPUT_COMPARE);
Timer2.attachInterrupt(1, fuelSchedule1Interrupt);
Timer2.attachInterrupt(2, fuelSchedule2Interrupt);
Timer2.attachInterrupt(3, fuelSchedule3Interrupt);
Timer2.attachInterrupt(4, fuelSchedule4Interrupt);
Timer3.attachInterrupt(1, ignitionSchedule1Interrupt);
Timer3.attachInterrupt(2, ignitionSchedule2Interrupt);
Timer3.attachInterrupt(3, ignitionSchedule3Interrupt);
Timer3.attachInterrupt(4, ignitionSchedule4Interrupt);
#endif
fuelSchedule1.Status = OFF;
fuelSchedule2.Status = OFF;
fuelSchedule3.Status = OFF;
fuelSchedule4.Status = OFF;
fuelSchedule5.Status = OFF;
fuelSchedule1.schedulesSet = 0;
fuelSchedule2.schedulesSet = 0;
fuelSchedule3.schedulesSet = 0;
fuelSchedule4.schedulesSet = 0;
fuelSchedule5.schedulesSet = 0;
ignitionSchedule1.Status = OFF;
ignitionSchedule2.Status = OFF;
ignitionSchedule3.Status = OFF;
ignitionSchedule4.Status = OFF;
ignitionSchedule5.Status = OFF;
ignitionSchedule1.schedulesSet = 0;
ignitionSchedule2.schedulesSet = 0;
ignitionSchedule3.schedulesSet = 0;
ignitionSchedule4.schedulesSet = 0;
ignitionSchedule5.schedulesSet = 0;
}
/*
These 8 function turn a schedule on, provides the time to start and the duration and gives it callback functions.
All 8 functions operate the same, just on different schedules
Args:
startCallback: The function to be called once the timeout is reached
timeout: The number of uS in the future that the startCallback 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 setFuelSchedule1(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)())
void setFuelSchedule1(unsigned long timeout, unsigned long duration)
{
if(fuelSchedule1.Status != RUNNING) //Check that we're not already part way through a schedule
{
//Callbacks no longer used, but retained for now:
//fuelSchedule1.StartCallback = startCallback;
//fuelSchedule1.EndCallback = endCallback;
fuelSchedule1.duration = duration;
//The following must be enclosed in the noInterupts block to avoid contention caused if the relevant interrupt fires before the state is fully set
noInterrupts();
fuelSchedule1.startCompare = FUEL1_COUNTER + uS_TO_TIMER_COMPARE_SLOW(timeout);
fuelSchedule1.endCompare = fuelSchedule1.startCompare + uS_TO_TIMER_COMPARE_SLOW(duration);
fuelSchedule1.Status = PENDING; //Turn this schedule on
fuelSchedule1.schedulesSet++; //Increment the number of times this schedule has been set
//Schedule 1 shares a timer with schedule 5
//if(channel5InjEnabled) { FUEL1_COMPARE = setQueue(timer3Aqueue, &fuelSchedule1, &fuelSchedule5, FUEL1_COUNTER); }
//else { timer3Aqueue[0] = &fuelSchedule1; timer3Aqueue[1] = &fuelSchedule1; timer3Aqueue[2] = &fuelSchedule1; timer3Aqueue[3] = &fuelSchedule1; FUEL1_COMPARE = fuelSchedule1.startCompare; }
//timer3Aqueue[0] = &fuelSchedule1; timer3Aqueue[1] = &fuelSchedule1; timer3Aqueue[2] = &fuelSchedule1; timer3Aqueue[3] = &fuelSchedule1;
FUEL1_COMPARE = fuelSchedule1.startCompare;
interrupts();
FUEL1_TIMER_ENABLE();
}
else
{
//If the schedule is already running, we can set the next schedule so it is ready to go
//This is required in cases of high rpm and high DC where there otherwise would not be enough time to set the schedule
fuelSchedule1.nextStartCompare = FUEL1_COUNTER + uS_TO_TIMER_COMPARE_SLOW(timeout);
fuelSchedule1.nextEndCompare = fuelSchedule1.nextStartCompare + uS_TO_TIMER_COMPARE_SLOW(duration);
fuelSchedule1.hasNextSchedule = true;
}
}
void setFuelSchedule2(unsigned long timeout, unsigned long duration)
{
if(fuelSchedule2.Status != RUNNING) //Check that we're not already part way through a schedule
{
//Callbacks no longer used, but retained for now:
//fuelSchedule2.StartCallback = startCallback;
//fuelSchedule2.EndCallback = endCallback;
fuelSchedule2.duration = duration;
//The following must be enclosed in the noInterupts block to avoid contention caused if the relevant interrupt fires before the state is fully set
noInterrupts();
fuelSchedule2.startCompare = FUEL2_COUNTER + uS_TO_TIMER_COMPARE_SLOW(timeout);
fuelSchedule2.endCompare = fuelSchedule2.startCompare + uS_TO_TIMER_COMPARE_SLOW(duration);
FUEL2_COMPARE = fuelSchedule2.startCompare; //Use the B compare unit of timer 3
fuelSchedule2.Status = PENDING; //Turn this schedule on
fuelSchedule2.schedulesSet++; //Increment the number of times this schedule has been set
interrupts();
FUEL2_TIMER_ENABLE();
}
else
{
//If the schedule is already running, we can set the next schedule so it is ready to go
//This is required in cases of high rpm and high DC where there otherwise would not be enough time to set the schedule
fuelSchedule2.nextStartCompare = FUEL2_COUNTER + uS_TO_TIMER_COMPARE_SLOW(timeout);
fuelSchedule2.nextEndCompare = fuelSchedule2.nextStartCompare + uS_TO_TIMER_COMPARE_SLOW(duration);
fuelSchedule2.hasNextSchedule = true;
}
}
//void setFuelSchedule3(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)())
void setFuelSchedule3(unsigned long timeout, unsigned long duration)
{
if(fuelSchedule3.Status != RUNNING)//Check that we're not already part way through a schedule
{
//Callbacks no longer used, but retained for now:
//fuelSchedule3.StartCallback = startCallback;
//fuelSchedule3.EndCallback = endCallback;
fuelSchedule3.duration = duration;
//The following must be enclosed in the noInterupts block to avoid contention caused if the relevant interrupt fires before the state is fully set
noInterrupts();
fuelSchedule3.startCompare = FUEL3_COUNTER + uS_TO_TIMER_COMPARE_SLOW(timeout);
fuelSchedule3.endCompare = fuelSchedule3.startCompare + uS_TO_TIMER_COMPARE_SLOW(duration);
FUEL3_COMPARE = fuelSchedule3.startCompare; //Use the C copmare unit of timer 3
fuelSchedule3.Status = PENDING; //Turn this schedule on
fuelSchedule3.schedulesSet++; //Increment the number of times this schedule has been set
interrupts();
FUEL3_TIMER_ENABLE();
}
else
{
//If the schedule is already running, we can set the next schedule so it is ready to go
//This is required in cases of high rpm and high DC where there otherwise would not be enough time to set the schedule
fuelSchedule3.nextStartCompare = FUEL3_COUNTER + uS_TO_TIMER_COMPARE_SLOW(timeout);
fuelSchedule3.nextEndCompare = fuelSchedule3.nextStartCompare + uS_TO_TIMER_COMPARE_SLOW(duration);
fuelSchedule3.hasNextSchedule = true;
}
}
//void setFuelSchedule4(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)())
void setFuelSchedule4(unsigned long timeout, unsigned long duration) //Uses timer 4 compare B
{
if(fuelSchedule4.Status != RUNNING) //Check that we're not already part way through a schedule
{
//Callbacks no longer used, but retained for now:
//fuelSchedule4.StartCallback = startCallback;
//fuelSchedule4.EndCallback = endCallback;
fuelSchedule4.duration = duration;
//The following must be enclosed in the noInterupts block to avoid contention caused if the relevant interrupt fires before the state is fully set
noInterrupts();
fuelSchedule4.startCompare = FUEL4_COUNTER + uS_TO_TIMER_COMPARE_SLOW(timeout);
fuelSchedule4.endCompare = fuelSchedule4.startCompare + uS_TO_TIMER_COMPARE_SLOW(duration);
FUEL4_COMPARE = fuelSchedule4.startCompare; //Use the C copmare unit of timer 3
fuelSchedule4.Status = PENDING; //Turn this schedule on
fuelSchedule4.schedulesSet++; //Increment the number of times this schedule has been set
interrupts();
FUEL4_TIMER_ENABLE();
}
else
{
//If the schedule is already running, we can set the next schedule so it is ready to go
//This is required in cases of high rpm and high DC where there otherwise would not be enough time to set the schedule
fuelSchedule4.nextStartCompare = FUEL4_COUNTER + uS_TO_TIMER_COMPARE_SLOW(timeout);
fuelSchedule4.nextEndCompare = fuelSchedule4.nextStartCompare + uS_TO_TIMER_COMPARE_SLOW(duration);
fuelSchedule4.hasNextSchedule = true;
}
}
void setFuelSchedule5(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)())
{
if(fuelSchedule5.Status != RUNNING) //Check that we're not already part way through a schedule
{
fuelSchedule5.StartCallback = startCallback; //Name the start callback function
fuelSchedule5.EndCallback = endCallback; //Name the end callback function
fuelSchedule5.duration = duration;
/*
* The following must be enclosed in the noIntterupts block to avoid contention caused if the relevant interrupts fires before the state is fully set
*/
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__)
noInterrupts();
fuelSchedule5.startCompare = TCNT3 + (timeout >> 4); //As above, but with bit shift instead of / 16
fuelSchedule5.endCompare = fuelSchedule5.startCompare + (duration >> 4);
fuelSchedule5.Status = PENDING; //Turn this schedule on
fuelSchedule5.schedulesSet++; //Increment the number of times this schedule has been set
OCR3A = setQueue(timer3Aqueue, &fuelSchedule1, &fuelSchedule5, TCNT3); //Schedule 1 shares a timer with schedule 5
interrupts();
TIMSK3 |= (1 << OCIE3A); //Turn on the A compare unit (ie turn on the interrupt)
#endif
}
else
{
//If the schedule is already running, we can set the next schedule so it is ready to go
//This is required in cases of high rpm and high DC where there otherwise would not be enough time to set the schedule
fuelSchedule5.nextStartCompare = FUEL5_COUNTER + uS_TO_TIMER_COMPARE_SLOW(timeout);
fuelSchedule5.nextEndCompare = fuelSchedule5.nextStartCompare + uS_TO_TIMER_COMPARE_SLOW(duration);
fuelSchedule5.hasNextSchedule = true;
}
}
//Ignition schedulers use Timer 5
void setIgnitionSchedule1(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)())
{
if(ignitionSchedule1.Status != RUNNING) //Check that we're not already part way through a schedule
{
ignitionSchedule1.StartCallback = startCallback; //Name the start callback function
ignitionSchedule1.EndCallback = endCallback; //Name the start callback function
ignitionSchedule1.duration = duration;
//Need to check that the timeout doesn't exceed the overflow
uint16_t timeout_timer_compare;
if (timeout > MAX_TIMER_PERIOD) { timeout_timer_compare = uS_TO_TIMER_COMPARE( (MAX_TIMER_PERIOD - 1) ); } // If the timeout is >4x (Each tick represents 4uS) the maximum allowed value of unsigned int (65535), the timer compare value will overflow when appliedcausing erratic behaviour such as erroneous sparking.
else { timeout_timer_compare = uS_TO_TIMER_COMPARE(timeout); } //Normal case
noInterrupts();
ignitionSchedule1.startCompare = IGN1_COUNTER + timeout_timer_compare; //As there is a tick every 4uS, there are timeout/4 ticks until the interrupt should be triggered ( >>2 divides by 4)
ignitionSchedule1.endCompare = ignitionSchedule1.startCompare + uS_TO_TIMER_COMPARE(duration);
IGN1_COMPARE = ignitionSchedule1.startCompare;
ignitionSchedule1.Status = PENDING; //Turn this schedule on
ignitionSchedule1.schedulesSet++;
interrupts();
IGN1_TIMER_ENABLE();
}
}
void setIgnitionSchedule2(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)())
{
if(ignitionSchedule2.Status != RUNNING) //Check that we're not already part way through a schedule
{
ignitionSchedule2.StartCallback = startCallback; //Name the start callback function
ignitionSchedule2.EndCallback = endCallback; //Name the start callback function
ignitionSchedule2.duration = duration;
//Need to check that the timeout doesn't exceed the overflow
uint16_t timeout_timer_compare;
if (timeout > MAX_TIMER_PERIOD) { timeout_timer_compare = uS_TO_TIMER_COMPARE( (MAX_TIMER_PERIOD - 1) ); } // If the timeout is >4x (Each tick represents 4uS) the maximum allowed value of unsigned int (65535), the timer compare value will overflow when appliedcausing erratic behaviour such as erroneous sparking.
else { timeout_timer_compare = uS_TO_TIMER_COMPARE(timeout); } //Normal case
noInterrupts();
ignitionSchedule2.startCompare = IGN2_COUNTER + timeout_timer_compare; //As there is a tick every 4uS, there are timeout/4 ticks until the interrupt should be triggered ( >>2 divides by 4)
ignitionSchedule2.endCompare = ignitionSchedule2.startCompare + uS_TO_TIMER_COMPARE(duration);
IGN2_COMPARE = ignitionSchedule2.startCompare;
ignitionSchedule2.Status = PENDING; //Turn this schedule on
ignitionSchedule2.schedulesSet++;
interrupts();
IGN2_TIMER_ENABLE();
}
}
void setIgnitionSchedule3(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)())
{
if(ignitionSchedule3.Status != RUNNING) //Check that we're not already part way through a schedule
{
ignitionSchedule3.StartCallback = startCallback; //Name the start callback function
ignitionSchedule3.EndCallback = endCallback; //Name the start callback function
ignitionSchedule3.duration = duration;
//Need to check that the timeout doesn't exceed the overflow
uint16_t timeout_timer_compare;
if (timeout > MAX_TIMER_PERIOD) { timeout_timer_compare = uS_TO_TIMER_COMPARE( (MAX_TIMER_PERIOD - 1) ); } // If the timeout is >4x (Each tick represents 4uS) the maximum allowed value of unsigned int (65535), the timer compare value will overflow when appliedcausing erratic behaviour such as erroneous sparking.
else { timeout_timer_compare = uS_TO_TIMER_COMPARE(timeout); } //Normal case
noInterrupts();
ignitionSchedule3.startCompare = IGN3_COUNTER + timeout_timer_compare; //As there is a tick every 4uS, there are timeout/4 ticks until the interrupt should be triggered ( >>2 divides by 4)
ignitionSchedule3.endCompare = ignitionSchedule3.startCompare + uS_TO_TIMER_COMPARE(duration);
IGN3_COMPARE = ignitionSchedule3.startCompare;
ignitionSchedule3.Status = PENDING; //Turn this schedule on
ignitionSchedule3.schedulesSet++;
interrupts();
IGN3_TIMER_ENABLE();
}
}
void setIgnitionSchedule4(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)())
{
if(ignitionSchedule4.Status != RUNNING) //Check that we're not already part way through a schedule
{
ignitionSchedule4.StartCallback = startCallback; //Name the start callback function
ignitionSchedule4.EndCallback = endCallback; //Name the start callback function
ignitionSchedule4.duration = duration;
//Need to check that the timeout doesn't exceed the overflow
uint16_t timeout_timer_compare;
if (timeout > MAX_TIMER_PERIOD) { timeout_timer_compare = uS_TO_TIMER_COMPARE_SLOW( (MAX_TIMER_PERIOD - 1) ); } // If the timeout is >4x (Each tick represents 4uS) the maximum allowed value of unsigned int (65535), the timer compare value will overflow when appliedcausing erratic behaviour such as erroneous sparking.
else { timeout_timer_compare = uS_TO_TIMER_COMPARE_SLOW(timeout); } //Normal case
noInterrupts();
ignitionSchedule4.startCompare = IGN4_COUNTER + timeout_timer_compare;
ignitionSchedule4.endCompare = ignitionSchedule4.startCompare + uS_TO_TIMER_COMPARE_SLOW(duration);
IGN4_COMPARE = ignitionSchedule4.startCompare;
ignitionSchedule4.Status = PENDING; //Turn this schedule on
ignitionSchedule4.schedulesSet++;
interrupts();
IGN4_TIMER_ENABLE();
}
}
void setIgnitionSchedule5(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)())
{
if(ignitionSchedule5.Status != RUNNING)//Check that we're not already part way through a schedule
{
ignitionSchedule5.StartCallback = startCallback; //Name the start callback function
ignitionSchedule5.EndCallback = endCallback; //Name the start callback function
ignitionSchedule5.duration = duration;
//Need to check that the timeout doesn't exceed the overflow
uint16_t timeout_timer_compare;
if (timeout > MAX_TIMER_PERIOD) { timeout_timer_compare = uS_TO_TIMER_COMPARE_SLOW( (MAX_TIMER_PERIOD - 1) ); } // If the timeout is >4x (Each tick represents 4uS) the maximum allowed value of unsigned int (65535), the timer compare value will overflow when appliedcausing erratic behaviour such as erroneous sparking.
else { timeout_timer_compare = uS_TO_TIMER_COMPARE_SLOW(timeout); } //Normal case
noInterrupts();
ignitionSchedule5.startCompare = IGN5_COUNTER + timeout_timer_compare;
ignitionSchedule5.endCompare = ignitionSchedule5.startCompare + uS_TO_TIMER_COMPARE_SLOW(duration);
IGN5_COMPARE = ignitionSchedule5.startCompare;
ignitionSchedule5.Status = PENDING; //Turn this schedule on
ignitionSchedule5.schedulesSet++;
interrupts();
IGN5_TIMER_ENABLE();
}
}
/*******************************************************************************************************************************************************************************************************/
//This function (All 8 ISR functions that are below) 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
//Timer3A (fuel schedule 1) Compare Vector
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER3_COMPA_vect, ISR_NOBLOCK) //fuelSchedules 1 and 5
#elif defined (CORE_TEENSY) || defined(CORE_STM32)
static inline void fuelSchedule1Interrupt() //Most ARM chips can simply call a function
#endif
{
if (fuelSchedule1.Status == PENDING) //Check to see if this schedule is turn on
{
//To use timer queue, change fuelShedule1 to timer3Aqueue[0];
if (configPage1.injLayout == INJ_SEMISEQUENTIAL) { openInjector1and4(); }
else { openInjector1(); }
fuelSchedule1.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
FUEL1_COMPARE = fuelSchedule1.endCompare;
}
else if (fuelSchedule1.Status == RUNNING)
{
//timer3Aqueue[0]->EndCallback();
if (configPage1.injLayout == INJ_SEMISEQUENTIAL) { closeInjector1and4(); }
else { closeInjector1(); }
fuelSchedule1.Status = OFF; //Turn off the schedule
fuelSchedule1.schedulesSet = 0;
//FUEL1_COMPARE = fuelSchedule1.endCompare;
//If there is a next schedule queued up, activate it
if(fuelSchedule1.hasNextSchedule == true)
{
FUEL1_COMPARE = fuelSchedule1.nextStartCompare;
fuelSchedule1.endCompare = fuelSchedule1.nextEndCompare;
fuelSchedule1.Status = PENDING;
fuelSchedule1.schedulesSet = 1;
fuelSchedule1.hasNextSchedule = false;
}
else { FUEL1_TIMER_DISABLE(); }
}
else if (fuelSchedule1.Status == OFF) { FUEL1_TIMER_DISABLE(); } //Safety check. Turn off this output compare unit and return without performing any action
}
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER3_COMPB_vect, ISR_NOBLOCK) //fuelSchedule2
#elif defined (CORE_TEENSY) || defined(CORE_STM32)
static inline void fuelSchedule2Interrupt() //Most ARM chips can simply call a function
#endif
{
if (fuelSchedule2.Status == PENDING) //Check to see if this schedule is turn on
{
//fuelSchedule2.StartCallback();
if (configPage1.injLayout == INJ_SEMISEQUENTIAL) { openInjector2and3(); }
else { openInjector2(); }
fuelSchedule2.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
FUEL2_COMPARE = fuelSchedule2.endCompare;
}
else if (fuelSchedule2.Status == RUNNING)
{
//fuelSchedule2.EndCallback();
if (configPage1.injLayout == INJ_SEMISEQUENTIAL) { closeInjector2and3(); }
else { closeInjector2(); }
fuelSchedule2.Status = OFF; //Turn off the schedule
fuelSchedule2.schedulesSet = 0;
//If there is a next schedule queued up, activate it
if(fuelSchedule2.hasNextSchedule == true)
{
FUEL2_COMPARE = fuelSchedule2.nextStartCompare;
fuelSchedule2.endCompare = fuelSchedule2.nextEndCompare;
fuelSchedule2.Status = PENDING;
fuelSchedule2.schedulesSet = 1;
fuelSchedule2.hasNextSchedule = false;
}
else { FUEL2_TIMER_DISABLE(); }
}
}
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER3_COMPC_vect, ISR_NOBLOCK) //fuelSchedule3
#elif defined (CORE_TEENSY) || defined(CORE_STM32)
static inline void fuelSchedule3Interrupt() //Most ARM chips can simply call a function
#endif
{
if (fuelSchedule3.Status == PENDING) //Check to see if this schedule is turn on
{
//fuelSchedule3.StartCallback();
//Hack for 5 cylinder
if(channel5InjEnabled) { openInjector3and5(); }
else { openInjector3(); }
fuelSchedule3.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
FUEL3_COMPARE = fuelSchedule3.endCompare;
}
else if (fuelSchedule3.Status == RUNNING)
{
//fuelSchedule3.EndCallback();
//Hack for 5 cylinder
if(channel5InjEnabled) { closeInjector3and5(); }
else { closeInjector3and5(); }
fuelSchedule3.Status = OFF; //Turn off the schedule
fuelSchedule3.schedulesSet = 0;
//If there is a next schedule queued up, activate it
if(fuelSchedule3.hasNextSchedule == true)
{
FUEL3_COMPARE = fuelSchedule3.nextStartCompare;
fuelSchedule3.endCompare = fuelSchedule3.nextEndCompare;
fuelSchedule3.Status = PENDING;
fuelSchedule3.schedulesSet = 1;
fuelSchedule3.hasNextSchedule = false;
}
else { FUEL3_TIMER_DISABLE(); }
}
}
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER4_COMPB_vect, ISR_NOBLOCK) //fuelSchedule4
#elif defined (CORE_TEENSY) || defined(CORE_STM32)
static inline void fuelSchedule4Interrupt() //Most ARM chips can simply call a function
#endif
{
if (fuelSchedule4.Status == PENDING) //Check to see if this schedule is turn on
{
//fuelSchedule4.StartCallback();
openInjector4();
fuelSchedule4.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
FUEL4_COMPARE = fuelSchedule4.endCompare;
}
else if (fuelSchedule4.Status == RUNNING)
{
//fuelSchedule4.EndCallback();
closeInjector4();
fuelSchedule4.Status = OFF; //Turn off the schedule
fuelSchedule4.schedulesSet = 0;
//If there is a next schedule queued up, activate it
if(fuelSchedule4.hasNextSchedule == true)
{
FUEL4_COMPARE = fuelSchedule4.nextStartCompare;
fuelSchedule4.endCompare = fuelSchedule4.nextEndCompare;
fuelSchedule4.Status = PENDING;
fuelSchedule4.schedulesSet = 1;
fuelSchedule4.hasNextSchedule = false;
}
else { FUEL4_TIMER_DISABLE(); }
}
}
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER5_COMPA_vect) //ignitionSchedule1
#elif defined (CORE_TEENSY) || defined(CORE_STM32)
static inline void ignitionSchedule1Interrupt() //Most ARM chips can simply call a function
#endif
{
if (ignitionSchedule1.Status == PENDING) //Check to see if this schedule is turn on
{
ignitionSchedule1.StartCallback();
ignitionSchedule1.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
ignitionSchedule1.startTime = micros();
IGN1_COMPARE = ignitionSchedule1.endCompare;
}
else if (ignitionSchedule1.Status == RUNNING)
{
ignitionSchedule1.EndCallback();
ignitionSchedule1.Status = OFF; //Turn off the schedule
ignitionSchedule1.schedulesSet = 0;
ignitionCount += 1; //Increment the igintion counter
IGN1_TIMER_DISABLE();
}
}
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER5_COMPB_vect) //ignitionSchedule2
#elif defined (CORE_TEENSY) || defined(CORE_STM32)
static inline void ignitionSchedule2Interrupt() //Most ARM chips can simply call a function
#endif
{
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)
ignitionSchedule2.startTime = micros();
IGN2_COMPARE = ignitionSchedule2.endCompare; //OCR5B = TCNT5 + (ignitionSchedule2.duration >> 2);
}
else if (ignitionSchedule2.Status == RUNNING)
{
ignitionSchedule2.Status = OFF; //Turn off the schedule
ignitionSchedule2.EndCallback();
ignitionSchedule2.schedulesSet = 0;
ignitionCount += 1; //Increment the igintion counter
IGN2_TIMER_DISABLE();
}
}
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER5_COMPC_vect) //ignitionSchedule3
#elif defined (CORE_TEENSY) || defined(CORE_STM32)
static inline void ignitionSchedule3Interrupt() //Most ARM chips can simply call a function
#endif
{
if (ignitionSchedule3.Status == PENDING) //Check to see if this schedule is turn on
{
ignitionSchedule3.StartCallback();
ignitionSchedule3.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
ignitionSchedule3.startTime = micros();
IGN3_COMPARE = ignitionSchedule3.endCompare; //OCR5C = TCNT5 + (ignitionSchedule3.duration >> 2);
}
else if (ignitionSchedule3.Status == RUNNING)
{
ignitionSchedule3.Status = OFF; //Turn off the schedule
ignitionSchedule3.EndCallback();
ignitionSchedule3.schedulesSet = 0;
ignitionCount += 1; //Increment the igintion counter
IGN3_TIMER_DISABLE();
}
}
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER4_COMPA_vect) //ignitionSchedule4
#elif defined (CORE_TEENSY) || defined(CORE_STM32)
static inline void ignitionSchedule4Interrupt() //Most ARM chips can simply call a function
#endif
{
if (ignitionSchedule4.Status == PENDING) //Check to see if this schedule is turn on
{
ignitionSchedule4.StartCallback();
ignitionSchedule4.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
ignitionSchedule4.startTime = micros();
IGN4_COMPARE = ignitionSchedule4.endCompare;
}
else if (ignitionSchedule4.Status == RUNNING)
{
ignitionSchedule4.Status = OFF; //Turn off the schedule
ignitionSchedule4.EndCallback();
ignitionSchedule4.schedulesSet = 0;
ignitionCount += 1; //Increment the igintion counter
IGN4_TIMER_DISABLE();
}
}
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this
ISR(TIMER1_COMPC_vect) //ignitionSchedule5
#elif defined (CORE_TEENSY) || defined(CORE_STM32)
static inline void ignitionSchedule5Interrupt() //Most ARM chips can simply call a function
#endif
{
if (ignitionSchedule5.Status == PENDING) //Check to see if this schedule is turn on
{
ignitionSchedule5.StartCallback();
ignitionSchedule5.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
ignitionSchedule5.startTime = micros();
IGN5_COMPARE = ignitionSchedule5.endCompare;
}
else if (ignitionSchedule5.Status == RUNNING)
{
ignitionSchedule5.Status = OFF; //Turn off the schedule
ignitionSchedule5.EndCallback();
ignitionSchedule5.schedulesSet = 0;
ignitionCount += 1; //Increment the igintion counter
IGN5_TIMER_DISABLE();
}
}
#if defined(CORE_TEENSY)
void ftm0_isr(void)
{
//Use separate variables for each test to ensure conversion to bool
bool interrupt1 = (FTM0_C0SC & FTM_CSC_CHF);
bool interrupt2 = (FTM0_C1SC & FTM_CSC_CHF);
bool interrupt3 = (FTM0_C2SC & FTM_CSC_CHF);
bool interrupt4 = (FTM0_C3SC & FTM_CSC_CHF);
bool interrupt5 = (FTM0_C4SC & FTM_CSC_CHF);
bool interrupt6 = (FTM0_C5SC & FTM_CSC_CHF);
bool interrupt7 = (FTM0_C6SC & FTM_CSC_CHF);
bool interrupt8 = (FTM0_C7SC & FTM_CSC_CHF);
if(interrupt1) { FTM0_C0SC &= ~FTM_CSC_CHF; fuelSchedule1Interrupt(); }
else if(interrupt2) { FTM0_C1SC &= ~FTM_CSC_CHF; fuelSchedule2Interrupt(); }
else if(interrupt3) { FTM0_C2SC &= ~FTM_CSC_CHF; fuelSchedule3Interrupt(); }
else if(interrupt4) { FTM0_C3SC &= ~FTM_CSC_CHF; fuelSchedule4Interrupt(); }
else if(interrupt5) { FTM0_C4SC &= ~FTM_CSC_CHF; ignitionSchedule1Interrupt(); }
else if(interrupt6) { FTM0_C5SC &= ~FTM_CSC_CHF; ignitionSchedule2Interrupt(); }
else if(interrupt7) { FTM0_C6SC &= ~FTM_CSC_CHF; ignitionSchedule3Interrupt(); }
else if(interrupt8) { FTM0_C7SC &= ~FTM_CSC_CHF; ignitionSchedule4Interrupt(); }
}
#endif