SimpleFOClibrary 2.4.0
Loading...
Searching...
No Matches
stm32_adc_utils.cpp
Go to the documentation of this file.
1#include "stm32_adc_utils.h"
2#include "stm32_mcu.h"
3#include "stm32_adc_hal.h"
4
5#if defined(_STM32_DEF_)
6
7#ifdef STM32F1xx
8#include "stm32f1xx_ll_adc.h"
9#endif
10#ifdef STM32F7xx
11#include "stm32f7xx_ll_adc.h"
12#endif
13
14
15extern ADC_HandleTypeDef hadc[];
16
17int _adcToIndex(ADC_TypeDef *AdcHandle){
18 if(AdcHandle == ADC1) return 0;
19#ifdef ADC2 // if ADC2 exists
20 else if(AdcHandle == ADC2) return 1;
21#endif
22#ifdef ADC3 // if ADC3 exists
23 else if(AdcHandle == ADC3) return 2;
24#endif
25#ifdef ADC4 // if ADC4 exists
26 else if(AdcHandle == ADC4) return 3;
27#endif
28#ifdef ADC5 // if ADC5 exists
29 else if(AdcHandle == ADC5) return 4;
30#endif
31 return 0;
32}
33int _adcToIndex(ADC_HandleTypeDef *AdcHandle){
34 return _adcToIndex(AdcHandle->Instance);
35}
36
37
38ADC_TypeDef* _indexToADC(uint8_t index){
39 switch (index) {
40 case 0:
41 return ADC1;
42 break;
43#ifdef ADC2 // if ADC2 exists
44 case 1:
45 return ADC2;
46 break;
47#endif
48#ifdef ADC3 // if ADC3 exists
49 case 2:
50 return ADC3;
51 break;
52#endif
53#ifdef ADC4 // if ADC4 exists
54 case 3:
55 return ADC4;
56 break;
57#endif
58#ifdef ADC5 // if ADC5 exists
59 case 4:
60 return ADC5;
61 break;
62#endif
63 }
64 return nullptr;
65}
66
67int _findIndexOfEntry(PinName pin) {
68 // remove the ALT if it is there
69 PinName pinName = (PinName)(pinName & ~ALTX_MASK);
70 int i = 0;
71 SIMPLEFOC_DEBUG("STM32-CS: Looking for pin ");
72 while (PinMap_ADC[i].pin !=NC) {
73 if (pinName == PinMap_ADC[i].pin )
74 return i;
75 i++;
76 SIMPLEFOC_DEBUG("STM32-CS: Looking for pin ", i);
77 }
78 return -1;
79}
80
81int _findIndexOfLastEntry(PinName pin) {
82 // remove the ALT if it is there
83 PinName pinName = (PinName)(pin & ~ALTX_MASK);
84
85 int i = 0;
86 while (PinMap_ADC[i].pin!=NC) {
87 if ( pinName == (PinMap_ADC[i].pin & ~ALTX_MASK)
88 && pinName != (PinMap_ADC[i+1].pin & ~ALTX_MASK))
89 return i;
90 i++;
91 }
92 return -1;
93}
94int _findIndexOfFirstEntry(PinName pin) {
95 // remove the ALT if it is there
96 PinName pinName = (PinName)(pin & ~ALTX_MASK);
97 int i = 0;
98 while (PinMap_ADC[i].pin !=NC) {
99 if (pinName == PinMap_ADC[i].pin )
100 return i;
101 i++;
102 }
103 return -1;
104}
105
106// functions finding the index of the first pin entry in the PinMap_ADC
107// returns -1 if not found
108int _findIndexOfFirstPinMapADCEntry(int pin) {
109 PinName pinName = digitalPinToPinName(pin);
110 // remove the ALT if it is there
111 return _findIndexOfFirstEntry(pinName);
112}
113
114// functions finding the index of the last pin entry in the PinMap_ADC
115// returns -1 if not found
116int _findIndexOfLastPinMapADCEntry(int pin) {
117 PinName pinName = digitalPinToPinName(pin);
118 // remove the ALT if it is there
119 return _findIndexOfLastEntry(pinName);
120}
121
122// find the best ADC for the given pin
123// returns the ADC_TypeDef pointer or nullptr if not found
124// It returns already configured ADC if possible
125// otherwise it returns the first available unconfigured ADC
126ADC_TypeDef* _findBestADCForRegularPin(int pin, ADC_HandleTypeDef adc_handles[]) {
127 PinName pinName = digitalPinToPinName(pin);
128 int index = _findIndexOfFirstPinMapADCEntry(pin);
129 int last_index = _findIndexOfLastPinMapADCEntry(pin);
130 if (index == -1) {
131 return nullptr;
132 }
133 for (int j = index; j <= last_index; j++) {
134 if (PinMap_ADC[j].pin == NC) {
135 break;
136 }
137 int adcIndex = _adcToIndex((ADC_TypeDef*)PinMap_ADC[j].peripheral);
138 if (adc_handles[adcIndex].Instance != NP) {
139 // if ADC is already configured, return it
140 return (ADC_TypeDef*)PinMap_ADC[j].peripheral;
141 }
142 }
143 // return the first available ADC
144 return (ADC_TypeDef*)PinMap_ADC[index].peripheral;
145}
146
147// find the best ADC combination for the given pins
148// returns the index of the best ADC
149// each pin can be connected to multiple ADCs
150// the function will try to find a single ADC that can be used for all pins
151// if not possible it will return nullptr
152ADC_TypeDef* _findBestADCForInjectedPins(int numPins, int pins[], ADC_HandleTypeDef adc_handles[]) {
153
154 // assuning that there is at most 5 ADCs
155 uint8_t pins_at_adc[ADC_COUNT] = {0};
156
157 // check how many pins are there and are not set
158 int no_pins = 0;
159 for (int i = 0; i < numPins; i++) {
160 if(_isset(pins[i])) no_pins++;
161 }
162
163 // loop over all elements and count the pins connected to each ADC
164 for (int i = 0; i < numPins; i++) {
165 int pin = pins[i];
166 if(!_isset(pin)) continue;
167
168 int index = _findIndexOfFirstPinMapADCEntry(pin);
169 int last_index = _findIndexOfLastPinMapADCEntry(pin);
170 if (index == -1) {
171 return nullptr;
172 }
173 for (int j = index; j <= last_index; j++) {
174 if (PinMap_ADC[j].pin == NC) {
175 break;
176 }
177 int adcIndex = _adcToIndex((ADC_TypeDef*)PinMap_ADC[j].peripheral);
178 pins_at_adc[adcIndex]++;
179 }
180 }
181
182#ifndef SIMPLEFOC_DISABLE_DEBUG
183 for (int i = 0; i < ADC_COUNT; i++) {
184 if(!pins_at_adc[i]) continue;
185 SimpleFOCDebug::print("STM32-CS: ADC");
187 SimpleFOCDebug::print(" pins: ");
188 SimpleFOCDebug::println(pins_at_adc[i]);
189 if (adc_handles[i].Instance != NP) {
190 // check if ADC injeted is already in use
191 if(!LL_ADC_INJ_IsTriggerSourceSWStart(adc_handles[i].Instance)) {
192 SimpleFOCDebug::print("STM32-CS: ADC");
194 SimpleFOCDebug::println(" already in use for injected channels!");
195 }
196 }
197 }
198#endif
199
200 // now take the first ADC that has all pins connected
201 for (int i = 0; i < ADC_COUNT; i++) {
202 if (adc_handles[i].Instance != NP) {
203 if (!LL_ADC_INJ_IsTriggerSourceSWStart(adc_handles[i].Instance))
204 continue; // ADC already in use for injected
205 }
206 if (pins_at_adc[i] == no_pins) {
207 return _indexToADC(i);
208 }
209 }
210 return nullptr;
211}
212
213
214
215/**
216 * @brief Return ADC HAL channel linked to a PinName
217 * @param pin: PinName
218 * @retval Valid HAL channel
219 */
220uint32_t _getADCChannelFromPinMap(PinName pin)
221{
222 uint32_t function = pinmap_function(pin, PinMap_ADC);
223 uint32_t channel = 0;
224 switch (STM_PIN_CHANNEL(function)) {
225#ifdef ADC_CHANNEL_0
226 case 0:
227 channel = ADC_CHANNEL_0;
228 break;
229#endif
230#ifdef ADC_CHANNEL_1
231 case 1:
232 channel = ADC_CHANNEL_1;
233 break;
234#endif
235#ifdef ADC_CHANNEL_2
236 case 2:
237 channel = ADC_CHANNEL_2;
238 break;
239#endif
240#ifdef ADC_CHANNEL_3
241 case 3:
242 channel = ADC_CHANNEL_3;
243 break;
244#endif
245#ifdef ADC_CHANNEL_4
246 case 4:
247 channel = ADC_CHANNEL_4;
248 break;
249#endif
250#ifdef ADC_CHANNEL_5
251 case 5:
252 channel = ADC_CHANNEL_5;
253 break;
254#endif
255#ifdef ADC_CHANNEL_6
256 case 6:
257 channel = ADC_CHANNEL_6;
258 break;
259#endif
260#ifdef ADC_CHANNEL_7
261 case 7:
262 channel = ADC_CHANNEL_7;
263 break;
264#endif
265#ifdef ADC_CHANNEL_8
266 case 8:
267 channel = ADC_CHANNEL_8;
268 break;
269#endif
270#ifdef ADC_CHANNEL_9
271 case 9:
272 channel = ADC_CHANNEL_9;
273 break;
274#endif
275#ifdef ADC_CHANNEL_10
276 case 10:
277 channel = ADC_CHANNEL_10;
278 break;
279#endif
280#ifdef ADC_CHANNEL_11
281 case 11:
282 channel = ADC_CHANNEL_11;
283 break;
284#endif
285#ifdef ADC_CHANNEL_12
286 case 12:
287 channel = ADC_CHANNEL_12;
288 break;
289#endif
290#ifdef ADC_CHANNEL_13
291 case 13:
292 channel = ADC_CHANNEL_13;
293 break;
294#endif
295#ifdef ADC_CHANNEL_14
296 case 14:
297 channel = ADC_CHANNEL_14;
298 break;
299#endif
300#ifdef ADC_CHANNEL_15
301 case 15:
302 channel = ADC_CHANNEL_15;
303 break;
304#ifdef ADC_CHANNEL_16
305 case 16:
306 channel = ADC_CHANNEL_16;
307 break;
308#endif
309 case 17:
310 channel = ADC_CHANNEL_17;
311 break;
312#ifdef ADC_CHANNEL_18
313 case 18:
314 channel = ADC_CHANNEL_18;
315 break;
316#endif
317#ifdef ADC_CHANNEL_19
318 case 19:
319 channel = ADC_CHANNEL_19;
320 break;
321#endif
322#ifdef ADC_CHANNEL_20
323 case 20:
324 channel = ADC_CHANNEL_20;
325 break;
326#endif
327#ifdef ADC_CHANNEL_21
328 case 21:
329 channel = ADC_CHANNEL_21;
330 break;
331#endif
332#ifdef ADC_CHANNEL_22
333 case 22:
334 channel = ADC_CHANNEL_22;
335 break;
336#endif
337#ifdef ADC_CHANNEL_23
338 case 23:
339 channel = ADC_CHANNEL_23;
340 break;
341#ifdef ADC_CHANNEL_24
342 case 24:
343 channel = ADC_CHANNEL_24;
344 break;
345#endif
346#ifdef ADC_CHANNEL_25
347 case 25:
348 channel = ADC_CHANNEL_25;
349 break;
350#endif
351#ifdef ADC_CHANNEL_26
352 case 26:
353 channel = ADC_CHANNEL_26;
354 break;
355#ifdef ADC_CHANNEL_27
356 case 27:
357 channel = ADC_CHANNEL_27;
358 break;
359#endif
360#ifdef ADC_CHANNEL_28
361 case 28:
362 channel = ADC_CHANNEL_28;
363 break;
364#endif
365#ifdef ADC_CHANNEL_29
366 case 29:
367 channel = ADC_CHANNEL_29;
368 break;
369#endif
370#ifdef ADC_CHANNEL_30
371 case 30:
372 channel = ADC_CHANNEL_30;
373 break;
374#endif
375#ifdef ADC_CHANNEL_31
376 case 31:
377 channel = ADC_CHANNEL_31;
378 break;
379#endif
380#endif
381#endif
382#endif
383 default:
384 _Error_Handler("ADC: Unknown adc channel", (int)(STM_PIN_CHANNEL(function)));
385 break;
386 }
387 return channel;
388}
389
390/**
391 * @brief Return ADC HAL channel linked to a PinName and the ADC handle
392 * @param pin: PinName
393 * @param AdcHandle: ADC_HandleTypeDef a pointer to the ADC handle
394 * @retval Valid HAL channel
395 */
396uint32_t _getADCChannel(PinName pin, ADC_TypeDef *AdcHandle )
397{
398 if (AdcHandle == NP) {
399 return _getADCChannelFromPinMap(pin);
400 }
401 // find the PinName that corresponds to the ADC
402 int first_ind = _findIndexOfFirstEntry(pin);
403 int last_ind = _findIndexOfLastEntry(pin);
404 if (first_ind == -1 || last_ind == -1) {
405 _Error_Handler("ADC: Pin not found in PinMap_ADC", (int)pin);
406 }
407 // find the channel
408 uint32_t channel = 0;
409 for (int i = first_ind; i <= last_ind; i++) {
410 if (PinMap_ADC[i].peripheral == AdcHandle) {
411 channel =_getADCChannelFromPinMap(PinMap_ADC[i].pin);
412 break;
413 }
414 }
415 return channel;
416}
417
418uint32_t _getADCInjectedRank(uint8_t ind){
419 switch (ind) {
420 #ifdef ADC_INJECTED_RANK_1
421 case 0:
422 return ADC_INJECTED_RANK_1;
423 break;
424#endif
425#ifdef ADC_INJECTED_RANK_2
426 case 1:
427 return ADC_INJECTED_RANK_2;
428 break;
429#endif
430#ifdef ADC_INJECTED_RANK_3
431 case 2:
432 return ADC_INJECTED_RANK_3;
433 break;
434#endif
435#ifdef ADC_INJECTED_RANK_4
436 case 3:
437 return ADC_INJECTED_RANK_4;
438 break;
439#endif
440 default:
441 return 0;
442 break;
443 }
444}
445
446// returns 0 if no interrupt is needed, 1 if interrupt is needed
447uint32_t _initTimerInterruptDownsampling(Stm32CurrentSenseParams* cs_params, STM32DriverParams* driver_params, Stm32AdcInterruptConfig& adc_interrupt_config){
448
449 // If DIR is 0 (upcounting), the next event is high-side active (PWM rising edge)
450 // If DIR is 1 (downcounting), the next event is low-side active (PWM falling edge)
451 bool next_event_high_side = (cs_params->timer_handle->Instance->CR1 & TIM_CR1_DIR) == 0;
452
453 // if timer has repetition counter - it will downsample using it
454 // and it does not need the software downsample
455 if( IS_TIM_REPETITION_COUNTER_INSTANCE(cs_params->timer_handle->Instance) ){
456 // adjust the initial timer state such that the trigger
457 // - only necessary for the timers that have repetition counters
458 // - basically make sure that the next trigger event is the one that is expected (high-side first then low-side)
459
460 // set the direction and the
461 for(int i=0; i< 6; i++){
462 if(driver_params->timers_handle[i] == NP) continue; // skip if not set
463 if(next_event_high_side){
464 // Set DIR bit to 0 (downcounting)
465 driver_params->timers_handle[i]->Instance->CR1 |= TIM_CR1_DIR;
466 // Set CNT to ARR so it starts upcounting from the top
467 driver_params->timers_handle[i]->Instance->CNT = driver_params->timers_handle[i]->Instance->ARR;
468 }else{
469 // Set DIR bit to 0 (upcounting)
470 driver_params->timers_handle[i]->Instance->CR1 &= ~TIM_CR1_DIR;
471 // Set CNT to ARR so it starts upcounting from zero
472 driver_params->timers_handle[i]->Instance->CNT = 0;// driver_params->timers_handle[i]->Instance->ARR;
473 }
474 }
475 return 0; // no interrupt is needed, the timer will handle the downsampling
476 }else{
477 if(!adc_interrupt_config.use_adc_interrupt){
478 // If the timer has no repetition counter, it needs to use the interrupt to downsample for low side sensing
479 adc_interrupt_config.use_adc_interrupt = 1;
480 // remember that this timer does not have the repetition counter - need to downasmple
481 adc_interrupt_config.needs_downsample = 1;
482
483 if(next_event_high_side) // Next event is high-side active
484 adc_interrupt_config.tim_downsample = 0; // skip the next interrupt (and every second one)
485 else // Next event is low-side active
486 adc_interrupt_config.tim_downsample = 1; // read the next one (and every second one after)
487
488 return 1; // interrupt is needed
489 }
490 }
491 return 1; // interrupt is needed
492}
493
494// returns 0 if no downsampling is needed, 1 if downsampling is needed, 2 if error
495uint8_t _handleInjectedConvCpltCallback(ADC_HandleTypeDef *AdcHandle, Stm32AdcInterruptConfig& adc_interrupt_config, uint32_t adc_val[4]) {
496 #ifndef ADC_INJECTED_RANK_1
497 return 0; // error: function not available
498 #else
499
500 // if the timer han't repetition counter - downsample two times
501 if( adc_interrupt_config.needs_downsample && adc_interrupt_config.tim_downsample++ > 0) {
502 adc_interrupt_config.tim_downsample = 0;
503 return 1;
504 }
505
506 adc_val[0]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_1);
507 adc_val[1]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_2);
508 adc_val[2]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_3);
509 adc_val[3]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_4);
510
511 return 0; // no downsampling needed
512 #endif
513}
514
515// reads the ADC injected voltage for the given pin
516// returns the voltage
517// if the pin is not found in the current sense parameters, returns 0
518float _readADCInjectedChannelVoltage(int pin, void* cs_params, Stm32AdcInterruptConfig& adc_interrupt_config, uint32_t adc_val[4]) {
519 #ifndef ADC_INJECTED_RANK_1
520 return 0; // error: function not available
521 #else
522
523 Stm32CurrentSenseParams* cs_p = (Stm32CurrentSenseParams*)cs_params;
524 uint8_t channel_no = 0;
525 uint8_t adc_index = (uint8_t)_adcToIndex(cs_p->adc_handle);
526 for(int i=0; i < 3; i++){
527 if( pin == cs_p->pins[i]){ // found in the buffer
528 if (adc_interrupt_config.use_adc_interrupt){
529 return adc_val[channel_no] * cs_p->adc_voltage_conv;
530 }else{
531 // an optimized way to go from i to the channel i=0 -> channel 1, i=1 -> channel 2, i=2 -> channel 3
532 uint32_t channel = _getADCInjectedRank(channel_no);
533 return HAL_ADCEx_InjectedGetValue(cs_p->adc_handle, channel) * cs_p->adc_voltage_conv;
534 }
535 }
536 if(_isset(cs_p->pins[i])) channel_no++;
537 }
538 return 0; // pin not found
539 #endif
540}
541
542
543
544int last_pin[ADC_COUNT] = {-1,-1,-1,-1,-1};
545uint32_t last_channel[ADC_COUNT] = {0,0,0,0,0};
546
547/**
548 * Read a regular ADC channel while injected channels are running for current sensing.
549 *
550 * This function performs a one-shot regular conversion on the same ADC that is being
551 * used for injected current sensing. Injected conversions have hardware priority and
552 * will pre-empt regular conversions, so this function may experience some latency.
553 *
554 * The function will retry a few times if the ADC returns HAL_BUSY, making it suitable
555 * for reading auxiliary sensors (temperature, voltage, potentiometers, etc.) while
556 * motor control is active.
557 *
558 * @param pin - Arduino pin number to read (must be on the same ADC as current sensing)
559 * @return float - Voltage reading in volts, or -1.0f on error
560 */
561float _readRegularADCVoltage(const int pin){
562
563 ADC_HandleTypeDef* hadc = _get_adc_handles();
564
565 int adc_index = NOT_SET;
566 for(int i = 0; i < ADC_COUNT; i++){
567 if(last_pin[i] == pin){
568 adc_index = i;
569 break;
570 }
571 }
572 // avoid re-configuring the channel if reading the same pin as last time
573 if(!_isset(adc_index)){
574 ADC_TypeDef* adc_instance = _findBestADCForRegularPin(pin, hadc);
575 if(adc_instance == NP){
576 #ifdef SIMPLEFOC_STM32_DEBUG
577 SIMPLEFOC_DEBUG("STM32-CS: ERR: Pin does not belong to any ADC!");
578 #endif
579 return -1.0f;
580 }
581 adc_index = _adcToIndex(adc_instance);
582
583 ADC_HandleTypeDef adc_handle = hadc[adc_index];
584 if (adc_handle.Instance == NP) {
585 #ifdef SIMPLEFOC_STM32_DEBUG
586 SIMPLEFOC_DEBUG("STM32-CS: WARN: ADC not configured, need to configure it: ADC", adc_index+1);
587 #endif
588 if(_adc_init_regular(adc_instance) != 0){
589 #ifdef SIMPLEFOC_STM32_DEBUG
590 SIMPLEFOC_DEBUG("STM32-CS: ERR: Failed to initialize ADC for pin ", pin);
591 #endif
592 return -1.0f;
593 }
594 }
595
596
597 last_pin[adc_index] = pin;
598 // Configure the regular channel for this pin
599 PinName pinName = analogInputToPinName(pin);
600 uint32_t channel = _getADCChannel(pinName, adc_instance);
601
602 last_channel[adc_index] = channel;
603 }
604
605
606 ADC_ChannelConfTypeDef sConfig = {0};
607 sConfig.Channel = last_channel[adc_index];
608 // the shortes possible sampling time
609 // this seems to be a constant in HAL - the shortest time enum is equal to 0
610 // G4 - 2.5 cycles
611 // F1, H7 - 1.5 cycles
612 // L4 - 2.5 cycles
613 // F4, F7 - 3 cycles
614 sConfig.SamplingTime = 0;
615
616#ifdef ADC_REGULAR_RANK_1
617 sConfig.Rank = ADC_REGULAR_RANK_1;
618#else
619 sConfig.Rank = 1;
620#endif
621#ifdef ADC_SINGLE_ENDED
622 sConfig.SingleDiff = ADC_SINGLE_ENDED;
623#endif
624#ifdef ADC_OFFSET_NONE
625 sConfig.OffsetNumber = ADC_OFFSET_NONE;
626#endif
627#ifndef STM32F1xx
628 sConfig.Offset = 0;
629#endif
630
631 if (HAL_ADC_ConfigChannel(&hadc[adc_index], &sConfig) != HAL_OK) {
632#ifdef SIMPLEFOC_STM32_DEBUG
633 SIMPLEFOC_DEBUG("STM32-CS: ERR: Failed to configure regular channel");
634#endif
635 return -1.0f;
636 }
637
638 // Try to start conversion, with retries for HAL_BUSY
639 // (ADC may be busy with injected conversion)
640 HAL_StatusTypeDef status;
641 int retries = 5;
642
643 do {
644 status = HAL_ADC_Start(&hadc[adc_index]);
645 if (status == HAL_BUSY) {
646 // Wait a bit for injected conversion to complete
647 delayMicroseconds(1);
648 retries--;
649 }
650 } while (status == HAL_BUSY && retries > 0);
651
652 if (status != HAL_OK) {
653#ifdef SIMPLEFOC_STM32_DEBUG
654 SIMPLEFOC_DEBUG("STM32-CS: ERR: ADC busy or failed to start");
655#endif
656 return -1.0f;
657 }
658
659 // Wait for conversion to complete
660 // Timeout of 1ms should be more than enough
661 if (HAL_ADC_PollForConversion(&hadc[adc_index], 1) == HAL_OK) {
662 uint32_t raw = HAL_ADC_GetValue(&hadc[adc_index]);
663 return raw * 3.3f / 4096.0f; // assuming 12-bit ADC and 3.3V reference
664 }
665
666#ifdef SIMPLEFOC_STM32_DEBUG
667 SIMPLEFOC_DEBUG("STM32-CS: ERR: Regular conversion timeout");
668#endif
669 return -1.0f;
670}
671
672#endif
#define SIMPLEFOC_DEBUG(msg,...)
static void print(const char *msg)
static void println()
#define NOT_SET
Definition foc_utils.h:34
#define _isset(a)
Definition foc_utils.h:13