Skip to content

Commit a40e148

Browse files
Merge pull request #478 from simplefoc/fix_lowside_sync_lost
Fix lowside sync lost
2 parents e235d70 + 8ab5336 commit a40e148

File tree

10 files changed

+316
-358
lines changed

10 files changed

+316
-358
lines changed

src/current_sense/hardware_specific/stm32/stm32_adc_utils.cpp

Lines changed: 88 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
#include "stm32_adc_utils.h"
2+
#include "stm32_mcu.h"
23

34
#if defined(_STM32_DEF_)
45

5-
// for searching the best ADCs, we need to know the number of ADCs
6-
// it might be better to use some HAL variable for example ADC_COUNT
7-
// here I've assumed the maximum number of ADCs is 5
8-
#define ADC_COUNT 5
9-
106

117

128
int _adcToIndex(ADC_TypeDef *AdcHandle){
@@ -344,4 +340,91 @@ uint32_t _getADCInjectedRank(uint8_t ind){
344340
}
345341
}
346342

343+
// returns 0 if no interrupt is needed, 1 if interrupt is needed
344+
uint32_t _initTimerInterruptDownsampling(Stm32CurrentSenseParams* cs_params, STM32DriverParams* driver_params, Stm32AdcInterruptConfig& adc_interrupt_config){
345+
346+
// If DIR is 0 (upcounting), the next event is high-side active (PWM rising edge)
347+
// If DIR is 1 (downcounting), the next event is low-side active (PWM falling edge)
348+
bool next_event_high_side = (cs_params->timer_handle->Instance->CR1 & TIM_CR1_DIR) == 0;
349+
350+
// if timer has repetition counter - it will downsample using it
351+
// and it does not need the software downsample
352+
if( IS_TIM_REPETITION_COUNTER_INSTANCE(cs_params->timer_handle->Instance) ){
353+
// adjust the initial timer state such that the trigger
354+
// - only necessary for the timers that have repetition counters
355+
// - basically make sure that the next trigger event is the one that is expected (high-side first then low-side)
356+
357+
// set the direction and the
358+
for(int i=0; i< 6; i++){
359+
if(driver_params->timers_handle[i] == NP) continue; // skip if not set
360+
if(next_event_high_side){
361+
// Set DIR bit to 0 (downcounting)
362+
driver_params->timers_handle[i]->Instance->CR1 |= TIM_CR1_DIR;
363+
// Set CNT to ARR so it starts upcounting from the top
364+
driver_params->timers_handle[i]->Instance->CNT = driver_params->timers_handle[i]->Instance->ARR;
365+
}else{
366+
// Set DIR bit to 0 (upcounting)
367+
driver_params->timers_handle[i]->Instance->CR1 &= ~TIM_CR1_DIR;
368+
// Set CNT to ARR so it starts upcounting from zero
369+
driver_params->timers_handle[i]->Instance->CNT = 0;// driver_params->timers_handle[i]->Instance->ARR;
370+
}
371+
}
372+
return 0; // no interrupt is needed, the timer will handle the downsampling
373+
}else{
374+
if(!adc_interrupt_config.use_adc_interrupt){
375+
// If the timer has no repetition counter, it needs to use the interrupt to downsample for low side sensing
376+
adc_interrupt_config.use_adc_interrupt = 1;
377+
// remember that this timer does not have the repetition counter - need to downasmple
378+
adc_interrupt_config.needs_downsample = 1;
379+
380+
if(next_event_high_side) // Next event is high-side active
381+
adc_interrupt_config.tim_downsample = 0; // skip the next interrupt (and every second one)
382+
else // Next event is low-side active
383+
adc_interrupt_config.tim_downsample = 1; // read the next one (and every second one after)
384+
385+
return 1; // interrupt is needed
386+
}
387+
}
388+
return 1; // interrupt is needed
389+
}
390+
391+
// returns 0 if no downsampling is needed, 1 if downsampling is needed, 2 if error
392+
uint8_t _handleInjectedConvCpltCallback(ADC_HandleTypeDef *AdcHandle, Stm32AdcInterruptConfig& adc_interrupt_config, uint32_t adc_val[4]) {
393+
394+
// if the timer han't repetition counter - downsample two times
395+
if( adc_interrupt_config.needs_downsample && adc_interrupt_config.tim_downsample++ > 0) {
396+
adc_interrupt_config.tim_downsample = 0;
397+
return 1;
398+
}
399+
400+
adc_val[0]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_1);
401+
adc_val[1]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_2);
402+
adc_val[2]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_3);
403+
adc_val[3]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_4);
404+
405+
return 0; // no downsampling needed
406+
}
407+
408+
// reads the ADC injected voltage for the given pin
409+
// returns the voltage
410+
// if the pin is not found in the current sense parameters, returns 0
411+
float _readADCInjectedChannelVoltage(int pin, void* cs_params, Stm32AdcInterruptConfig& adc_interrupt_config, uint32_t adc_val[4]) {
412+
Stm32CurrentSenseParams* cs_p = (Stm32CurrentSenseParams*)cs_params;
413+
uint8_t channel_no = 0;
414+
uint8_t adc_index = (uint8_t)_adcToIndex(cs_p->adc_handle);
415+
for(int i=0; i < 3; i++){
416+
if( pin == cs_p->pins[i]){ // found in the buffer
417+
if (adc_interrupt_config.use_adc_interrupt){
418+
return adc_val[channel_no] * cs_p->adc_voltage_conv;
419+
}else{
420+
// an optimized way to go from i to the channel i=0 -> channel 1, i=1 -> channel 2, i=2 -> channel 3
421+
uint32_t channel = _getADCInjectedRank(channel_no);
422+
return HAL_ADCEx_InjectedGetValue(cs_p->adc_handle, channel) * cs_p->adc_voltage_conv;
423+
}
424+
}
425+
if(_isset(cs_p->pins[i])) channel_no++;
426+
}
427+
return 0; // pin not found
428+
}
429+
347430
#endif

