Line |
Branch |
Decision |
Exec |
Source |
1 |
|
|
|
#include "pch.h" |
2 |
|
|
|
|
3 |
|
|
|
#include "adc_subscription.h" |
4 |
|
|
|
|
5 |
|
|
|
#if EFI_UNIT_TEST |
6 |
|
|
|
|
7 |
|
|
48 |
/*static*/ AdcSubscriptionEntry * AdcSubscription::SubscribeSensor(FunctionalSensorBase&, adc_channel_e, float, float) { |
8 |
|
|
48 |
return nullptr; |
9 |
|
|
|
} |
10 |
|
|
|
|
11 |
|
|
16 |
/*static*/ void AdcSubscription::UnsubscribeSensor(FunctionalSensorBase&) { |
12 |
|
|
16 |
} |
13 |
|
|
|
|
14 |
|
|
✗ |
/*static*/ void AdcSubscription::UnsubscribeSensor(FunctionalSensorBase&, adc_channel_e) { |
15 |
|
|
✗ |
} |
16 |
|
|
|
|
17 |
|
|
|
#else |
18 |
|
|
|
|
19 |
|
|
|
static AdcSubscriptionEntry s_entries[16]; |
20 |
|
|
|
|
21 |
|
|
|
static AdcSubscriptionEntry* findEntry(FunctionalSensorBase* sensor) { |
22 |
|
|
|
for (size_t i = 0; i < efi::size(s_entries); i++) { |
23 |
|
|
|
if (s_entries[i].Sensor == sensor) { |
24 |
|
|
|
return &s_entries[i]; |
25 |
|
|
|
} |
26 |
|
|
|
} |
27 |
|
|
|
|
28 |
|
|
|
return nullptr; |
29 |
|
|
|
} |
30 |
|
|
|
|
31 |
|
|
|
static AdcSubscriptionEntry* findEntry() { |
32 |
|
|
|
// Find an entry with no sensor set |
33 |
|
|
|
return findEntry(nullptr); |
34 |
|
|
|
} |
35 |
|
|
|
|
36 |
|
|
|
/*static*/ AdcSubscriptionEntry* AdcSubscription::SubscribeSensor(FunctionalSensorBase &sensor, |
37 |
|
|
|
adc_channel_e channel, |
38 |
|
|
|
float lowpassCutoff, |
39 |
|
|
|
float voltsPerAdcVolt /*= 0.0f*/) { |
40 |
|
|
|
// Don't subscribe null channels |
41 |
|
|
|
if (!isAdcChannelValid(channel)) { |
42 |
|
|
|
return nullptr; |
43 |
|
|
|
} |
44 |
|
|
|
|
45 |
|
|
|
// If you passed the same sensor again, resubscribe it with the new parameters |
46 |
|
|
|
AdcSubscriptionEntry* entry = findEntry(&sensor); |
47 |
|
|
|
|
48 |
|
|
|
if (entry) { |
49 |
|
|
|
// If the channel didn't change, we're already set |
50 |
|
|
|
if (entry->Channel == channel) { |
51 |
|
|
|
return entry; |
52 |
|
|
|
} |
53 |
|
|
|
|
54 |
|
|
|
// avoid updates to this while we're mucking with the configuration |
55 |
|
|
|
entry->Sensor = nullptr; |
56 |
|
|
|
} else { |
57 |
|
|
|
// If not already registered, get an empty (new) entry |
58 |
|
|
|
entry = findEntry(); |
59 |
|
|
|
} |
60 |
|
|
|
|
61 |
|
|
|
const char* name = sensor.getSensorName(); |
62 |
|
|
|
|
63 |
|
|
|
// Ensure that a free entry was found |
64 |
|
|
|
if (!entry) { |
65 |
|
|
|
firmwareError(ObdCode::CUSTOM_INVALID_ADC, "too many ADC subscriptions subscribing %s", name); |
66 |
|
|
|
return nullptr; |
67 |
|
|
|
} |
68 |
|
|
|
|
69 |
|
|
|
#if EFI_PROD_CODE && HAL_USE_ADC |
70 |
|
|
|
// Enable the input pin |
71 |
|
|
|
/** |
72 |
|
|
|
TODO: this code is similar to initIfValid, what is the plan? shall we extract helper method or else? |
73 |
|
|
|
*/ |
74 |
|
|
|
brain_pin_e pin = getAdcChannelBrainPin(name, channel); |
75 |
|
|
|
if (pin != Gpio::Invalid) { |
76 |
|
|
|
// todo: external muxes for internal ADC #3350 |
77 |
|
|
|
/* reuqest pin only for parent */ |
78 |
|
|
|
if (!adcIsMuxedInput(channel)) { |
79 |
|
|
|
efiSetPadMode(name, pin, PAL_MODE_INPUT_ANALOG); |
80 |
|
|
|
} else { |
81 |
|
|
|
efiSetPadModeWithoutOwnershipAcquisition(name, pin, PAL_MODE_INPUT_ANALOG); |
82 |
|
|
|
} |
83 |
|
|
|
} |
84 |
|
|
|
|
85 |
|
|
|
// if 0, default to the board's divider coefficient for given channel |
86 |
|
|
|
if (voltsPerAdcVolt == 0) { |
87 |
|
|
|
voltsPerAdcVolt = getAnalogInputDividerCoefficient(channel); |
88 |
|
|
|
} |
89 |
|
|
|
#endif /* EFI_PROD_CODE && HAL_USE_ADC */ |
90 |
|
|
|
// Populate the entry |
91 |
|
|
|
entry->VoltsPerAdcVolt = voltsPerAdcVolt; |
92 |
|
|
|
entry->Channel = channel; |
93 |
|
|
|
// TODO: main_loop use hzForPeriod(ADC_UPDATE_RATE) here |
94 |
|
|
|
entry->Filter.configureLowpass(SLOW_ADC_RATE, lowpassCutoff); |
95 |
|
|
|
entry->HasUpdated = false; |
96 |
|
|
|
|
97 |
|
|
|
// Set the sensor last - it's the field we use to determine whether this entry is in use |
98 |
|
|
|
entry->Sensor = &sensor; |
99 |
|
|
|
return entry; |
100 |
|
|
|
} |
101 |
|
|
|
|
102 |
|
|
|
/*static*/ void AdcSubscription::UnsubscribeSensor(FunctionalSensorBase& sensor) { |
103 |
|
|
|
auto entry = findEntry(&sensor); |
104 |
|
|
|
|
105 |
|
|
|
if (!entry) { |
106 |
|
|
|
// This sensor wasn't configured, skip it |
107 |
|
|
|
return; |
108 |
|
|
|
} |
109 |
|
|
|
|
110 |
|
|
|
#if EFI_PROD_CODE && HAL_USE_ADC |
111 |
|
|
|
// Release the pin |
112 |
|
|
|
efiSetPadUnused(getAdcChannelBrainPin("adc unsubscribe", entry->Channel)); |
113 |
|
|
|
#endif // EFI_PROD_CODE && HAL_USE_ADC |
114 |
|
|
|
|
115 |
|
|
|
sensor.unregister(); |
116 |
|
|
|
|
117 |
|
|
|
// clear the sensor first to mark this entry not in use |
118 |
|
|
|
entry->Sensor = nullptr; |
119 |
|
|
|
|
120 |
|
|
|
entry->VoltsPerAdcVolt = 0; |
121 |
|
|
|
entry->Channel = EFI_ADC_NONE; |
122 |
|
|
|
} |
123 |
|
|
|
|
124 |
|
|
|
/*static*/ void AdcSubscription::UnsubscribeSensor(FunctionalSensorBase& sensor, adc_channel_e channel) { |
125 |
|
|
|
// Find the old sensor |
126 |
|
|
|
auto entry = findEntry(&sensor); |
127 |
|
|
|
|
128 |
|
|
|
// if the channel changed, unsubscribe! |
129 |
|
|
|
if (entry && entry->Channel != channel) { |
130 |
|
|
|
AdcSubscription::UnsubscribeSensor(sensor); |
131 |
|
|
|
} |
132 |
|
|
|
} |
133 |
|
|
|
|
134 |
|
|
|
void AdcSubscription::ResetFilters() { |
135 |
|
|
|
for (size_t i = 0; i < efi::size(s_entries); i++) { |
136 |
|
|
|
auto &entry = s_entries[i]; |
137 |
|
|
|
entry.HasUpdated = false; |
138 |
|
|
|
} |
139 |
|
|
|
} |
140 |
|
|
|
|
141 |
|
|
|
void AdcSubscription::UpdateSubscribers(efitick_t nowNt) { |
142 |
|
|
|
ScopePerf perf(PE::AdcSubscriptionUpdateSubscribers); |
143 |
|
|
|
|
144 |
|
|
|
for (size_t i = 0; i < efi::size(s_entries); i++) { |
145 |
|
|
|
auto &entry = s_entries[i]; |
146 |
|
|
|
|
147 |
|
|
|
if (!entry.Sensor) { |
148 |
|
|
|
// Skip unconfigured entries |
149 |
|
|
|
continue; |
150 |
|
|
|
} |
151 |
|
|
|
|
152 |
|
|
|
auto mcuVolts = adcGetRawVoltage("sensor", entry.Channel); |
153 |
|
|
|
if (mcuVolts) { |
154 |
|
|
|
entry.sensorVolts = mcuVolts.value_or(0) * entry.VoltsPerAdcVolt; |
155 |
|
|
|
|
156 |
|
|
|
// On the very first update, preload the filter as if we've been |
157 |
|
|
|
// seeing this value for a long time. This prevents a slow ramp-up |
158 |
|
|
|
// towards the correct value just after startup |
159 |
|
|
|
if (!entry.HasUpdated) { |
160 |
|
|
|
entry.Filter.cookSteadyState(entry.sensorVolts); |
161 |
|
|
|
entry.HasUpdated = true; |
162 |
|
|
|
} |
163 |
|
|
|
|
164 |
|
|
|
float filtered = entry.Filter.filter(entry.sensorVolts); |
165 |
|
|
|
|
166 |
|
|
|
entry.Sensor->postRawValue(filtered, nowNt); |
167 |
|
|
|
} else { |
168 |
|
|
|
|
169 |
|
|
|
} |
170 |
|
|
|
} |
171 |
|
|
|
} |
172 |
|
|
|
|
173 |
|
|
|
#if EFI_PROD_CODE && HAL_USE_ADC |
174 |
|
|
|
void AdcSubscription::PrintInfo() { |
175 |
|
|
|
for (size_t i = 0; i < efi::size(s_entries); i++) { |
176 |
|
|
|
auto& entry = s_entries[i]; |
177 |
|
|
|
|
178 |
|
|
|
if (!entry.Sensor) { |
179 |
|
|
|
// Skip unconfigured entries |
180 |
|
|
|
continue; |
181 |
|
|
|
} |
182 |
|
|
|
|
183 |
|
|
|
const auto name = entry.Sensor->getSensorName(); |
184 |
|
|
|
auto mcuVolts = adcGetRawVoltage("sensor", entry.Channel); |
185 |
|
|
|
float sensorVolts = mcuVolts.value_or(0) * entry.VoltsPerAdcVolt; |
186 |
|
|
|
auto channel = entry.Channel; |
187 |
|
|
|
|
188 |
|
|
|
char pinNameBuffer[16]; |
189 |
|
|
|
|
190 |
|
|
|
efiPrintf( |
191 |
|
|
|
"%s ADC%d m=%d %s adc=%.2f/input=%.2fv/divider=%.2f %s", |
192 |
|
|
|
name, |
193 |
|
|
|
channel, |
194 |
|
|
|
(int)getAdcMode(channel), |
195 |
|
|
|
getPinNameByAdcChannel(name, channel, pinNameBuffer, sizeof(pinNameBuffer)), |
196 |
|
|
|
mcuVolts.value_or(0), sensorVolts, entry.VoltsPerAdcVolt, |
197 |
|
|
|
mcuVolts ? "valid" : "INVALID" |
198 |
|
|
|
); |
199 |
|
|
|
} |
200 |
|
|
|
} |
201 |
|
|
|
#endif // EFI_PROD_CODE && HAL_USE_ADC |
202 |
|
|
|
|
203 |
|
|
|
#endif // !EFI_UNIT_TEST |
204 |
|
|
|
|