GCC Code Coverage Report


Directory: ./
File: firmware/controllers/actuators/idle_hardware.cpp
Date: 2025-10-03 00:57:22
Coverage Exec Excl Total
Lines: 64.3% 9 0 14
Functions: 100.0% 1 0 1
Branches: 75.0% 6 0 8
Decisions: 75.0% 3 - 4

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