src/current_sense/hardware_specific/stm32/stm32_adc_utils.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,18 @@
77

88
#define _TRGO_NOT_AVAILABLE 12345
99

10+
// for searching the best ADCs, we need to know the number of ADCs
11+
// it might be better to use some HAL variable for example ADC_COUNT
12+
// here I've assumed the maximum number of ADCs is 5
13+
#define ADC_COUNT 5
14+
15+
1016
#include "../../../common/foc_utils.h"
1117
#include "../../../communication/SimpleFOCDebug.h"
18+
#include "../../../drivers/hardware_specific/stm32/stm32_mcu.h"
19+
#include "stm32_mcu.h"
20+
21+
1222

1323
/* Exported Functions */
1424
/**
@@ -34,5 +44,22 @@ int _adcToIndex(ADC_TypeDef *AdcHandle);
3444
int _findIndexOfFirstPinMapADCEntry(int pin);
3545
int _findIndexOfLastPinMapADCEntry(int pin);
3646
ADC_TypeDef* _findBestADCForPins(int num_pins, int pins[]);
47+
48+
49+
// Structure to hold ADC interrupt configuration per ADC instance
50+
struct Stm32AdcInterruptConfig {
51+
bool needs_downsample = 0;
52+
uint8_t tim_downsample = 0;
53+
bool use_adc_interrupt = 0;
54+
};
55+
56+
// returns 0 if no interrupt is needed, 1 if interrupt is needed
57+
uint32_t _initTimerInterruptDownsampling(Stm32CurrentSenseParams* cs_params, STM32DriverParams* driver_params, Stm32AdcInterruptConfig& adc_interrupt_config);
58+
// returns 0 if no downsampling is needed, 1 if downsampling is needed, 2 if error
59+
uint8_t _handleInjectedConvCpltCallback(ADC_HandleTypeDef *AdcHandle, Stm32AdcInterruptConfig& adc_interrupt_config, uint32_t adc_val[4]);
60+
// reads the ADC injected voltage for the given pin
61+
// returns the voltage
62+
// if the pin is not found in the current sense parameters, returns 0
63+
float _readADCInjectedChannelVoltage(int pin, void* cs_params, Stm32AdcInterruptConfig& adc_interrupt_config, uint32_t adc_val[4]);
3764
#endif
3865
#endif

src/current_sense/hardware_specific/stm32/stm32f1/stm32f1_mcu.cpp

Lines changed: 28 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,25 @@
1313
#define _ADC_VOLTAGE_F1 3.3f
1414
#define _ADC_RESOLUTION_F1 4096.0f
1515

16-
// array of values of 4 injected channels per adc instance (3)
17-
uint32_t adc_val[3][4]={0};
18-
// does adc interrupt need a downsample - per adc (3)
19-
bool needs_downsample[3] = {1};
20-
// downsampling variable - per adc (3)
21-
uint8_t tim_downsample[3] = {0};
16+
// array of values of 4 injected channels per adc instance (5)
17+
uint32_t adc_val[5][4]={0};
2218

2319
#ifdef SIMPLEFOC_STM32_ADC_INTERRUPT
24-
uint8_t use_adc_interrupt = 1;
20+
#define USE_ADC_INTERRUPT 1
2521
#else
26-
uint8_t use_adc_interrupt = 0;
22+
#define USE_ADC_INTERRUPT 0
2723
#endif
2824

25+
// structure containing the configuration of the adc interrupt
26+
Stm32AdcInterruptConfig adc_interrupt_config[5] = {
27+
{0, 0, USE_ADC_INTERRUPT}, // ADC1
28+
{0, 0, USE_ADC_INTERRUPT}, // ADC2
29+
{0, 0, USE_ADC_INTERRUPT}, // ADC3
30+
{0, 0, USE_ADC_INTERRUPT}, // ADC4
31+
{0, 0, USE_ADC_INTERRUPT} // ADC5
32+
};
33+
34+
2935
void* _configureADCLowSide(const void* driver_params, const int pinA, const int pinB, const int pinC){
3036

3137
Stm32CurrentSenseParams* cs_params= new Stm32CurrentSenseParams {
@@ -48,34 +54,23 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){
4854
// stop all the timers for the driver
4955
stm32_pause(driver_params);
5056

51-
// if timer has repetition counter - it will downsample using it
52-
// and it does not need the software downsample
53-
if( IS_TIM_REPETITION_COUNTER_INSTANCE(cs_params->timer_handle->Instance) ){
54-
// adjust the initial timer state such that the trigger
55-
// - for DMA transfer aligns with the pwm peaks instead of throughs.
56-
// - for interrupt based ADC transfer
57-
// - only necessary for the timers that have repetition counters
58-
cs_params->timer_handle->Instance->CR1 |= TIM_CR1_DIR;
59-
cs_params->timer_handle->Instance->CNT = cs_params->timer_handle->Instance->ARR;
60-
// remember that this timer has repetition counter - no need to downasmple
61-
needs_downsample[_adcToIndex(cs_params->adc_handle)] = 0;
62-
}else{
63-
if(!use_adc_interrupt){
64-
// If the timer has no repetition counter, it needs to use the interrupt to downsample for low side sensing
65-
use_adc_interrupt = 1;
66-
#ifdef SIMPLEFOC_STM32_DEBUG
67-
SIMPLEFOC_DEBUG("STM32-CS: timer has no repetition counter, ADC interrupt has to be used");
68-
#endif
69-
}
57+
// get the index of the adc
58+
int adc_index = _adcToIndex(cs_params->adc_handle);
59+
60+
bool tim_interrupt = _initTimerInterruptDownsampling(cs_params, driver_params, adc_interrupt_config[adc_index]);
61+
if(tim_interrupt) {
62+
// error in the timer interrupt initialization
63+
SIMPLEFOC_DEBUG("STM32-CS: timer has no repetition counter, ADC interrupt has to be used");
7064
}
65+
7166
// set the trigger output event
7267
LL_TIM_SetTriggerOutput(cs_params->timer_handle->Instance, LL_TIM_TRGO_UPDATE);
7368

7469
// Start the adc calibration
7570
HAL_ADCEx_Calibration_Start(cs_params->adc_handle);
7671

7772
// start the adc
78-
if(use_adc_interrupt){
73+
if(adc_interrupt_config[adc_index].use_adc_interrupt){
7974
HAL_NVIC_SetPriority(ADC1_2_IRQn, 0, 0);
8075
HAL_NVIC_EnableIRQ(ADC1_2_IRQn);
8176

@@ -97,37 +92,16 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){
9792

9893
// function reading an ADC value and returning the read voltage
9994
float _readADCVoltageLowSide(const int pin, const void* cs_params){
100-
uint8_t channel_no = 0;
101-
for(int i=0; i < 3; i++){
102-
if( pin == ((Stm32CurrentSenseParams*)cs_params)->pins[i]){ // found in the buffer
103-
if (use_adc_interrupt){
104-
return adc_val[_adcToIndex(((Stm32CurrentSenseParams*)cs_params)->adc_handle)][channel_no] * ((Stm32CurrentSenseParams*)cs_params)->adc_voltage_conv;
105-
}else{
106-
// an optimized way to go from i to the channel i=0 -> channel 1, i=1 -> channel 2, i=2 -> channel 3
107-
uint32_t channel = _getADCInjectedRank(channel_no);
108-
return HAL_ADCEx_InjectedGetValue(((Stm32CurrentSenseParams*)cs_params)->adc_handle, channel) * ((Stm32CurrentSenseParams*)cs_params)->adc_voltage_conv;
109-
}
110-
}
111-
if(_isset(((Stm32CurrentSenseParams*)cs_params)->pins[i])) channel_no++;
112-
}
113-
return 0;
95+
uint8_t adc_index = (uint8_t)_adcToIndex(((Stm32CurrentSenseParams*)cs_params)->adc_handle);
96+
return _readADCInjectedChannelVoltage(pin, (void*)cs_params, adc_interrupt_config[adc_index], adc_val[adc_index]);
11497
}
11598

11699
extern "C" {
117100
void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef *AdcHandle){
118-
// calculate the instance
119-
int adc_index = _adcToIndex(AdcHandle);
120-
121-
// if the timer han't repetition counter - downsample two times
122-
if( needs_downsample[adc_index] && tim_downsample[adc_index]++ > 0) {
123-
tim_downsample[adc_index] = 0;
124-
return;
125-
}
126-
127-
adc_val[adc_index][0]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_1);
128-
adc_val[adc_index][1]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_2);
129-
adc_val[adc_index][2]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_3);
101+
uint8_t adc_index = (uint8_t)_adcToIndex(AdcHandle);
102+
_handleInjectedConvCpltCallback(AdcHandle, adc_interrupt_config[adc_index], adc_val[adc_index]);
130103
}
131104
}
132105

106+
133107
#endif

src/current_sense/hardware_specific/stm32/stm32f4/stm32f4_mcu.cpp

Lines changed: 26 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,24 @@
1414
#define _ADC_VOLTAGE_F4 3.3f
1515
#define _ADC_RESOLUTION_F4 4096.0f
1616

17-
18-
// array of values of 4 injected channels per adc instance (3)
19-
uint32_t adc_val[3][4]={0};
20-
// does adc interrupt need a downsample - per adc (3)
21-
bool needs_downsample[3] = {1};
22-
// downsampling variable - per adc (3)
23-
uint8_t tim_downsample[3] = {0};
24-
2517
#ifdef SIMPLEFOC_STM32_ADC_INTERRUPT
26-
uint8_t use_adc_interrupt = 1;
18+
#define USE_ADC_INTERRUPT 1
2719
#else
28-
uint8_t use_adc_interrupt = 0;
20+
#define USE_ADC_INTERRUPT 0
2921
#endif
3022

23+
// array of values of 4 injected channels per adc instance (5)
24+
uint32_t adc_val[5][4]={0};
25+
26+
// structure containing the configuration of the adc interrupt
27+
Stm32AdcInterruptConfig adc_interrupt_config[5] = {
28+
{0, 0, USE_ADC_INTERRUPT}, // ADC1
29+
{0, 0, USE_ADC_INTERRUPT}, // ADC2
30+
{0, 0, USE_ADC_INTERRUPT}, // ADC3
31+
{0, 0, USE_ADC_INTERRUPT}, // ADC4
32+
{0, 0, USE_ADC_INTERRUPT} // ADC5
33+
};
34+
3135
void* _configureADCLowSide(const void* driver_params, const int pinA, const int pinB, const int pinC){
3236

3337
Stm32CurrentSenseParams* cs_params= new Stm32CurrentSenseParams {
@@ -50,31 +54,20 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){
5054
// stop all the timers for the driver
5155
stm32_pause(driver_params);
5256

53-
// if timer has repetition counter - it will downsample using it
54-
// and it does not need the software downsample
55-
if( IS_TIM_REPETITION_COUNTER_INSTANCE(cs_params->timer_handle->Instance) ){
56-
// adjust the initial timer state such that the trigger
57-
// - for DMA transfer aligns with the pwm peaks instead of throughs.
58-
// - for interrupt based ADC transfer
59-
// - only necessary for the timers that have repetition counters
60-
cs_params->timer_handle->Instance->CR1 |= TIM_CR1_DIR;
61-
cs_params->timer_handle->Instance->CNT = cs_params->timer_handle->Instance->ARR;
62-
// remember that this timer has repetition counter - no need to downasmple
63-
needs_downsample[_adcToIndex(cs_params->adc_handle)] = 0;
64-
}else{
65-
if(!use_adc_interrupt){
66-
// If the timer has no repetition counter, it needs to use the interrupt to downsample for low side sensing
67-
use_adc_interrupt = 1;
68-
#ifdef SIMPLEFOC_STM32_DEBUG
57+
// get the index of the adc
58+
int adc_index = _adcToIndex(cs_params->adc_handle);
59+
60+
bool tim_interrupt = _initTimerInterruptDownsampling(cs_params, driver_params, adc_interrupt_config[adc_index]);
61+
if(tim_interrupt) {
62+
// error in the timer interrupt initialization
6963
SIMPLEFOC_DEBUG("STM32-CS: timer has no repetition counter, ADC interrupt has to be used");
70-
#endif
71-
}
7264
}
65+
7366
// set the trigger output event
7467
LL_TIM_SetTriggerOutput(cs_params->timer_handle->Instance, LL_TIM_TRGO_UPDATE);
7568

7669
// start the adc
77-
if (use_adc_interrupt){
70+
if (adc_interrupt_config[adc_index].use_adc_interrupt){
7871
// enable interrupt
7972
HAL_NVIC_SetPriority(ADC_IRQn, 0, 0);
8073
HAL_NVIC_EnableIRQ(ADC_IRQn);
@@ -96,36 +89,14 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){
9689

9790
// function reading an ADC value and returning the read voltage
9891
float _readADCVoltageLowSide(const int pin, const void* cs_params){
99-
uint8_t channel_no = 0;
100-
for(int i=0; i < 3; i++){
101-
if( pin == ((Stm32CurrentSenseParams*)cs_params)->pins[i]){ // found in the buffer
102-
if (use_adc_interrupt){
103-
return adc_val[_adcToIndex(((Stm32CurrentSenseParams*)cs_params)->adc_handle)][channel_no] * ((Stm32CurrentSenseParams*)cs_params)->adc_voltage_conv;
104-
}else{
105-
// an optimized way to go from i to the channel i=0 -> channel 1, i=1 -> channel 2, i=2 -> channel 3
106-
uint32_t channel = _getADCInjectedRank(channel_no);
107-
return HAL_ADCEx_InjectedGetValue(((Stm32CurrentSenseParams*)cs_params)->adc_handle, channel) * ((Stm32CurrentSenseParams*)cs_params)->adc_voltage_conv;
108-
}
109-
}
110-
if(_isset(((Stm32CurrentSenseParams*)cs_params)->pins[i])) channel_no++;
111-
}
112-
return 0;
92+
uint8_t adc_index = (uint8_t)_adcToIndex(((Stm32CurrentSenseParams*)cs_params)->adc_handle);
93+
return _readADCInjectedChannelVoltage(pin, (void*)cs_params, adc_interrupt_config[adc_index], adc_val[adc_index]);
11394
}
11495

11596
extern "C" {
11697
void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef *AdcHandle){
117-
// calculate the instance
118-
int adc_index = _adcToIndex(AdcHandle);
119-
120-
// if the timer han't repetition counter - downsample two times
121-
if( needs_downsample[adc_index] && tim_downsample[adc_index]++ > 0) {
122-
tim_downsample[adc_index] = 0;
123-
return;
124-
}
125-
126-
adc_val[adc_index][0]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_1);
127-
adc_val[adc_index][1]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_2);
128-
adc_val[adc_index][2]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_3);
98+
uint8_t adc_index = (uint8_t)_adcToIndex(AdcHandle);
99+
_handleInjectedConvCpltCallback(AdcHandle, adc_interrupt_config[adc_index], adc_val[adc_index]);
129100
}
130101
}
131102

0 commit comments

Comments
 (0)