Air Conditioning Control - Feature Implementation (#665)

* 19.09.2021

* Final testing of AC Control, some idle features fixed

AC control feature added, better than the existing idle-up feature (which can still be used for other things, e.g. electrical load detection). Air conditioning is locked out with coolant temp, RPM high/low, and high TPS. So the A/C automatically cuts out when driving hard.

Idle step now works correctly with closed loop PWM, open loop PWM, and closed+open loop PWM. Untested with stepper motor, but no reason it shouldn't work.

* Fixed mistakenly incremented page sizes

* Initial changes as per HWright9

-Renamed engineRunSeconds to acAfterEngineStartDelay
-Formatted large if statements better
-Fixed acStartDelay overflow bug
-Improved readability of logic

* Final fixes as per HWright9's feedback

-Add high/low RPM lockout delay, similar to the high TPM lockout delay
-General tidy-up

* Added stand-alone fan, moved config data in EEPROM

-Added additional configurable stand-alone A/C fan output, for when there is dedicated cooling fan for the A/C compressor. This is independent of the engine cooling fan logic.
-Moved config storage in EEPROM to configPage9, as noisymime's SD card logging has used the (previously unused) bytes I had used in configPage13.
-Minor bug fix - rename Aux in 1-16 to Aux in 0-15

* Revert to current master branch - as of master commit 97f8ef795a

* A/C Control Re-Integrated from AC-Control-Clean-3 (@Corey-Harding). Tested & ready to merge.

Additionally, added @HazuTo25's lines into the update() routine to configure default A/C settings.

* Changed updates.ino to just set A/C to disabled

* Fix change reverted by mistake - master merge commit 73badbce8c

* Fix remaining mistakes from previous master merge

* Remove test statements left in by mistake

* define unusedBits

* Remove test statements left in by mistake

* Increase timing granularity to 0.1s

* idleUpRPMAdder

* Remove another line put in by mistake by auto merge

* idleUpRPMFixes

* Update speeduino.ino

* Tweak A/C idle up descriptions

* Tweak A/C TS descriptors again

* Fixed alignment bug that turned page 15 config values into gobbledegook.

This had the symptom of the A/C request never triggering, because when a pin was assigned in TS (e.g. I did 27), a completely different pin would be read from config15 (in my case 22 - connecting the button to pin 22 would work in this case, even though TS was set to 27).

* Fix bit count - should be 6 to match ini file

* Increase minimum RPM lockout granularity

* Change granularity of A/C minimum RPM lockout to 10 RPM; Inline some functions for readability

* Add static inline function prototypes to auxiliaries.h as per the style guide.

* Fixed page 15 merge errors

* Style changes to suit new pinIsUsed() checks in setPinMappings()

* Add PWM Fan Control Minimum Clamp Value when A/C Compressor Engaged

* Fix comment

* Fix bug with stand-alone fan initialisation

Pin was unable to be used in prog. I/O even if fan was disabled, because it was always initialised as an output even if it was disabled. Fixed in this commit.

* Correction to Fahrenheit temperature scaling

* Move A/C updates to correct next release

Co-authored-by: shiznit304 <62686180+shiznit304@users.noreply.github.com>
Co-authored-by: Josh Stewart <josh@noisymime.org>
This commit is contained in:
Afroboltski 2022-09-07 12:23:01 +12:00 committed by GitHub
parent a4b00dca90
commit 75d6c2ff61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 594 additions and 35 deletions

View File

@ -1405,7 +1405,38 @@ page = 15
unused15_1_3 = bits, U08, 80, [7:7], "False", "INVALID"
boostDCWhenDisabled = scalar, U08, 81, "%", 1, 0, 0, 100, 0
boostControlEnableThreshold = scalar, U08, 82, "kpa", 1, 0.0, 0.0, 255, 0
unused15_3_176 = array, U08, 83, [173], "%", 1.0, 0.0, 0.0, 255, 0
; Air conditioning control
airConEnable = bits, U08, 83, [0:0], "Off", "On"
airConCompPol = bits, U08, 83, [1:1], "Normal", "Inverted"
airConReqPol = bits, U08, 83, [2:2], "Normal", "Inverted"
airConTurnsFanOn = bits, U08, 83, [3:3], "No", "Yes"
airConFanEnabled = bits, U08, 83, [4:4], "Disabled", "Enabled"
airConFanPol = bits, U08, 83, [5:5], "Normal", "Inverted"
airConUnused1 = bits, U08, 83, [6:7], "0", "1", "2", "3"
airConCompPin = bits, U08, 84, [0:5], "Unused", "INVALID", "INVALID", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "INVALID", "A8", "A9", "A10", "A11", "A12", "A13", "A14", "A15", "INVALID"
airConUnused2 = bits, U08, 84, [6:7], "0", "1", "2", "3"
airConReqPin = bits, U08, 85, [0:5], "Unused", "INVALID", "INVALID", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "INVALID", "A8", "A9", "A10", "A11", "A12", "A13", "A14", "A15", "INVALID"
airConUnused3 = bits, U08, 85, [6:7], "0", "1", "2", "3"
airConTPSCut = scalar, U08, 86, "%", 0.5, 0.0, 0.0, 100.0, 1
airConMinRPM = scalar, U08, 87, "RPM", 10, 0.0, 0.0, 2550, 0
airConMaxRPM = scalar, U08, 88, "RPM", 100, 0.0, 0.0, 25500, 0
#if CELSIUS
airConClTempCut = scalar, U08, 89, "C", 1.0, -40, -40, 215, 0
#else
airConClTempCut = scalar, U08, 89, "F", 1.8, -22.23, -40, 419, 0
#endif
airConIdleSteps = scalar, U08, 90, "%/Steps", 1.0, 0.0, 0.0, 255, 0
airConTPSCutTime = scalar, U08, 91, "s", 0.1, 0.0, 0.0, 25.5, 1
airConCompOnDelay = scalar, U08, 92, "s", 0.1, 0.0, 0.0, 25.5, 1
airConAfterStartDelay = scalar, U08, 93, "s", 0.1, 0.0, 0.0, 25.5, 1
airConRPMCutTime = scalar, U08, 94, "s", 0.1, 0.0, 0.0, 25.5, 1
airConFanPin = bits, U08, 95, [0:5], "Unused", "INVALID", "INVALID", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "INVALID", "A8", "A9", "A10", "A11", "A12", "A13", "A14", "A15", "INVALID"
airConUnused4 = bits, U08, 95, [6:7], "0", "1", "2", "3"
airConIdleUpRPMAdder = scalar, U08, 96, "Added Target RPM", 10.0, 0.0, 0.0, 250.0, 0
airConPwmFanMinDuty = scalar, U08, 97, "%", 0.5, 0.0, 0.0, 100.0, 1
Unused15_98_255 = array, U08, 98, [158], "%", 1.0, 0.0, 0.0, 255, 0
;-------------------------------------------------------------------------------
@ -1508,6 +1539,15 @@ page = 15
requiresPowerCycle = caninput_sel15b
requiresPowerCycle = outputPin
requiresPowerCycle = airConEnable
requiresPowerCycle = airConCompPin
requiresPowerCycle = airConReqPin
requiresPowerCycle = airConFanPin
requiresPowerCycle = airConReqPol
requiresPowerCycle = airConCompPol
requiresPowerCycle = airConFanPol
requiresPowerCycle = airConFanEnabled
defaultValue = pinLayout, 1
defaultValue = TrigPattern, 0
defaultValue = useResync, 1
@ -1586,6 +1626,8 @@ page = 15
defaultValue = ANGLEFILTER_VVT, 0
defaultValue = idleAdvStartDelay, 0.2 ;0.2S for a quick gear change without change the table advance
defaultValue = boostByGearEnabled, 0
defaultValue = airConIdleUpRPMAdder, 100
defaultValue = airConPwmFanMinDuty, 80
#if CELSIUS
defaultValue = vvtMinClt, 70
#else
@ -1630,6 +1672,30 @@ page = 15
defaultValue = ADCFILTER_BARO, 64
defaultValue = FILTER_FLEX, 75
; AirCon Default Values
defaultValue = airConEnable, 0
defaultValue = airConCompPol, 0
defaultValue = airConReqPol, 0
defaultValue = airConTurnsFanOn, 1
defaultValue = airConCompPin, 0
defaultValue = airConReqPin, 0
defaultValue = airConFanPin, 0
defaultValue = airConFanPol, 0
defaultValue = airConFanEnabled, 0
defaultValue = airConTPSCut, 65
defaultValue = airConMinRPM, 500
defaultValue = airConMaxRPM, 4700
defaultValue = airConClTempCut, 120
defaultValue = airConIdleSteps, 0
defaultValue = airConTPSCutTime, 5.00
defaultValue = airConCompOnDelay, 0.4
defaultValue = airConAfterStartDelay, 5.00
defaultValue = airConRPMCutTime, 5.00
;Again, force the setting from the controller for the trigger edges. This is particularly useful for the Oct 2018 update where the names of the edges changed
controllerPriority = TrigEdge
controllerPriority = TrigEdgeSec
;VSS Ratios and calibration need controller priority so they can be set with the command buttons
controllerPriority = vssPulsesPerKm
controllerPriority = vssRatio1
@ -1840,6 +1906,7 @@ menuDialog = main
subMenu = fuelpump, "Fuel Pump"
subMenu = NitrousControl, "Nitrous"
subMenu = vssSettings, "VSS and Gear detection"
subMenu = airCon, "Air Conditioning Control"
subMenu = std_separator
subMenu = boostSettings, "Boost Control"
subMenu = boostLoad, "Boost Targets/Duty", 8, { boostEnabled }
@ -1954,6 +2021,27 @@ menuDialog = main
fanPin = "The Arduino pin that the fan control signal will output on. This is NOT necessarily the same as the connector pin on any particular board"
fanSP = "The trigger temperature for the fan. Fan will be enabled above this temp"
airConEnable = "Whether we are doing A/C control"
airConCompPol = "Normal should be used in most cases - where a logic HIGH output turns on the output power transistor, which is the ON state. Inverted makes logic LOW the ON state, in the rare case that turning off the transistor turns the compressor on, or you are using some sort of depletion mode FET".
airConReqPol = "Normal should be used in most cases - where the A/C button provides a ground when pressed. Inverted removes the internal pullup resistor, and makes +5V the ON signal instead of ground."
airConTurnsFanOn = "Whether the cooling fan turns on when the A/C compressor is running."
airConCompPin = "The Arduino pin that the A/C request compressor signal is output on. This is NOT necesarrily the same as the connector pin on any particlar board."
airConReqPin = "The Arduino pin that the A/C request control signal is input on. This is NOT necesarrily the same as the connector pin on any particlar board."
airConFanPin = "The Arduino pin that the A/C fan control signal is output on (if enabled). This is only needed if you have a stand-alone A/C fan, separate to the cooling fan (which can be enabled to come on with the A/C compressor). This is NOT necesarrily the same as the connector pin on any particlar board."
airConFanPol = "Normal should be used in most cases - where a logic HIGH output turns on the output power transistor, which is the ON state. Inverted makes logic LOW the ON state, in the rare case that turning off the transistor turns the fan on, or you are using some sort of depletion mode FET".
airConFanEnabled = "Enable/disable stand-alone A/C fan. This would be a separate fan that only comes on when the A/C compressor is engaged, and should not be needed if the main cooling fan comes on with the A/C."
airConTPSCut = "The TPS position at which the air compressor will be locked out and turn off. When TPS falls below this level (minus a 5% hysteresis), the A/C compressor will kick back in after the delay period (TPS Cut Time), provided the A/C request signal is still present."
airConMinRPM = "The minimum RPM at which we will allow the A/C compressor to run (this setting is in 10 RPM steps)."
airConMaxRPM = "The maximum RPM at which we will allow the A/C compressor to run (this setting is in 100 RPM steps)."
airConClTempCut = "The coolant temperature above which we will NOT allow the A/C compressor to run."
airConIdleSteps = "Increase the idle PWM (%) or stepper motor (steps) up by this amount when the A/C request is active. Note that some cars have a separate idle-up valve that switches on automatically in tandem with the A/C compressor. In this case, this setting might be set to zero."
airConTPSCutTime = "The TPS lockout time, for which the A/C compressor will not run after a high TPS condition has gone away. This prevents the compressor cutting in on gear changes if the car is being driven hard."
airConCompOnDelay = "The delay period between a successful A/C request signal, and the compressor kicking in. This gives the idle-up time to kick in before the compressor loads the engine."
airConAfterStartDelay = "The delay period after first starting the car, during which the A/C compressor will not run."
airConRPMCutTime = "The RPM lockout time, for which the A/C compressor will not run after a high or low RPM condition has gone away. This prevents the compressor cutting in on gear changes if the car is being driven hard, or cutting in to early after nearly stalling the engine."
airConIdleUpRPMAdder = "Increase the idle RPM target (as used by Idle Advance and Closed-Loop Idle) by this amount when the A/C request is active."
airConPwmFanMinDuty = "If you are using a PWM cooling fan, the fan duty will be clamped to this value or higher (based on the coolant temp./duty cycle table) when the A/C compressor is engaged."
vssPulsesPerKm = "The number of pulses on the VSS signal per KM/Mile"
vssSmoothing = "A smoothing factor to help reduce noise in the VSS signal. Typical values are between 0 and 50"
@ -2644,6 +2732,43 @@ menuDialog = main
dialog = pwmFan, "PWM Fan Curve",
panel = pwm_fan_curve,
dialog = airConIdleUp, "Idle Up When A/C Compressor On"
field = "Idle-Up Amount", airConIdleSteps, { airConEnable }
field = "Idle-Up RPM Adder", airConIdleUpRPMAdder, { airConEnable }
dialog = airConRPMLimit,"RPM Limiting"
field = "Min. RPM for A/C Operation", airConMinRPM, { airConEnable }
field = "Max. RPM for A/C Operation", airConMaxRPM, { airConEnable }
field = "Lockout time after high/low RPM", airConRPMCutTime, { airConEnable }
dialog = airConTPSLimit,"TPS Limiting"
field = "Cut A/C at TPS Position", airConTPSCut, { airConEnable }
field = "Lockout time after high TPS", airConTPSCutTime, { airConEnable }
dialog = airConCoolantTempLimit,"Coolant Temp. Limiting"
field = "Coolant A/C Cutout Temp.", airConClTempCut, { airConEnable }
dialog = airConFanSettings
field = "Run Engine Cooling Fan with A/C", airConTurnsFanOn, { airConEnable }
field = "Min. Duty for PWM Cooling Fan", airConPwmFanMinDuty, { airConEnable }
field = "Stand-Alone A/C Fan", airConFanEnabled, { airConEnable }
field = "Stand-Alone A/C Fan Output Pin", airConFanPin, { airConEnable && airConFanEnabled }
field = "Stand-Alone A/C Fan Output Polarity", airConFanPol, {airConEnable && airConFanEnabled }
dialog = airCon,"Air Conditioning"
field = "Air Conditioning Enable", airConEnable
field = "A/C Request Input Pin", airConReqPin, { airConEnable }
field = "A/C Compressor Output Pin", airConCompPin, { airConEnable }
field = "A/C Request Input Polarity", airConReqPol, { airConEnable }
field = "A/C Comp. Output Polarity", airConCompPol, { airConEnable }
field = "A/C Comp. On Delay (During Operation)", airConCompOnDelay, { airConEnable }
field = "A/C Initial Delay After Cranking", airConAfterStartDelay, { airConEnable }
panel = airConRPMLimit
panel = airConTPSLimit
panel = airConCoolantTempLimit
panel = airConFanSettings
panel = airConIdleUp
dialog = stepper_idle, "Stepper Idle"
field = "Step time (ms)", iacStepTime, { iacAlgorithm == 4 || iacAlgorithm == 5 || iacAlgorithm == 7 }
field = "Cool time (ms)", iacCoolTime, { iacAlgorithm == 4 || iacAlgorithm == 5 || iacAlgorithm == 7 }
@ -4988,6 +5113,11 @@ cmdVSSratio6 = "E\x99\x06"
indicator = { outputsStatus6 }, "Programmable out 7 Off", "Programmable out 7 ON", white, black, green, black
indicator = { outputsStatus7 }, "Programmable out 8 Off", "Programmable out 8 ON", white, black, green, black
;AC stuff
indicator = { airConRequest }, "A/C Request OFF", "A/C Request ON", white, black, green, black
indicator = { airConCompressor }, "A/C Comp OFF", "A/C Comp ON", white, black, green, black
indicator = { airConFanStatus }, "A/C Fan OFF", "A/C Fan ON", white, black, green, black
;sd card indicators
indicator = { sd_status & 1}, "No SD Card", "SD Present", white, black, green, black
indicator = { sd_status & 4}, "SD ready", "SD ready", white, black, green, black
@ -5003,7 +5133,7 @@ cmdVSSratio6 = "E\x99\x06"
; you change it.
ochGetCommand = "r\$tsCanId\x30%2o%2c"
ochBlockSize = 122
ochBlockSize = 123
secl = scalar, U08, 0, "sec", 1.000, 0.000
status1 = scalar, U08, 1, "bits", 1.000, 0.000
@ -5148,9 +5278,19 @@ cmdVSSratio6 = "E\x99\x06"
sd_status = scalar, U08, 118, "", 1.0, 0.0
emap = scalar, U16, 119, "kpa", 1.000, 0.000
fanDuty = scalar, U08, 121, "%", 0.5, 0.000
;sd_filenum = scalar, U16, 122, "", 1, 0
;sd_error = scalar, U08, 124, "", 1, 0
;sd_phase = scalar, U08, 125, "", 1, 0
airConStatus = scalar, U08, 122, "bits", 1.000, 0.000
airConRequest = bits, U08, 122, [0:0]
airConCompressor = bits, U08, 122, [1:1]
airConRPMMLockout = bits, U08, 122, [2:2]
airConTPSLockout = bits, U08, 122, [3:3]
airConTurningOn = bits, U08, 122, [4:4]
airConCLTLockout = bits, U08, 122, [5:5]
airConFanStatus = bits, U08, 122, [6:6]
airConUnusedBits = bits, U08, 122, [7:7]
;sd_filenum = scalar, U16, 123, "", 1, 0
;sd_error = scalar, U08, 125, "", 1, 0
;sd_phase = scalar, U08, 126, "", 1, 0
#if CELSIUS
coolant = { coolantRaw - 40 } ; Temperature readings are offset by 40 to allow for negatives

View File

@ -10,10 +10,17 @@ void boostByGear();
void idleControl();
void vvtControl();
void initialiseFan();
void initialiseAirCon();
void nitrousControl();
void fanControl();
void airConControl();
bool READ_AIRCON_REQUEST();
void wmiControl();
static inline void checkAirConCoolantLockout();
static inline void checkAirConTPSLockout();
static inline void checkAirConRPMLockout();
#define SIMPLE_BOOST_P 1
#define SIMPLE_BOOST_I 1
#define SIMPLE_BOOST_D 1
@ -31,6 +38,10 @@ void wmiControl();
#define N2O_STAGE1_PIN_HIGH() (digitalWrite(configPage10.n2o_stage1_pin, HIGH))
#define N2O_STAGE2_PIN_LOW() (digitalWrite(configPage10.n2o_stage2_pin, LOW))
#define N2O_STAGE2_PIN_HIGH() (digitalWrite(configPage10.n2o_stage2_pin, HIGH))
#define AIRCON_PIN_LOW() (digitalWrite(pinAirConComp, LOW))
#define AIRCON_PIN_HIGH() (digitalWrite(pinAirConComp, HIGH))
#define AIRCON_FAN_PIN_LOW() (digitalWrite(pinAirConFan, LOW))
#define AIRCON_FAN_PIN_HIGH() (digitalWrite(pinAirConFan, HIGH))
#else
#define BOOST_PIN_LOW() *boost_pin_port &= ~(boost_pin_mask)
#define BOOST_PIN_HIGH() *boost_pin_port |= (boost_pin_mask)
@ -44,15 +55,24 @@ void wmiControl();
#define N2O_STAGE1_PIN_HIGH() *n2o_stage1_pin_port |= (n2o_stage1_pin_mask)
#define N2O_STAGE2_PIN_LOW() *n2o_stage2_pin_port &= ~(n2o_stage2_pin_mask)
#define N2O_STAGE2_PIN_HIGH() *n2o_stage2_pin_port |= (n2o_stage2_pin_mask)
#define AIRCON_PIN_LOW() *aircon_comp_pin_port &= ~(aircon_comp_pin_mask)
#define AIRCON_PIN_HIGH() *aircon_comp_pin_port |= (aircon_comp_pin_mask)
#define AIRCON_FAN_PIN_LOW() *aircon_fan_pin_port &= ~(aircon_fan_pin_mask)
#define AIRCON_FAN_PIN_HIGH() *aircon_fan_pin_port |= (aircon_fan_pin_mask)
#endif
#define READ_N2O_ARM_PIN() ((*n2o_arming_pin_port & n2o_arming_pin_mask) ? true : false)
#define AIRCON_ON() {((((configPage15.airConCompPol&1)==1)) ? AIRCON_PIN_LOW() : AIRCON_PIN_HIGH()); BIT_SET(currentStatus.airConStatus, BIT_AIRCON_COMPRESSOR);}
#define AIRCON_OFF() {((((configPage15.airConCompPol&1)==1)) ? AIRCON_PIN_HIGH() : AIRCON_PIN_LOW()); BIT_CLEAR(currentStatus.airConStatus, BIT_AIRCON_COMPRESSOR);}
#define AIRCON_FAN_ON() {((((configPage15.airConFanPol&1)==1)) ? AIRCON_FAN_PIN_LOW() : AIRCON_FAN_PIN_HIGH()); BIT_SET(currentStatus.airConStatus, BIT_AIRCON_FAN);}
#define AIRCON_FAN_OFF() {((((configPage15.airConFanPol&1)==1)) ? AIRCON_FAN_PIN_HIGH() : AIRCON_FAN_PIN_LOW()); BIT_CLEAR(currentStatus.airConStatus, BIT_AIRCON_FAN);}
#define VVT1_PIN_ON() VVT1_PIN_HIGH();
#define VVT1_PIN_OFF() VVT1_PIN_LOW();
#define VVT2_PIN_ON() VVT2_PIN_HIGH();
#define VVT2_PIN_OFF() VVT2_PIN_LOW();
#define VVT_TIME_DELAY_MULTIPLIER 50
#define FAN_ON() ((configPage6.fanInv) ? FAN_PIN_LOW() : FAN_PIN_HIGH())
@ -74,6 +94,12 @@ volatile PORT_TYPE *n2o_stage2_pin_port;
volatile PINMASK_TYPE n2o_stage2_pin_mask;
volatile PORT_TYPE *n2o_arming_pin_port;
volatile PINMASK_TYPE n2o_arming_pin_mask;
volatile PORT_TYPE *aircon_comp_pin_port;
volatile PINMASK_TYPE aircon_comp_pin_mask;
volatile PORT_TYPE *aircon_fan_pin_port;
volatile PINMASK_TYPE aircon_fan_pin_mask;
volatile PORT_TYPE *aircon_req_pin_port;
volatile PINMASK_TYPE aircon_req_pin_mask;
volatile bool boost_pwm_state;
unsigned int boost_pwm_max_count; //Used for variable PWM frequency
@ -111,5 +137,12 @@ long vvt2_pid_current_angle;
void boostInterrupt();
void vvtInterrupt();
bool acIsEnabled;
bool acStandAloneFanIsEnabled;
uint8_t acStartDelay;
uint8_t acTPSLockoutDelay;
uint8_t acRPMLockoutDelay;
uint8_t acAfterEngineStartDelay;
bool waitedAfterCranking; // This starts false and prevents the A/C from running until a few seconds after cranking
#endif
#endif

View File

@ -16,6 +16,234 @@ integerPID_ideal boostPID(&currentStatus.MAP, &currentStatus.boostDuty , &curren
integerPID vvtPID(&vvt_pid_current_angle, &currentStatus.vvt1Duty, &vvt_pid_target_angle, configPage10.vvtCLKP, configPage10.vvtCLKI, configPage10.vvtCLKD, configPage6.vvtPWMdir); //This is the PID object if that algorithm is used. Needs to be global as it maintains state outside of each function call
integerPID vvt2PID(&vvt2_pid_current_angle, &currentStatus.vvt2Duty, &vvt2_pid_target_angle, configPage10.vvtCLKP, configPage10.vvtCLKI, configPage10.vvtCLKD, configPage4.vvt2PWMdir); //This is the PID object if that algorithm is used. Needs to be global as it maintains state outside of each function call
/*
Air Conditioning Control
*/
void initialiseAirCon()
{
if( (configPage15.airConEnable&1) == 1 &&
pinAirConRequest != 0 &&
pinAirConComp != 0 )
{
// Hold the A/C off until a few seconds after cranking
acAfterEngineStartDelay = 0;
waitedAfterCranking = false;
acStartDelay = 0;
acTPSLockoutDelay = 0;
acRPMLockoutDelay = 0;
BIT_CLEAR(currentStatus.airConStatus, BIT_AIRCON_REQUEST); // Bit 0
BIT_CLEAR(currentStatus.airConStatus, BIT_AIRCON_COMPRESSOR); // Bit 1
BIT_CLEAR(currentStatus.airConStatus, BIT_AIRCON_RPM_LOCKOUT); // Bit 2
BIT_CLEAR(currentStatus.airConStatus, BIT_AIRCON_TPS_LOCKOUT); // Bit 3
BIT_CLEAR(currentStatus.airConStatus, BIT_AIRCON_TURNING_ON); // Bit 4
BIT_CLEAR(currentStatus.airConStatus, BIT_AIRCON_CLT_LOCKOUT); // Bit 5
BIT_CLEAR(currentStatus.airConStatus, BIT_AIRCON_FAN); // Bit 6
aircon_req_pin_port = portInputRegister(digitalPinToPort(pinAirConRequest));
aircon_req_pin_mask = digitalPinToBitMask(pinAirConRequest);
aircon_comp_pin_port = portOutputRegister(digitalPinToPort(pinAirConComp));
aircon_comp_pin_mask = digitalPinToBitMask(pinAirConComp);
AIRCON_OFF();
if((configPage15.airConFanEnabled > 0) && (pinAirConFan != 0))
{
aircon_fan_pin_port = portOutputRegister(digitalPinToPort(pinAirConFan));
aircon_fan_pin_mask = digitalPinToBitMask(pinAirConFan);
AIRCON_FAN_OFF();
acStandAloneFanIsEnabled = true;
}
else
{
acStandAloneFanIsEnabled = false;
}
acIsEnabled = true;
}
else
{
acIsEnabled = false;
}
}
void airConControl()
{
if(acIsEnabled == true)
{
// ------------------------------------------------------------------------------------------------------
// Check that the engine has been running past the post-start delay period before enabling the compressor
// ------------------------------------------------------------------------------------------------------
if (BIT_CHECK(currentStatus.engine, BIT_ENGINE_RUN))
{
if(acAfterEngineStartDelay >= configPage15.airConAfterStartDelay)
{
waitedAfterCranking = true;
}
else
{
acAfterEngineStartDelay++;
}
}
else
{
acAfterEngineStartDelay = 0;
waitedAfterCranking = false;
}
// --------------------------------------------------------------------
// Determine the A/C lockouts based on the noted parameters
// These functions set/clear the globl currentStatus.airConStatus bits.
// --------------------------------------------------------------------
checkAirConCoolantLockout();
checkAirConTPSLockout();
checkAirConRPMLockout();
// -----------------------------------------
// Check the A/C Request Signal (A/C Button)
// -----------------------------------------
if( READ_AIRCON_REQUEST() == true &&
waitedAfterCranking == true &&
BIT_CHECK(currentStatus.airConStatus, BIT_AIRCON_TPS_LOCKOUT) == false &&
BIT_CHECK(currentStatus.airConStatus, BIT_AIRCON_RPM_LOCKOUT) == false &&
BIT_CHECK(currentStatus.airConStatus, BIT_AIRCON_CLT_LOCKOUT) == false )
{
// Set the BIT_AIRCON_TURNING_ON bit to notify the idle system to idle up & the cooling fan to start (if enabled)
BIT_SET(currentStatus.airConStatus, BIT_AIRCON_TURNING_ON);
// Stand-alone fan operation
if(acStandAloneFanIsEnabled == true)
{
AIRCON_FAN_ON();
}
// Start the A/C compressor after the "Compressor On" delay period
if(acStartDelay >= configPage15.airConCompOnDelay)
{
AIRCON_ON();
}
else
{
acStartDelay++;
}
}
else
{
BIT_CLEAR(currentStatus.airConStatus, BIT_AIRCON_TURNING_ON);
// Stand-alone fan operation
if(acStandAloneFanIsEnabled == true)
{
AIRCON_FAN_OFF();
}
AIRCON_OFF();
acStartDelay = 0;
}
}
}
bool READ_AIRCON_REQUEST()
{
if(acIsEnabled == false)
{
return false;
}
// Read the status of the A/C request pin (A/C button), taking into account the pin's polarity
bool acReqPinStatus = ( ((configPage15.airConReqPol&1)==1) ?
!!(*aircon_req_pin_port & aircon_req_pin_mask) :
!(*aircon_req_pin_port & aircon_req_pin_mask));
BIT_WRITE(currentStatus.airConStatus, BIT_AIRCON_REQUEST, acReqPinStatus);
return acReqPinStatus;
}
static inline void checkAirConCoolantLockout()
{
// ---------------------------
// Coolant Temperature Lockout
// ---------------------------
int offTemp = (int)configPage15.airConClTempCut - CALIBRATION_TEMPERATURE_OFFSET;
if (currentStatus.coolant > offTemp)
{
// A/C is cut off due to high coolant
BIT_SET(currentStatus.airConStatus, BIT_AIRCON_CLT_LOCKOUT);
}
else if (currentStatus.coolant < (offTemp - 1))
{
// Adds a bit of hysteresis (2 degrees) to removing the lockout
// Yes, it is 2 degrees (not 1 degree or 3 degrees) because we go "> offTemp" to enable and "< (offtemp-1)" to disable,
// e.g. if offTemp is 100, it needs to go GREATER than 100 to enable, i.e. 101, and then 98 to disable,
// because the coolant temp is an integer. So 98.5 degrees to 100.5 degrees is the analog null zone where nothing happens,
// depending on sensor calibration and table interpolation.
// Hopefully offTemp wasn't -40... otherwise underflow... but that would be ridiculous
BIT_CLEAR(currentStatus.airConStatus, BIT_AIRCON_CLT_LOCKOUT);
}
}
static inline void checkAirConTPSLockout()
{
// ------------------------------
// High Throttle Position Lockout
// ------------------------------
if (currentStatus.TPS > configPage15.airConTPSCut)
{
// A/C is cut off due to high TPS
BIT_SET(currentStatus.airConStatus, BIT_AIRCON_TPS_LOCKOUT);
acTPSLockoutDelay = 0;
}
else if ( (BIT_CHECK(currentStatus.airConStatus, BIT_AIRCON_TPS_LOCKOUT) == true) &&
(currentStatus.TPS <= configPage15.airConTPSCut) )
{
// No need for hysteresis as we have the stand-down delay period after the high TPS condition goes away.
if (acTPSLockoutDelay >= configPage15.airConTPSCutTime)
{
BIT_CLEAR(currentStatus.airConStatus, BIT_AIRCON_TPS_LOCKOUT);
}
else
{
acTPSLockoutDelay++;
}
}
else
{
acTPSLockoutDelay = 0;
}
}
static inline void checkAirConRPMLockout()
{
// --------------------
// High/Low RPM Lockout
// --------------------
if ( (currentStatus.RPM < (configPage15.airConMinRPMdiv10 * 10)) ||
(currentStatus.RPMdiv100 > configPage15.airConMaxRPMdiv100) )
{
// A/C is cut off due to high/low RPM
BIT_SET(currentStatus.airConStatus, BIT_AIRCON_RPM_LOCKOUT);
acRPMLockoutDelay = 0;
}
else if ( (currentStatus.RPM >= (configPage15.airConMinRPMdiv10 * 10)) &&
(currentStatus.RPMdiv100 <= configPage15.airConMaxRPMdiv100) )
{
// No need to add hysteresis as we have the stand-down delay period after the high/low RPM condition goes away.
if (acRPMLockoutDelay >= configPage15.airConRPMCutTime)
{
BIT_CLEAR(currentStatus.airConStatus, BIT_AIRCON_RPM_LOCKOUT);
}
else
{
acRPMLockoutDelay++;
}
}
else
{
acRPMLockoutDelay = 0;
}
}
/*
Fan control
*/
@ -47,12 +275,16 @@ void fanControl()
int offTemp = onTemp - configPage6.fanHyster;
bool fanPermit = false;
if ( configPage2.fanWhenOff == true) { fanPermit = true; }
else { fanPermit = BIT_CHECK(currentStatus.engine, BIT_ENGINE_RUN); }
if ( (currentStatus.coolant >= onTemp) && (fanPermit == true) )
if ( (fanPermit == true) &&
((currentStatus.coolant >= onTemp) ||
((configPage15.airConTurnsFanOn&1) == 1 &&
BIT_CHECK(currentStatus.airConStatus, BIT_AIRCON_TURNING_ON) == true)) )
{
//Fan needs to be turned on.
//Fan needs to be turned on - either by high coolant temp, or from an A/C request (to ensure there is airflow over the A/C radiator).
if(BIT_CHECK(currentStatus.engine, BIT_ENGINE_CRANK) && (configPage2.fanWhenCranking == 0))
{
//If the user has elected to disable the fan during cranking, make sure it's off
@ -89,7 +321,17 @@ void fanControl()
}
else
{
currentStatus.fanDuty = table2D_getValue(&fanPWMTable, currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET); //In normal situation read PWM duty from the table
byte tempFanDuty = table2D_getValue(&fanPWMTable, currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET); //In normal situation read PWM duty from the table
if((configPage15.airConTurnsFanOn&1) == 1 &&
BIT_CHECK(currentStatus.airConStatus, BIT_AIRCON_TURNING_ON) == true)
{
// Clamp the fan duty to airConPwmFanMinDuty or above, to ensure there is airflow over the A/C radiator
if(tempFanDuty < configPage15.airConPwmFanMinDuty)
{
tempFanDuty = configPage15.airConPwmFanMinDuty;
}
}
currentStatus.fanDuty = tempFanDuty;
#if defined(PWM_FAN_AVAILABLE)
fan_pwm_value = halfPercentage(currentStatus.fanDuty, fan_pwm_max_count); //update FAN PWM value last
if (currentStatus.fanDuty > 0)

View File

@ -1,7 +1,7 @@
#ifndef CANCOMMS_H
#define CANCOMMS_H
#define NEW_CAN_PACKET_SIZE 122
#define NEW_CAN_PACKET_SIZE 123
#define CAN_PACKET_SIZE 75
#if ( defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) )

View File

@ -343,6 +343,7 @@ void sendcanValues(uint16_t offset, uint16_t packetLength, byte cmd, byte portTy
fullStatus[119] = lowByte(currentStatus.EMAP); //2 bytes for EMAP
fullStatus[120] = highByte(currentStatus.EMAP);
fullStatus[121] = currentStatus.fanDuty;
fullStatus[122] = currentStatus.airConStatus;
for(byte x=0; x<packetLength; x++)
{

View File

@ -230,6 +230,15 @@
#define BIT_STATUS4_UNUSED7 6
#define BIT_STATUS4_UNUSED8 7
#define BIT_AIRCON_REQUEST 0 //Indicates whether the A/C button is pressed
#define BIT_AIRCON_COMPRESSOR 1 //Indicates whether the A/C compressor is running
#define BIT_AIRCON_RPM_LOCKOUT 2 //Indicates the A/C is locked out due to the RPM being too high/low, or the post-high/post-low-RPM "stand-down" lockout period
#define BIT_AIRCON_TPS_LOCKOUT 3 //Indicates the A/C is locked out due to high TPS, or the post-high-TPS "stand-down" lockout period
#define BIT_AIRCON_TURNING_ON 4 //Indicates the A/C request is on (i.e. A/C button pressed), the lockouts are off, however the start delay has not yet elapsed. This gives the idle up time to kick in before the compressor.
#define BIT_AIRCON_CLT_LOCKOUT 5 //Indicates the A/C is locked out either due to high coolant temp.
#define BIT_AIRCON_FAN 6 //Indicates whether the A/C fan is running
#define BIT_AIRCON_UNUSED8 7
#define VALID_MAP_MAX 1022 //The largest ADC value that is valid for the MAP sensor
#define VALID_MAP_MIN 2 //The smallest ADC value that is valid for the MAP sensor
@ -622,10 +631,11 @@ extern volatile byte LOOP_TIMER;
//These functions all do checks on a pin to determine if it is already in use by another (higher importance) function
#define pinIsInjector(pin) ( ((pin) == pinInjector1) || ((pin) == pinInjector2) || ((pin) == pinInjector3) || ((pin) == pinInjector4) || ((pin) == pinInjector5) || ((pin) == pinInjector6) || ((pin) == pinInjector7) || ((pin) == pinInjector8) )
#define pinIsIgnition(pin) ( ((pin) == pinCoil1) || ((pin) == pinCoil2) || ((pin) == pinCoil3) || ((pin) == pinCoil4) || ((pin) == pinCoil5) || ((pin) == pinCoil6) || ((pin) == pinCoil7) || ((pin) == pinCoil8) )
//#define pinIsOutput(pin) ( pinIsInjector((pin)) || pinIsIgnition((pin)) || ((pin) == pinFuelPump) || ((pin) == pinFan) || ((pin) == pinVVT_1) || ((pin) == pinVVT_2) || ( ((pin) == pinBoost) && configPage6.boostEnabled) || ((pin) == pinIdle1) || ((pin) == pinIdle2) || ((pin) == pinTachOut) || ((pin) == pinStepperEnable) || ((pin) == pinStepperStep) )
//#define pinIsOutput(pin) ( pinIsInjector((pin)) || pinIsIgnition((pin)) || ((pin) == pinFuelPump) || ((pin) == pinFan) || ((pin) == pinAirConComp) || ((pin) == pinAirConFan)|| ((pin) == pinVVT_1) || ((pin) == pinVVT_2) || ( ((pin) == pinBoost) && configPage6.boostEnabled) || ((pin) == pinIdle1) || ((pin) == pinIdle2) || ((pin) == pinTachOut) || ((pin) == pinStepperEnable) || ((pin) == pinStepperStep) )
#define pinIsSensor(pin) ( ((pin) == pinCLT) || ((pin) == pinIAT) || ((pin) == pinMAP) || ((pin) == pinTPS) || ((pin) == pinO2) || ((pin) == pinBat) || (((pin) == pinFlex) && (configPage2.flexEnabled != 0)) )
//#define pinIsUsed(pin) ( pinIsSensor((pin)) || pinIsOutput((pin)) || pinIsReserved((pin)) )
/** The status struct with current values for all 'live' variables.
* In current version this is 64 bytes. Instantiated as global currentStatus.
* int *ADC (Analog-to-digital value / count) values contain the "raw" value from AD conversion, which get converted to
@ -748,6 +758,7 @@ struct statuses {
long vvt2Duty; //Has to be a long for PID calcs (CL VVT control)
byte outputsStatus;
byte TS_SD_Status; //TunerStudios SD card status
byte airConStatus;
};
/** Page 2 of the config - mostly variables that are required for fuel.
@ -1443,7 +1454,37 @@ struct config15 {
byte unused15_1 : 7; //7bits unused
byte boostDCWhenDisabled;
byte boostControlEnableThreshold; //if fixed value enable set threshold here.
byte unused15_3_176[173];
//Byte 83 - Air conditioning binary points
byte airConEnable : 1;
byte airConCompPol : 1;
byte airConReqPol : 1;
byte airConTurnsFanOn : 1;
byte airConFanEnabled : 1;
byte airConFanPol : 1;
byte airConUnused1 : 2;
//Bytes 84-97 - Air conditioning analog points
byte airConCompPin : 6;
byte airConUnused2 : 2;
byte airConReqPin : 6;
byte airConUnused3 : 2;
byte airConTPSCut;
byte airConMinRPMdiv10;
byte airConMaxRPMdiv100;
byte airConClTempCut;
byte airConIdleSteps;
byte airConTPSCutTime;
byte airConCompOnDelay;
byte airConAfterStartDelay;
byte airConRPMCutTime;
byte airConFanPin : 6;
byte airConUnused4 : 2;
byte airConIdleUpRPMAdder;
byte airConPwmFanMinDuty;
//Bytes 98-255
byte Unused15_98_255[158];
#if defined(CORE_AVR)
};
@ -1451,7 +1492,6 @@ struct config15 {
} __attribute__((__packed__)); //The 32 bit systems require all structs to be fully packed
#endif
extern byte pinInjector1; //Output pin injector 1
extern byte pinInjector2; //Output pin injector 2
extern byte pinInjector3; //Output pin injector 3
@ -1531,7 +1571,9 @@ extern byte pinSDEnable; //Input for manually enabling SD logging
#ifdef USE_SPI_EEPROM
extern byte pinSPIFlash_CS;
#endif
extern byte pinAirConComp; // Air conditioning compressor output
extern byte pinAirConFan; // Stand-alone air conditioning fan output
extern byte pinAirConRequest; // Air conditioning request input
/* global variables */ // from speeduino.ino
//#ifndef UNIT_TEST

View File

@ -250,6 +250,9 @@ byte pinSDEnable;
#ifdef USE_SPI_EEPROM
byte pinSPIFlash_CS;
#endif
byte pinAirConComp; // Air conditioning compressor output (See: auxiliaries.ino)
byte pinAirConFan; // Stand-alone air conditioning fan output (See: auxiliaries.ino)
byte pinAirConRequest; // Air conditioning request input (See: auxiliaries.ino)
struct statuses currentStatus; /**< The master global "live" status struct. Contains all values that are updated frequently and used across modules */
struct config2 configPage2;
@ -316,7 +319,9 @@ inline bool pinIsOutput(byte pin)
|| ((pin == pinStepperEnable) && isIdleSteper)
|| ((pin == pinStepperStep) && isIdleSteper)
|| ((pin == pinStepperDir) && isIdleSteper)
|| (pin == pinTachOut) )
|| (pin == pinTachOut)
|| ((pin == pinAirConComp) && (configPage15.airConEnable > 0))
|| ((pin == pinAirConFan) && (configPage15.airConEnable > 0) && (configPage15.airConFanEnabled > 0)) )
{
used = true;
}

View File

@ -473,9 +473,12 @@ void idleControl()
//Standard running
currentStatus.idleLoad = table2D_getValue(&iacPWMTable, currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET); //All temps are offset by 40 degrees
}
// Add air conditioning idle-up - we only do this if the engine is running (A/C should never engage with engine off).
if(configPage15.airConIdleSteps>0 && BIT_CHECK(currentStatus.airConStatus, BIT_AIRCON_TURNING_ON) == true) { currentStatus.idleLoad += configPage15.airConIdleSteps; }
}
if(currentStatus.idleUpActive == true) { currentStatus.idleLoad += configPage2.idleUpAdder; } //Add Idle Up amount if active
if( currentStatus.idleLoad > 100 ) { currentStatus.idleLoad = 100; } //Safety Check
idle_pwm_target_value = percentage(currentStatus.idleLoad, idle_pwm_max_count);
@ -504,17 +507,38 @@ void idleControl()
{
idle_cl_target_rpm = (uint16_t)currentStatus.CLIdleTarget * 10; //Multiply the byte target value back out by 10
if( BIT_CHECK(LOOP_TIMER, BIT_TIMER_1HZ) ) { idlePID.SetTunings(configPage6.idleKP, configPage6.idleKI, configPage6.idleKD); } //Re-read the PID settings once per second
PID_computed = idlePID.Compute(true);
long TEMP_idle_pwm_target_value;
if(PID_computed == true)
{
idle_pwm_target_value = idle_pid_target_value>>2; //increased resolution
currentStatus.idleLoad = ((unsigned long)(idle_pwm_target_value * 100UL) / idle_pwm_max_count);
if(currentStatus.idleUpActive == true) { currentStatus.idleLoad += configPage2.idleUpAdder; } //Add Idle Up amount if active
TEMP_idle_pwm_target_value = idle_pid_target_value;
// Add an offset to the duty cycle, outside of the closed loop. When tuned correctly, the extra load from
// the air conditioning should exactly cancel this out and the PID loop will be relatively unaffected.
if(configPage15.airConIdleSteps>0 && BIT_CHECK(currentStatus.airConStatus, BIT_AIRCON_TURNING_ON) == true)
{
// Add air conditioning idle-up
// We are adding percentage steps, but the loop doesn't operate in percentage steps - it works in PWM count
TEMP_idle_pwm_target_value += percentage(configPage15.airConIdleSteps, idle_pwm_max_count<<2);
if(TEMP_idle_pwm_target_value > (idle_pwm_max_count<<2)) { TEMP_idle_pwm_target_value = (idle_pwm_max_count<<2); }
}
// Fixed this by putting it here, however I have not tested it. It used to be after the calculation of idle_pwm_target_value, meaning the percentage would update in currentStatus, but the idle would not actually increase.
if(currentStatus.idleUpActive == true)
{
// Add Idle Up amount if active
// Again, we use configPage15.airConIdleSteps * idle_pwm_max_count / 100 because we are adding percentage steps, but the loop doesn't operate in percentage steps - it works in PWM count
TEMP_idle_pwm_target_value += percentage(configPage2.idleUpAdder, idle_pwm_max_count<<2);
if(TEMP_idle_pwm_target_value > (idle_pwm_max_count<<2)) { TEMP_idle_pwm_target_value = (idle_pwm_max_count<<2); }
}
// Now assign the real PWM value
idle_pwm_target_value = TEMP_idle_pwm_target_value>>2; //increased resolution
currentStatus.idleLoad = ((unsigned long)(idle_pwm_target_value * 100UL) / idle_pwm_max_count);
}
idleCounter++;
}
}
break;
@ -541,19 +565,39 @@ void idleControl()
{
//Read the OL table as feedforward term
FeedForwardTerm = percentage(table2D_getValue(&iacPWMTable, currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET), idle_pwm_max_count<<2); //All temps are offset by 40 degrees
// Add an offset to the feed forward term. When tuned correctly, the extra load from the air conditioning
// should exactly cancel this out and the PID loop will be relatively unaffected.
if(configPage15.airConIdleSteps>0 && BIT_CHECK(currentStatus.airConStatus, BIT_AIRCON_TURNING_ON) == true)
{
// Add air conditioning idle-up
// We are adding percentage steps, but the loop doesn't operate in percentage steps - it works in PWM count <<2 (PWM count * 4)
FeedForwardTerm += percentage(configPage15.airConIdleSteps, (idle_pwm_max_count<<2));
if(FeedForwardTerm > (idle_pwm_max_count<<2)) { FeedForwardTerm = (idle_pwm_max_count<<2); }
}
// Fixed this by putting it here, however I have not tested it. It used to be after the calculation of idle_pwm_target_value, meaning the percentage would update in currentStatus, but the idle would not actually increase.
if(currentStatus.idleUpActive == true)
{
// Add Idle Up amount if active
// Again, we are adding percentage steps, but the loop doesn't operate in percentage steps - it works in PWM count <<2 (PWM count * 4)
FeedForwardTerm += percentage(configPage2.idleUpAdder, (idle_pwm_max_count<<2));
if(FeedForwardTerm > (idle_pwm_max_count<<2)) { FeedForwardTerm = (idle_pwm_max_count<<2); }
}
idle_cl_target_rpm = (uint16_t)currentStatus.CLIdleTarget * 10; //Multiply the byte target value back out by 10
if( BIT_CHECK(LOOP_TIMER, BIT_TIMER_1HZ) ) { idlePID.SetTunings(configPage6.idleKP, configPage6.idleKI, configPage6.idleKD); } //Re-read the PID settings once per second
if((currentStatus.RPM - idle_cl_target_rpm > configPage2.iacRPMlimitHysteresis*10) || (currentStatus.TPS > configPage2.iacTPSlimit)){ //reset integral to zero when TPS is bigger than set value in TS (opening throttle so not idle anymore). OR when RPM higher than Idle Target + RPM Histeresis (coming back from high rpm with throttle closed)
idlePID.ResetIntegeral();
}
PID_computed = idlePID.Compute(true, FeedForwardTerm);
if(PID_computed == true)
{
idle_pwm_target_value = idle_pid_target_value>>2; //increased resolution
currentStatus.idleLoad = ((unsigned long)(idle_pwm_target_value * 100UL) / idle_pwm_max_count);
if(currentStatus.idleUpActive == true) { currentStatus.idleLoad += configPage2.idleUpAdder; } //Add Idle Up amount if active
}
idleCounter++;
}
@ -600,6 +644,10 @@ void idleControl()
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
}
if(currentStatus.idleUpActive == true) { idleStepper.targetIdleStep += configPage2.idleUpAdder; } //Add Idle Up amount if active
// Add air conditioning idle-up - we only do this if the engine is running (A/C should never engage with engine off).
if(configPage15.airConIdleSteps>0 && BIT_CHECK(currentStatus.airConStatus, BIT_AIRCON_TURNING_ON) == true) { idleStepper.targetIdleStep += configPage15.airConIdleSteps; }
iacStepTime_uS = configPage6.iacStepTime * 1000;
iacCoolTime_uS = configPage9.iacCoolTime * 1000;
@ -680,8 +728,12 @@ void idleControl()
}
idleStepper.targetIdleStep = idle_pid_target_value>>2; //Increase resolution
// Add air conditioning idle-up - we only do this if the engine is running (A/C should never engage with engine off).
if(configPage15.airConIdleSteps>0 && BIT_CHECK(currentStatus.airConStatus, BIT_AIRCON_TURNING_ON) == true) { idleStepper.targetIdleStep += configPage15.airConIdleSteps; }
}
if(currentStatus.idleUpActive == true) { idleStepper.targetIdleStep += configPage2.idleUpAdder; } //Add Idle Up amount if active
//limit to the configured max steps. This must include any idle up adder, to prevent over-opening.
if (idleStepper.targetIdleStep > (configPage9.iacMaxSteps * 3) )
{

View File

@ -344,6 +344,7 @@ void initialiseAll()
//initialiseDisplay();
initialiseIdle();
initialiseFan();
initialiseAirCon();
initialiseAuxPWM();
initialiseCorrections();
BIT_CLEAR(currentStatus.engineProtectStatus, PROTECT_IO_ERROR); //Clear the I/O error bit. The bit will be set in initialiseADC() if there is problem in there.
@ -2629,7 +2630,12 @@ void setPinMapping(byte boardID)
//Currently there's no default pin for closed throttle position sensor
pinCTPS = pinTranslate(configPage2.CTPSPin);
// Air conditioning control initialisation
if (((configPage15.airConCompPin&63) != 0) && ((configPage15.airConCompPin&63) < BOARD_MAX_IO_PINS) ) { pinAirConComp = pinTranslate(configPage15.airConCompPin&63); }
if (((configPage15.airConFanPin&63) != 0) && ((configPage15.airConFanPin&63) < BOARD_MAX_IO_PINS) ) { pinAirConFan = pinTranslate(configPage15.airConFanPin&63); }
if (((configPage15.airConReqPin&63) != 0) && ((configPage15.airConReqPin&63) < BOARD_MAX_IO_PINS) ) { pinAirConRequest = pinTranslate(configPage15.airConReqPin&63); }
/* Reset control is a special case. If reset control is enabled, it needs its initial state set BEFORE its pinMode.
If that doesn't happen and reset control is in "Serial Command" mode, the Arduino will end up in a reset loop
because the control pin will go low as soon as the pinMode is set to OUTPUT. */
@ -2839,6 +2845,32 @@ void setPinMapping(byte boardID)
if (configPage10.wmiEmptyPolarity == 0) { pinMode(pinWMIEmpty, INPUT_PULLUP); } //Normal setting
else { pinMode(pinWMIEmpty, INPUT); } //inverted setting
}
}
if((pinAirConComp>0) && ((configPage15.airConEnable&1) == 1))
{
pinMode(pinAirConComp, OUTPUT);
}
if((pinAirConRequest > 0) && ((configPage15.airConEnable&1) == 1) && (!pinIsOutput(pinAirConRequest)))
{
if((configPage15.airConReqPol&1) == 1)
{
// Inverted
// +5V is ON, Use external pull-down resistor for OFF
pinMode(pinAirConRequest, INPUT);
}
else
{
//Normal
// Pin pulled to Ground is ON. Floating (internally pulled up to +5V) is OFF.
pinMode(pinAirConRequest, INPUT_PULLUP);
}
}
if((pinAirConFan > 0) && ((configPage15.airConEnable&1) == 1) && ((configPage15.airConFanEnabled&1) == 1))
{
pinMode(pinAirConFan, OUTPUT);
}
//These must come after the above pinMode statements

