2015-05-29 00:33:00 -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
*/
2017-01-17 22:37:55 -08:00
# include "idle.h"
2015-05-29 00:33:00 -07:00
/*
2015-09-25 17:12:59 -07:00
These functions cover the PWM and stepper idle control
2015-05-29 00:33:00 -07:00
*/
2015-08-20 06:21:27 -07:00
/*
Idle Control
Currently limited to on / off control and open loop PWM and stepper drive
*/
2016-05-28 06:53:15 -07:00
integerPID idlePID ( & currentStatus . longRPM , & idle_pwm_target_value , & idle_cl_target_rpm , configPage3 . idleKP , configPage3 . idleKI , configPage3 . idleKD , DIRECT ) ; //This is the PID object if that algorithm is used. Needs to be global as it maintains state outside of each function call
2016-04-06 20:28:13 -07:00
2015-05-29 00:33:00 -07:00
void initialiseIdle ( )
{
2017-02-19 22:57:46 -08:00
//By default, turn off the PWM interrupt (It gets turned on below if needed)
2016-02-02 13:22:51 -08:00
# if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
2016-02-04 11:25:07 -08:00
TIMSK4 & = ~ ( 1 < < OCIE4C ) ; // Disable timer channel for idle
2016-02-02 13:22:51 -08:00
# endif
2017-02-19 22:57:46 -08:00
2015-08-20 06:21:27 -07:00
//Initialising comprises of setting the 2D tables with the relevant values from the config pages
switch ( configPage4 . iacAlgorithm )
{
case 0 :
//Case 0 is no idle control ('None')
break ;
2017-02-19 22:57:46 -08:00
2015-08-20 06:21:27 -07:00
case 1 :
//Case 1 is on/off idle control
if ( currentStatus . coolant < configPage4 . iacFastTemp )
{
digitalWrite ( pinIdle1 , HIGH ) ;
}
break ;
2017-02-19 22:57:46 -08:00
2015-08-20 06:21:27 -07:00
case 2 :
//Case 2 is PWM open loop
iacPWMTable . xSize = 10 ;
2015-08-25 20:27:50 -07:00
iacPWMTable . valueSize = SIZE_BYTE ;
2015-08-20 06:21:27 -07:00
iacPWMTable . values = configPage4 . iacOLPWMVal ;
iacPWMTable . axisX = configPage4 . iacBins ;
2017-02-19 22:57:46 -08:00
2015-08-20 06:21:27 -07:00
iacCrankDutyTable . xSize = 4 ;
2016-05-08 21:00:52 -07:00
iacCrankDutyTable . valueSize = SIZE_BYTE ;
2015-08-20 06:21:27 -07:00
iacCrankDutyTable . values = configPage4 . iacCrankDuty ;
iacCrankDutyTable . axisX = configPage4 . iacCrankBins ;
2017-02-19 22:57:46 -08:00
2015-09-25 17:12:59 -07:00
idle_pin_port = portOutputRegister ( digitalPinToPort ( pinIdle1 ) ) ;
idle_pin_mask = digitalPinToBitMask ( pinIdle1 ) ;
2016-01-12 22:06:55 -08:00
idle2_pin_port = portOutputRegister ( digitalPinToPort ( pinIdle2 ) ) ;
idle2_pin_mask = digitalPinToBitMask ( pinIdle2 ) ;
2015-09-29 00:21:00 -07:00
idle_pwm_max_count = 1000000L / ( 16 * configPage3 . idleFreq * 2 ) ; //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz
2016-10-06 23:34:27 -07:00
enableIdle ( ) ;
2015-08-20 06:21:27 -07:00
break ;
2017-02-19 22:57:46 -08:00
2015-08-20 06:21:27 -07:00
case 3 :
//Case 3 is PWM closed loop
iacClosedLoopTable . xSize = 10 ;
2016-05-08 21:00:52 -07:00
iacClosedLoopTable . valueSize = SIZE_BYTE ;
2015-08-20 06:21:27 -07:00
iacClosedLoopTable . values = configPage4 . iacCLValues ;
iacClosedLoopTable . axisX = configPage4 . iacBins ;
2017-02-19 22:57:46 -08:00
2015-08-20 06:21:27 -07:00
iacCrankDutyTable . xSize = 4 ;
2016-05-08 21:00:52 -07:00
iacCrankDutyTable . valueSize = SIZE_BYTE ;
2015-08-20 06:21:27 -07:00
iacCrankDutyTable . values = configPage4 . iacCrankDuty ;
iacCrankDutyTable . axisX = configPage4 . iacCrankBins ;
2016-04-06 20:28:13 -07:00
idle_pin_port = portOutputRegister ( digitalPinToPort ( pinIdle1 ) ) ;
idle_pin_mask = digitalPinToBitMask ( pinIdle1 ) ;
idle2_pin_port = portOutputRegister ( digitalPinToPort ( pinIdle2 ) ) ;
idle2_pin_mask = digitalPinToBitMask ( pinIdle2 ) ;
idle_pwm_max_count = 1000000L / ( 16 * configPage3 . idleFreq * 2 ) ; //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz
2017-02-19 22:57:46 -08:00
idlePID . SetOutputLimits ( percentage ( configPage1 . iacCLminDuty , idle_pwm_max_count ) , percentage ( configPage1 . iacCLmaxDuty , idle_pwm_max_count ) ) ;
2016-05-28 06:53:15 -07:00
idlePID . SetTunings ( configPage3 . idleKP , configPage3 . idleKI , configPage3 . idleKD ) ;
2016-04-08 06:52:32 -07:00
idlePID . SetMode ( AUTOMATIC ) ; //Turn PID on
2015-08-20 06:21:27 -07:00
break ;
2017-02-19 22:57:46 -08:00
2015-08-20 06:21:27 -07:00
case 4 :
//Case 2 is Stepper open loop
iacStepTable . xSize = 10 ;
2015-08-25 20:27:50 -07:00
iacStepTable . valueSize = SIZE_BYTE ;
2015-08-20 06:21:27 -07:00
iacStepTable . values = configPage4 . iacOLStepVal ;
iacStepTable . axisX = configPage4 . iacBins ;
2017-02-19 22:57:46 -08:00
2015-08-20 06:21:27 -07:00
iacCrankStepsTable . xSize = 4 ;
iacCrankStepsTable . values = configPage4 . iacCrankSteps ;
iacCrankStepsTable . axisX = configPage4 . iacCrankBins ;
2015-08-25 20:27:50 -07:00
iacStepTime = configPage4 . iacStepTime * 1000 ;
2017-02-19 22:57:46 -08:00
2016-12-11 04:23:54 -08:00
//homeStepper(); //Returns the stepper to the 'home' position
completedHomeSteps = 0 ;
2015-08-25 20:27:50 -07:00
idleStepper . stepperStatus = SOFF ;
2015-08-20 06:21:27 -07:00
break ;
2017-02-19 22:57:46 -08:00
2015-08-20 06:21:27 -07:00
case 5 :
//Case 5 is Stepper closed loop
iacClosedLoopTable . xSize = 10 ;
iacClosedLoopTable . values = configPage4 . iacCLValues ;
iacClosedLoopTable . axisX = configPage4 . iacBins ;
2017-02-19 22:57:46 -08:00
2015-08-20 06:21:27 -07:00
iacCrankStepsTable . xSize = 4 ;
iacCrankStepsTable . values = configPage4 . iacCrankSteps ;
iacCrankStepsTable . axisX = configPage4 . iacCrankBins ;
2015-08-25 20:27:50 -07:00
iacStepTime = configPage4 . iacStepTime * 1000 ;
2017-02-19 22:57:46 -08:00
2015-08-25 20:27:50 -07:00
homeStepper ( ) ; //Returns the stepper to the 'home' position
idleStepper . stepperStatus = SOFF ;
2015-08-20 06:21:27 -07:00
break ;
}
2017-01-12 03:33:26 -08:00
idleInitComplete = configPage4 . iacAlgorithm ; //Sets which idle method was initialised
2015-05-29 00:33:00 -07:00
}
2015-08-20 06:21:27 -07:00
void idleControl ( )
2015-05-29 00:33:00 -07:00
{
2017-01-12 03:33:26 -08:00
if ( idleInitComplete ! = configPage4 . iacAlgorithm ) { initialiseIdle ( ) ; }
2017-02-19 22:57:46 -08:00
2015-08-20 06:21:27 -07:00
switch ( configPage4 . iacAlgorithm )
{
case 0 : //Case 0 is no idle control ('None')
break ;
2017-02-19 22:57:46 -08:00
2015-08-20 06:21:27 -07:00
case 1 : //Case 1 is on/off idle control
if ( ( currentStatus . coolant + CALIBRATION_TEMPERATURE_OFFSET ) < configPage4 . iacFastTemp ) //All temps are offset by 40 degrees
{
digitalWrite ( pinIdle1 , HIGH ) ;
idleOn = true ;
}
else if ( idleOn ) { digitalWrite ( pinIdle1 , LOW ) ; idleOn = false ; }
break ;
2017-02-19 22:57:46 -08:00
2015-08-20 06:21:27 -07:00
case 2 : //Case 2 is PWM open loop
//Check for cranking pulsewidth
if ( BIT_CHECK ( currentStatus . engine , BIT_ENGINE_CRANK ) )
{
//Currently cranking. Use the cranking table
2016-01-12 22:06:55 -08:00
currentStatus . idleDuty = table2D_getValue ( & iacCrankDutyTable , currentStatus . coolant + CALIBRATION_TEMPERATURE_OFFSET ) ; //All temps are offset by 40 degrees
idle_pwm_target_value = percentage ( currentStatus . idleDuty , idle_pwm_max_count ) ;
2015-08-20 06:21:27 -07:00
idleOn = true ;
}
2016-05-08 20:18:13 -07:00
else
2015-08-20 06:21:27 -07:00
{
//Standard running
2016-01-12 22:06:55 -08:00
currentStatus . idleDuty = table2D_getValue ( & iacPWMTable , currentStatus . coolant + CALIBRATION_TEMPERATURE_OFFSET ) ; //All temps are offset by 40 degrees
2016-10-06 23:34:27 -07:00
if ( currentStatus . idleDuty = = 0 ) { disableIdle ( ) ; break ; }
enableIdle ( ) ;
2016-01-12 22:06:55 -08:00
idle_pwm_target_value = percentage ( currentStatus . idleDuty , idle_pwm_max_count ) ;
2015-08-20 06:21:27 -07:00
idleOn = true ;
}
break ;
2017-02-19 22:57:46 -08:00
2016-04-06 20:28:13 -07:00
case 3 : //Case 3 is PWM closed loop
//No cranking specific value for closed loop (yet?)
2016-05-08 21:00:52 -07:00
idle_cl_target_rpm = table2D_getValue ( & iacClosedLoopTable , currentStatus . coolant + CALIBRATION_TEMPERATURE_OFFSET ) * 10 ; //All temps are offset by 40 degrees
2016-05-28 06:53:15 -07:00
//idlePID.SetTunings(configPage3.idleKP, configPage3.idleKI, configPage3.idleKD);
2016-04-06 20:28:13 -07:00
idlePID . Compute ( ) ;
2016-10-06 23:34:27 -07:00
if ( idle_pwm_target_value = = 0 ) { disableIdle ( ) ; }
else { enableIdle ( ) ; } //Turn on the C compare unit (ie turn on the interrupt)
2016-05-08 21:00:52 -07:00
//idle_pwm_target_value = 104;
2015-08-20 06:21:27 -07:00
break ;
2017-02-19 22:57:46 -08:00
2015-08-20 06:21:27 -07:00
case 4 : //Case 4 is open loop stepper control
2015-08-20 21:14:47 -07:00
//First thing to check is whether there is currently a step going on and if so, whether it needs to be turned off
2015-08-25 20:27:50 -07:00
if ( idleStepper . stepperStatus = = STEPPING | | idleStepper . stepperStatus = = COOLING )
2015-08-20 21:14:47 -07:00
{
2015-08-25 20:27:50 -07:00
if ( micros ( ) > ( idleStepper . stepStartTime + iacStepTime ) )
2015-08-20 21:14:47 -07:00
{
2015-08-25 20:27:50 -07:00
if ( idleStepper . stepperStatus = = STEPPING )
{
//Means we're currently in a step, but it needs to be turned off
digitalWrite ( pinStepperStep , LOW ) ; //Turn off the step
idleStepper . stepStartTime = micros ( ) ;
idleStepper . stepperStatus = COOLING ; //'Cooling' is the time the stepper needs to sit in LOW state before the next step can be made
return ;
}
else
{
2017-02-19 22:57:46 -08:00
//Means we're in COOLING status but have been in this state long enough to
2015-08-25 20:27:50 -07:00
idleStepper . stepperStatus = SOFF ;
}
2015-08-20 21:14:47 -07:00
}
else
{
//Means we're in a step, but it doesn't need to turn off yet. No further action at this time
return ;
}
}
2016-12-11 04:23:54 -08:00
if ( completedHomeSteps < ( configPage4 . iacStepHome * 3 ) ) //Home steps are divided by 3 from TS
{
digitalWrite ( pinStepperDir , STEPPER_BACKWARD ) ; //Sets stepper direction to backwards
digitalWrite ( pinStepperStep , HIGH ) ;
idleStepper . stepStartTime = micros ( ) ;
idleStepper . stepperStatus = STEPPING ;
completedHomeSteps + + ;
idleOn = true ;
}
2015-08-20 06:21:27 -07:00
//Check for cranking pulsewidth
2016-12-11 04:23:54 -08:00
else if ( BIT_CHECK ( currentStatus . engine , BIT_ENGINE_CRANK ) )
2015-08-20 06:21:27 -07:00
{
//Currently cranking. Use the cranking table
2015-08-25 20:27:50 -07:00
idleStepper . targetIdleStep = table2D_getValue ( & iacCrankStepsTable , ( currentStatus . coolant + CALIBRATION_TEMPERATURE_OFFSET ) ) * 3 ; //All temps are offset by 40 degrees. Step counts are divided by 3 in TS. Multiply back out here
if ( idleStepper . targetIdleStep > ( idleStepper . curIdleStep - configPage4 . iacStepHyster ) & & idleStepper . targetIdleStep < ( idleStepper . curIdleStep + configPage4 . iacStepHyster ) ) { return ; } //Hysteris check
else if ( idleStepper . targetIdleStep < idleStepper . curIdleStep ) { digitalWrite ( pinStepperDir , STEPPER_BACKWARD ) ; idleStepper . curIdleStep - - ; } //Sets stepper direction to backwards
else if ( idleStepper . targetIdleStep > idleStepper . curIdleStep ) { digitalWrite ( pinStepperDir , STEPPER_FORWARD ) ; idleStepper . curIdleStep + + ; } //Sets stepper direction to forwards
2017-02-19 22:57:46 -08:00
2015-08-20 21:14:47 -07:00
digitalWrite ( pinStepperStep , HIGH ) ;
idleStepper . stepStartTime = micros ( ) ;
idleStepper . stepperStatus = STEPPING ;
2017-02-19 22:57:46 -08:00
idleOn = true ;
2015-08-20 06:21:27 -07:00
}
2015-08-25 20:27:50 -07:00
else if ( ( currentStatus . coolant + CALIBRATION_TEMPERATURE_OFFSET ) < iacStepTable . axisX [ IDLE_TABLE_SIZE - 1 ] )
2015-08-20 06:21:27 -07:00
{
//Standard running
2016-12-11 04:23:54 -08:00
if ( ( mainLoopCount & 255 ) = = 1 )
{
//Only do a lookup of the required value around 4 times per second. Any more than this can create too much jitter and require a hyster value that is too high
idleStepper . targetIdleStep = table2D_getValue ( & iacStepTable , ( currentStatus . coolant + CALIBRATION_TEMPERATURE_OFFSET ) ) * 3 ; //All temps are offset by 40 degrees. Step counts are divided by 3 in TS. Multiply back out here
}
2015-08-25 20:27:50 -07:00
if ( idleStepper . targetIdleStep > ( idleStepper . curIdleStep - configPage4 . iacStepHyster ) & & idleStepper . targetIdleStep < ( idleStepper . curIdleStep + configPage4 . iacStepHyster ) ) { return ; } //Hysteris check
else if ( idleStepper . targetIdleStep < idleStepper . curIdleStep ) { digitalWrite ( pinStepperDir , STEPPER_BACKWARD ) ; idleStepper . curIdleStep - - ; } //Sets stepper direction to backwards
else if ( idleStepper . targetIdleStep > idleStepper . curIdleStep ) { digitalWrite ( pinStepperDir , STEPPER_FORWARD ) ; idleStepper . curIdleStep + + ; } //Sets stepper direction to forwards
2017-02-19 22:57:46 -08:00
2015-08-20 21:14:47 -07:00
digitalWrite ( pinStepperStep , HIGH ) ;
idleStepper . stepStartTime = micros ( ) ;
idleStepper . stepperStatus = STEPPING ;
2017-02-19 22:57:46 -08:00
idleOn = true ;
2015-08-20 06:21:27 -07:00
}
2017-02-19 22:57:46 -08:00
2015-08-20 06:21:27 -07:00
break ;
}
2015-05-29 00:33:00 -07:00
}
2015-08-20 21:14:47 -07:00
/*
A simple function to home the stepper motor ( If in use )
*/
void homeStepper ( )
{
//Need to 'home' the stepper on startup
digitalWrite ( pinStepperDir , STEPPER_BACKWARD ) ; //Sets stepper direction to backwards
2015-08-25 20:27:50 -07:00
for ( int x = 0 ; x < ( configPage4 . iacStepHome * 3 ) ; x + + ) //Step counts are divided by 3 in TS. Multiply back out here
2015-08-20 21:14:47 -07:00
{
digitalWrite ( pinStepperStep , HIGH ) ;
2015-08-25 20:27:50 -07:00
delayMicroseconds ( iacStepTime ) ;
digitalWrite ( pinStepperStep , LOW ) ;
delayMicroseconds ( iacStepTime ) ;
2015-08-20 21:14:47 -07:00
}
digitalWrite ( pinStepperDir , STEPPER_FORWARD ) ;
2017-02-19 22:57:46 -08:00
idleStepper . curIdleStep = 0 ;
2015-08-20 21:14:47 -07:00
idleStepper . targetIdleStep = 0 ;
idleStepper . stepperStatus = SOFF ;
}
2015-09-25 17:12:59 -07:00
//The interrupt to turn off the idle pwm
2016-01-12 22:06:55 -08:00
# if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
2016-10-06 23:34:27 -07:00
//This function simply turns off the idle PWM and sets the pin low
static inline void disableIdle ( )
{
TIMSK4 & = ~ ( 1 < < OCIE4C ) ; //Turn off interrupt
2017-02-19 22:57:46 -08:00
digitalWrite ( pinIdle1 , LOW ) ;
2016-10-06 23:34:27 -07:00
}
//Any common functions associated with starting the Idle
//Typically this is enabling the PWM interrupt
static inline void enableIdle ( )
{
TIMSK4 | = ( 1 < < OCIE4C ) ; //Turn on the C compare unit (ie turn on the interrupt)
}
2015-09-29 00:21:00 -07:00
ISR ( TIMER4_COMPC_vect )
2015-09-25 17:12:59 -07:00
{
if ( idle_pwm_state )
{
2017-02-19 22:57:46 -08:00
if ( configPage4 . iacPWMdir = = 0 )
{
2016-05-27 05:44:40 -07:00
//Normal direction
* idle_pin_port & = ~ ( idle_pin_mask ) ; // Switch pin to low (1 pin mode)
if ( configPage4 . iacChannels ) { * idle2_pin_port | = ( idle2_pin_mask ) ; } //If 2 idle channels are in use, flip idle2 to be the opposite of idle1
}
else
{
//Reversed direction
* idle_pin_port | = ( idle_pin_mask ) ; // Switch pin high
if ( configPage4 . iacChannels ) { * idle2_pin_port & = ~ ( idle2_pin_mask ) ; } //If 2 idle channels are in use, flip idle2 to be the opposite of idle1
}
2015-09-29 00:21:00 -07:00
OCR4C = TCNT4 + ( idle_pwm_max_count - idle_pwm_cur_value ) ;
2017-02-19 22:57:46 -08:00
idle_pwm_state = false ;
2015-09-25 17:12:59 -07:00
}
else
{
2017-02-19 22:57:46 -08:00
if ( configPage4 . iacPWMdir = = 0 )
{
2016-05-27 05:44:40 -07:00
//Normal direction
* idle_pin_port | = ( idle_pin_mask ) ; // Switch pin high
if ( configPage4 . iacChannels ) { * idle2_pin_port & = ~ ( idle2_pin_mask ) ; } //If 2 idle channels are in use, flip idle2 to be the opposite of idle1
}
else
{
//Reversed direction
* idle_pin_port & = ~ ( idle_pin_mask ) ; // Switch pin to low (1 pin mode)
if ( configPage4 . iacChannels ) { * idle2_pin_port | = ( idle2_pin_mask ) ; } //If 2 idle channels are in use, flip idle2 to be the opposite of idle1
}
2015-09-29 16:27:37 -07:00
OCR4C = TCNT4 + idle_pwm_target_value ;
idle_pwm_cur_value = idle_pwm_target_value ;
2017-02-19 22:57:46 -08:00
idle_pwm_state = true ;
2015-09-25 17:12:59 -07:00
}
2017-02-19 22:57:46 -08:00
2015-09-25 17:12:59 -07:00
}
2016-10-09 22:58:19 -07:00
# elif defined (CORE_TEENSY)
2016-10-06 23:34:27 -07:00
//This function simply turns off the idle PWM and sets the pin low
static inline void disableIdle ( )
{
2017-02-19 22:57:46 -08:00
digitalWrite ( pinIdle1 , LOW ) ;
2016-10-06 23:34:27 -07:00
}
static inline void enableIdle ( ) { }
2016-01-12 22:06:55 -08:00
# endif