248 lines
9.3 KiB
C
248 lines
9.3 KiB
C
/** @file
|
|
Injector and Ignition (on/off) scheduling (structs).
|
|
|
|
This scheduler is designed to maintain 2 schedules for use by the fuel and ignition systems.
|
|
It functions by waiting for the overflow vectors from each of the timers in use to overflow, which triggers an interrupt.
|
|
|
|
## Technical
|
|
|
|
Currently I am prescaling the 16-bit timers to 256 for injection and 64 for ignition.
|
|
This means that the counter increments every 16us (injection) / 4uS (ignition) and will overflow every 1048576uS.
|
|
|
|
Max Period = (Prescale)*(1/Frequency)*(2^17)
|
|
|
|
For more details see https://playground.arduino.cc/Code/Timer1/ (OLD: http://playground.arduino.cc/code/timer1 ).
|
|
This means that the precision of the scheduler is:
|
|
|
|
- 16uS (+/- 8uS of target) for fuel
|
|
- 4uS (+/- 2uS) for ignition
|
|
|
|
## Features
|
|
|
|
This differs from most other schedulers in that its calls are non-recurring (ie when you schedule an event at a certain time and once it has occurred,
|
|
it will not reoccur unless you explicitly ask/re-register for it).
|
|
Each timer can have only 1 callback associated with it at any given time. If you call the setCallback function a 2nd time,
|
|
the original schedule will be overwritten and not occur.
|
|
|
|
## Timer identification
|
|
|
|
Arduino timers usage for injection and ignition schedules:
|
|
- timer3 is used for schedule 1(?) (fuel 1,2,3,4 ign 7,8)
|
|
- timer4 is used for schedule 2(?) (fuel 5,6 ign 4,5,6)
|
|
- timer5 is used ... (fuel 7,8, ign 1,2,3)
|
|
|
|
Timers 3,4 and 5 are 16-bit timers (ie count to 65536).
|
|
See page 136 of the processors datasheet: http://www.atmel.com/Images/doc2549.pdf .
|
|
|
|
256 prescale gives tick every 16uS.
|
|
256 prescale gives overflow every 1048576uS (This means maximum wait time is 1.0485 seconds).
|
|
|
|
*/
|
|
#ifndef SCHEDULER_H
|
|
#define SCHEDULER_H
|
|
|
|
#include "globals.h"
|
|
|
|
#define USE_IGN_REFRESH
|
|
#define IGNITION_REFRESH_THRESHOLD 30 //Time in uS that the refresh functions will check to ensure there is enough time before changing the end compare
|
|
|
|
#define DWELL_AVERAGE_ALPHA 30
|
|
#define DWELL_AVERAGE(input) (((long)input * (256 - DWELL_AVERAGE_ALPHA) + ((long)currentStatus.actualDwell * DWELL_AVERAGE_ALPHA))) >> 8
|
|
//#define DWELL_AVERAGE(input) (currentStatus.dwell) //Can be use to disable the above for testing
|
|
|
|
void initialiseSchedulers(void);
|
|
void beginInjectorPriming(void);
|
|
|
|
void disablePendingFuelSchedule(byte channel);
|
|
void disablePendingIgnSchedule(byte channel);
|
|
|
|
void refreshIgnitionSchedule1(unsigned long timeToEnd);
|
|
|
|
//The ARM cores use separate functions for their ISRs
|
|
#if defined(ARDUINO_ARCH_STM32) || defined(CORE_TEENSY)
|
|
void fuelSchedule1Interrupt(void);
|
|
void fuelSchedule2Interrupt(void);
|
|
void fuelSchedule3Interrupt(void);
|
|
void fuelSchedule4Interrupt(void);
|
|
#if (INJ_CHANNELS >= 5)
|
|
void fuelSchedule5Interrupt(void);
|
|
#endif
|
|
#if (INJ_CHANNELS >= 6)
|
|
void fuelSchedule6Interrupt(void);
|
|
#endif
|
|
#if (INJ_CHANNELS >= 7)
|
|
void fuelSchedule7Interrupt(void);
|
|
#endif
|
|
#if (INJ_CHANNELS >= 8)
|
|
void fuelSchedule8Interrupt(void);
|
|
#endif
|
|
#if (IGN_CHANNELS >= 1)
|
|
void ignitionSchedule1Interrupt(void);
|
|
#endif
|
|
#if (IGN_CHANNELS >= 2)
|
|
void ignitionSchedule2Interrupt(void);
|
|
#endif
|
|
#if (IGN_CHANNELS >= 3)
|
|
void ignitionSchedule3Interrupt(void);
|
|
#endif
|
|
#if (IGN_CHANNELS >= 4)
|
|
void ignitionSchedule4Interrupt(void);
|
|
#endif
|
|
#if (IGN_CHANNELS >= 5)
|
|
void ignitionSchedule5Interrupt(void);
|
|
#endif
|
|
#if (IGN_CHANNELS >= 6)
|
|
void ignitionSchedule6Interrupt(void);
|
|
#endif
|
|
#if (IGN_CHANNELS >= 7)
|
|
void ignitionSchedule7Interrupt(void);
|
|
#endif
|
|
#if (IGN_CHANNELS >= 8)
|
|
void ignitionSchedule8Interrupt(void);
|
|
#endif
|
|
#endif
|
|
/** Schedule statuses.
|
|
* - OFF - Schedule turned off and there is no scheduled plan
|
|
* - PENDING - There's a scheduled plan, but is has not started to run yet
|
|
* - STAGED - (???, Not used)
|
|
* - RUNNING - Schedule is currently running
|
|
*/
|
|
enum ScheduleStatus {OFF, PENDING, STAGED, RUNNING}; //The statuses that a schedule can have
|
|
|
|
/** Ignition schedule.
|
|
*/
|
|
struct IgnitionSchedule {
|
|
|
|
// Deduce the real types of the counter and compare registers.
|
|
// COMPARE_TYPE is NOT the same - it's just an integer type wide enough to
|
|
// store 16-bit counter/compare calculation results.
|
|
using counter_t = decltype(IGN1_COUNTER);
|
|
using compare_t = decltype(IGN1_COMPARE);
|
|
|
|
IgnitionSchedule( counter_t &counter, compare_t &compare,
|
|
void (&_pTimerDisable)(), void (&_pTimerEnable)())
|
|
: counter(counter)
|
|
, compare(compare)
|
|
, pTimerDisable(_pTimerDisable)
|
|
, pTimerEnable(_pTimerEnable)
|
|
{
|
|
}
|
|
|
|
volatile unsigned long duration;///< Scheduled duration (uS ?)
|
|
volatile ScheduleStatus Status; ///< Schedule status: OFF, PENDING, STAGED, RUNNING
|
|
void (*pStartCallback)(void); ///< Start Callback function for schedule
|
|
void (*pEndCallback)(void); ///< End Callback function for schedule
|
|
volatile unsigned long startTime; /**< The system time (in uS) that the schedule started, used by the overdwell protection in timers.ino */
|
|
volatile COMPARE_TYPE startCompare; ///< The counter value of the timer when this will start
|
|
volatile COMPARE_TYPE endCompare; ///< The counter value of the timer when this will end
|
|
|
|
COMPARE_TYPE nextStartCompare; ///< Planned start of next schedule (when current schedule is RUNNING)
|
|
COMPARE_TYPE nextEndCompare; ///< Planned end of next schedule (when current schedule is RUNNING)
|
|
volatile bool hasNextSchedule = false; ///< Enable flag for planned next schedule (when current schedule is RUNNING)
|
|
volatile bool endScheduleSetByDecoder = false;
|
|
|
|
counter_t &counter; // Reference to the counter register. E.g. TCNT3
|
|
compare_t &compare; // Reference to the compare register. E.g. OCR3A
|
|
void (&pTimerDisable)(); // Reference to the timer disable function
|
|
void (&pTimerEnable)(); // Reference to the timer enable function
|
|
};
|
|
|
|
void _setIgnitionScheduleRunning(IgnitionSchedule &schedule, unsigned long timeout, unsigned long duration);
|
|
void _setIgnitionScheduleNext(IgnitionSchedule &schedule, unsigned long timeout, unsigned long duration);
|
|
|
|
inline __attribute__((always_inline)) void setIgnitionSchedule(IgnitionSchedule &schedule, unsigned long timeout, unsigned long duration) {
|
|
if(schedule.Status != RUNNING) { //Check that we're not already part way through a schedule
|
|
_setIgnitionScheduleRunning(schedule, timeout, duration);
|
|
}
|
|
// Check whether timeout exceeds the maximum future time. This can potentially occur on sequential setups when below ~115rpm
|
|
else if(timeout < MAX_TIMER_PERIOD){
|
|
_setIgnitionScheduleNext(schedule, timeout, duration);
|
|
}
|
|
}
|
|
|
|
/** Fuel injection schedule.
|
|
* Fuel schedules don't use the callback pointers, or the startTime/endScheduleSetByDecoder variables.
|
|
* They are removed in this struct to save RAM.
|
|
*/
|
|
struct FuelSchedule {
|
|
|
|
// Deduce the real types of the counter and compare registers.
|
|
// COMPARE_TYPE is NOT the same - it's just an integer type wide enough to
|
|
// store 16-bit counter/compare calculation results.
|
|
using counter_t = decltype(FUEL1_COUNTER);
|
|
using compare_t = decltype(FUEL1_COMPARE);
|
|
|
|
FuelSchedule( counter_t &counter, compare_t &compare,
|
|
void (&_pTimerDisable)(), void (&_pTimerEnable)())
|
|
: counter(counter)
|
|
, compare(compare)
|
|
, pTimerDisable(_pTimerDisable)
|
|
, pTimerEnable(_pTimerEnable)
|
|
{
|
|
}
|
|
|
|
volatile unsigned long duration;///< Scheduled duration (uS ?)
|
|
volatile ScheduleStatus Status; ///< Schedule status: OFF, PENDING, STAGED, RUNNING
|
|
volatile COMPARE_TYPE startCompare; ///< The counter value of the timer when this will start
|
|
volatile COMPARE_TYPE endCompare; ///< The counter value of the timer when this will end
|
|
void (*pStartFunction)(void);
|
|
void (*pEndFunction)(void);
|
|
COMPARE_TYPE nextStartCompare;
|
|
COMPARE_TYPE nextEndCompare;
|
|
volatile bool hasNextSchedule = false;
|
|
|
|
counter_t &counter; // Reference to the counter register. E.g. TCNT3
|
|
compare_t &compare; // Reference to the compare register. E.g. OCR3A
|
|
void (&pTimerDisable)(); // Reference to the timer disable function
|
|
void (&pTimerEnable)(); // Reference to the timer enable function
|
|
};
|
|
|
|
void _setFuelScheduleRunning(FuelSchedule &schedule, unsigned long timeout, unsigned long duration);
|
|
void _setFuelScheduleNext(FuelSchedule &schedule, unsigned long timeout, unsigned long duration);
|
|
|
|
inline __attribute__((always_inline)) void setFuelSchedule(FuelSchedule &schedule, unsigned long timeout, unsigned long duration)
|
|
{
|
|
if(schedule.Status != RUNNING)
|
|
{ //Check that we're not already part way through a schedule
|
|
_setFuelScheduleRunning(schedule, timeout, duration);
|
|
}
|
|
else if(timeout < MAX_TIMER_PERIOD)
|
|
{
|
|
_setFuelScheduleNext(schedule, timeout, duration);
|
|
}
|
|
}
|
|
|
|
extern FuelSchedule fuelSchedule1;
|
|
extern FuelSchedule fuelSchedule2;
|
|
extern FuelSchedule fuelSchedule3;
|
|
extern FuelSchedule fuelSchedule4;
|
|
#if INJ_CHANNELS >= 5
|
|
extern FuelSchedule fuelSchedule5;
|
|
#endif
|
|
#if INJ_CHANNELS >= 6
|
|
extern FuelSchedule fuelSchedule6;
|
|
#endif
|
|
#if INJ_CHANNELS >= 7
|
|
extern FuelSchedule fuelSchedule7;
|
|
#endif
|
|
#if INJ_CHANNELS >= 8
|
|
extern FuelSchedule fuelSchedule8;
|
|
#endif
|
|
|
|
extern IgnitionSchedule ignitionSchedule1;
|
|
extern IgnitionSchedule ignitionSchedule2;
|
|
extern IgnitionSchedule ignitionSchedule3;
|
|
extern IgnitionSchedule ignitionSchedule4;
|
|
extern IgnitionSchedule ignitionSchedule5;
|
|
#if IGN_CHANNELS >= 6
|
|
extern IgnitionSchedule ignitionSchedule6;
|
|
#endif
|
|
#if IGN_CHANNELS >= 7
|
|
extern IgnitionSchedule ignitionSchedule7;
|
|
#endif
|
|
#if IGN_CHANNELS >= 8
|
|
extern IgnitionSchedule ignitionSchedule8;
|
|
#endif
|
|
|
|
#endif // SCHEDULER_H
|