View File

@ -12,14 +12,14 @@
#include <assert.h>
#ifndef UNIT_TEST // Scope guard for unit testing
#define LOG_ENTRY_SIZE 122 /**< The size of the live data packet. This MUST match ochBlockSize setting in the ini file */
#define SD_LOG_ENTRY_SIZE 122 /**< The size of the live data packet used by the SD card.*/
#define LOG_ENTRY_SIZE 123 /**< The size of the live data packet. This MUST match ochBlockSize setting in the ini file */
#define SD_LOG_ENTRY_SIZE 123 /**< The size of the live data packet used by the SD card.*/
#else
#define LOG_ENTRY_SIZE 1 /**< The size of the live data packet. This MUST match ochBlockSize setting in the ini file */
#define SD_LOG_ENTRY_SIZE 1 /**< The size of the live data packet used by the SD card.*/
#endif
#define SD_LOG_NUM_FIELDS 89 /**< The number of fields that are in the log. This is always smaller than the entry size due to some fields being 2 bytes */
#define SD_LOG_NUM_FIELDS 90 /**< The number of fields that are in the log. This is always smaller than the entry size due to some fields being 2 bytes */
byte getTSLogEntry(uint16_t);
int16_t getReadableLogEntry(uint16_t);
@ -123,8 +123,8 @@ const char header_85[] PROGMEM = "Advance 2";
const char header_86[] PROGMEM = "SD Status";
const char header_87[] PROGMEM = "EMAP";
const char header_88[] PROGMEM = "Fan Duty";
const char header_89[] PROGMEM = "AirConStatus";
/*
const char header_89[] PROGMEM = "";
const char header_90[] PROGMEM = "";
const char header_91[] PROGMEM = "";
const char header_92[] PROGMEM = "";
@ -248,8 +248,8 @@ const char* const header_table[] PROGMEM = { header_0,\
header_86,\
header_87,\
header_88,\
/*
header_89,\
/*
header_90,\
header_91,\
header_92,\

View File

@ -164,6 +164,7 @@ byte getTSLogEntry(uint16_t byteNum)
case 119: statusValue = lowByte(currentStatus.EMAP); break; //2 bytes for EMAP
case 120: statusValue = highByte(currentStatus.EMAP); break;
case 121: statusValue = currentStatus.fanDuty; break;
case 122: statusValue = currentStatus.airConStatus; break;
}
return statusValue;
@ -285,6 +286,7 @@ int16_t getReadableLogEntry(uint16_t logIndex)
case 86: statusValue = currentStatus.TS_SD_Status; break; //SD card status
case 87: statusValue = currentStatus.EMAP; break;
case 88: statusValue = currentStatus.fanDuty; break;
case 89: statusValue = currentStatus.airConStatus; break;
}
return statusValue;

View File

@ -29,7 +29,6 @@ uint16_t getPageSize(byte pageNum /**< [in] The page number */ );
#define ignMap2Page 14
#define boostvvtPage2 15
// ============================== Per-byte page access ==========================
/**

View File

@ -293,6 +293,9 @@ void loop()
//updateFullStatus();
checkProgrammableIO();
idleControl(); //Perform any idle related actions. This needs to be run at 10Hz to align with the idle taper resolution of 0.1s
// Air conditioning control
airConControl();
//if( (isEepromWritePending() == true) && (serialReceivePending == false) && (micros() > deferEEPROMWritesUntil)) { writeAllConfig(); } //Used for slower EEPROM writes (Currently this runs in the 30Hz block)
@ -342,6 +345,7 @@ void loop()
if( (configPage2.idleAdvEnabled >= 1) || (configPage6.iacAlgorithm != IAC_ALGORITHM_NONE) )
{
currentStatus.CLIdleTarget = (byte)table2D_getValue(&idleTargetTable, currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET); //All temps are offset by 40 degrees
if(BIT_CHECK(currentStatus.airConStatus, BIT_AIRCON_TURNING_ON)) { currentStatus.CLIdleTarget += configPage15.airConIdleUpRPMAdder; } //Adds Idle Up RPM amount if active
}
#ifdef SD_LOGGING
@ -350,7 +354,7 @@ void loop()
currentStatus.fuelPressure = getFuelPressure();
currentStatus.oilPressure = getOilPressure();
if(auxIsEnabled == true)
{
//TODO dazq to clean this right up :)

View File

@ -287,7 +287,7 @@ void writeConfig(uint8_t pageNum)
result = writeTable(&dwellTable, decltype(dwellTable)::type_key, result.changeWriteAddress(EEPROM_CONFIG12_MAP3));
break;
case progOutsPage:
case progOutsPage:
/*---------------------------------------------------
| Config page 13 (See storage.h for data layout)
-----------------------------------------------------*/

