2015-05-28 16:49:44 -07:00
/*
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
*/
2014-05-06 04:07:49 -07:00
/*
Timers are used for having actions performed repeatedly at a fixed interval ( Eg every 100 ms )
They should not be confused with Schedulers , which are for performing an action once at a given point of time in the future
2014-01-30 20:02:32 -08:00
2014-05-06 04:07:49 -07:00
Timers are typically low resolution ( Compared to Schedulers ) , with maximum frequency currently being approximately every 10 ms
*/
2015-02-14 09:04:00 -08:00
# include "timers.h"
# include "globals.h"
2016-06-24 01:02:53 -07:00
# include "sensors.h"
2014-01-30 20:02:32 -08:00
2017-01-10 22:31:23 -08:00
# if defined(CORE_AVR)
# include <avr/wdt.h>
# endif
2017-03-22 23:19:18 -07:00
void initialiseTimers ( )
{
2016-11-20 21:45:21 -08:00
# if defined(CORE_AVR) //AVR chips use the ISR for this
2017-03-22 23:19:18 -07:00
//Configure Timer2 for our low-freq interrupt code.
2014-02-06 01:48:19 -08:00
TCCR2B = 0x00 ; //Disbale Timer2 while we set it up
2015-04-13 21:41:44 -07:00
TCNT2 = 131 ; //Preload timer2 with 131 cycles, leaving 125 till overflow. As the timer runs at 125Khz, this causes overflow to occur at 1Khz = 1ms
2014-02-06 01:48:19 -08:00
TIFR2 = 0x00 ; //Timer2 INT Flag Reg: Clear Timer Overflow Flag
TIMSK2 = 0x01 ; //Timer2 Set Overflow Interrupt enabled.
TCCR2A = 0x00 ; //Timer2 Control Reg A: Wave Gen Mode normal
2015-04-13 21:41:44 -07:00
/* Now configure the prescaler to CPU clock divided by 128 = 125Khz */
2015-02-09 05:00:16 -08:00
TCCR2B | = ( 1 < < CS22 ) | ( 1 < < CS20 ) ; // Set bits
TCCR2B & = ~ ( 1 < < CS21 ) ; // Clear bit
2017-01-10 22:31:23 -08:00
//Enable the watchdog timer for 2 second resets (Good reference: https://tushev.org/articles/arduino/5/arduino-and-watchdog-timer)
2017-06-28 05:48:54 -07:00
//Boooooooooo WDT is currently broken on Mega 2560 bootloaders :(
//wdt_enable(WDTO_2S);
2017-03-22 23:19:18 -07:00
2016-10-10 04:38:40 -07:00
# elif defined (CORE_TEENSY)
//Uses the PIT timer on Teensy.
lowResTimer . begin ( oneMSInterval , 1000 ) ;
2017-03-22 23:19:18 -07:00
# elif defined(CORE_STM32)
2017-04-21 07:30:13 -07:00
Timer4 . setPeriod ( 1000 ) ; // Set up period
// Set up an interrupt
Timer4 . setMode ( TIMER_CH1 , TIMER_OUTPUT_COMPARE ) ;
Timer4 . attachInterrupt ( 1 , oneMSInterval ) ;
2016-06-24 01:02:53 -07:00
# endif
2016-11-20 21:46:13 -08:00
dwellLimit_uS = ( 1000 * configPage2 . dwellLimit ) ;
2017-01-24 16:10:12 -08:00
lastRPM_100ms = 0 ;
2014-01-30 20:02:32 -08:00
}
//Timer2 Overflow Interrupt Vector, called when the timer overflows.
2015-04-13 21:41:44 -07:00
//Executes every ~1ms.
2016-06-24 01:02:53 -07:00
# if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) //AVR chips use the ISR for this
2017-03-22 23:19:18 -07:00
ISR ( TIMER2_OVF_vect , ISR_NOBLOCK )
# elif defined (CORE_TEENSY) || defined(CORE_STM32)
2016-10-10 04:38:40 -07:00
void oneMSInterval ( ) //Most ARM chips can simply call a function
2016-06-24 01:02:53 -07:00
# endif
2014-01-30 20:02:32 -08:00
{
2017-03-22 23:19:18 -07:00
2014-01-30 20:02:32 -08:00
//Increment Loop Counters
2017-01-24 16:10:12 -08:00
loop100ms + + ;
2014-05-06 04:07:49 -07:00
loop250ms + + ;
2014-01-30 20:02:32 -08:00
loopSec + + ;
2016-11-08 17:57:04 -08:00
2017-01-24 16:10:12 -08:00
unsigned long targetOverdwellTime ;
2017-03-22 23:19:18 -07:00
2015-02-09 05:19:58 -08:00
//Overdwell check
2016-11-20 21:46:13 -08:00
targetOverdwellTime = micros ( ) - dwellLimit_uS ; //Set a target time in the past that all coil charging must have begun after. If the coil charge began before this time, it's been running too long
2017-08-09 18:44:32 -07:00
bool isCrankLocked = configPage2 . ignCranklock & & ( currentStatus . RPM < currentStatus . crankRPM ) ; //Dwell limiter is disabled during cranking on setups using the locked cranking timing. WE HAVE to do the RPM check here as relying on the engine cranking bit can be potentially too slow in updating
2015-02-09 05:19:58 -08:00
//Check first whether each spark output is currently on. Only check it's dwell time if it is
2017-03-22 23:19:18 -07:00
2017-08-08 17:37:53 -07:00
if ( ignitionSchedule1 . Status = = RUNNING ) { if ( ( ignitionSchedule1 . startTime < targetOverdwellTime ) & & ( configPage2 . useDwellLim ) & & ( isCrankLocked ! = true ) ) { endCoil1Charge ( ) ; } }
if ( ignitionSchedule2 . Status = = RUNNING ) { if ( ( ignitionSchedule2 . startTime < targetOverdwellTime ) & & ( configPage2 . useDwellLim ) & & ( isCrankLocked ! = true ) ) { endCoil2Charge ( ) ; } }
if ( ignitionSchedule3 . Status = = RUNNING ) { if ( ( ignitionSchedule3 . startTime < targetOverdwellTime ) & & ( configPage2 . useDwellLim ) & & ( isCrankLocked ! = true ) ) { endCoil3Charge ( ) ; } }
if ( ignitionSchedule4 . Status = = RUNNING ) { if ( ( ignitionSchedule4 . startTime < targetOverdwellTime ) & & ( configPage2 . useDwellLim ) & & ( isCrankLocked ! = true ) ) { endCoil4Charge ( ) ; } }
if ( ignitionSchedule5 . Status = = RUNNING ) { if ( ( ignitionSchedule5 . startTime < targetOverdwellTime ) & & ( configPage2 . useDwellLim ) & & ( isCrankLocked ! = true ) ) { endCoil5Charge ( ) ; } }
2017-01-24 16:10:12 -08:00
//Loop executed every 100ms loop
//Anything inside this if statement will run every 100ms.
if ( loop100ms = = 100 )
{
loop100ms = 0 ; //Reset counter
currentStatus . rpmDOT = ( currentStatus . RPM - lastRPM_100ms ) * 10 ; //This is the RPM per second that the engine has accelerated/decelleratedin the last loop
lastRPM_100ms = currentStatus . RPM ; //Record the current RPM for next calc
}
2017-03-22 23:19:18 -07:00
2015-04-13 21:41:44 -07:00
//Loop executed every 250ms loop (1ms x 250 = 250ms)
2014-05-06 04:07:49 -07:00
//Anything inside this if statement will run every 250ms.
2017-03-22 23:19:18 -07:00
if ( loop250ms = = 250 )
2014-01-30 20:02:32 -08:00
{
2017-08-16 18:24:26 -07:00
loop250ms = 0 ; //Reset Counter
2017-01-10 22:31:23 -08:00
# if defined(CORE_AVR)
2017-06-28 05:48:54 -07:00
//Reset watchdog timer (Not active currently)
//wdt_reset();
2017-08-16 18:24:26 -07:00
//DIY watchdog
if ( initialisationComplete = = true & & last250msLoopCount = = mainLoopCount ) { setup ( ) ; } //This is a sign of a crash.
else { last250msLoopCount = mainLoopCount ; }
2017-01-10 22:31:23 -08:00
# endif
2014-01-30 20:02:32 -08:00
}
2017-03-22 23:19:18 -07:00
2015-04-13 21:41:44 -07:00
//Loop executed every 1 second (1ms x 1000 = 1000ms)
2017-03-22 23:19:18 -07:00
if ( loopSec = = 1000 )
2014-01-30 20:02:32 -08:00
{
loopSec = 0 ; //Reset counter.
2014-02-06 01:48:19 -08:00
2016-11-20 21:46:13 -08:00
dwellLimit_uS = ( 1000 * configPage2 . dwellLimit ) ; //Update uS value incase setting has changed
2017-08-09 18:44:32 -07:00
currentStatus . crankRPM = ( ( unsigned int ) configPage2 . crankRPM * 100 ) ;
2017-08-08 17:37:53 -07:00
//if ( configPage2.ignCranklock && BIT_CHECK(currentStatus.engine, BIT_ENGINE_CRANK)) { dwellLimit_uS = dwellLimit_uS * 4; } //Make sure the overdwell doesn't clobber the fixed ignition cranking if enabled.
2017-03-22 23:19:18 -07:00
2014-02-17 22:08:55 -08:00
//**************************************************************************************************************************************************
//This updates the runSecs variable
2014-02-06 01:48:19 -08:00
//If the engine is running or cranking, we need ot update the run time counter.
2014-12-23 15:25:51 -08:00
if ( BIT_CHECK ( currentStatus . engine , BIT_ENGINE_RUN ) )
2017-03-22 23:19:18 -07:00
{ //NOTE - There is a potential for a ~1sec gap between engine crank starting and ths runSec number being incremented. This may delay ASE!
2014-02-06 01:48:19 -08:00
if ( currentStatus . runSecs < = 254 ) //Ensure we cap out at 255 and don't overflow. (which would reset ASE)
{ currentStatus . runSecs + + ; } //Increment our run counter by 1 second.
}
2014-02-17 22:08:55 -08:00
//**************************************************************************************************************************************************
//This records the number of main loops the system has completed in the last second
currentStatus . loopsPerSecond = mainLoopCount ;
mainLoopCount = 0 ;
//**************************************************************************************************************************************************
2014-05-08 03:46:38 -07:00
//increament secl (secl is simply a counter that increments every second and is used to track whether the system has unexpectedly reset
currentStatus . secl + + ;
2016-06-24 01:02:53 -07:00
//**************************************************************************************************************************************************
2015-06-02 05:56:18 -07:00
//Check the fan output status
if ( configPage4 . fanEnable = = 1 )
2017-03-22 23:19:18 -07:00
{
fanControl ( ) ; // Fucntion to turn the cooling fan on/off
2015-06-02 05:56:18 -07:00
}
2017-03-22 23:19:18 -07:00
2015-12-20 20:45:52 -08:00
//Check whether fuel pump priming is complete
if ( ! fpPrimed )
{
2017-03-22 23:19:18 -07:00
if ( currentStatus . secl > = configPage1 . fpPrime )
2015-12-20 20:45:52 -08:00
{
fpPrimed = true ; //Mark the priming as being completed
if ( currentStatus . RPM = = 0 ) { digitalWrite ( pinFuelPump , LOW ) ; fuelPumpOn = false ; } //If we reach here then the priming is complete, however only turn off the fuel pump if the engine isn't running
}
}
2016-06-24 01:02:53 -07:00
//**************************************************************************************************************************************************
//Set the flex reading (if enabled). The flexCounter is updated with every pulse from the sensor. If cleared once per second, we get a frequency reading
2017-06-28 05:48:54 -07:00
if ( configPage1 . flexEnabled = = true )
2016-06-24 01:02:53 -07:00
{
2016-06-26 17:31:52 -07:00
if ( flexCounter < 50 )
2016-06-24 01:02:53 -07:00
{
2016-11-28 01:37:24 -08:00
currentStatus . ethanolPct = 0 ; //Standard GM Continental sensor reads from 50Hz (0 ethanol) to 150Hz (Pure ethanol). Subtracting 50 from the frequency therefore gives the ethanol percentage.
2016-06-26 17:31:52 -07:00
flexCounter = 0 ;
}
else if ( flexCounter > 151 ) //1 pulse buffer
{
2017-03-22 23:19:18 -07:00
2016-06-26 17:31:52 -07:00
if ( flexCounter < 169 )
{
2016-11-28 01:37:24 -08:00
currentStatus . ethanolPct = 100 ;
2016-06-26 17:31:52 -07:00
flexCounter = 0 ;
}
else
{
//This indicates an error condition. Spec of the sensor is that errors are above 170Hz)
2016-11-28 01:37:24 -08:00
currentStatus . ethanolPct = 0 ;
2016-06-26 17:31:52 -07:00
flexCounter = 0 ;
}
2016-06-24 01:02:53 -07:00
}
else
{
2016-11-28 01:37:24 -08:00
currentStatus . ethanolPct = flexCounter - 50 ; //Standard GM Continental sensor reads from 50Hz (0 ethanol) to 150Hz (Pure ethanol). Subtracting 50 from the frequency therefore gives the ethanol percentage.
2016-06-24 01:02:53 -07:00
flexCounter = 0 ;
}
2017-01-02 20:07:22 -08:00
//Off by 1 error check
if ( currentStatus . ethanolPct = = 1 ) { currentStatus . ethanolPct = 0 ; }
2017-03-22 23:19:18 -07:00
2016-06-24 01:02:53 -07:00
}
2014-02-18 02:05:13 -08:00
}
2016-06-24 01:02:53 -07:00
# if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) //AVR chips use the ISR for this
2017-03-22 23:19:18 -07:00
//Reset Timer2 to trigger in another ~1ms
2015-04-13 21:41:44 -07:00
TCNT2 = 131 ; //Preload timer2 with 100 cycles, leaving 156 till overflow.
2014-01-30 20:02:32 -08:00
TIFR2 = 0x00 ; //Timer2 INT Flag Reg: Clear Timer Overflow Flag
2017-03-22 23:19:18 -07:00
# endif
2017-04-21 07:30:13 -07:00
}