GCC Code Coverage Report


Directory: ./
File: firmware/controllers/actuators/vvt.cpp
Date: 2025-10-03 00:57:22
Coverage Exec Excl Total
Lines: 83.9% 47 0 56
Functions: 90.0% 9 0 10
Branches: 56.2% 18 0 32
Decisions: 50.0% 5 - 10

Line Branch Decision Exec Source
1 /*
2 * @file vvt.cpp
3 *
4 * @date Jun 26, 2016
5 * @author Andrey Belomutskiy, (c) 2012-2020
6 */
7
8 #include "pch.h"
9
10 #include "local_version_holder.h"
11 #include "vvt.h"
12 #include "bench_test.h"
13
14 using vvt_map_t = Map3D<VVT_TABLE_RPM_SIZE, VVT_TABLE_SIZE, int8_t, uint16_t, uint16_t>;
15
16 // todo: rename to intakeVvtTable?
17 static vvt_map_t vvtTable1{"vvt1"};
18 static vvt_map_t vvtTable2{"vvt2"};
19
20 5 VvtController::VvtController(int p_index)
21 5 : index(p_index)
22 5 , m_bank(BANK_BY_INDEX(p_index))
23 5 , m_cam(CAM_BY_INDEX(p_index))
24 {
25 5 }
26
27 4 void VvtController::init(const ValueProvider3D* targetMap, IPwm* pwm) {
28 // Use the same settings for the Nth cam in every bank (ie, all exhaust cams use the same PID)
29 4 m_pid.initPidClass(&engineConfiguration->auxPid[m_cam]);
30
31 4 m_targetMap = targetMap;
32 4 m_pwm = pwm;
33 4 }
34
35 1 void VvtController::onFastCallback() {
36
2/4
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 1 time.
1 if (!m_pwm || !m_targetMap) {
37 // not init yet
38 return;
39 }
40
41 1 m_isRpmHighEnough = Sensor::getOrZero(SensorType::Rpm) > engineConfiguration->vvtControlMinRpm;
42 1 m_isCltWarmEnough = Sensor::getOrZero(SensorType::Clt) > engineConfiguration->vvtControlMinClt;
43
44 1 auto nowNt = getTimeNowNt();
45 1 m_engineRunningLongEnough = engine->rpmCalculator.getSecondsSinceEngineStart(nowNt) > engineConfiguration->vvtActivationDelayMs / MS_PER_SECOND;
46
47 1 update();
48 }
49
50 void VvtController::onConfigurationChange(engine_configuration_s const * previousConfig) {
51 if (!previousConfig || !m_pid.isSame(&previousConfig->auxPid[m_cam])) {
52 m_pid.reset();
53 }
54 }
55
56 2 expected<angle_t> VvtController::observePlant() {
57 #if EFI_SHAFT_POSITION_INPUT
58
1/1
✓ Branch 2 taken 2 times.
2 return engine->triggerCentral.getVVTPosition(m_bank, m_cam);
59 #else
60 return unexpected;
61 #endif // EFI_SHAFT_POSITION_INPUT
62 }
63
64 2 expected<angle_t> VvtController::getSetpoint() {
65
1/1
✓ Branch 1 taken 2 times.
2 float rpm = Sensor::getOrZero(SensorType::Rpm);
66 6 bool enabled = m_engineRunningLongEnough &&
67 #if EFI_PROD_CODE || EFI_UNIT_TEST
68 // simulator functional test does not have CLT or flag?
69
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 m_isCltWarmEnough &&
70 #endif
71
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 m_isRpmHighEnough;
72
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 2 times.
2 if (!enabled) {
73 return unexpected;
74 }
75
76
1/1
✓ Branch 1 taken 2 times.
2 float load = getFuelingLoad();
77
1/1
✓ Branch 2 taken 2 times.
2 float target = m_targetMap->getValue(rpm, load);
78
79 #if EFI_TUNER_STUDIO
80 2 engine->outputChannels.vvtTargets[index] = target;
81 #endif
82
83 2 vvtTarget = target;
84
85 2 return target;
86 }
87
88 2 expected<percent_t> VvtController::getOpenLoop(angle_t target) {
89 // TODO: could we do VVT open loop?
90 UNUSED(target);
91 2 return 0;
92 }
93
94 3 static bool shouldInvertVvt(int camIndex) {
95 // grumble grumble, can't do an array of bits in c++
96
2/3
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
3 switch (camIndex) {
97
1/1
✓ Decision 'true' taken 2 times.
2 case 0: return engineConfiguration->invertVvtControlIntake;
98
1/1
✓ Decision 'true' taken 1 time.
1 case 1: return engineConfiguration->invertVvtControlExhaust;
99 }
100
101 return false;
102 }
103
104 3 expected<percent_t> VvtController::getClosedLoop(angle_t target, angle_t observation) {
105 // User labels say "advance" and "retard"
106 // "advance" means that additional solenoid duty makes indicated VVT position more positive
107 // "retard" means that additional solenoid duty makes indicated VVT position more negative
108 3 bool isInverted = shouldInvertVvt(m_cam);
109
3/3
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 3 times.
3 m_pid.setErrorAmplification(isInverted ? -1.0f : 1.0f);
110
111
1/1
✓ Branch 2 taken 3 times.
3 float retVal = m_pid.getOutput(target, observation);
112
113 #if EFI_TUNER_STUDIO
114
1/1
✓ Branch 2 taken 3 times.
3 m_pid.postState(engine->outputChannels.vvtStatus[index]);
115 #endif /* EFI_TUNER_STUDIO */
116
117 6 return retVal;
118 }
119
120 1 void VvtController::setOutput(expected<percent_t> outputValue) {
121 #if EFI_SHAFT_POSITION_INPUT
122 1 vvtOutput = outputValue.value_or(0);
123
124
1/2
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
1/2
✓ Decision 'true' taken 1 time.
✗ Decision 'false' not taken.
1 if (outputValue) {
125 1 m_pwm->setSimplePwmDutyCycle(PERCENT_TO_DUTY(outputValue.Value));
126 } else {
127 m_pwm->setSimplePwmDutyCycle(0);
128
129 // we need to avoid accumulating iTerm while engine is not running
130 m_pid.reset();
131 }
132 #endif // EFI_SHAFT_POSITION_INPUT
133 1 }
134
135 #if EFI_VVT_PID
136
137 static const char *vvtOutputNames[CAM_INPUTS_COUNT] = {
138 "Vvt Output#1",
139 #if CAM_INPUTS_COUNT > 1
140 "Vvt Output#2",
141 #endif
142 #if CAM_INPUTS_COUNT > 2
143 "Vvt Output#3",
144 #endif
145 #if CAM_INPUTS_COUNT > 3
146 "Vvt Output#4",
147 #endif
148 };
149
150 static OutputPin vvtPins[CAM_INPUTS_COUNT];
151 static SimplePwm vvtPwms[CAM_INPUTS_COUNT] = { "VVT1", "VVT2", "VVT3", "VVT4" };
152
153 OutputPin* getVvtOutputPin(int index) {
154 return &vvtPins[index];
155 }
156
157 static void applyVvtPinState(int stateIndex, PwmConfig *state) /* pwm_gen_callback */ {
158 OutputPin *output = state->outputPins[0];
159 if (output == getOutputOnTheBenchTest()) {
160 return;
161 }
162 state->applyPwmValue(output, stateIndex);
163 }
164
165 static void turnVvtPidOn(int index) {
166 if (!isBrainPinValid(engineConfiguration->vvtPins[index])) {
167 return;
168 }
169
170 startSimplePwmExt(&vvtPwms[index], vvtOutputNames[index],
171 &engine->scheduler,
172 engineConfiguration->vvtPins[index],
173 getVvtOutputPin(index),
174 engineConfiguration->vvtOutputFrequency, 0.1,
175 applyVvtPinState);
176 }
177
178 void startVvtControlPins() {
179 for (int i = 0;i <CAM_INPUTS_COUNT;i++) {
180 turnVvtPidOn(i);
181 }
182 }
183
184 void stopVvtControlPins() {
185 for (int i = 0;i < CAM_INPUTS_COUNT;i++) {
186 getVvtOutputPin(i)->deInit();
187 }
188 }
189
190 void initVvtActuators() {
191
192 vvtTable1.initTable(config->vvtTable1, config->vvtTable1RpmBins, config->vvtTable1LoadBins);
193 vvtTable2.initTable(config->vvtTable2, config->vvtTable2RpmBins, config->vvtTable2LoadBins);
194
195
196 engine->module<VvtController1>()->init(&vvtTable1, &vvtPwms[0]);
197 engine->module<VvtController2>()->init(&vvtTable2, &vvtPwms[1]);
198 engine->module<VvtController3>()->init(&vvtTable1, &vvtPwms[2]);
199 engine->module<VvtController4>()->init(&vvtTable2, &vvtPwms[3]);
200
201 startVvtControlPins();
202 }
203
204 #endif
205