2015-05-30 12:36:40 -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
*/
2018-07-19 00:35:35 -07:00
# include "auxiliaries.h"
# include "globals.h"
# include "src/PID_v1/PID_v1.h"
2017-07-30 06:04:37 -07:00
//Old PID method. Retained incase the new one has issues
2018-01-23 17:05:50 -08:00
//integerPID boostPID(&MAPx100, &boost_pwm_target_value, &boostTargetx100, configPage6.boostKP, configPage6.boostKI, configPage6.boostKD, DIRECT);
integerPID_ideal boostPID ( & currentStatus . MAP , & currentStatus . boostDuty , & currentStatus . boostTarget , & configPage10 . boostSens , & configPage10 . boostIntv , configPage6 . boostKP , configPage6 . boostKI , configPage6 . boostKD , DIRECT ) ; //This is the PID object if that algorithm is used. Needs to be global as it maintains state outside of each function call
2015-05-30 12:36:40 -07:00
/*
2015-08-20 06:21:27 -07:00
Fan control
2015-05-30 12:36:40 -07:00
*/
void initialiseFan ( )
{
2018-01-23 17:05:50 -08:00
if ( configPage6 . fanInv = = 1 ) { fanHIGH = LOW ; fanLOW = HIGH ; }
2017-05-31 01:13:04 -07:00
else { fanHIGH = HIGH ; fanLOW = LOW ; }
2016-10-26 13:03:42 -07:00
digitalWrite ( pinFan , fanLOW ) ; //Initiallise program with the fan in the off state
currentStatus . fanOn = false ;
2018-05-30 15:29:17 -07:00
fan_pin_port = portOutputRegister ( digitalPinToPort ( pinFan ) ) ;
fan_pin_mask = digitalPinToBitMask ( pinFan ) ;
2015-05-30 12:36:40 -07:00
}
2015-06-02 05:56:18 -07:00
void fanControl ( )
2015-05-30 12:36:40 -07:00
{
2018-01-23 17:05:50 -08:00
if ( configPage6 . fanEnable = = 1 )
2016-10-26 13:03:42 -07:00
{
2018-01-23 17:05:50 -08:00
int onTemp = ( int ) configPage6 . fanSP - CALIBRATION_TEMPERATURE_OFFSET ;
int offTemp = onTemp - configPage6 . fanHyster ;
2017-02-11 01:33:07 -08:00
2018-05-30 15:29:17 -07:00
if ( currentStatus . coolant > = onTemp )
{
//Fan needs to be turned on. Checked for normal or inverted fan signal
if ( configPage6 . fanInv = = 0 ) { FAN_PIN_HIGH ( ) ; }
else { FAN_PIN_LOW ( ) ; }
currentStatus . fanOn = true ;
}
else if ( currentStatus . coolant < = offTemp )
{
//Fan needs to be turned off. Checked for normal or inverted fan signal
if ( configPage6 . fanInv = = 0 ) { FAN_PIN_LOW ( ) ; }
else { FAN_PIN_HIGH ( ) ; }
currentStatus . fanOn = false ;
}
2016-10-26 13:03:42 -07:00
}
2015-05-30 12:36:40 -07:00
}
2015-08-20 06:21:27 -07:00
2015-09-26 16:14:29 -07:00
void initialiseAuxPWM ( )
{
2017-07-23 23:34:13 -07:00
# if defined(CORE_AVR)
TCCR1B = 0x00 ; //Disbale Timer1 while we set it up
TCNT1 = 0 ; //Reset Timer Count
TIFR1 = 0x00 ; //Timer1 INT Flag Reg: Clear Timer Overflow Flag
TCCR1A = 0x00 ; //Timer1 Control Reg A: Wave Gen Mode normal (Simply counts up from 0 to 65535 (16-bit int)
TCCR1B = ( 1 < < CS12 ) ; //Timer1 Control Reg B: Timer Prescaler set to 256. 1 tick = 16uS. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg
2017-07-23 23:40:50 -07:00
# elif defined(CORE_TEENSY)
2018-04-03 00:47:33 -07:00
//FlexTimer 1 is used for boost and VVT. There are 8 channels on this module
FTM1_MODE | = FTM_MODE_WPDIS ; // Write Protection Disable
FTM1_MODE | = FTM_MODE_FTMEN ; //Flex Timer module enable
FTM1_MODE | = FTM_MODE_INIT ;
FTM1_SC | = FTM_SC_CLKS ( 0 b1 ) ; // Set internal clocked
FTM1_SC | = FTM_SC_PS ( 0 b111 ) ; //Set prescaler to 128 (2.1333uS tick time)
//Enable each compare channel individually
FTM1_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
FTM1_C0SC | = FTM_CSC_MSA ; //Enable Compare mode
FTM1_C0SC | = FTM_CSC_CHIE ; //Enable channel compare interrupt
FTM1_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
FTM1_C1SC | = FTM_CSC_MSA ; //Enable Compare mode
FTM1_C1SC | = FTM_CSC_CHIE ; //Enable channel compare interrupt
2017-07-23 23:34:13 -07:00
# endif
2017-02-11 01:33:07 -08:00
2015-09-26 16:14:29 -07:00
boost_pin_port = portOutputRegister ( digitalPinToPort ( pinBoost ) ) ;
boost_pin_mask = digitalPinToBitMask ( pinBoost ) ;
2015-09-26 23:41:07 -07:00
vvt_pin_port = portOutputRegister ( digitalPinToPort ( pinVVT_1 ) ) ;
vvt_pin_mask = digitalPinToBitMask ( pinVVT_1 ) ;
2018-07-01 03:37:57 -07:00
n2o_stage1_pin_port = portOutputRegister ( digitalPinToPort ( configPage10 . n2o_stage1_pin ) ) ;
n2o_stage1_pin_mask = digitalPinToBitMask ( configPage10 . n2o_stage1_pin ) ;
n2o_stage2_pin_port = portOutputRegister ( digitalPinToPort ( configPage10 . n2o_stage2_pin ) ) ;
n2o_stage2_pin_mask = digitalPinToBitMask ( configPage10 . n2o_stage2_pin ) ;
2018-07-02 22:46:23 -07:00
n2o_arming_pin_port = portInputRegister ( digitalPinToPort ( configPage10 . n2o_arming_pin ) ) ;
n2o_arming_pin_mask = digitalPinToBitMask ( configPage10 . n2o_arming_pin ) ;
if ( configPage10 . n2o_pin_polarity = = 1 ) { pinMode ( configPage10 . n2o_arming_pin , INPUT_PULLUP ) ; }
else { pinMode ( configPage10 . n2o_arming_pin , INPUT ) ; }
2017-02-11 01:33:07 -08:00
2018-04-03 00:47:33 -07:00
# if defined(CORE_STM32) || defined(CORE_TEENSY) //2uS resolution Min 8Hz, Max 5KHz
boost_pwm_max_count = 1000000L / ( 2 * configPage6 . boostFreq * 2 ) ; //Converts the frequency in Hz to the number of ticks (at 2uS) it takes to complete 1 cycle. The x2 is there because the frequency is stored at half value (in a byte) to allow freqneucies up to 511Hz
vvt_pwm_max_count = 1000000L / ( 2 * configPage6 . vvtFreq * 2 ) ; //Converts the frequency in Hz to the number of ticks (at 2uS) it takes to complete 1 cycle
2017-08-27 21:35:49 -07:00
# else
2018-01-23 17:05:50 -08:00
boost_pwm_max_count = 1000000L / ( 16 * configPage6 . boostFreq * 2 ) ; //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle. The x2 is there because the frequency is stored at half value (in a byte) to allow freqneucies up to 511Hz
vvt_pwm_max_count = 1000000L / ( 16 * configPage6 . vvtFreq * 2 ) ; //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle
2017-08-27 21:35:49 -07:00
# endif
2017-07-23 23:34:13 -07:00
ENABLE_VVT_TIMER ( ) ; //Turn on the B compare unit (ie turn on the interrupt)
2016-05-22 03:12:44 -07:00
2018-01-23 17:05:50 -08:00
boostPID . SetOutputLimits ( configPage2 . boostMinDuty , configPage2 . boostMaxDuty ) ;
if ( configPage6 . boostMode = = BOOST_MODE_SIMPLE ) { boostPID . SetTunings ( 100 , 100 , 100 ) ; }
else { boostPID . SetTunings ( configPage6 . boostKP , configPage6 . boostKI , configPage6 . boostKD ) ; }
2017-02-11 01:33:07 -08:00
2017-03-16 00:17:12 -07:00
currentStatus . boostDuty = 0 ;
2017-02-11 01:33:07 -08:00
boostCounter = 0 ;
2017-08-31 13:38:41 -07:00
# if defined(CORE_STM32) //Need to be initialised last due to instant interrupt
2017-09-02 09:26:41 -07:00
Timer1 . setMode ( 2 , TIMER_OUTPUT_COMPARE ) ;
Timer1 . setMode ( 3 , TIMER_OUTPUT_COMPARE ) ;
2017-09-01 12:16:45 -07:00
if ( boost_pwm_max_count > 0 ) { Timer1 . attachInterrupt ( 2 , boostInterrupt ) ; }
if ( vvt_pwm_max_count > 0 ) { Timer1 . attachInterrupt ( 3 , vvtInterrupt ) ; }
2017-08-31 13:38:41 -07:00
Timer1 . resume ( ) ;
# endif
2018-07-01 03:37:57 -07:00
currentStatus . nitrous_status = NITROUS_OFF ;
2018-07-02 22:46:23 -07:00
2015-09-26 16:14:29 -07:00
}
2015-08-20 06:21:27 -07:00
2017-09-03 18:50:55 -07:00
# define BOOST_HYSTER 40
2015-09-26 16:14:29 -07:00
void boostControl ( )
{
2018-01-23 17:05:50 -08:00
if ( configPage6 . boostEnabled = = 1 )
2015-09-26 23:41:07 -07:00
{
2018-05-14 21:05:45 -07:00
if ( configPage4 . boostType = = OPEN_LOOP_BOOST )
2017-05-12 01:14:14 -07:00
{
2018-05-14 21:05:45 -07:00
//Open loop
currentStatus . boostDuty = get3DTableValue ( & boostTable , currentStatus . TPS , currentStatus . RPM ) * 2 * 100 ;
if ( currentStatus . boostDuty > 10000 ) { currentStatus . boostDuty = 10000 ; } //Safety check
if ( currentStatus . boostDuty = = 0 ) { DISABLE_BOOST_TIMER ( ) ; BOOST_PIN_LOW ( ) ; } //If boost duty is 0, shut everything down
2018-01-20 14:33:14 -08:00
else
{
2018-05-14 21:05:45 -07:00
boost_pwm_target_value = ( ( unsigned long ) ( currentStatus . boostDuty ) * boost_pwm_max_count ) / 10000 ; //Convert boost duty (Which is a % multipled by 100) to a pwm count
ENABLE_BOOST_TIMER ( ) ; //Turn on the compare unit (ie turn on the interrupt) if boost duty >0
2017-05-31 01:13:04 -07:00
}
2018-05-14 21:05:45 -07:00
}
else if ( configPage4 . boostType = = CLOSED_LOOP_BOOST )
{
if ( ( boostCounter & 7 ) = = 1 ) { currentStatus . boostTarget = get3DTableValue ( & boostTable , currentStatus . TPS , currentStatus . RPM ) * 2 ; } //Boost target table is in kpa and divided by 2
2018-06-29 01:08:17 -07:00
if ( currentStatus . MAP > = 100 ) //Only engage boost control above 100kpa.
2017-05-31 01:13:04 -07:00
{
2018-05-14 21:05:45 -07:00
//If flex fuel is enabled, there can be an adder to the boost target based on ethanol content
if ( configPage2 . flexEnabled = = 1 )
2017-09-03 03:28:49 -07:00
{
2018-05-14 21:05:45 -07:00
currentStatus . boostTarget + = table2D_getValue ( & flexBoostTable , currentStatus . ethanolPct ) ; ;
2017-09-03 03:28:49 -07:00
}
else
{
2018-05-14 21:05:45 -07:00
currentStatus . flexBoostCorrection = 0 ;
}
if ( currentStatus . boostTarget > 0 )
{
//This only needs to be run very infrequently, once every 16 calls to boostControl(). This is approx. once per second
if ( ( boostCounter & 15 ) = = 1 )
2017-09-03 18:50:55 -07:00
{
2018-05-14 21:05:45 -07:00
boostPID . SetOutputLimits ( configPage2 . boostMinDuty , configPage2 . boostMaxDuty ) ;
if ( configPage6 . boostMode = = BOOST_MODE_SIMPLE ) { boostPID . SetTunings ( 100 , 100 , 100 ) ; }
else { boostPID . SetTunings ( configPage6 . boostKP , configPage6 . boostKI , configPage6 . boostKD ) ; }
2017-09-03 18:50:55 -07:00
}
2017-07-24 00:05:55 -07:00
2018-05-14 21:05:45 -07:00
bool PIDcomputed = boostPID . Compute ( ) ; //Compute() returns false if the required interval has not yet passed.
if ( currentStatus . boostDuty = = 0 ) { DISABLE_BOOST_TIMER ( ) ; BOOST_PIN_LOW ( ) ; } //If boost duty is 0, shut everything down
else
{
if ( PIDcomputed = = true )
{
boost_pwm_target_value = ( ( unsigned long ) ( currentStatus . boostDuty ) * boost_pwm_max_count ) / 10000 ; //Convert boost duty (Which is a % multipled by 100) to a pwm count
ENABLE_BOOST_TIMER ( ) ; //Turn on the compare unit (ie turn on the interrupt) if boost duty >0
}
}
}
else
{
//If boost target is 0, turn everything off
boostDisable ( ) ;
}
2017-05-31 01:13:04 -07:00
}
else
{
2018-05-14 21:05:45 -07:00
//Boost control does nothing if kPa below the hyster point
2017-09-03 18:50:55 -07:00
boostDisable ( ) ;
2018-05-14 21:05:45 -07:00
} //MAP above boost + hyster
} //Open / Cloosed loop
2015-09-26 23:41:07 -07:00
}
2018-01-20 14:33:14 -08:00
else { // Disable timer channel and zero the flex boost correction status
2018-04-03 00:47:33 -07:00
DISABLE_BOOST_TIMER ( ) ;
2018-01-20 14:33:14 -08:00
currentStatus . flexBoostCorrection = 0 ;
}
2017-02-11 01:33:07 -08:00
boostCounter + + ;
2015-09-26 23:41:07 -07:00
}
void vvtControl ( )
{
2018-01-23 17:05:50 -08:00
if ( configPage6 . vvtEnabled = = 1 )
2015-09-26 23:41:07 -07:00
{
byte vvtDuty = get3DTableValue ( & vvtTable , currentStatus . TPS , currentStatus . RPM ) ;
2017-10-02 02:01:08 -07:00
//VVT table can be used for controlling on/off switching. If this is turned on, then disregard any interpolation or non-binary values
2018-01-23 17:05:50 -08:00
if ( ( configPage6 . VVTasOnOff = = true ) & & ( vvtDuty < 100 ) ) { vvtDuty = 0 ; }
2017-10-02 02:01:08 -07:00
2017-07-23 23:04:29 -07:00
if ( vvtDuty = = 0 )
{
//Make sure solenoid is off (0% duty)
VVT_PIN_LOW ( ) ;
DISABLE_VVT_TIMER ( ) ;
}
else if ( vvtDuty > = 100 )
{
//Make sure solenoid is on (100% duty)
VVT_PIN_HIGH ( ) ;
DISABLE_VVT_TIMER ( ) ;
}
else
{
vvt_pwm_target_value = percentage ( vvtDuty , vvt_pwm_max_count ) ;
ENABLE_VVT_TIMER ( ) ;
}
2015-09-26 23:41:07 -07:00
}
2017-07-23 23:04:29 -07:00
else { DISABLE_VVT_TIMER ( ) ; } // Disable timer channel
2015-09-26 16:14:29 -07:00
}
2017-02-11 01:33:07 -08:00
2018-06-29 08:19:51 -07:00
void nitrousControl ( )
{
2018-07-02 22:46:23 -07:00
bool nitrousOn = false ; //This tracks whether the control gets turned on at any point.
2018-06-29 08:19:51 -07:00
if ( configPage10 . n2o_enable > 0 )
{
2018-07-01 03:37:57 -07:00
bool isArmed = READ_N2O_ARM_PIN ( ) ;
if ( configPage10 . n2o_pin_polarity = = 1 ) { isArmed = ! isArmed ; } //If nitrous is active when pin is low, flip the reading (n2o_pin_polarity = 0 = active when High)
2018-06-29 08:19:51 -07:00
//Perform the main checks to see if nitrous is ready
if ( ( isArmed = = true ) & & ( currentStatus . coolant > ( configPage10 . n2o_minCLT - CALIBRATION_TEMPERATURE_OFFSET ) ) & & ( currentStatus . TPS > configPage10 . n2o_minTPS ) & & ( currentStatus . O2 < configPage10 . n2o_maxAFR ) & & ( currentStatus . MAP < configPage10 . n2o_maxMAP ) )
{
2018-07-02 22:46:23 -07:00
uint16_t realStage1MinRPM = ( uint16_t ) configPage10 . n2o_stage1_minRPM * 100 ;
uint16_t realStage1MaxRPM = ( uint16_t ) configPage10 . n2o_stage1_maxRPM * 100 ;
uint16_t realStage2MinRPM = ( uint16_t ) configPage10 . n2o_stage2_minRPM * 100 ;
uint16_t realStage2MaxRPM = ( uint16_t ) configPage10 . n2o_stage2_maxRPM * 100 ;
2018-06-29 08:19:51 -07:00
if ( ( currentStatus . RPM > realStage1MinRPM ) & & ( currentStatus . RPM < realStage1MaxRPM ) )
{
currentStatus . nitrous_status = NITROUS_STAGE1 ;
BIT_SET ( currentStatus . status3 , BIT_STATUS3_NITROUS ) ;
2018-07-01 03:37:57 -07:00
N2O_STAGE1_PIN_HIGH ( ) ;
2018-07-02 22:46:23 -07:00
nitrousOn = true ;
2018-06-29 08:19:51 -07:00
}
2018-07-02 22:46:23 -07:00
if ( configPage10 . n2o_enable = = NITROUS_STAGE2 ) //This is really just a sanity check
2018-06-29 08:19:51 -07:00
{
2018-07-02 22:46:23 -07:00
if ( ( currentStatus . RPM > realStage2MinRPM ) & & ( currentStatus . RPM < realStage2MaxRPM ) )
{
currentStatus . nitrous_status = NITROUS_STAGE2 ;
BIT_SET ( currentStatus . status3 , BIT_STATUS3_NITROUS ) ;
N2O_STAGE2_PIN_HIGH ( ) ;
nitrousOn = true ;
}
2018-06-29 08:19:51 -07:00
}
}
2018-07-02 22:46:23 -07:00
}
if ( nitrousOn = = false )
2018-06-29 08:19:51 -07:00
{
currentStatus . nitrous_status = NITROUS_OFF ;
BIT_CLEAR ( currentStatus . status3 , BIT_STATUS3_NITROUS ) ;
2018-07-01 03:37:57 -07:00
N2O_STAGE1_PIN_LOW ( ) ;
N2O_STAGE2_PIN_LOW ( ) ;
2018-06-29 08:19:51 -07:00
}
}
2017-09-03 18:50:55 -07:00
void boostDisable ( )
{
boostPID . Initialize ( ) ; //This resets the ITerm value to prevent rubber banding
currentStatus . boostDuty = 0 ;
DISABLE_BOOST_TIMER ( ) ; //Turn off timer
BOOST_PIN_LOW ( ) ; //Make sure solenoid is off (0% duty)
}
2015-09-26 23:41:07 -07:00
//The interrupt to control the Boost PWM
2017-07-23 23:34:13 -07:00
# if defined(CORE_AVR)
ISR ( TIMER1_COMPA_vect )
2017-08-27 21:35:49 -07:00
# elif defined (CORE_TEENSY) || defined(CORE_STM32)
2017-07-23 23:34:13 -07:00
static inline void boostInterrupt ( ) //Most ARM chips can simply call a function
# endif
2015-09-26 23:41:07 -07:00
{
2017-09-23 07:47:48 -07:00
if ( boost_pwm_state = = true )
2015-09-26 23:41:07 -07:00
{
2017-07-23 23:34:13 -07:00
BOOST_PIN_LOW ( ) ; // Switch pin to low
BOOST_TIMER_COMPARE = BOOST_TIMER_COUNTER + ( boost_pwm_max_count - boost_pwm_cur_value ) ;
2015-09-26 23:41:07 -07:00
boost_pwm_state = false ;
}
else
{
2017-07-23 23:34:13 -07:00
BOOST_PIN_HIGH ( ) ; // Switch pin high
BOOST_TIMER_COMPARE = BOOST_TIMER_COUNTER + boost_pwm_target_value ;
2015-09-26 23:41:07 -07:00
boost_pwm_cur_value = boost_pwm_target_value ;
boost_pwm_state = true ;
2017-02-11 01:33:07 -08:00
}
2015-09-26 23:41:07 -07:00
}
2015-08-20 06:21:27 -07:00
2015-09-26 23:41:07 -07:00
//The interrupt to control the VVT PWM
2017-07-23 23:34:13 -07:00
# if defined(CORE_AVR)
ISR ( TIMER1_COMPB_vect )
2017-08-27 21:35:49 -07:00
# elif defined (CORE_TEENSY) || defined(CORE_STM32)
2017-07-23 23:34:13 -07:00
static inline void vvtInterrupt ( ) //Most ARM chips can simply call a function
# endif
2015-09-26 23:41:07 -07:00
{
2017-09-23 07:47:48 -07:00
if ( vvt_pwm_state = = true )
2015-09-26 23:41:07 -07:00
{
2017-07-23 23:34:13 -07:00
VVT_PIN_LOW ( ) ; // Switch pin to low
VVT_TIMER_COMPARE = VVT_TIMER_COUNTER + ( vvt_pwm_max_count - vvt_pwm_cur_value ) ;
2015-09-26 23:41:07 -07:00
vvt_pwm_state = false ;
}
else
{
2017-07-23 23:34:13 -07:00
VVT_PIN_HIGH ( ) ; // Switch pin high
VVT_TIMER_COMPARE = VVT_TIMER_COUNTER + vvt_pwm_target_value ;
2015-09-26 23:41:07 -07:00
vvt_pwm_cur_value = vvt_pwm_target_value ;
vvt_pwm_state = true ;
2017-02-11 01:33:07 -08:00
}
2015-09-26 23:41:07 -07:00
}
2018-04-03 00:47:33 -07:00
# if defined(CORE_TEENSY)
void ftm1_isr ( void )
{
//FTM1 only has 2 compare channels
//Use separate variables for each test to ensure conversion to bool
bool interrupt1 = ( FTM1_C0SC & FTM_CSC_CHF ) ;
bool interrupt2 = ( FTM1_C1SC & FTM_CSC_CHF ) ;
if ( interrupt1 ) { FTM1_C0SC & = ~ FTM_CSC_CHF ; boostInterrupt ( ) ; }
else if ( interrupt2 ) { FTM1_C1SC & = ~ FTM_CSC_CHF ; vvtInterrupt ( ) ; }
}
# endif