View File

@ -99,7 +99,11 @@
* | 3201 |64 | boostLUT table (8x8) | @ref EEPROM_CONFIG15_MAP |
* | 3265 |8 | boostLUT table (X axis) (RPM) | |
* | 3273 |8 | boostLUT table (Y axis) (targetBoost)| |
* | 3281 |176 | Page 15 settings | @ref EEPROM_CONFIG15_START |
* | 3281 |1 | boostLUT enable | @ref EEPROM_CONFIG15_START |
* | 3282 |1 | boostDCWhenDisabled | |
* | 3283 |1 | boostControlEnableThreshold | |
* | 3284 |14 | A/C Control Settings | |
* | 3298 |159 | Page 15 spare | |
* | 3457 |217 | EMPTY | |
* | 3674 |4 | CLT Calibration CRC32 | |
* | 3678 |4 | IAT Calibration CRC32 | |

View File

@ -625,7 +625,6 @@ void doUpdates()
configPage2.canBMWCluster = 0;
configPage2.canVAGCluster = 0;
configPage15.boostDCWhenDisabled = 0;
configPage15.boostControlEnable = EN_BOOST_CONTROL_BARO;
@ -668,8 +667,8 @@ void doUpdates()
configPage9.afrProtectMinMAP = 90; //Is divided by 2, vlue represents 180kPa
configPage9.afrProtectMinRPM = 40; //4000 RPM min
configPage9.afrProtectMinTPS = 160; //80% TPS min
configPage9.afrProtectDeviation = 14; //1.4 AFR deviation
configPage9.afrProtectDeviation = 14; //1.4 AFR deviation
writeAllConfig();
storeEEPROMVersion(20);
}
@ -679,6 +678,10 @@ void doUpdates()
//202210
configPage2.taeMinChange = 4; //Default is 2% minimum change to match prior behaviour. (4 = 2% account for 0.5 resolution)
configPage2.maeMinChange = 2; //Default is 2% minimum change to match prior behaviour.
//AC Control (configPage15)
//Set A/C default values - these line up with the ini file defaults
configPage15.airConEnable = 0;
writeAllConfig();
storeEEPROMVersion(21);