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"
2018-12-29 09:26:30 -08:00
# include "maths.h"
# include "timers.h"
# include "src/PID_v1/PID_v1.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
*/
2018-01-23 17:05:50 -08:00
integerPID idlePID ( & currentStatus . longRPM , & idle_pid_target_value , & idle_cl_target_rpm , configPage6 . idleKP , configPage6 . idleKI , configPage6 . 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-28 19:42:24 -08:00
//By default, turn off the PWM interrupt (It gets turned on below if needed)
IDLE_TIMER_DISABLE ( ) ;
2017-08-02 18:46:48 -07:00
# if defined(CORE_AVR) //AVR chips use the ISR for this
2017-02-28 19:42:24 -08:00
//No timer work required for AVRs. Timer is shared with the schedules and setup in there.
# elif defined (CORE_TEENSY)
2018-01-23 17:05:50 -08:00
if ( ( configPage6 . iacAlgorithm = = IAC_ALGORITHM_PWM_OL ) | | ( configPage6 . iacAlgorithm = = IAC_ALGORITHM_PWM_CL ) )
2017-04-14 15:42:33 -07:00
{
2018-12-29 09:26:30 -08:00
//FlexTimer 2, compare channel 0 is used for idle
2017-02-28 19:42:24 -08:00
FTM2_MODE | = FTM_MODE_WPDIS ; // Write Protection Disable
FTM2_MODE | = FTM_MODE_FTMEN ; //Flex Timer module enable
FTM2_MODE | = FTM_MODE_INIT ;
FTM2_SC = 0x00 ; // Set this to zero before changing the modulus
FTM2_CNTIN = 0x0000 ; //Shouldn't be needed, but just in case
FTM2_CNT = 0x0000 ; // Reset the count to zero
FTM2_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 ( 32 kHz )
* 11 External clock
*/
FTM2_SC | = FTM_SC_CLKS ( 0 b10 ) ;
/*
* Trim the slow clock from 32 kHz down to 31.25 kHz ( The slowest it will go )
* This is somewhat imprecise and documentation is not good .
* I poked the chip until I figured out the values associated with 31.25 kHz
*/
MCG_C3 = 0x9B ;
/*
* 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.13333 uS on the teensy 3.5 :
* 32000 Hz = F_BUS
* 128 * 1000000u S / F_BUS = 2.133 uS
*
* 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
*/
FTM2_SC | = FTM_SC_PS ( 0 b0 ) ; //No prescaler
//Setup the channels (See Pg 1014 of K64 DS).
FTM2_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
FTM2_C0SC | = FTM_CSC_MSA ; //Enable Compare mode
2018-12-29 09:26:30 -08:00
//The below enables channel compare interrupt, but this is done in idleControl()
//FTM2_C0SC |= FTM_CSC_CHIE;
FTM2_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
FTM2_C1SC | = FTM_CSC_MSA ; //Enable Compare mode
//Enable channel compare interrupt (This is currently disabled as not in use)
//FTM2_C1SC |= FTM_CSC_CHIE;
2017-02-28 19:42:24 -08:00
// enable IRQ Interrupt
NVIC_ENABLE_IRQ ( IRQ_FTM2 ) ;
2017-04-14 15:42:33 -07:00
}
2017-02-28 19:42:24 -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
2018-01-23 17:05:50 -08:00
switch ( configPage6 . iacAlgorithm )
2015-08-20 06:21:27 -07:00
{
2017-03-20 04:29:42 -07:00
case IAC_ALGORITHM_NONE : //Case 0 is no idle control ('None')
2015-08-20 06:21:27 -07:00
break ;
2017-02-19 22:57:46 -08:00
2017-03-20 04:29:42 -07:00
case IAC_ALGORITHM_ONOFF :
2015-08-20 06:21:27 -07:00
//Case 1 is on/off idle control
2018-01-23 17:05:50 -08:00
if ( ( currentStatus . coolant + CALIBRATION_TEMPERATURE_OFFSET ) < configPage6 . iacFastTemp )
2015-08-20 06:21:27 -07:00
{
digitalWrite ( pinIdle1 , HIGH ) ;
2017-03-29 06:01:52 -07:00
idleOn = true ;
2015-08-20 06:21:27 -07:00
}
break ;
2017-02-19 22:57:46 -08:00
2017-03-20 04:29:42 -07:00
case IAC_ALGORITHM_PWM_OL :
2015-08-20 06:21:27 -07:00
//Case 2 is PWM open loop
iacPWMTable . xSize = 10 ;
2015-08-25 20:27:50 -07:00
iacPWMTable . valueSize = SIZE_BYTE ;
2018-01-23 17:05:50 -08:00
iacPWMTable . values = configPage6 . iacOLPWMVal ;
iacPWMTable . axisX = configPage6 . iacBins ;
2017-02-19 22:57:46 -08:00
2017-03-22 18:25:51 -07:00
2015-08-20 06:21:27 -07:00
iacCrankDutyTable . xSize = 4 ;
2016-05-08 21:00:52 -07:00
iacCrankDutyTable . valueSize = SIZE_BYTE ;
2018-01-23 17:05:50 -08:00
iacCrankDutyTable . values = configPage6 . iacCrankDuty ;
iacCrankDutyTable . axisX = configPage6 . 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 ) ;
2017-08-03 20:13:32 -07:00
# if defined(CORE_STM32)
2018-01-23 17:05:50 -08:00
idle_pwm_max_count = 1000000L / ( configPage6 . idleFreq * 2 ) ; //Converts the frequency in Hz to the number of ticks (at 2uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 5KHz
2018-12-29 09:26:30 -08:00
# elif defined(CORE_AVR)
2018-01-23 17:05:50 -08:00
idle_pwm_max_count = 1000000L / ( 16 * configPage6 . 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
2018-12-29 09:26:30 -08:00
# elif defined(CORE_TEENSY)
idle_pwm_max_count = 1000000L / ( 32 * configPage6 . 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-08-03 20:13:32 -07:00
# endif
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
2017-03-20 04:29:42 -07:00
case IAC_ALGORITHM_PWM_CL :
2015-08-20 06:21:27 -07:00
//Case 3 is PWM closed loop
iacClosedLoopTable . xSize = 10 ;
2016-05-08 21:00:52 -07:00
iacClosedLoopTable . valueSize = SIZE_BYTE ;
2018-01-23 17:05:50 -08:00
iacClosedLoopTable . values = configPage6 . iacCLValues ;
iacClosedLoopTable . axisX = configPage6 . 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 ;
2018-01-23 17:05:50 -08:00
iacCrankDutyTable . values = configPage6 . iacCrankDuty ;
iacCrankDutyTable . axisX = configPage6 . 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 ) ;
2017-08-03 20:13:32 -07:00
# if defined(CORE_STM32)
2018-01-23 17:05:50 -08:00
idle_pwm_max_count = 1000000L / ( configPage6 . idleFreq * 2 ) ; //Converts the frequency in Hz to the number of ticks (at 2uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 5KHz
2018-12-29 09:26:30 -08:00
# elif defined(CORE_AVR)
2018-01-23 17:05:50 -08:00
idle_pwm_max_count = 1000000L / ( 16 * configPage6 . 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
2018-12-29 09:26:30 -08:00
# elif defined(CORE_TEENSY)
idle_pwm_max_count = 1000000L / ( 32 * configPage6 . 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-08-03 20:13:32 -07:00
# endif
2018-01-23 17:05:50 -08:00
idlePID . SetOutputLimits ( percentage ( configPage2 . iacCLminDuty , idle_pwm_max_count ) , percentage ( configPage2 . iacCLmaxDuty , idle_pwm_max_count ) ) ;
idlePID . SetTunings ( configPage6 . idleKP , configPage6 . idleKI , configPage6 . idleKD ) ;
2016-04-08 06:52:32 -07:00
idlePID . SetMode ( AUTOMATIC ) ; //Turn PID on
2017-03-13 23:25:59 -07:00
idleCounter = 0 ;
2015-08-20 06:21:27 -07:00
break ;
2017-02-19 22:57:46 -08:00
2017-03-20 04:29:42 -07:00
case IAC_ALGORITHM_STEP_OL :
2015-08-20 06:21:27 -07:00
//Case 2 is Stepper open loop
iacStepTable . xSize = 10 ;
2015-08-25 20:27:50 -07:00
iacStepTable . valueSize = SIZE_BYTE ;
2018-01-23 17:05:50 -08:00
iacStepTable . values = configPage6 . iacOLStepVal ;
iacStepTable . axisX = configPage6 . iacBins ;
2017-02-19 22:57:46 -08:00
2015-08-20 06:21:27 -07:00
iacCrankStepsTable . xSize = 4 ;
2018-12-29 09:26:30 -08:00
iacCrankStepsTable . valueSize = SIZE_BYTE ;
2018-01-23 17:05:50 -08:00
iacCrankStepsTable . values = configPage6 . iacCrankSteps ;
iacCrankStepsTable . axisX = configPage6 . iacCrankBins ;
iacStepTime = configPage6 . iacStepTime * 1000 ;
2017-02-19 22:57:46 -08:00
2016-12-11 04:23:54 -08:00
completedHomeSteps = 0 ;
2017-03-20 04:29:42 -07:00
idleStepper . curIdleStep = 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
2017-03-20 04:29:42 -07:00
case IAC_ALGORITHM_STEP_CL :
2015-08-20 06:21:27 -07:00
//Case 5 is Stepper closed loop
iacClosedLoopTable . xSize = 10 ;
2017-03-10 01:34:39 -08:00
iacClosedLoopTable . valueSize = SIZE_BYTE ;
2018-01-23 17:05:50 -08:00
iacClosedLoopTable . values = configPage6 . iacCLValues ;
iacClosedLoopTable . axisX = configPage6 . iacBins ;
2017-02-19 22:57:46 -08:00
2015-08-20 06:21:27 -07:00
iacCrankStepsTable . xSize = 4 ;
2018-12-29 09:26:30 -08:00
iacCrankStepsTable . valueSize = SIZE_BYTE ;
2018-01-23 17:05:50 -08:00
iacCrankStepsTable . values = configPage6 . iacCrankSteps ;
iacCrankStepsTable . axisX = configPage6 . iacCrankBins ;
iacStepTime = configPage6 . iacStepTime * 1000 ;
2017-02-19 22:57:46 -08:00
2017-03-10 01:34:39 -08:00
completedHomeSteps = 0 ;
2017-03-13 23:25:59 -07:00
idleCounter = 0 ;
2017-03-20 04:29:42 -07:00
idleStepper . curIdleStep = 0 ;
2015-08-25 20:27:50 -07:00
idleStepper . stepperStatus = SOFF ;
2017-03-10 01:34:39 -08:00
2018-01-23 17:05:50 -08:00
idlePID . SetOutputLimits ( 0 , ( configPage6 . iacStepHome * 3 ) ) ; //Maximum number of steps probably needs its own setting
idlePID . SetTunings ( configPage6 . idleKP , configPage6 . idleKI , configPage6 . idleKD ) ;
2017-03-10 01:34:39 -08:00
idlePID . SetMode ( AUTOMATIC ) ; //Turn PID on
2015-08-20 06:21:27 -07:00
break ;
2017-08-02 18:46:48 -07:00
default :
//Well this just shouldn't happen
break ;
2015-08-20 06:21:27 -07:00
}
2018-01-23 17:05:50 -08:00
idleInitComplete = configPage6 . iacAlgorithm ; //Sets which idle method was initialised
2017-03-20 04:29:42 -07:00
currentStatus . idleLoad = 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 ( 4 , TIMER_OUTPUT_COMPARE ) ;
2017-09-01 12:16:45 -07:00
if ( idle_pwm_max_count > 0 ) { Timer1 . attachInterrupt ( 4 , idleInterrupt ) ; } //on first flash the configPage4.iacAlgorithm is invalid
2017-08-31 13:38:41 -07:00
Timer1 . resume ( ) ;
# endif
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
{
2018-01-23 17:05:50 -08:00
if ( idleInitComplete ! = configPage6 . iacAlgorithm ) { initialiseIdle ( ) ; }
2018-04-07 21:40:12 -07:00
if ( currentStatus . RPM > 0 ) { enableIdle ( ) ; }
2017-02-19 22:57:46 -08:00
2018-05-04 00:55:58 -07:00
//Check whether the idleUp is active
if ( configPage2 . idleUpEnabled = = true )
{
if ( configPage2 . idleUpPolarity = = 0 ) { currentStatus . idleUpActive = ! digitalRead ( pinIdleUp ) ; } //Normal mode (ground switched)
else { currentStatus . idleUpActive = digitalRead ( pinIdleUp ) ; } //Inverted mode (5v activates idleUp)
}
else { currentStatus . idleUpActive = false ; }
2018-01-23 17:05:50 -08:00
switch ( configPage6 . iacAlgorithm )
2015-08-20 06:21:27 -07:00
{
2017-03-13 23:25:59 -07:00
case IAC_ALGORITHM_NONE : //Case 0 is no idle control ('None')
2015-08-20 06:21:27 -07:00
break ;
2017-02-19 22:57:46 -08:00
2017-03-13 23:25:59 -07:00
case IAC_ALGORITHM_ONOFF : //Case 1 is on/off idle control
2018-01-23 17:05:50 -08:00
if ( ( currentStatus . coolant + CALIBRATION_TEMPERATURE_OFFSET ) < configPage6 . iacFastTemp ) //All temps are offset by 40 degrees
2015-08-20 06:21:27 -07:00
{
digitalWrite ( pinIdle1 , HIGH ) ;
idleOn = true ;
}
else if ( idleOn ) { digitalWrite ( pinIdle1 , LOW ) ; idleOn = false ; }
break ;
2017-02-19 22:57:46 -08:00
2017-03-13 23:25:59 -07:00
case IAC_ALGORITHM_PWM_OL : //Case 2 is PWM open loop
2015-08-20 06:21:27 -07:00
//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
2018-05-04 00:55:58 -07:00
if ( currentStatus . idleUpActive = = true ) { currentStatus . idleDuty + = configPage2 . idleUpAdder ; } //Add Idle Up amount if active
2018-04-07 21:40:12 -07:00
if ( currentStatus . idleDuty = = 0 ) { disableIdle ( ) ; break ; }
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 ;
}
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
2018-05-04 00:55:58 -07:00
if ( currentStatus . idleUpActive = = true ) { currentStatus . idleDuty + = configPage2 . idleUpAdder ; } //Add Idle Up amount if active
2016-10-06 23:34:27 -07:00
if ( currentStatus . idleDuty = = 0 ) { disableIdle ( ) ; break ; }
2016-01-12 22:06:55 -08:00
idle_pwm_target_value = percentage ( currentStatus . idleDuty , idle_pwm_max_count ) ;
2018-12-29 09:26:30 -08:00
currentStatus . idleLoad = currentStatus . idleDuty > > 1 ; //Idle Load is divided by 2 in order to send to TS
2015-08-20 06:21:27 -07:00
idleOn = true ;
}
2018-12-29 09:26:30 -08:00
2015-08-20 06:21:27 -07:00
break ;
2017-02-19 22:57:46 -08:00
2017-03-13 23:25:59 -07:00
case IAC_ALGORITHM_PWM_CL : //Case 3 is PWM closed loop
2016-04-06 20:28:13 -07:00
//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
2018-01-23 17:05:50 -08:00
if ( ( idleCounter & 31 ) = = 1 ) { idlePID . SetTunings ( configPage6 . idleKP , configPage6 . idleKI , configPage6 . idleKD ) ; } //This only needs to be run very infrequently, once every 32 calls to idleControl(). This is approx. once per second
2016-04-06 20:28:13 -07:00
idlePID . Compute ( ) ;
2017-03-10 01:34:39 -08:00
idle_pwm_target_value = idle_pid_target_value ;
2016-10-06 23:34:27 -07:00
if ( idle_pwm_target_value = = 0 ) { disableIdle ( ) ; }
2017-03-20 04:29:42 -07:00
currentStatus . idleLoad = ( ( unsigned long ) ( idle_pwm_target_value * 100UL ) / idle_pwm_max_count ) > > 1 ;
2018-05-04 00:55:58 -07:00
if ( currentStatus . idleUpActive = = true ) { currentStatus . idleDuty + = configPage2 . idleUpAdder ; } //Add Idle Up amount if active
2016-05-08 21:00:52 -07:00
//idle_pwm_target_value = 104;
2017-03-13 23:25:59 -07:00
idleCounter + + ;
2015-08-20 06:21:27 -07:00
break ;
2017-02-19 22:57:46 -08:00
2017-03-13 23:25:59 -07:00
case IAC_ALGORITHM_STEP_OL : //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
2017-11-26 14:24:00 -08:00
if ( ( checkForStepping ( ) = = false ) & & ( isStepperHomed ( ) = = true ) ) //Check that homing is complete and that there's not currently a step already taking place. MUST BE IN THIS ORDER!
2015-08-20 06:21:27 -07:00
{
2017-08-02 18:46:48 -07:00
//Check for cranking pulsewidth
if ( BIT_CHECK ( currentStatus . engine , BIT_ENGINE_CRANK ) )
2016-12-11 04:23:54 -08:00
{
2017-08-02 18:46:48 -07:00
//Currently cranking. Use the cranking table
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
2018-05-04 00:55:58 -07:00
if ( currentStatus . idleUpActive = = true ) { idleStepper . targetIdleStep + = configPage2 . idleUpAdder ; } //Add Idle Up amount if active
2017-08-02 18:46:48 -07:00
doStep ( ) ;
2016-12-11 04:23:54 -08:00
}
2017-08-02 18:46:48 -07:00
else if ( ( currentStatus . coolant + CALIBRATION_TEMPERATURE_OFFSET ) < iacStepTable . axisX [ IDLE_TABLE_SIZE - 1 ] )
{
//Standard running
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
2018-05-04 00:55:58 -07:00
if ( currentStatus . idleUpActive = = true ) { idleStepper . targetIdleStep + = configPage2 . idleUpAdder ; } //Add Idle Up amount if active
2018-01-23 17:05:50 -08:00
iacStepTime = configPage6 . iacStepTime * 1000 ;
2017-08-02 18:46:48 -07:00
}
doStep ( ) ;
}
currentStatus . idleLoad = idleStepper . curIdleStep > > 1 ; //Current step count (Divided by 2 for byte)
2015-08-20 06:21:27 -07:00
}
2017-03-10 01:34:39 -08:00
break ;
2017-03-13 23:25:59 -07:00
case IAC_ALGORITHM_STEP_CL : //Case 5 is closed loop stepper control
2017-03-10 01:34:39 -08:00
//First thing to check is whether there is currently a step going on and if so, whether it needs to be turned off
2017-11-26 14:24:00 -08:00
if ( ( checkForStepping ( ) = = false ) & & ( isStepperHomed ( ) = = true ) ) //Check that homing is complete and that there's not currently a step already taking place. MUST BE IN THIS ORDER!
2017-04-02 04:17:29 -07:00
{
2017-08-02 18:46:48 -07:00
if ( ( idleCounter & 31 ) = = 1 )
{
//This only needs to be run very infrequently, once every 32 calls to idleControl(). This is approx. once per second
2018-01-23 17:05:50 -08:00
idlePID . SetTunings ( configPage6 . idleKP , configPage6 . idleKI , configPage6 . idleKD ) ;
iacStepTime = configPage6 . iacStepTime * 1000 ;
2017-08-02 18:46:48 -07:00
}
2017-03-10 01:34:39 -08:00
2017-08-02 18:46:48 -07:00
idle_cl_target_rpm = table2D_getValue ( & iacClosedLoopTable , currentStatus . coolant + CALIBRATION_TEMPERATURE_OFFSET ) * 10 ; //All temps are offset by 40 degrees
2018-05-04 00:55:58 -07:00
if ( currentStatus . idleUpActive = = true ) { idle_pid_target_value + = configPage2 . idleUpAdder ; } //Add Idle Up amount if active
2017-08-02 18:46:48 -07:00
idlePID . Compute ( ) ;
idleStepper . targetIdleStep = idle_pid_target_value ;
2017-02-19 22:57:46 -08:00
2017-08-02 18:46:48 -07:00
doStep ( ) ;
currentStatus . idleLoad = idleStepper . curIdleStep > > 1 ; //Current step count (Divided by 2 for byte)
idleCounter + + ;
}
break ;
default :
//There really should be a valid idle type
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
/*
2017-03-10 01:34:39 -08:00
Checks whether the stepper has been homed yet . If it hasn ' t , will handle the next step
Returns :
True : If the system has been homed . No other action is taken
False : If the motor has not yet been homed . Will also perform another homing step .
*/
static inline byte isStepperHomed ( )
{
2017-08-02 18:46:48 -07:00
bool isHomed = true ; //As it's the most common scenario, default value is true
2018-01-23 17:05:50 -08:00
if ( completedHomeSteps < ( configPage6 . iacStepHome * 3 ) ) //Home steps are divided by 3 from TS
2017-03-10 01:34:39 -08:00
{
digitalWrite ( pinStepperDir , STEPPER_BACKWARD ) ; //Sets stepper direction to backwards
2017-11-26 14:24:00 -08:00
digitalWrite ( pinStepperEnable , LOW ) ; //Enable the DRV8825
2017-03-10 01:34:39 -08:00
digitalWrite ( pinStepperStep , HIGH ) ;
2018-03-09 19:07:06 -08:00
idleStepper . stepStartTime = micros_safe ( ) ;
2017-03-10 01:34:39 -08:00
idleStepper . stepperStatus = STEPPING ;
completedHomeSteps + + ;
idleOn = true ;
2017-08-02 18:46:48 -07:00
isHomed = false ;
2017-03-10 01:34:39 -08:00
}
2017-08-02 18:46:48 -07:00
return isHomed ;
2017-03-10 01:34:39 -08:00
}
/*
Checks whether a step is currently underway or whether the motor is in ' cooling ' state ( ie whether it ' s ready to begin another step or not )
Returns :
True : If a step is underway or motor is ' cooling '
False : If the motor is ready for another step
*/
static inline byte checkForStepping ( )
{
2017-08-02 18:46:48 -07:00
bool isStepping = false ;
if ( ( idleStepper . stepperStatus = = STEPPING ) | | ( idleStepper . stepperStatus = = COOLING ) )
2017-03-10 01:34:39 -08:00
{
2018-03-09 19:07:06 -08:00
if ( micros_safe ( ) > ( idleStepper . stepStartTime + iacStepTime ) )
2017-03-10 01:34:39 -08: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
2018-03-09 19:07:06 -08:00
idleStepper . stepStartTime = micros_safe ( ) ;
2017-03-10 01:34:39 -08:00
idleStepper . stepperStatus = COOLING ; //'Cooling' is the time the stepper needs to sit in LOW state before the next step can be made
2017-08-02 18:46:48 -07:00
isStepping = true ;
2017-03-10 01:34:39 -08:00
}
else
{
2017-04-02 04:17:29 -07:00
//Means we're in COOLING status but have been in this state long enough. Go into off state
2017-03-10 01:34:39 -08:00
idleStepper . stepperStatus = SOFF ;
2017-04-02 04:17:29 -07:00
digitalWrite ( pinStepperEnable , HIGH ) ; //Disable the DRV8825
2017-03-10 01:34:39 -08:00
}
}
else
{
//Means we're in a step, but it doesn't need to turn off yet. No further action at this time
2017-08-02 18:46:48 -07:00
isStepping = true ;
2017-03-10 01:34:39 -08:00
}
}
2017-08-02 18:46:48 -07:00
return isStepping ;
2017-03-10 01:34:39 -08:00
}
/*
Performs a step
2015-08-20 21:14:47 -07:00
*/
2017-03-10 01:34:39 -08:00
static inline void doStep ( )
2015-08-20 21:14:47 -07:00
{
2018-01-23 17:05:50 -08:00
if ( ( idleStepper . targetIdleStep < = ( idleStepper . curIdleStep - configPage6 . iacStepHyster ) ) | | ( idleStepper . targetIdleStep > = ( idleStepper . curIdleStep + configPage6 . iacStepHyster ) ) ) //Hysteris check
2017-08-02 18:46:48 -07:00
{
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
digitalWrite ( pinStepperEnable , LOW ) ; //Enable the DRV8825
digitalWrite ( pinStepperStep , HIGH ) ;
2018-03-09 19:07:06 -08:00
idleStepper . stepStartTime = micros_safe ( ) ;
2017-08-02 18:46:48 -07:00
idleStepper . stepperStatus = STEPPING ;
idleOn = true ;
}
2015-08-20 21:14:47 -07:00
}
2015-09-25 17:12:59 -07:00
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 ( )
{
2018-01-23 17:05:50 -08:00
if ( ( configPage6 . iacAlgorithm = = IAC_ALGORITHM_PWM_CL ) | | ( configPage6 . iacAlgorithm = = IAC_ALGORITHM_PWM_OL ) )
2017-03-20 04:29:42 -07:00
{
IDLE_TIMER_DISABLE ( ) ;
digitalWrite ( pinIdle1 , LOW ) ;
}
2018-01-23 17:05:50 -08:00
else if ( ( configPage6 . iacAlgorithm = = IAC_ALGORITHM_STEP_CL ) | | ( configPage6 . iacAlgorithm = = IAC_ALGORITHM_STEP_OL ) )
2017-03-20 04:29:42 -07:00
{
2017-11-27 15:09:54 -08:00
//Only disable the stepper motor if homing is completed
2018-07-22 15:28:56 -07:00
if ( ( checkForStepping ( ) = = false ) & & ( isStepperHomed ( ) = = true ) )
2017-11-26 22:39:53 -08:00
{
digitalWrite ( pinStepperEnable , HIGH ) ; //Disable the DRV8825
idleStepper . targetIdleStep = idleStepper . curIdleStep ; //Don't try to move anymore
}
2017-03-20 04:29:42 -07:00
}
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 ( )
{
2018-01-23 17:05:50 -08:00
if ( ( configPage6 . iacAlgorithm = = IAC_ALGORITHM_PWM_CL ) | | ( configPage6 . iacAlgorithm = = IAC_ALGORITHM_PWM_OL ) )
2017-03-20 04:29:42 -07:00
{
IDLE_TIMER_ENABLE ( ) ;
}
2018-01-23 17:05:50 -08:00
else if ( ( configPage6 . iacAlgorithm = = IAC_ALGORITHM_STEP_CL ) | | ( configPage6 . iacAlgorithm = = IAC_ALGORITHM_STEP_OL ) )
2017-03-20 04:29:42 -07:00
{
}
2016-10-06 23:34:27 -07:00
}
2017-08-02 18:46:48 -07:00
# if defined(CORE_AVR) //AVR chips use the ISR for this
2015-09-29 00:21:00 -07:00
ISR ( TIMER4_COMPC_vect )
2017-03-22 18:25:51 -07:00
# elif defined (CORE_TEENSY) || defined (CORE_STM32)
2017-02-28 19:42:24 -08:00
static inline void idleInterrupt ( ) //Most ARM chips can simply call a function
# endif
2015-09-25 17:12:59 -07:00
{
if ( idle_pwm_state )
{
2018-01-23 17:05:50 -08:00
if ( configPage6 . iacPWMdir = = 0 )
2017-02-19 22:57:46 -08:00
{
2016-05-27 05:44:40 -07:00
//Normal direction
* idle_pin_port & = ~ ( idle_pin_mask ) ; // Switch pin to low (1 pin mode)
2018-01-23 17:05:50 -08:00
if ( configPage6 . iacChannels = = 1 ) { * idle2_pin_port | = ( idle2_pin_mask ) ; } //If 2 idle channels are in use, flip idle2 to be the opposite of idle1
2016-05-27 05:44:40 -07:00
}
else
{
//Reversed direction
* idle_pin_port | = ( idle_pin_mask ) ; // Switch pin high
2018-01-23 17:05:50 -08:00
if ( configPage6 . iacChannels = = 1 ) { * idle2_pin_port & = ~ ( idle2_pin_mask ) ; } //If 2 idle channels are in use, flip idle2 to be the opposite of idle1
2016-05-27 05:44:40 -07:00
}
2017-02-28 19:42:24 -08:00
IDLE_COMPARE = IDLE_COUNTER + ( 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
{
2018-01-23 17:05:50 -08:00
if ( configPage6 . iacPWMdir = = 0 )
2017-02-19 22:57:46 -08:00
{
2016-05-27 05:44:40 -07:00
//Normal direction
* idle_pin_port | = ( idle_pin_mask ) ; // Switch pin high
2018-01-23 17:05:50 -08:00
if ( configPage6 . iacChannels = = 1 ) { * idle2_pin_port & = ~ ( idle2_pin_mask ) ; } //If 2 idle channels are in use, flip idle2 to be the opposite of idle1
2016-05-27 05:44:40 -07:00
}
else
{
//Reversed direction
* idle_pin_port & = ~ ( idle_pin_mask ) ; // Switch pin to low (1 pin mode)
2018-01-23 17:05:50 -08:00
if ( configPage6 . iacChannels = = 1 ) { * idle2_pin_port | = ( idle2_pin_mask ) ; } //If 2 idle channels are in use, flip idle2 to be the opposite of idle1
2016-05-27 05:44:40 -07:00
}
2017-02-28 19:42:24 -08:00
IDLE_COMPARE = IDLE_COUNTER + idle_pwm_target_value ;
2015-09-29 16:27:37 -07:00
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
}
}
2018-12-29 09:26:30 -08:00
# if defined(CORE_TEENSY)
void ftm2_isr ( void )
{
//FTM2 only has 2 compare channels
//Use separate variables for each test to ensure conversion to bool
bool interrupt1 = ( FTM2_C0SC & FTM_CSC_CHF ) ;
bool interrupt2 = ( FTM2_C1SC & FTM_CSC_CHF ) ; //Not currently used
if ( interrupt1 ) { FTM2_C0SC & = ~ FTM_CSC_CHF ; idleInterrupt ( ) ; }
else if ( interrupt2 ) { FTM2_C1SC & = ~ FTM_CSC_CHF ; } //Add a callback function here if this is ever used
}
# endif