Line | Branch | Decision | Exec | Source |
---|---|---|---|---|
1 | /** | |||
2 | * @file idle_hardware.cpp | |||
3 | * @brief Idle Air Control valve hardware | |||
4 | * | |||
5 | * @date November 3, 2020 | |||
6 | * | |||
7 | * This is just the hardware interface - deciding where to put the valve happens in idle_thread.cpp | |||
8 | */ | |||
9 | ||||
10 | #include "pch.h" | |||
11 | ||||
12 | #if EFI_IDLE_CONTROL | |||
13 | #include "idle_hardware.h" | |||
14 | ||||
15 | #include "electronic_throttle.h" | |||
16 | ||||
17 | #include "dc_motors.h" | |||
18 | #if ! EFI_UNIT_TEST | |||
19 | #include "stepper.h" | |||
20 | /* Storing two following structs in CCM memory cause HardFault (at least on F4) | |||
21 | * This need deep debuging. Until it is moved out of CMM. */ | |||
22 | static StepDirectionStepper iacStepperHw /*CCM_OPTIONAL*/; | |||
23 | static DualHBridgeStepper iacHbridgeHw /*CCM_OPTIONAL*/; | |||
24 | StepperMotor iacMotor CCM_OPTIONAL; | |||
25 | #endif /* EFI_UNIT_TEST */ | |||
26 | ||||
27 | static SimplePwm idleSolenoidOpen("idle open"); | |||
28 | static SimplePwm idleSolenoidClose("idle close"); | |||
29 | ||||
30 | 1121 | void applyIACposition(percent_t position) { | ||
31 | /** | |||
32 | * currently idle level is an percent value (0-100 range), and PWM takes a float in the 0..1 range | |||
33 | * todo: unify? | |||
34 | */ | |||
35 | 1121 | float duty = PERCENT_TO_DUTY(position); | ||
36 | ||||
37 | #if EFI_ELECTRONIC_THROTTLE_BODY | |||
38 | 1121 | setEtbIdlePosition(position); | ||
39 | #endif // EFI_ELECTRONIC_THROTTLE_BODY | |||
40 | ||||
41 | #if EFI_UNIT_TEST | |||
42 | if (false) { | |||
43 | #endif // EFI_UNIT_TEST | |||
44 | ||||
45 | #if ! EFI_UNIT_TEST | |||
46 | if (engineConfiguration->useStepperIdle) { | |||
47 | iacMotor.setTargetPosition(duty * engineConfiguration->idleStepperTotalSteps); | |||
48 | #endif /* EFI_UNIT_TEST */ | |||
49 | } else { | |||
50 | // if not spinning or running a bench test, turn off the idle valve(s) to be quieter and save power | |||
51 | #if EFI_SHAFT_POSITION_INPUT | |||
52 |
5/6✓ Branch 1 taken 851 times.
✓ Branch 2 taken 270 times.
✓ Branch 3 taken 851 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 851 times.
✓ Branch 6 taken 270 times.
|
2/2✓ Decision 'true' taken 851 times.
✓ Decision 'false' taken 270 times.
|
1121 | if (!engine->triggerCentral.engineMovedRecently() && engine->timeToStopIdleTest == 0) { |
53 | 851 | idleSolenoidOpen.setSimplePwmDutyCycle(0); | ||
54 | 851 | idleSolenoidClose.setSimplePwmDutyCycle(0); | ||
55 | 851 | return; | ||
56 | } | |||
57 | #endif // EFI_SHAFT_POSITION_INPUT | |||
58 | ||||
59 |
1/2✓ Branch 0 taken 270 times.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 270 times.
✗ Decision 'false' not taken.
|
270 | if (!engineConfiguration->isDoubleSolenoidIdle) { |
60 | 270 | idleSolenoidOpen.setSimplePwmDutyCycle(duty); | ||
61 | } else { | |||
62 | // use 0.01..0.99 range | |||
63 | ✗ | float idle_range = 0.98; // move to config? | ||
64 | ||||
65 | ✗ | float idle_open = 0.01 + idle_range * duty; | ||
66 | ✗ | float idle_close = 0.01 + idle_range * (1.0 - duty); | ||
67 | ||||
68 | ✗ | idleSolenoidOpen.setSimplePwmDutyCycle(idle_open); | ||
69 | ✗ | idleSolenoidClose.setSimplePwmDutyCycle(idle_close); | ||
70 | } | |||
71 | } | |||
72 | } | |||
73 | ||||
74 | #if !EFI_UNIT_TEST | |||
75 | ||||
76 | bool isIdleHardwareRestartNeeded() { | |||
77 | return isConfigurationChanged(stepperEnablePin) || | |||
78 | isConfigurationChanged(stepperEnablePinMode) || | |||
79 | isConfigurationChanged(idle.stepperStepPin) || | |||
80 | isConfigurationChanged(idle.solenoidFrequency) || | |||
81 | isConfigurationChanged(useStepperIdle) || | |||
82 | isConfigurationChanged(idle.solenoidPin) || | |||
83 | isConfigurationChanged(secondSolenoidPin) || | |||
84 | isConfigurationChanged(useRawOutputToDriveIdleStepper) || | |||
85 | isConfigurationChanged(stepper_raw_output[0]) || | |||
86 | isConfigurationChanged(stepper_raw_output[1]) || | |||
87 | isConfigurationChanged(stepper_raw_output[2]) || | |||
88 | isConfigurationChanged(stepper_raw_output[3]); | |||
89 | } | |||
90 | ||||
91 | bool isIdleMotorBusy() { | |||
92 | if (!engineConfiguration->useStepperIdle) { | |||
93 | // todo: check other motor types? | |||
94 | return false; | |||
95 | } | |||
96 | return iacMotor.isBusy(); | |||
97 | } | |||
98 | ||||
99 | void initIdleHardware() { | |||
100 | if (engineConfiguration->useStepperIdle) { | |||
101 | StepperHw* hw; | |||
102 | ||||
103 | if (engineConfiguration->useRawOutputToDriveIdleStepper) { | |||
104 | // four Push-Pull outputs to directly drive stepper idle air valve coils | |||
105 | auto motorA = initDcMotor(engineConfiguration->stepper_raw_output[0], | |||
106 | engineConfiguration->stepper_raw_output[1], ETB_COUNT + 0); | |||
107 | auto motorB = initDcMotor(engineConfiguration->stepper_raw_output[2], | |||
108 | engineConfiguration->stepper_raw_output[3], ETB_COUNT + 1); | |||
109 | ||||
110 | iacHbridgeHw.initialize( | |||
111 | motorA, | |||
112 | motorB, | |||
113 | engineConfiguration->idleStepperReactionTime | |||
114 | ); | |||
115 | ||||
116 | hw = &iacHbridgeHw; | |||
117 | } else if (engineConfiguration->useHbridgesToDriveIdleStepper) { | |||
118 | auto motorA = initDcMotor("DC dis-1", engineConfiguration->stepperDcIo[0], | |||
119 | ETB_COUNT + 0, engineConfiguration->stepper_dc_use_two_wires); | |||
120 | auto motorB = initDcMotor("DC dis-2", engineConfiguration->stepperDcIo[1], | |||
121 | ETB_COUNT + 1, engineConfiguration->stepper_dc_use_two_wires); | |||
122 | ||||
123 | iacHbridgeHw.initialize( | |||
124 | motorA, | |||
125 | motorB, | |||
126 | engineConfiguration->idleStepperReactionTime | |||
127 | ); | |||
128 | ||||
129 | hw = &iacHbridgeHw; | |||
130 | } else { | |||
131 | // like DRV8825? | |||
132 | iacStepperHw.initialize( | |||
133 | engineConfiguration->idle.stepperStepPin, | |||
134 | engineConfiguration->idle.stepperDirectionPin, | |||
135 | engineConfiguration->stepperDirectionPinMode, | |||
136 | engineConfiguration->idleStepperReactionTime, | |||
137 | engineConfiguration->stepperEnablePin, | |||
138 | engineConfiguration->stepperEnablePinMode | |||
139 | ); | |||
140 | ||||
141 | hw = &iacStepperHw; | |||
142 | } | |||
143 | ||||
144 | iacMotor.initialize(hw, engineConfiguration->idleStepperTotalSteps); | |||
145 | } else if (isBrainPinValid(engineConfiguration->idle.solenoidPin)) { | |||
146 | // we are here for single or double solenoid idle | |||
147 | ||||
148 | /** | |||
149 | * Start PWM for idleValvePin | |||
150 | */ | |||
151 | // todo: even for double-solenoid mode we can probably use same single SimplePWM | |||
152 | startSimplePwm(&idleSolenoidOpen, "Idle Valve Open", | |||
153 | &engine->scheduler, | |||
154 | &enginePins.idleSolenoidPin, | |||
155 | engineConfiguration->idle.solenoidFrequency, PERCENT_TO_DUTY(config->cltIdleCorrTable[0][0])); | |||
156 | ||||
157 | if (engineConfiguration->isDoubleSolenoidIdle) { | |||
158 | if (!isBrainPinValid(engineConfiguration->secondSolenoidPin)) { | |||
159 | criticalError("Second idle pin should be configured for double solenoid mode."); | |||
160 | return; | |||
161 | } | |||
162 | ||||
163 | startSimplePwm(&idleSolenoidClose, "Idle Valve Close", | |||
164 | &engine->scheduler, | |||
165 | &enginePins.secondIdleSolenoidPin, | |||
166 | engineConfiguration->idle.solenoidFrequency, PERCENT_TO_DUTY(config->cltIdleCorrTable[0][0])); | |||
167 | } | |||
168 | } | |||
169 | } | |||
170 | ||||
171 | #endif | |||
172 | ||||
173 | #endif // EFI_IDLE_HARDWARE | |||
174 |