rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
stm32_adc_v4.cpp
Go to the documentation of this file.
1/**
2 * @file stm32_adc_v4.cpp
3 * @brief Port implementation for the STM32 "v4" ADC found on the STM32H7
4 *
5 * @date February 25, 2021
6 * @author Matthew Kennedy, (c) 2021
7 */
8
9#include "pch.h"
10
11#if HAL_USE_ADC
12
13#include "mpu_util.h"
14#include "map_averaging.h"
15
16#ifdef ADC_MUX_PIN
17#error "ADC mux not yet supported on STM32H7"
18#endif
19
20#ifndef H7_ADC_SPEED
21#define H7_ADC_SPEED (10000)
22#endif
23
24#ifndef H7_ADC_OVERSAMPLE
25#define H7_ADC_OVERSAMPLE (4)
26#endif
27
28static_assert((H7_ADC_OVERSAMPLE & (H7_ADC_OVERSAMPLE - 1)) == 0, "H7_ADC_OVERSAMPLE must be a power of 2");
29
30static constexpr size_t log2_int(size_t x) {
31 size_t result = 0;
32 while (x >>= 1) result++;
33 return result;
34}
35
36// poor man's unit test
37static_assert(log2_int(4) == 2);
38static_assert(log2_int(16) == 4);
39
40// Shift the result by log2(N) bits to divide by N
41static constexpr int H7_ADC_SHIFT_BITS = log2_int(H7_ADC_OVERSAMPLE);
42
44 // Init slow ADC
45 adcStart(&ADCD1, NULL);
46
47#if STM32_ADC_USE_ADC3
48 // Knock/trigger scope ADC
49 adcStart(&ADCD3, nullptr);
50#endif // STM32_ADC_USE_ADC3
51
52 // Connect the analog switches between {PA0_C, PA1_C, PC2_C, PC3_C} and their non-C counterparts
53 // This lets us use normal (non-direct) analog on those channels
54 SYSCFG->PMCR &= ~(SYSCFG_PMCR_PA0SO | SYSCFG_PMCR_PA1SO | SYSCFG_PMCR_PC2SO | SYSCFG_PMCR_PC3SO);
55}
56
58 // Ugh, internal temp sensor is wired to ADC3, which makes it nearly useless on the H7.
59 return 0;
60}
61
63 // TODO: implement me!
65}
66
68
69static void adc_callback(ADCDriver *adcp) {
70 // State may not be complete if we get a callback for "half done"
71 if (adcIsBufferComplete(adcp)) {
72 // here we invoke 'fast' from slow ADC due to https://github.com/rusefi/rusefi/issues/3301
73 onFastAdcComplete(adcp->samples);
74 }
75
76 assertInterruptPriority(__func__, EFI_IRQ_ADC_PRIORITY);
77}
78
79// ADC Clock is 25MHz
80// 16.5 sampling + 8.5 conversion = 25 cycles per sample total
81// 16 channels * 4x oversample = 64 samples per batch
82// (25 * 64) / 25MHz -> 64 microseconds to sample all channels
83#define ADC_SAMPLING_SLOW ADC_SMPR_SMP_16P5
84
85// Sample the 16 channels that line up with the STM32F4/F7
86constexpr size_t slowChannelCount = 16;
87
88// Conversion group for slow channels
89// This simply samples every channel in sequence
90static constexpr ADCConversionGroup convGroupSlow = {
91 .circular = true, // Continuous mode means we will auto re-trigger on every timer event
92 .num_channels = slowChannelCount,
93 .end_cb = adc_callback,
94 .error_cb = nullptr,
95 .cfgr = ADC_CFGR_EXTEN_0 | (4 << ADC_CFGR_EXTSEL_Pos), // External trigger ch4, rising edge: TIM3 TRGO
96 .cfgr2 = (H7_ADC_OVERSAMPLE - 1) << ADC_CFGR2_OVSR_Pos | // Oversample by Nx (register contains N-1)
97 H7_ADC_SHIFT_BITS << ADC_CFGR2_OVSS_Pos | // shift the result right log2(N) bits to make a 16 bit result out of the internal oversample sum
98 ADC_CFGR2_ROVSE, // Enable oversampling
99 .ccr = 0,
100 .pcsel = 0xFFFFFFFF, // enable analog switches on all channels
101 // Thresholds aren't used
102 .ltr1 = 0, .htr1 = 0, .ltr2 = 0, .htr2 = 0, .ltr3 = 0, .htr3 = 0,
103 .awd2cr = 0,
104 .awd3cr = 0,
105 .smpr = {
106 // Configure all channels to use ADC_SAMPLING_SLOW time
107 ADC_SMPR1_SMP_AN0(ADC_SAMPLING_SLOW) |
108 ADC_SMPR1_SMP_AN1(ADC_SAMPLING_SLOW) |
109 ADC_SMPR1_SMP_AN2(ADC_SAMPLING_SLOW) |
110 ADC_SMPR1_SMP_AN3(ADC_SAMPLING_SLOW) |
111 ADC_SMPR1_SMP_AN4(ADC_SAMPLING_SLOW) |
112 ADC_SMPR1_SMP_AN5(ADC_SAMPLING_SLOW) |
113 ADC_SMPR1_SMP_AN6(ADC_SAMPLING_SLOW) |
114 ADC_SMPR1_SMP_AN7(ADC_SAMPLING_SLOW) |
115 ADC_SMPR1_SMP_AN8(ADC_SAMPLING_SLOW) |
116 ADC_SMPR1_SMP_AN9(ADC_SAMPLING_SLOW),
117 ADC_SMPR2_SMP_AN10(ADC_SAMPLING_SLOW) |
118 ADC_SMPR2_SMP_AN11(ADC_SAMPLING_SLOW) |
119 ADC_SMPR2_SMP_AN12(ADC_SAMPLING_SLOW) |
120 ADC_SMPR2_SMP_AN13(ADC_SAMPLING_SLOW) |
121 ADC_SMPR2_SMP_AN14(ADC_SAMPLING_SLOW) |
122 ADC_SMPR2_SMP_AN15(ADC_SAMPLING_SLOW) |
123 ADC_SMPR2_SMP_AN16(ADC_SAMPLING_SLOW) |
124 ADC_SMPR2_SMP_AN17(ADC_SAMPLING_SLOW) |
125 ADC_SMPR2_SMP_AN18(ADC_SAMPLING_SLOW) |
126 ADC_SMPR2_SMP_AN19(ADC_SAMPLING_SLOW)
127 },
128 .sqr = {
129 // The seemingly insane values here exist to put the values
130 // in the buffer in the same order as the ADCv2 (F4/F7) ADC
131 ADC_SQR1_SQ1_N(16) | // PA0 (aka PA0_C)
132 ADC_SQR1_SQ2_N(17) | // PA1 (aka PA1_C)
133 ADC_SQR1_SQ3_N(14) | // PA2
134 ADC_SQR1_SQ4_N(15), // PA3
135 ADC_SQR2_SQ5_N(18) | // PA4
136 ADC_SQR2_SQ6_N(19) | // PA5
137 ADC_SQR2_SQ7_N(3) | // PA6
138 ADC_SQR2_SQ8_N(7) | // PA7
139 ADC_SQR2_SQ9_N(9), // PB0
140 ADC_SQR3_SQ10_N(5) | // PB1
141 ADC_SQR3_SQ11_N(10) | // PC0
142 ADC_SQR3_SQ12_N(11) | // PC1
143 ADC_SQR3_SQ13_N(12) | // PC2 (aka PC2_C)
144 ADC_SQR3_SQ14_N(13), // PC3 (aka PC3_C)
145 ADC_SQR4_SQ15_N(4) | // PC4
146 ADC_SQR4_SQ16_N(8) // PC5
147 },
148};
149
150static bool didStart = false;
151
152bool readSlowAnalogInputs(adcsample_t* convertedSamples) {
153 // This only needs to happen once, as the timer will continue firing the ADC and writing to the buffer without our help
154 if (didStart) {
155 return true;
156 }
157 didStart = true;
158
159 fastSampleBuffer = convertedSamples;
160
161 {
162 chibios_rt::CriticalSectionLocker csl;
163 // Oversampling and right-shift happen in hardware, so we can sample directly to the output buffer
164 adcStartConversionI(&ADCD1, &convGroupSlow, convertedSamples, 1);
165 }
166
167 constexpr uint32_t samplingRate = H7_ADC_SPEED;
168 constexpr uint32_t timerCountFrequency = samplingRate * 10;
169 constexpr uint32_t timerPeriod = timerCountFrequency / samplingRate;
170
171 static constexpr GPTConfig gptCfg = {
172 timerCountFrequency,
173 nullptr,
174 TIM_CR2_MMS_1, // TRGO on update event
175 0
176 };
177
178 // Start timer
179 gptStart(&GPTD3, &gptCfg);
180 gptStartContinuous(&GPTD3, timerPeriod);
181
182 // Return true if OK
183 return true;
184}
185
188 return invalidAdcToken;
189 }
190
191 // H7 always samples all fast channels, nothing to do here but compute index
192 return channel - EFI_ADC_0;
193}
194
196 if (token == invalidAdcToken) {
197 return 0;
198 }
199
200 return fastSampleBuffer[token];
201}
202
203#ifdef EFI_SOFTWARE_KNOCK
204#include "knock_config.h"
205
206static_assert((H7_KNOCK_OVERSAMPLE & (H7_KNOCK_OVERSAMPLE - 1)) == 0, "H7_ADC_OVERSAMPLE must be a power of 2");
207static constexpr int H7_KNOCK_ADC_SHIFT_BITS = log2_int(H7_KNOCK_OVERSAMPLE);
208
209static void knockCompletionCallback(ADCDriver* adcp) {
210 if (adcIsBufferComplete(adcp)) {
212 }
213
214 assertInterruptPriority(__func__, EFI_IRQ_ADC_PRIORITY);
215}
216
217static void knockErrorCallback(ADCDriver*, adcerror_t) {
218}
219
220static const uint32_t smpr1 =
221 ADC_SMPR1_SMP_AN0(KNOCK_SAMPLE_TIME) |
222 ADC_SMPR1_SMP_AN1(KNOCK_SAMPLE_TIME) |
223 ADC_SMPR1_SMP_AN2(KNOCK_SAMPLE_TIME) |
224 ADC_SMPR1_SMP_AN3(KNOCK_SAMPLE_TIME) |
225 ADC_SMPR1_SMP_AN4(KNOCK_SAMPLE_TIME) |
226 ADC_SMPR1_SMP_AN5(KNOCK_SAMPLE_TIME) |
227 ADC_SMPR1_SMP_AN6(KNOCK_SAMPLE_TIME) |
228 ADC_SMPR1_SMP_AN7(KNOCK_SAMPLE_TIME) |
229 ADC_SMPR1_SMP_AN8(KNOCK_SAMPLE_TIME) |
230 ADC_SMPR1_SMP_AN9(KNOCK_SAMPLE_TIME);
231
232static const uint32_t smpr2 =
233 ADC_SMPR2_SMP_AN10(KNOCK_SAMPLE_TIME) |
234 ADC_SMPR2_SMP_AN11(KNOCK_SAMPLE_TIME) |
235 ADC_SMPR2_SMP_AN12(KNOCK_SAMPLE_TIME) |
236 ADC_SMPR2_SMP_AN13(KNOCK_SAMPLE_TIME) |
237 ADC_SMPR2_SMP_AN14(KNOCK_SAMPLE_TIME) |
238 ADC_SMPR2_SMP_AN15(KNOCK_SAMPLE_TIME) |
239 ADC_SMPR2_SMP_AN16(KNOCK_SAMPLE_TIME) |
240 ADC_SMPR2_SMP_AN17(KNOCK_SAMPLE_TIME) |
241 ADC_SMPR2_SMP_AN18(KNOCK_SAMPLE_TIME) |
242 ADC_SMPR2_SMP_AN19(KNOCK_SAMPLE_TIME);
243
244static const ADCConversionGroup adcConvGroupCh1 = {
245 .circular = FALSE,
246 .num_channels = 1,
247 .end_cb = &knockCompletionCallback,
248 .error_cb = &knockErrorCallback,
249 .cfgr = 0,
250 .cfgr2 = (H7_KNOCK_OVERSAMPLE - 1) << ADC_CFGR2_OVSR_Pos | // Oversample by Nx (register contains N-1)
251 H7_KNOCK_ADC_SHIFT_BITS << ADC_CFGR2_OVSS_Pos | // shift the result right log2(N) bits to make a 16 bit result out of the internal oversample sum
252 ADC_CFGR2_ROVSE, // Enable oversampling
253 .ccr = 0,
254 .pcsel = 0xFFFFFFFF, // enable analog switches on all channels
255 // Thresholds aren't used
256 .ltr1 = 0, .htr1 = 0, .ltr2 = 0, .htr2 = 0, .ltr3 = 0, .htr3 = 0,
257 .awd2cr = 0,
258 .awd3cr = 0,
259 .smpr = {smpr1, smpr2},
260 .sqr = {
261 ADC_SQR1_SQ1_N(KNOCK_ADC_CH1),
262 0,
263 0
264 },
265};
266
267// Not all boards have a second channel - configure it if it exists
268#if KNOCK_HAS_CH2
269static const ADCConversionGroup adcConvGroupCh2 = {
270 .circular = FALSE,
271 .num_channels = 1,
272 .end_cb = &knockCompletionCallback,
273 .error_cb = &knockErrorCallback,
274 .cfgr = 0,
275 .cfgr2 = (H7_ADC_OVERSAMPLE - 1) << ADC_CFGR2_OVSR_Pos | // Oversample by Nx (register contains N-1)
276 H7_ADC_SHIFT_BITS << ADC_CFGR2_OVSS_Pos | // shift the result right log2(N) bits to make a 16 bit result out of the internal oversample sum
277 ADC_CFGR2_ROVSE, // Enable oversampling
278 .ccr = 0,
279 .pcsel = 0xFFFFFFFF, // enable analog switches on all channels
280 // Thresholds aren't used
281 .ltr1 = 0, .htr1 = 0, .ltr2 = 0, .htr2 = 0, .ltr3 = 0, .htr3 = 0,
282 .awd2cr = 0,
283 .awd3cr = 0,
284 .smpr = {smpr1, smpr2},
285 .sqr = {
286 ADC_SQR1_SQ1_N(KNOCK_ADC_CH2),
287 0,
288 0
289 },
290};
291#endif // KNOCK_HAS_CH2
292
293const ADCConversionGroup* getKnockConversionGroup(uint8_t channelIdx) {
294#if KNOCK_HAS_CH2
295 if (channelIdx == 1) {
296 return &adcConvGroupCh2;
297 }
298#else
299 (void)channelIdx;
300#endif // KNOCK_HAS_CH2
301
302 return &adcConvGroupCh1;
303}
304
305#endif // EFI_SOFTWARE_KNOCK
306
307#endif // HAL_USE_ADC
static constexpr AdcToken invalidAdcToken
Definition adc_inputs.h:110
bool isAdcChannelValid(adc_channel_e hwChannel)
Definition adc_inputs.h:23
uint16_t channel
Definition adc_inputs.h:104
uint32_t AdcToken
Definition adc_inputs.h:98
static constexpr engine_configuration_s * engineConfiguration
ADCDriver ADCD3
ADC3 driver identifier.
Definition hal_adc_lld.c:54
adcerror_t
Possible ADC failure causes.
uint16_t adcsample_t
ADC sample data type.
ADCDriver ADCD1
ADC1 driver identifier.
Definition hal_adc_lld.c:44
GPTDriver GPTD3
GPTD3 driver identifier.
Definition hal_gpt_lld.c:59
void onFastAdcComplete(adcsample_t *)
Definition hardware.cpp:278
float uint8_t channelIdx
void assertInterruptPriority(const char *func, uint8_t expectedPrio)
void onKnockSamplingComplete()
const ADCConversionGroup * getKnockConversionGroup(uint8_t channelIdx)
adcsample_t * fastSampleBuffer
static const uint32_t smpr1
bool readSlowAnalogInputs(adcsample_t *convertedSamples)
static constexpr ADCConversionGroup convGroupSlow
adcsample_t getFastAdc(AdcToken token)
static bool didStart
static const ADCConversionGroup adcConvGroupCh2
static void knockCompletionCallback(ADCDriver *adcp)
static const uint32_t smpr2
static void adc_callback(ADCDriver *adcp)
float getMcuVrefVoltage()
static constexpr int H7_ADC_SHIFT_BITS
static void knockErrorCallback(ADCDriver *, adcerror_t)
float getMcuTemperature()
void portInitAdc()
static constexpr size_t log2_int(size_t x)
constexpr size_t slowChannelCount
static const ADCConversionGroup adcConvGroupCh1
static constexpr int H7_KNOCK_ADC_SHIFT_BITS
AdcToken enableFastAdcChannel(const char *, adc_channel_e channel)
Driver configuration structure.