From 311dfe74249799d0e92d46945a8004e428e55329 Mon Sep 17 00:00:00 2001 From: gospar Date: Wed, 28 May 2025 17:22:23 +0200 Subject: [PATCH 1/2] bugfix for low-side sync --- .../stm32/stm32f1/stm32f1_mcu.cpp | 42 +++++++--- .../stm32/stm32f4/stm32f4_mcu.cpp | 51 ++++++++---- .../stm32/stm32f7/stm32f7_hal.cpp | 8 -- .../stm32/stm32f7/stm32f7_mcu.cpp | 77 ++++++++++++++----- .../stm32/stm32g4/stm32g4_mcu.cpp | 41 +++++++--- .../stm32/stm32h7/stm32h7_hal.cpp | 18 +++-- .../stm32/stm32h7/stm32h7_mcu.cpp | 71 ++++++++++++----- .../stm32/stm32l4/stm32l4_mcu.cpp | 41 +++++++--- 8 files changed, 255 insertions(+), 94 deletions(-) diff --git a/src/current_sense/hardware_specific/stm32/stm32f1/stm32f1_mcu.cpp b/src/current_sense/hardware_specific/stm32/stm32f1/stm32f1_mcu.cpp index 6d69dc89..fc6a1c04 100644 --- a/src/current_sense/hardware_specific/stm32/stm32f1/stm32f1_mcu.cpp +++ b/src/current_sense/hardware_specific/stm32/stm32f1/stm32f1_mcu.cpp @@ -16,7 +16,7 @@ // array of values of 4 injected channels per adc instance (3) uint32_t adc_val[3][4]={0}; // does adc interrupt need a downsample - per adc (3) -bool needs_downsample[3] = {1}; +bool needs_downsample[3] = {0}; // downsampling variable - per adc (3) uint8_t tim_downsample[3] = {0}; @@ -48,26 +48,49 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ // stop all the timers for the driver stm32_pause(driver_params); + // If DIR is 0 (upcounting), the next event is high-side active (PWM rising edge) + // If DIR is 1 (downcounting), the next event is low-side active (PWM falling edge) + bool next_event_high_side = (cs_params->timer_handle->Instance->CR1 & TIM_CR1_DIR) == 0; + // if timer has repetition counter - it will downsample using it // and it does not need the software downsample if( IS_TIM_REPETITION_COUNTER_INSTANCE(cs_params->timer_handle->Instance) ){ // adjust the initial timer state such that the trigger - // - for DMA transfer aligns with the pwm peaks instead of throughs. - // - for interrupt based ADC transfer // - only necessary for the timers that have repetition counters - cs_params->timer_handle->Instance->CR1 |= TIM_CR1_DIR; - cs_params->timer_handle->Instance->CNT = cs_params->timer_handle->Instance->ARR; - // remember that this timer has repetition counter - no need to downasmple - needs_downsample[_adcToIndex(cs_params->adc_handle)] = 0; + // - basically make sure that the next trigger event is the one that is expected (high-side first then low-side) + + // set the direction and the + for(int i=0; i< 6; i++){ + if(driver_params->timers_handle[i] == NP) continue; // skip if not set + if(next_event_high_side){ + // Set DIR bit to 0 (downcounting) + driver_params->timers_handle[i]->Instance->CR1 |= TIM_CR1_DIR; + // Set CNT to ARR so it starts upcounting from the top + driver_params->timers_handle[i]->Instance->CNT = driver_params->timers_handle[i]->Instance->ARR; + }else{ + // Set DIR bit to 0 (upcounting) + driver_params->timers_handle[i]->Instance->CR1 &= ~TIM_CR1_DIR; + // Set CNT to ARR so it starts upcounting from zero + driver_params->timers_handle[i]->Instance->CNT = 0;// driver_params->timers_handle[i]->Instance->ARR; + } + } }else{ if(!use_adc_interrupt){ // If the timer has no repetition counter, it needs to use the interrupt to downsample for low side sensing use_adc_interrupt = 1; - #ifdef SIMPLEFOC_STM32_DEBUG + uint8_t adc_index = _adcToIndex(cs_params->adc_handle); + // remember that this timer does not have the repetition counter - need to downasmple + needs_downsample[adc_index] = 1; + + if(next_event_high_side) // Next event is high-side active + tim_downsample[adc_index] = 0; // skip the next interrupt (and every second one) + else // Next event is low-side active + tim_downsample[adc_index] = 1; // read the next one (and every second one after) + SIMPLEFOC_DEBUG("STM32-CS: timer has no repetition counter, ADC interrupt has to be used"); - #endif } } + // set the trigger output event LL_TIM_SetTriggerOutput(cs_params->timer_handle->Instance, LL_TIM_TRGO_UPDATE); @@ -127,6 +150,7 @@ extern "C" { adc_val[adc_index][0]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_1); adc_val[adc_index][1]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_2); adc_val[adc_index][2]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_3); + adc_val[adc_index][3]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_4); } } diff --git a/src/current_sense/hardware_specific/stm32/stm32f4/stm32f4_mcu.cpp b/src/current_sense/hardware_specific/stm32/stm32f4/stm32f4_mcu.cpp index be49adfe..f0d48e31 100644 --- a/src/current_sense/hardware_specific/stm32/stm32f4/stm32f4_mcu.cpp +++ b/src/current_sense/hardware_specific/stm32/stm32f4/stm32f4_mcu.cpp @@ -18,7 +18,7 @@ // array of values of 4 injected channels per adc instance (3) uint32_t adc_val[3][4]={0}; // does adc interrupt need a downsample - per adc (3) -bool needs_downsample[3] = {1}; +bool needs_downsample[3] = {0}; // downsampling variable - per adc (3) uint8_t tim_downsample[3] = {0}; @@ -50,25 +50,47 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ // stop all the timers for the driver stm32_pause(driver_params); + // If DIR is 0 (upcounting), the next event is high-side active (PWM rising edge) + // If DIR is 1 (downcounting), the next event is low-side active (PWM falling edge) + bool next_event_high_side = (cs_params->timer_handle->Instance->CR1 & TIM_CR1_DIR) == 0; + // if timer has repetition counter - it will downsample using it // and it does not need the software downsample if( IS_TIM_REPETITION_COUNTER_INSTANCE(cs_params->timer_handle->Instance) ){ // adjust the initial timer state such that the trigger - // - for DMA transfer aligns with the pwm peaks instead of throughs. - // - for interrupt based ADC transfer // - only necessary for the timers that have repetition counters - cs_params->timer_handle->Instance->CR1 |= TIM_CR1_DIR; - cs_params->timer_handle->Instance->CNT = cs_params->timer_handle->Instance->ARR; - // remember that this timer has repetition counter - no need to downasmple - needs_downsample[_adcToIndex(cs_params->adc_handle)] = 0; + // - basically make sure that the next trigger event is the one that is expected (high-side first then low-side) + + // set the direction and the + for(int i=0; i< 6; i++){ + if(driver_params->timers_handle[i] == NP) continue; // skip if not set + if(next_event_high_side){ + // Set DIR bit to 0 (downcounting) + driver_params->timers_handle[i]->Instance->CR1 |= TIM_CR1_DIR; + // Set CNT to ARR so it starts upcounting from the top + driver_params->timers_handle[i]->Instance->CNT = driver_params->timers_handle[i]->Instance->ARR; + }else{ + // Set DIR bit to 0 (upcounting) + driver_params->timers_handle[i]->Instance->CR1 &= ~TIM_CR1_DIR; + // Set CNT to ARR so it starts upcounting from zero + driver_params->timers_handle[i]->Instance->CNT = 0;// driver_params->timers_handle[i]->Instance->ARR; + } + } }else{ if(!use_adc_interrupt){ - // If the timer has no repetition counter, it needs to use the interrupt to downsample for low side sensing - use_adc_interrupt = 1; - #ifdef SIMPLEFOC_STM32_DEBUG - SIMPLEFOC_DEBUG("STM32-CS: timer has no repetition counter, ADC interrupt has to be used"); - #endif - } + // If the timer has no repetition counter, it needs to use the interrupt to downsample for low side sensing + use_adc_interrupt = 1; + uint8_t adc_index = _adcToIndex(cs_params->adc_handle); + // remember that this timer does not have the repetition counter - need to downasmple + needs_downsample[adc_index] = 1; + + if(next_event_high_side) // Next event is high-side active + tim_downsample[adc_index] = 0; // skip the next interrupt (and every second one) + else // Next event is low-side active + tim_downsample[adc_index] = 1; // read the next one (and every second one after) + + SIMPLEFOC_DEBUG("STM32-CS: timer has no repetition counter, ADC interrupt has to be used"); + } } // set the trigger output event LL_TIM_SetTriggerOutput(cs_params->timer_handle->Instance, LL_TIM_TRGO_UPDATE); @@ -125,7 +147,8 @@ extern "C" { adc_val[adc_index][0]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_1); adc_val[adc_index][1]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_2); - adc_val[adc_index][2]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_3); + adc_val[adc_index][2]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_3); + adc_val[adc_index][3]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_4); } } diff --git a/src/current_sense/hardware_specific/stm32/stm32f7/stm32f7_hal.cpp b/src/current_sense/hardware_specific/stm32/stm32f7/stm32f7_hal.cpp index ec5ccb64..595eb2a4 100644 --- a/src/current_sense/hardware_specific/stm32/stm32f7/stm32f7_hal.cpp +++ b/src/current_sense/hardware_specific/stm32/stm32f7/stm32f7_hal.cpp @@ -132,12 +132,6 @@ int _adc_init(Stm32CurrentSenseParams* cs_params, const STM32DriverParams* drive } } - #ifdef SIMPLEFOC_STM32_ADC_INTERRUPT - // enable interrupt - HAL_NVIC_SetPriority(ADC_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(ADC_IRQn); - #endif - cs_params->adc_handle = &hadc; return 0; } @@ -174,13 +168,11 @@ int _adc_gpio_init(Stm32CurrentSenseParams* cs_params, const int pinA, const int return 0; } -#ifdef SIMPLEFOC_STM32_ADC_INTERRUPT extern "C" { void ADC_IRQHandler(void) { HAL_ADC_IRQHandler(&hadc); } } -#endif #endif \ No newline at end of file diff --git a/src/current_sense/hardware_specific/stm32/stm32f7/stm32f7_mcu.cpp b/src/current_sense/hardware_specific/stm32/stm32f7/stm32f7_mcu.cpp index 664262dc..0a093418 100644 --- a/src/current_sense/hardware_specific/stm32/stm32f7/stm32f7_mcu.cpp +++ b/src/current_sense/hardware_specific/stm32/stm32f7/stm32f7_mcu.cpp @@ -18,9 +18,16 @@ // array of values of 4 injected channels per adc instance (3) uint32_t adc_val[3][4]={0}; // does adc interrupt need a downsample - per adc (3) -bool needs_downsample[3] = {1}; +bool needs_downsample[3] = {0}; // downsampling variable - per adc (3) -uint8_t tim_downsample[3] = {1}; +uint8_t tim_downsample[3] = {0}; + + +#ifdef SIMPLEFOC_STM32_ADC_INTERRUPT +uint8_t use_adc_interrupt = 1; +#else +uint8_t use_adc_interrupt = 0; +#endif void* _configureADCLowSide(const void* driver_params, const int pinA, const int pinB, const int pinC){ @@ -44,28 +51,61 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ // stop all the timers for the driver stm32_pause(driver_params); + // If DIR is 0 (upcounting), the next event is high-side active (PWM rising edge) + // If DIR is 1 (downcounting), the next event is low-side active (PWM falling edge) + bool next_event_high_side = (cs_params->timer_handle->Instance->CR1 & TIM_CR1_DIR) == 0; + // if timer has repetition counter - it will downsample using it // and it does not need the software downsample if( IS_TIM_REPETITION_COUNTER_INSTANCE(cs_params->timer_handle->Instance) ){ // adjust the initial timer state such that the trigger - // - for DMA transfer aligns with the pwm peaks instead of throughs. - // - for interrupt based ADC transfer // - only necessary for the timers that have repetition counters - - cs_params->timer_handle->Instance->CR1 |= TIM_CR1_DIR; - cs_params->timer_handle->Instance->CNT = cs_params->timer_handle->Instance->ARR; - // remember that this timer has repetition counter - no need to downasmple - needs_downsample[_adcToIndex(cs_params->adc_handle)] = 0; + // - basically make sure that the next trigger event is the one that is expected (high-side first then low-side) + + // set the direction and the + for(int i=0; i< 6; i++){ + if(driver_params->timers_handle[i] == NP) continue; // skip if not set + if(next_event_high_side){ + // Set DIR bit to 0 (downcounting) + driver_params->timers_handle[i]->Instance->CR1 |= TIM_CR1_DIR; + // Set CNT to ARR so it starts upcounting from the top + driver_params->timers_handle[i]->Instance->CNT = driver_params->timers_handle[i]->Instance->ARR; + }else{ + // Set DIR bit to 0 (upcounting) + driver_params->timers_handle[i]->Instance->CR1 &= ~TIM_CR1_DIR; + // Set CNT to ARR so it starts upcounting from zero + driver_params->timers_handle[i]->Instance->CNT = 0;// driver_params->timers_handle[i]->Instance->ARR; + } + } + }else{ + if(!use_adc_interrupt){ + // If the timer has no repetition counter, it needs to use the interrupt to downsample for low side sensing + use_adc_interrupt = 1; + uint8_t adc_index = _adcToIndex(cs_params->adc_handle); + // remember that this timer does not have the repetition counter - need to downasmple + needs_downsample[adc_index] = 1; + + if(next_event_high_side) // Next event is high-side active + tim_downsample[adc_index] = 0; // skip the next interrupt (and every second one) + else // Next event is low-side active + tim_downsample[adc_index] = 1; // read the next one (and every second one after) + + SIMPLEFOC_DEBUG("STM32-CS: timer has no repetition counter, ADC interrupt has to be used"); + } } + // set the trigger output event LL_TIM_SetTriggerOutput(cs_params->timer_handle->Instance, LL_TIM_TRGO_UPDATE); // start the adc - #ifdef SIMPLEFOC_STM32_ADC_INTERRUPT - HAL_ADCEx_InjectedStart_IT(cs_params->adc_handle); - #else - HAL_ADCEx_InjectedStart(cs_params->adc_handle); - #endif + if (use_adc_interrupt){ + // enable interrupt + HAL_NVIC_SetPriority(ADC_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(ADC_IRQn); + HAL_ADCEx_InjectedStart_IT(cs_params->adc_handle); + }else{ + HAL_ADCEx_InjectedStart(cs_params->adc_handle); + } // restart all the timers of the driver stm32_resume(driver_params); @@ -82,20 +122,19 @@ float _readADCVoltageLowSide(const int pin, const void* cs_params){ uint8_t channel_no = 0; for(int i=0; i < 3; i++){ if( pin == ((Stm32CurrentSenseParams*)cs_params)->pins[i]){ // found in the buffer - #ifdef SIMPLEFOC_STM32_ADC_INTERRUPT + if (use_adc_interrupt){ return adc_val[_adcToIndex(((Stm32CurrentSenseParams*)cs_params)->adc_handle)][channel_no] * ((Stm32CurrentSenseParams*)cs_params)->adc_voltage_conv; - #else + }else{ // an optimized way to go from i to the channel i=0 -> channel 1, i=1 -> channel 2, i=2 -> channel 3 uint32_t channel = _getADCInjectedRank(channel_no); return HAL_ADCEx_InjectedGetValue(((Stm32CurrentSenseParams*)cs_params)->adc_handle, channel) * ((Stm32CurrentSenseParams*)cs_params)->adc_voltage_conv; - #endif + } } if(_isset(((Stm32CurrentSenseParams*)cs_params)->pins[i])) channel_no++; } return 0; } -#ifdef SIMPLEFOC_STM32_ADC_INTERRUPT extern "C" { void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef *AdcHandle){ @@ -111,8 +150,8 @@ extern "C" { adc_val[adc_index][0]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_1); adc_val[adc_index][1]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_2); adc_val[adc_index][2]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_3); + adc_val[adc_index][3]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_4); } } -#endif #endif \ No newline at end of file diff --git a/src/current_sense/hardware_specific/stm32/stm32g4/stm32g4_mcu.cpp b/src/current_sense/hardware_specific/stm32/stm32g4/stm32g4_mcu.cpp index 5db5cea7..ae81173e 100644 --- a/src/current_sense/hardware_specific/stm32/stm32g4/stm32g4_mcu.cpp +++ b/src/current_sense/hardware_specific/stm32/stm32g4/stm32g4_mcu.cpp @@ -20,7 +20,7 @@ // array of values of 4 injected channels per adc instance (5) uint32_t adc_val[5][4]={0}; // does adc interrupt need a downsample - per adc (5) -bool needs_downsample[5] = {1}; +bool needs_downsample[5] = {0}; // downsampling variable - per adc (5) uint8_t tim_downsample[5] = {0}; @@ -52,24 +52,47 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ // stop all the timers for the driver stm32_pause(driver_params); + + // If DIR is 0 (upcounting), the next event is high-side active (PWM rising edge) + // If DIR is 1 (downcounting), the next event is low-side active (PWM falling edge) + bool next_event_high_side = (cs_params->timer_handle->Instance->CR1 & TIM_CR1_DIR) == 0; + // if timer has repetition counter - it will downsample using it // and it does not need the software downsample if( IS_TIM_REPETITION_COUNTER_INSTANCE(cs_params->timer_handle->Instance) ){ // adjust the initial timer state such that the trigger - // - for DMA transfer aligns with the pwm peaks instead of throughs. - // - for interrupt based ADC transfer // - only necessary for the timers that have repetition counters - cs_params->timer_handle->Instance->CR1 |= TIM_CR1_DIR; - cs_params->timer_handle->Instance->CNT = cs_params->timer_handle->Instance->ARR; - // remember that this timer has repetition counter - no need to downasmple - needs_downsample[_adcToIndex(cs_params->adc_handle)] = 0; + // - basically make sure that the next trigger event is the one that is expected (high-side first then low-side) + + // set the direction and the + for(int i=0; i< 6; i++){ + if(driver_params->timers_handle[i] == NP) continue; // skip if not set + if(next_event_high_side){ + // Set DIR bit to 0 (downcounting) + driver_params->timers_handle[i]->Instance->CR1 |= TIM_CR1_DIR; + // Set CNT to ARR so it starts upcounting from the top + driver_params->timers_handle[i]->Instance->CNT = driver_params->timers_handle[i]->Instance->ARR; + }else{ + // Set DIR bit to 0 (upcounting) + driver_params->timers_handle[i]->Instance->CR1 &= ~TIM_CR1_DIR; + // Set CNT to ARR so it starts upcounting from zero + driver_params->timers_handle[i]->Instance->CNT = 0;// driver_params->timers_handle[i]->Instance->ARR; + } + } }else{ if(!use_adc_interrupt){ // If the timer has no repetition counter, it needs to use the interrupt to downsample for low side sensing use_adc_interrupt = 1; - #ifdef SIMPLEFOC_STM32_DEBUG + uint8_t adc_index = _adcToIndex(cs_params->adc_handle); + // remember that this timer does not have the repetition counter - need to downasmple + needs_downsample[adc_index] = 1; + + if(next_event_high_side) // Next event is high-side active + tim_downsample[adc_index] = 0; // skip the next interrupt (and every second one) + else // Next event is low-side active + tim_downsample[adc_index] = 1; // read the next one (and every second one after) + SIMPLEFOC_DEBUG("STM32-CS: timer has no repetition counter, ADC interrupt has to be used"); - #endif } } diff --git a/src/current_sense/hardware_specific/stm32/stm32h7/stm32h7_hal.cpp b/src/current_sense/hardware_specific/stm32/stm32h7/stm32h7_hal.cpp index 61ae2de3..32ddba65 100644 --- a/src/current_sense/hardware_specific/stm32/stm32h7/stm32h7_hal.cpp +++ b/src/current_sense/hardware_specific/stm32/stm32h7/stm32h7_hal.cpp @@ -59,7 +59,7 @@ int _adc_init(Stm32CurrentSenseParams* cs_params, const STM32DriverParams* drive // https://github.com/stm32duino/Arduino_Core_STM32/blob/e156c32db24d69cb4818208ccc28894e2f427cfa/system/Drivers/STM32H7xx_HAL_Driver/Inc/stm32h7xx_hal_adc.h#L170C13-L170C27 hadc.Init.DMAContinuousRequests = DISABLE; // not sure about this one!!! maybe use: ADC_SAMPLING_MODE_NORMAL - hadc.Init.SamplingMode = ADC_SAMPLING_MODE_BULB; + hadc.Init.SamplingMode = ADC_SAMPLING_MODE_NORMAL; #endif hadc.Init.NbrOfConversion = 1; hadc.Init.NbrOfDiscConversion = 0; @@ -152,13 +152,6 @@ int _adc_init(Stm32CurrentSenseParams* cs_params, const STM32DriverParams* drive return -1; } } - - delay(1000); - #ifdef SIMPLEFOC_STM32_ADC_INTERRUPT - // enable interrupt - HAL_NVIC_SetPriority(ADC_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(ADC_IRQn); - #endif cs_params->adc_handle = &hadc; return 0; @@ -204,4 +197,13 @@ extern "C" { } } +#ifdef ADC3 +extern "C" { + void ADC3_IRQHandler(void) + { + HAL_ADC_IRQHandler(&hadc); + } +} +#endif + #endif \ No newline at end of file diff --git a/src/current_sense/hardware_specific/stm32/stm32h7/stm32h7_mcu.cpp b/src/current_sense/hardware_specific/stm32/stm32h7/stm32h7_mcu.cpp index f826e69d..e487b734 100644 --- a/src/current_sense/hardware_specific/stm32/stm32h7/stm32h7_mcu.cpp +++ b/src/current_sense/hardware_specific/stm32/stm32h7/stm32h7_mcu.cpp @@ -18,9 +18,9 @@ // array of values of 4 injected channels per adc instance (3) uint32_t adc_val[3][4]={0}; // does adc interrupt need a downsample - per adc (3) -bool needs_downsample[3] = {1}; +bool needs_downsample[3] = {0}; // downsampling variable - per adc (3) -uint8_t tim_downsample[3] = {1}; +uint8_t tim_downsample[3] = {0}; #ifdef SIMPLEFOC_STM32_ADC_INTERRUPT uint8_t use_adc_interrupt = 1; @@ -50,25 +50,46 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ // stop all the timers for the driver stm32_pause(driver_params); + // If DIR is 0 (upcounting), the next event is high-side active (PWM rising edge) + // If DIR is 1 (downcounting), the next event is low-side active (PWM falling edge) + bool next_event_high_side = (cs_params->timer_handle->Instance->CR1 & TIM_CR1_DIR) == 0; + // if timer has repetition counter - it will downsample using it // and it does not need the software downsample if( IS_TIM_REPETITION_COUNTER_INSTANCE(cs_params->timer_handle->Instance) ){ // adjust the initial timer state such that the trigger - // - for DMA transfer aligns with the pwm peaks instead of throughs. - // - for interrupt based ADC transfer // - only necessary for the timers that have repetition counters - - cs_params->timer_handle->Instance->CR1 |= TIM_CR1_DIR; - cs_params->timer_handle->Instance->CNT = cs_params->timer_handle->Instance->ARR; - // remember that this timer has repetition counter - no need to downasmple - needs_downsample[_adcToIndex(cs_params->adc_handle)] = 0; + // - basically make sure that the next trigger event is the one that is expected (high-side first then low-side) + + // set the direction and the + for(int i=0; i< 6; i++){ + if(driver_params->timers_handle[i] == NP) continue; // skip if not set + if(next_event_high_side){ + // Set DIR bit to 0 (downcounting) + driver_params->timers_handle[i]->Instance->CR1 |= TIM_CR1_DIR; + // Set CNT to ARR so it starts upcounting from the top + driver_params->timers_handle[i]->Instance->CNT = driver_params->timers_handle[i]->Instance->ARR; + }else{ + // Set DIR bit to 0 (upcounting) + driver_params->timers_handle[i]->Instance->CR1 &= ~TIM_CR1_DIR; + // Set CNT to ARR so it starts upcounting from zero + driver_params->timers_handle[i]->Instance->CNT = 0;// driver_params->timers_handle[i]->Instance->ARR; + } + } }else{ if(!use_adc_interrupt){ // If the timer has no repetition counter, it needs to use the interrupt to downsample for low side sensing use_adc_interrupt = 1; - #ifdef SIMPLEFOC_STM32_DEBUG + uint8_t adc_index = _adcToIndex(cs_params->adc_handle); + // remember that this timer does not have the repetition counter - need to downasmple + needs_downsample[adc_index] = 1; + + if(next_event_high_side) // Next event is high-side active + tim_downsample[adc_index] = 0; // skip the next interrupt (and every second one) + else // Next event is low-side active + tim_downsample[adc_index] = 1; // read the next one (and every second one after) + SIMPLEFOC_DEBUG("STM32-CS: timer has no repetition counter, ADC interrupt has to be used"); - #endif } } @@ -85,10 +106,26 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ // start the adc if(use_adc_interrupt){ + + if(cs_params->adc_handle->Instance == ADC1){ // enable interrupt HAL_NVIC_SetPriority(ADC_IRQn, 0, 0); HAL_NVIC_EnableIRQ(ADC_IRQn); - + } + #ifdef ADC2 // if defined ADC2 + else if(cs_params->adc_handle->Instance == ADC2) { + // enable interrupt + HAL_NVIC_SetPriority(ADC3_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(ADC3_IRQn); + } + #endif + #ifdef ADC3 // if defined ADC3 + else if(cs_params->adc_handle->Instance == ADC3) { + // enable interrupt + HAL_NVIC_SetPriority(ADC3_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(ADC3_IRQn); + } + #endif if(HAL_ADCEx_InjectedStart_IT(cs_params->adc_handle) != HAL_OK){ #ifdef SIMPLEFOC_STM32_DEBUG SIMPLEFOC_DEBUG("STM32-CS: ERR: cannot start injected channels in interrupt mode!"); @@ -117,9 +154,6 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ // function reading an ADC value and returning the read voltage float _readADCVoltageLowSide(const int pin, const void* cs_params){ - // print all values in the buffer - // SIMPLEFOC_DEBUG("adc_a:", (int)HAL_ADCEx_InjectedGetValue(((Stm32CurrentSenseParams*)cs_params)->adc_handle, _getADCInjectedRank(0))); - // SIMPLEFOC_DEBUG("adc_b:", (int)HAL_ADCEx_InjectedGetValue(((Stm32CurrentSenseParams*)cs_params)->adc_handle, _getADCInjectedRank(1))); uint8_t channel_no = 0; for(int i=0; i < 3; i++){ if( pin == ((Stm32CurrentSenseParams*)cs_params)->pins[i]){ // found in the buffer @@ -143,14 +177,15 @@ extern "C" { int adc_index = _adcToIndex(AdcHandle); // if the timer han't repetition counter - downsample two times - if( needs_downsample[adc_index] && tim_downsample[adc_index]++ > 1) { - tim_downsample[adc_index] = 1; + if( needs_downsample[adc_index] && tim_downsample[adc_index]++ > 0) { + tim_downsample[adc_index] = 0; return; } adc_val[adc_index][0]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_1); adc_val[adc_index][1]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_2); - adc_val[adc_index][2]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_3); + adc_val[adc_index][2]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_3); + adc_val[adc_index][3]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_4); } } diff --git a/src/current_sense/hardware_specific/stm32/stm32l4/stm32l4_mcu.cpp b/src/current_sense/hardware_specific/stm32/stm32l4/stm32l4_mcu.cpp index 4a6e529c..fab44911 100644 --- a/src/current_sense/hardware_specific/stm32/stm32l4/stm32l4_mcu.cpp +++ b/src/current_sense/hardware_specific/stm32/stm32l4/stm32l4_mcu.cpp @@ -19,7 +19,7 @@ // array of values of 4 injected channels per adc instance (5) uint32_t adc_val[5][4]={0}; // does adc interrupt need a downsample - per adc (5) -bool needs_downsample[5] = {1}; +bool needs_downsample[5] = {0}; // downsampling variable - per adc (5) uint8_t tim_downsample[5] = {0}; @@ -51,27 +51,50 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ // stop all the timers for the driver stm32_pause(driver_params); + // If DIR is 0 (upcounting), the next event is high-side active (PWM rising edge) + // If DIR is 1 (downcounting), the next event is low-side active (PWM falling edge) + bool next_event_high_side = (cs_params->timer_handle->Instance->CR1 & TIM_CR1_DIR) == 0; + // if timer has repetition counter - it will downsample using it // and it does not need the software downsample if( IS_TIM_REPETITION_COUNTER_INSTANCE(cs_params->timer_handle->Instance) ){ // adjust the initial timer state such that the trigger - // - for DMA transfer aligns with the pwm peaks instead of throughs. - // - for interrupt based ADC transfer // - only necessary for the timers that have repetition counters - cs_params->timer_handle->Instance->CR1 |= TIM_CR1_DIR; - cs_params->timer_handle->Instance->CNT = cs_params->timer_handle->Instance->ARR; - // remember that this timer has repetition counter - no need to downasmple - needs_downsample[_adcToIndex(cs_params->adc_handle)] = 0; + // - basically make sure that the next trigger event is the one that is expected (high-side first then low-side) + + // set the direction and the + for(int i=0; i< 6; i++){ + if(driver_params->timers_handle[i] == NP) continue; // skip if not set + if(next_event_high_side){ + // Set DIR bit to 0 (downcounting) + driver_params->timers_handle[i]->Instance->CR1 |= TIM_CR1_DIR; + // Set CNT to ARR so it starts upcounting from the top + driver_params->timers_handle[i]->Instance->CNT = driver_params->timers_handle[i]->Instance->ARR; + }else{ + // Set DIR bit to 0 (upcounting) + driver_params->timers_handle[i]->Instance->CR1 &= ~TIM_CR1_DIR; + // Set CNT to ARR so it starts upcounting from zero + driver_params->timers_handle[i]->Instance->CNT = 0;// driver_params->timers_handle[i]->Instance->ARR; + } + } }else{ if(!use_adc_interrupt){ // If the timer has no repetition counter, it needs to use the interrupt to downsample for low side sensing use_adc_interrupt = 1; - #ifdef SIMPLEFOC_STM32_DEBUG + uint8_t adc_index = _adcToIndex(cs_params->adc_handle); + // remember that this timer does not have the repetition counter - need to downasmple + needs_downsample[adc_index] = 1; + + if(next_event_high_side) // Next event is high-side active + tim_downsample[adc_index] = 0; // skip the next interrupt (and every second one) + else // Next event is low-side active + tim_downsample[adc_index] = 1; // read the next one (and every second one after) + SIMPLEFOC_DEBUG("STM32-CS: timer has no repetition counter, ADC interrupt has to be used"); - #endif } } + // set the trigger output event LL_TIM_SetTriggerOutput(cs_params->timer_handle->Instance, LL_TIM_TRGO_UPDATE); From 8ab5336a4091aceb69a414f65605bd34e6f5acec Mon Sep 17 00:00:00 2001 From: gospar Date: Tue, 3 Jun 2025 12:14:38 +0200 Subject: [PATCH 2/2] refactored the injected channel config --- .../stm32/stm32_adc_utils.cpp | 93 ++++++++++- .../hardware_specific/stm32/stm32_adc_utils.h | 27 ++++ .../stm32/stm32f1/stm32f1_mcu.cpp | 104 ++++--------- .../stm32/stm32f4/stm32f4_mcu.cpp | 106 ++++--------- .../stm32/stm32f7/stm32f7_mcu.cpp | 106 ++++--------- .../stm32/stm32g4/stm32g4_mcu.cpp | 99 +++--------- .../stm32/stm32h7/stm32h7_hal.cpp | 2 +- .../stm32/stm32h7/stm32h7_mcu.cpp | 146 ++++++------------ .../stm32/stm32l4/stm32l4_mcu.cpp | 98 +++--------- 9 files changed, 289 insertions(+), 492 deletions(-) diff --git a/src/current_sense/hardware_specific/stm32/stm32_adc_utils.cpp b/src/current_sense/hardware_specific/stm32/stm32_adc_utils.cpp index 33774a55..65f3e8cb 100644 --- a/src/current_sense/hardware_specific/stm32/stm32_adc_utils.cpp +++ b/src/current_sense/hardware_specific/stm32/stm32_adc_utils.cpp @@ -1,12 +1,8 @@ #include "stm32_adc_utils.h" +#include "stm32_mcu.h" #if defined(_STM32_DEF_) -// for searching the best ADCs, we need to know the number of ADCs -// it might be better to use some HAL variable for example ADC_COUNT -// here I've assumed the maximum number of ADCs is 5 -#define ADC_COUNT 5 - int _adcToIndex(ADC_TypeDef *AdcHandle){ @@ -344,4 +340,91 @@ uint32_t _getADCInjectedRank(uint8_t ind){ } } +// returns 0 if no interrupt is needed, 1 if interrupt is needed +uint32_t _initTimerInterruptDownsampling(Stm32CurrentSenseParams* cs_params, STM32DriverParams* driver_params, Stm32AdcInterruptConfig& adc_interrupt_config){ + + // If DIR is 0 (upcounting), the next event is high-side active (PWM rising edge) + // If DIR is 1 (downcounting), the next event is low-side active (PWM falling edge) + bool next_event_high_side = (cs_params->timer_handle->Instance->CR1 & TIM_CR1_DIR) == 0; + + // if timer has repetition counter - it will downsample using it + // and it does not need the software downsample + if( IS_TIM_REPETITION_COUNTER_INSTANCE(cs_params->timer_handle->Instance) ){ + // adjust the initial timer state such that the trigger + // - only necessary for the timers that have repetition counters + // - basically make sure that the next trigger event is the one that is expected (high-side first then low-side) + + // set the direction and the + for(int i=0; i< 6; i++){ + if(driver_params->timers_handle[i] == NP) continue; // skip if not set + if(next_event_high_side){ + // Set DIR bit to 0 (downcounting) + driver_params->timers_handle[i]->Instance->CR1 |= TIM_CR1_DIR; + // Set CNT to ARR so it starts upcounting from the top + driver_params->timers_handle[i]->Instance->CNT = driver_params->timers_handle[i]->Instance->ARR; + }else{ + // Set DIR bit to 0 (upcounting) + driver_params->timers_handle[i]->Instance->CR1 &= ~TIM_CR1_DIR; + // Set CNT to ARR so it starts upcounting from zero + driver_params->timers_handle[i]->Instance->CNT = 0;// driver_params->timers_handle[i]->Instance->ARR; + } + } + return 0; // no interrupt is needed, the timer will handle the downsampling + }else{ + if(!adc_interrupt_config.use_adc_interrupt){ + // If the timer has no repetition counter, it needs to use the interrupt to downsample for low side sensing + adc_interrupt_config.use_adc_interrupt = 1; + // remember that this timer does not have the repetition counter - need to downasmple + adc_interrupt_config.needs_downsample = 1; + + if(next_event_high_side) // Next event is high-side active + adc_interrupt_config.tim_downsample = 0; // skip the next interrupt (and every second one) + else // Next event is low-side active + adc_interrupt_config.tim_downsample = 1; // read the next one (and every second one after) + + return 1; // interrupt is needed + } + } + return 1; // interrupt is needed +} + +// returns 0 if no downsampling is needed, 1 if downsampling is needed, 2 if error +uint8_t _handleInjectedConvCpltCallback(ADC_HandleTypeDef *AdcHandle, Stm32AdcInterruptConfig& adc_interrupt_config, uint32_t adc_val[4]) { + + // if the timer han't repetition counter - downsample two times + if( adc_interrupt_config.needs_downsample && adc_interrupt_config.tim_downsample++ > 0) { + adc_interrupt_config.tim_downsample = 0; + return 1; + } + + adc_val[0]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_1); + adc_val[1]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_2); + adc_val[2]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_3); + adc_val[3]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_4); + + return 0; // no downsampling needed +} + +// reads the ADC injected voltage for the given pin +// returns the voltage +// if the pin is not found in the current sense parameters, returns 0 +float _readADCInjectedChannelVoltage(int pin, void* cs_params, Stm32AdcInterruptConfig& adc_interrupt_config, uint32_t adc_val[4]) { + Stm32CurrentSenseParams* cs_p = (Stm32CurrentSenseParams*)cs_params; + uint8_t channel_no = 0; + uint8_t adc_index = (uint8_t)_adcToIndex(cs_p->adc_handle); + for(int i=0; i < 3; i++){ + if( pin == cs_p->pins[i]){ // found in the buffer + if (adc_interrupt_config.use_adc_interrupt){ + return adc_val[channel_no] * cs_p->adc_voltage_conv; + }else{ + // an optimized way to go from i to the channel i=0 -> channel 1, i=1 -> channel 2, i=2 -> channel 3 + uint32_t channel = _getADCInjectedRank(channel_no); + return HAL_ADCEx_InjectedGetValue(cs_p->adc_handle, channel) * cs_p->adc_voltage_conv; + } + } + if(_isset(cs_p->pins[i])) channel_no++; + } + return 0; // pin not found +} + #endif \ No newline at end of file diff --git a/src/current_sense/hardware_specific/stm32/stm32_adc_utils.h b/src/current_sense/hardware_specific/stm32/stm32_adc_utils.h index 7e74a96f..1294e8f1 100644 --- a/src/current_sense/hardware_specific/stm32/stm32_adc_utils.h +++ b/src/current_sense/hardware_specific/stm32/stm32_adc_utils.h @@ -7,8 +7,18 @@ #define _TRGO_NOT_AVAILABLE 12345 +// for searching the best ADCs, we need to know the number of ADCs +// it might be better to use some HAL variable for example ADC_COUNT +// here I've assumed the maximum number of ADCs is 5 +#define ADC_COUNT 5 + + #include "../../../common/foc_utils.h" #include "../../../communication/SimpleFOCDebug.h" +#include "../../../drivers/hardware_specific/stm32/stm32_mcu.h" +#include "stm32_mcu.h" + + /* Exported Functions */ /** @@ -34,5 +44,22 @@ int _adcToIndex(ADC_TypeDef *AdcHandle); int _findIndexOfFirstPinMapADCEntry(int pin); int _findIndexOfLastPinMapADCEntry(int pin); ADC_TypeDef* _findBestADCForPins(int num_pins, int pins[]); + + +// Structure to hold ADC interrupt configuration per ADC instance +struct Stm32AdcInterruptConfig { + bool needs_downsample = 0; + uint8_t tim_downsample = 0; + bool use_adc_interrupt = 0; +}; + +// returns 0 if no interrupt is needed, 1 if interrupt is needed +uint32_t _initTimerInterruptDownsampling(Stm32CurrentSenseParams* cs_params, STM32DriverParams* driver_params, Stm32AdcInterruptConfig& adc_interrupt_config); +// returns 0 if no downsampling is needed, 1 if downsampling is needed, 2 if error +uint8_t _handleInjectedConvCpltCallback(ADC_HandleTypeDef *AdcHandle, Stm32AdcInterruptConfig& adc_interrupt_config, uint32_t adc_val[4]); +// reads the ADC injected voltage for the given pin +// returns the voltage +// if the pin is not found in the current sense parameters, returns 0 +float _readADCInjectedChannelVoltage(int pin, void* cs_params, Stm32AdcInterruptConfig& adc_interrupt_config, uint32_t adc_val[4]); #endif #endif diff --git a/src/current_sense/hardware_specific/stm32/stm32f1/stm32f1_mcu.cpp b/src/current_sense/hardware_specific/stm32/stm32f1/stm32f1_mcu.cpp index fc6a1c04..7b0e9024 100644 --- a/src/current_sense/hardware_specific/stm32/stm32f1/stm32f1_mcu.cpp +++ b/src/current_sense/hardware_specific/stm32/stm32f1/stm32f1_mcu.cpp @@ -13,19 +13,25 @@ #define _ADC_VOLTAGE_F1 3.3f #define _ADC_RESOLUTION_F1 4096.0f -// array of values of 4 injected channels per adc instance (3) -uint32_t adc_val[3][4]={0}; -// does adc interrupt need a downsample - per adc (3) -bool needs_downsample[3] = {0}; -// downsampling variable - per adc (3) -uint8_t tim_downsample[3] = {0}; +// array of values of 4 injected channels per adc instance (5) +uint32_t adc_val[5][4]={0}; #ifdef SIMPLEFOC_STM32_ADC_INTERRUPT -uint8_t use_adc_interrupt = 1; +#define USE_ADC_INTERRUPT 1 #else -uint8_t use_adc_interrupt = 0; +#define USE_ADC_INTERRUPT 0 #endif +// structure containing the configuration of the adc interrupt +Stm32AdcInterruptConfig adc_interrupt_config[5] = { + {0, 0, USE_ADC_INTERRUPT}, // ADC1 + {0, 0, USE_ADC_INTERRUPT}, // ADC2 + {0, 0, USE_ADC_INTERRUPT}, // ADC3 + {0, 0, USE_ADC_INTERRUPT}, // ADC4 + {0, 0, USE_ADC_INTERRUPT} // ADC5 +}; + + void* _configureADCLowSide(const void* driver_params, const int pinA, const int pinB, const int pinC){ Stm32CurrentSenseParams* cs_params= new Stm32CurrentSenseParams { @@ -48,47 +54,13 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ // stop all the timers for the driver stm32_pause(driver_params); - // If DIR is 0 (upcounting), the next event is high-side active (PWM rising edge) - // If DIR is 1 (downcounting), the next event is low-side active (PWM falling edge) - bool next_event_high_side = (cs_params->timer_handle->Instance->CR1 & TIM_CR1_DIR) == 0; - - // if timer has repetition counter - it will downsample using it - // and it does not need the software downsample - if( IS_TIM_REPETITION_COUNTER_INSTANCE(cs_params->timer_handle->Instance) ){ - // adjust the initial timer state such that the trigger - // - only necessary for the timers that have repetition counters - // - basically make sure that the next trigger event is the one that is expected (high-side first then low-side) - - // set the direction and the - for(int i=0; i< 6; i++){ - if(driver_params->timers_handle[i] == NP) continue; // skip if not set - if(next_event_high_side){ - // Set DIR bit to 0 (downcounting) - driver_params->timers_handle[i]->Instance->CR1 |= TIM_CR1_DIR; - // Set CNT to ARR so it starts upcounting from the top - driver_params->timers_handle[i]->Instance->CNT = driver_params->timers_handle[i]->Instance->ARR; - }else{ - // Set DIR bit to 0 (upcounting) - driver_params->timers_handle[i]->Instance->CR1 &= ~TIM_CR1_DIR; - // Set CNT to ARR so it starts upcounting from zero - driver_params->timers_handle[i]->Instance->CNT = 0;// driver_params->timers_handle[i]->Instance->ARR; - } - } - }else{ - if(!use_adc_interrupt){ - // If the timer has no repetition counter, it needs to use the interrupt to downsample for low side sensing - use_adc_interrupt = 1; - uint8_t adc_index = _adcToIndex(cs_params->adc_handle); - // remember that this timer does not have the repetition counter - need to downasmple - needs_downsample[adc_index] = 1; - - if(next_event_high_side) // Next event is high-side active - tim_downsample[adc_index] = 0; // skip the next interrupt (and every second one) - else // Next event is low-side active - tim_downsample[adc_index] = 1; // read the next one (and every second one after) - - SIMPLEFOC_DEBUG("STM32-CS: timer has no repetition counter, ADC interrupt has to be used"); - } + // get the index of the adc + int adc_index = _adcToIndex(cs_params->adc_handle); + + bool tim_interrupt = _initTimerInterruptDownsampling(cs_params, driver_params, adc_interrupt_config[adc_index]); + if(tim_interrupt) { + // error in the timer interrupt initialization + SIMPLEFOC_DEBUG("STM32-CS: timer has no repetition counter, ADC interrupt has to be used"); } // set the trigger output event @@ -98,7 +70,7 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ HAL_ADCEx_Calibration_Start(cs_params->adc_handle); // start the adc - if(use_adc_interrupt){ + if(adc_interrupt_config[adc_index].use_adc_interrupt){ HAL_NVIC_SetPriority(ADC1_2_IRQn, 0, 0); HAL_NVIC_EnableIRQ(ADC1_2_IRQn); @@ -120,38 +92,16 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ // function reading an ADC value and returning the read voltage float _readADCVoltageLowSide(const int pin, const void* cs_params){ - uint8_t channel_no = 0; - for(int i=0; i < 3; i++){ - if( pin == ((Stm32CurrentSenseParams*)cs_params)->pins[i]){ // found in the buffer - if (use_adc_interrupt){ - return adc_val[_adcToIndex(((Stm32CurrentSenseParams*)cs_params)->adc_handle)][channel_no] * ((Stm32CurrentSenseParams*)cs_params)->adc_voltage_conv; - }else{ - // an optimized way to go from i to the channel i=0 -> channel 1, i=1 -> channel 2, i=2 -> channel 3 - uint32_t channel = _getADCInjectedRank(channel_no); - return HAL_ADCEx_InjectedGetValue(((Stm32CurrentSenseParams*)cs_params)->adc_handle, channel) * ((Stm32CurrentSenseParams*)cs_params)->adc_voltage_conv; - } - } - if(_isset(((Stm32CurrentSenseParams*)cs_params)->pins[i])) channel_no++; - } - return 0; + uint8_t adc_index = (uint8_t)_adcToIndex(((Stm32CurrentSenseParams*)cs_params)->adc_handle); + return _readADCInjectedChannelVoltage(pin, (void*)cs_params, adc_interrupt_config[adc_index], adc_val[adc_index]); } extern "C" { void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef *AdcHandle){ - // calculate the instance - int adc_index = _adcToIndex(AdcHandle); - - // if the timer han't repetition counter - downsample two times - if( needs_downsample[adc_index] && tim_downsample[adc_index]++ > 0) { - tim_downsample[adc_index] = 0; - return; - } - - adc_val[adc_index][0]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_1); - adc_val[adc_index][1]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_2); - adc_val[adc_index][2]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_3); - adc_val[adc_index][3]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_4); + uint8_t adc_index = (uint8_t)_adcToIndex(AdcHandle); + _handleInjectedConvCpltCallback(AdcHandle, adc_interrupt_config[adc_index], adc_val[adc_index]); } } + #endif \ No newline at end of file diff --git a/src/current_sense/hardware_specific/stm32/stm32f4/stm32f4_mcu.cpp b/src/current_sense/hardware_specific/stm32/stm32f4/stm32f4_mcu.cpp index f0d48e31..7bace501 100644 --- a/src/current_sense/hardware_specific/stm32/stm32f4/stm32f4_mcu.cpp +++ b/src/current_sense/hardware_specific/stm32/stm32f4/stm32f4_mcu.cpp @@ -14,20 +14,24 @@ #define _ADC_VOLTAGE_F4 3.3f #define _ADC_RESOLUTION_F4 4096.0f - -// array of values of 4 injected channels per adc instance (3) -uint32_t adc_val[3][4]={0}; -// does adc interrupt need a downsample - per adc (3) -bool needs_downsample[3] = {0}; -// downsampling variable - per adc (3) -uint8_t tim_downsample[3] = {0}; - #ifdef SIMPLEFOC_STM32_ADC_INTERRUPT -uint8_t use_adc_interrupt = 1; +#define USE_ADC_INTERRUPT 1 #else -uint8_t use_adc_interrupt = 0; +#define USE_ADC_INTERRUPT 0 #endif +// array of values of 4 injected channels per adc instance (5) +uint32_t adc_val[5][4]={0}; + +// structure containing the configuration of the adc interrupt +Stm32AdcInterruptConfig adc_interrupt_config[5] = { + {0, 0, USE_ADC_INTERRUPT}, // ADC1 + {0, 0, USE_ADC_INTERRUPT}, // ADC2 + {0, 0, USE_ADC_INTERRUPT}, // ADC3 + {0, 0, USE_ADC_INTERRUPT}, // ADC4 + {0, 0, USE_ADC_INTERRUPT} // ADC5 +}; + void* _configureADCLowSide(const void* driver_params, const int pinA, const int pinB, const int pinC){ Stm32CurrentSenseParams* cs_params= new Stm32CurrentSenseParams { @@ -50,53 +54,20 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ // stop all the timers for the driver stm32_pause(driver_params); - // If DIR is 0 (upcounting), the next event is high-side active (PWM rising edge) - // If DIR is 1 (downcounting), the next event is low-side active (PWM falling edge) - bool next_event_high_side = (cs_params->timer_handle->Instance->CR1 & TIM_CR1_DIR) == 0; - - // if timer has repetition counter - it will downsample using it - // and it does not need the software downsample - if( IS_TIM_REPETITION_COUNTER_INSTANCE(cs_params->timer_handle->Instance) ){ - // adjust the initial timer state such that the trigger - // - only necessary for the timers that have repetition counters - // - basically make sure that the next trigger event is the one that is expected (high-side first then low-side) - - // set the direction and the - for(int i=0; i< 6; i++){ - if(driver_params->timers_handle[i] == NP) continue; // skip if not set - if(next_event_high_side){ - // Set DIR bit to 0 (downcounting) - driver_params->timers_handle[i]->Instance->CR1 |= TIM_CR1_DIR; - // Set CNT to ARR so it starts upcounting from the top - driver_params->timers_handle[i]->Instance->CNT = driver_params->timers_handle[i]->Instance->ARR; - }else{ - // Set DIR bit to 0 (upcounting) - driver_params->timers_handle[i]->Instance->CR1 &= ~TIM_CR1_DIR; - // Set CNT to ARR so it starts upcounting from zero - driver_params->timers_handle[i]->Instance->CNT = 0;// driver_params->timers_handle[i]->Instance->ARR; - } - } - }else{ - if(!use_adc_interrupt){ - // If the timer has no repetition counter, it needs to use the interrupt to downsample for low side sensing - use_adc_interrupt = 1; - uint8_t adc_index = _adcToIndex(cs_params->adc_handle); - // remember that this timer does not have the repetition counter - need to downasmple - needs_downsample[adc_index] = 1; - - if(next_event_high_side) // Next event is high-side active - tim_downsample[adc_index] = 0; // skip the next interrupt (and every second one) - else // Next event is low-side active - tim_downsample[adc_index] = 1; // read the next one (and every second one after) - - SIMPLEFOC_DEBUG("STM32-CS: timer has no repetition counter, ADC interrupt has to be used"); - } + // get the index of the adc + int adc_index = _adcToIndex(cs_params->adc_handle); + + bool tim_interrupt = _initTimerInterruptDownsampling(cs_params, driver_params, adc_interrupt_config[adc_index]); + if(tim_interrupt) { + // error in the timer interrupt initialization + SIMPLEFOC_DEBUG("STM32-CS: timer has no repetition counter, ADC interrupt has to be used"); } + // set the trigger output event LL_TIM_SetTriggerOutput(cs_params->timer_handle->Instance, LL_TIM_TRGO_UPDATE); // start the adc - if (use_adc_interrupt){ + if (adc_interrupt_config[adc_index].use_adc_interrupt){ // enable interrupt HAL_NVIC_SetPriority(ADC_IRQn, 0, 0); HAL_NVIC_EnableIRQ(ADC_IRQn); @@ -118,37 +89,14 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ // function reading an ADC value and returning the read voltage float _readADCVoltageLowSide(const int pin, const void* cs_params){ - uint8_t channel_no = 0; - for(int i=0; i < 3; i++){ - if( pin == ((Stm32CurrentSenseParams*)cs_params)->pins[i]){ // found in the buffer - if (use_adc_interrupt){ - return adc_val[_adcToIndex(((Stm32CurrentSenseParams*)cs_params)->adc_handle)][channel_no] * ((Stm32CurrentSenseParams*)cs_params)->adc_voltage_conv; - }else{ - // an optimized way to go from i to the channel i=0 -> channel 1, i=1 -> channel 2, i=2 -> channel 3 - uint32_t channel = _getADCInjectedRank(channel_no); - return HAL_ADCEx_InjectedGetValue(((Stm32CurrentSenseParams*)cs_params)->adc_handle, channel) * ((Stm32CurrentSenseParams*)cs_params)->adc_voltage_conv; - } - } - if(_isset(((Stm32CurrentSenseParams*)cs_params)->pins[i])) channel_no++; - } - return 0; + uint8_t adc_index = (uint8_t)_adcToIndex(((Stm32CurrentSenseParams*)cs_params)->adc_handle); + return _readADCInjectedChannelVoltage(pin, (void*)cs_params, adc_interrupt_config[adc_index], adc_val[adc_index]); } extern "C" { void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef *AdcHandle){ - // calculate the instance - int adc_index = _adcToIndex(AdcHandle); - - // if the timer han't repetition counter - downsample two times - if( needs_downsample[adc_index] && tim_downsample[adc_index]++ > 0) { - tim_downsample[adc_index] = 0; - return; - } - - adc_val[adc_index][0]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_1); - adc_val[adc_index][1]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_2); - adc_val[adc_index][2]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_3); - adc_val[adc_index][3]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_4); + uint8_t adc_index = (uint8_t)_adcToIndex(AdcHandle); + _handleInjectedConvCpltCallback(AdcHandle, adc_interrupt_config[adc_index], adc_val[adc_index]); } } diff --git a/src/current_sense/hardware_specific/stm32/stm32f7/stm32f7_mcu.cpp b/src/current_sense/hardware_specific/stm32/stm32f7/stm32f7_mcu.cpp index 0a093418..d8bac8d8 100644 --- a/src/current_sense/hardware_specific/stm32/stm32f7/stm32f7_mcu.cpp +++ b/src/current_sense/hardware_specific/stm32/stm32f7/stm32f7_mcu.cpp @@ -14,21 +14,25 @@ #define _ADC_VOLTAGE 3.3f #define _ADC_RESOLUTION 4096.0f - -// array of values of 4 injected channels per adc instance (3) -uint32_t adc_val[3][4]={0}; -// does adc interrupt need a downsample - per adc (3) -bool needs_downsample[3] = {0}; -// downsampling variable - per adc (3) -uint8_t tim_downsample[3] = {0}; - +// array of values of 4 injected channels per adc instance (5) +uint32_t adc_val[5][4]={0}; #ifdef SIMPLEFOC_STM32_ADC_INTERRUPT -uint8_t use_adc_interrupt = 1; +#define USE_ADC_INTERRUPT 1 #else -uint8_t use_adc_interrupt = 0; +#define USE_ADC_INTERRUPT 0 #endif +// structure containing the configuration of the adc interrupt +Stm32AdcInterruptConfig adc_interrupt_config[5] = { + {0, 0, USE_ADC_INTERRUPT}, // ADC1 + {0, 0, USE_ADC_INTERRUPT}, // ADC2 + {0, 0, USE_ADC_INTERRUPT}, // ADC3 + {0, 0, USE_ADC_INTERRUPT}, // ADC4 + {0, 0, USE_ADC_INTERRUPT} // ADC5 +}; + + void* _configureADCLowSide(const void* driver_params, const int pinA, const int pinB, const int pinC){ Stm32CurrentSenseParams* cs_params= new Stm32CurrentSenseParams { @@ -51,54 +55,20 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ // stop all the timers for the driver stm32_pause(driver_params); - // If DIR is 0 (upcounting), the next event is high-side active (PWM rising edge) - // If DIR is 1 (downcounting), the next event is low-side active (PWM falling edge) - bool next_event_high_side = (cs_params->timer_handle->Instance->CR1 & TIM_CR1_DIR) == 0; - - // if timer has repetition counter - it will downsample using it - // and it does not need the software downsample - if( IS_TIM_REPETITION_COUNTER_INSTANCE(cs_params->timer_handle->Instance) ){ - // adjust the initial timer state such that the trigger - // - only necessary for the timers that have repetition counters - // - basically make sure that the next trigger event is the one that is expected (high-side first then low-side) - - // set the direction and the - for(int i=0; i< 6; i++){ - if(driver_params->timers_handle[i] == NP) continue; // skip if not set - if(next_event_high_side){ - // Set DIR bit to 0 (downcounting) - driver_params->timers_handle[i]->Instance->CR1 |= TIM_CR1_DIR; - // Set CNT to ARR so it starts upcounting from the top - driver_params->timers_handle[i]->Instance->CNT = driver_params->timers_handle[i]->Instance->ARR; - }else{ - // Set DIR bit to 0 (upcounting) - driver_params->timers_handle[i]->Instance->CR1 &= ~TIM_CR1_DIR; - // Set CNT to ARR so it starts upcounting from zero - driver_params->timers_handle[i]->Instance->CNT = 0;// driver_params->timers_handle[i]->Instance->ARR; - } - } - }else{ - if(!use_adc_interrupt){ - // If the timer has no repetition counter, it needs to use the interrupt to downsample for low side sensing - use_adc_interrupt = 1; - uint8_t adc_index = _adcToIndex(cs_params->adc_handle); - // remember that this timer does not have the repetition counter - need to downasmple - needs_downsample[adc_index] = 1; - - if(next_event_high_side) // Next event is high-side active - tim_downsample[adc_index] = 0; // skip the next interrupt (and every second one) - else // Next event is low-side active - tim_downsample[adc_index] = 1; // read the next one (and every second one after) - - SIMPLEFOC_DEBUG("STM32-CS: timer has no repetition counter, ADC interrupt has to be used"); - } + // get the index of the adc + int adc_index = _adcToIndex(cs_params->adc_handle); + + bool tim_interrupt = _initTimerInterruptDownsampling(cs_params, driver_params, adc_interrupt_config[adc_index]); + if(tim_interrupt) { + // error in the timer interrupt initialization + SIMPLEFOC_DEBUG("STM32-CS: timer has no repetition counter, ADC interrupt has to be used"); } // set the trigger output event LL_TIM_SetTriggerOutput(cs_params->timer_handle->Instance, LL_TIM_TRGO_UPDATE); // start the adc - if (use_adc_interrupt){ + if (adc_interrupt_config[adc_index].use_adc_interrupt){ // enable interrupt HAL_NVIC_SetPriority(ADC_IRQn, 0, 0); HAL_NVIC_EnableIRQ(ADC_IRQn); @@ -119,38 +89,14 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ // function reading an ADC value and returning the read voltage float _readADCVoltageLowSide(const int pin, const void* cs_params){ - uint8_t channel_no = 0; - for(int i=0; i < 3; i++){ - if( pin == ((Stm32CurrentSenseParams*)cs_params)->pins[i]){ // found in the buffer - if (use_adc_interrupt){ - return adc_val[_adcToIndex(((Stm32CurrentSenseParams*)cs_params)->adc_handle)][channel_no] * ((Stm32CurrentSenseParams*)cs_params)->adc_voltage_conv; - }else{ - // an optimized way to go from i to the channel i=0 -> channel 1, i=1 -> channel 2, i=2 -> channel 3 - uint32_t channel = _getADCInjectedRank(channel_no); - return HAL_ADCEx_InjectedGetValue(((Stm32CurrentSenseParams*)cs_params)->adc_handle, channel) * ((Stm32CurrentSenseParams*)cs_params)->adc_voltage_conv; - } - } - if(_isset(((Stm32CurrentSenseParams*)cs_params)->pins[i])) channel_no++; - } - return 0; + uint8_t adc_index = (uint8_t)_adcToIndex(((Stm32CurrentSenseParams*)cs_params)->adc_handle); + return _readADCInjectedChannelVoltage(pin, (void*)cs_params, adc_interrupt_config[adc_index], adc_val[adc_index]); } extern "C" { void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef *AdcHandle){ - - // calculate the instance - int adc_index = _adcToIndex(AdcHandle); - - // if the timer han't repetition counter - downsample two times - if( needs_downsample[adc_index] && tim_downsample[adc_index]++ > 0) { - tim_downsample[adc_index] = 0; - return; - } - - adc_val[adc_index][0]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_1); - adc_val[adc_index][1]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_2); - adc_val[adc_index][2]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_3); - adc_val[adc_index][3]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_4); + uint8_t adc_index = (uint8_t)_adcToIndex(AdcHandle); + _handleInjectedConvCpltCallback(AdcHandle, adc_interrupt_config[adc_index], adc_val[adc_index]); } } diff --git a/src/current_sense/hardware_specific/stm32/stm32g4/stm32g4_mcu.cpp b/src/current_sense/hardware_specific/stm32/stm32g4/stm32g4_mcu.cpp index ae81173e..fd13e2b2 100644 --- a/src/current_sense/hardware_specific/stm32/stm32g4/stm32g4_mcu.cpp +++ b/src/current_sense/hardware_specific/stm32/stm32g4/stm32g4_mcu.cpp @@ -16,20 +16,25 @@ #define _ADC_VOLTAGE_G4 3.3f #define _ADC_RESOLUTION_G4 4096.0f - // array of values of 4 injected channels per adc instance (5) uint32_t adc_val[5][4]={0}; -// does adc interrupt need a downsample - per adc (5) -bool needs_downsample[5] = {0}; -// downsampling variable - per adc (5) -uint8_t tim_downsample[5] = {0}; #ifdef SIMPLEFOC_STM32_ADC_INTERRUPT -uint8_t use_adc_interrupt = 1; +#define USE_ADC_INTERRUPT 1 #else -uint8_t use_adc_interrupt = 0; +#define USE_ADC_INTERRUPT 0 #endif +// structure containing the configuration of the adc interrupt +Stm32AdcInterruptConfig adc_interrupt_config[5] = { + {0, 0, USE_ADC_INTERRUPT}, // ADC1 + {0, 0, USE_ADC_INTERRUPT}, // ADC2 + {0, 0, USE_ADC_INTERRUPT}, // ADC3 + {0, 0, USE_ADC_INTERRUPT}, // ADC4 + {0, 0, USE_ADC_INTERRUPT} // ADC5 +}; + + void* _configureADCLowSide(const void* driver_params, const int pinA, const int pinB, const int pinC){ Stm32CurrentSenseParams* cs_params= new Stm32CurrentSenseParams { @@ -52,48 +57,13 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ // stop all the timers for the driver stm32_pause(driver_params); + // get the index of the adc + int adc_index = _adcToIndex(cs_params->adc_handle); - // If DIR is 0 (upcounting), the next event is high-side active (PWM rising edge) - // If DIR is 1 (downcounting), the next event is low-side active (PWM falling edge) - bool next_event_high_side = (cs_params->timer_handle->Instance->CR1 & TIM_CR1_DIR) == 0; - - // if timer has repetition counter - it will downsample using it - // and it does not need the software downsample - if( IS_TIM_REPETITION_COUNTER_INSTANCE(cs_params->timer_handle->Instance) ){ - // adjust the initial timer state such that the trigger - // - only necessary for the timers that have repetition counters - // - basically make sure that the next trigger event is the one that is expected (high-side first then low-side) - - // set the direction and the - for(int i=0; i< 6; i++){ - if(driver_params->timers_handle[i] == NP) continue; // skip if not set - if(next_event_high_side){ - // Set DIR bit to 0 (downcounting) - driver_params->timers_handle[i]->Instance->CR1 |= TIM_CR1_DIR; - // Set CNT to ARR so it starts upcounting from the top - driver_params->timers_handle[i]->Instance->CNT = driver_params->timers_handle[i]->Instance->ARR; - }else{ - // Set DIR bit to 0 (upcounting) - driver_params->timers_handle[i]->Instance->CR1 &= ~TIM_CR1_DIR; - // Set CNT to ARR so it starts upcounting from zero - driver_params->timers_handle[i]->Instance->CNT = 0;// driver_params->timers_handle[i]->Instance->ARR; - } - } - }else{ - if(!use_adc_interrupt){ - // If the timer has no repetition counter, it needs to use the interrupt to downsample for low side sensing - use_adc_interrupt = 1; - uint8_t adc_index = _adcToIndex(cs_params->adc_handle); - // remember that this timer does not have the repetition counter - need to downasmple - needs_downsample[adc_index] = 1; - - if(next_event_high_side) // Next event is high-side active - tim_downsample[adc_index] = 0; // skip the next interrupt (and every second one) - else // Next event is low-side active - tim_downsample[adc_index] = 1; // read the next one (and every second one after) - - SIMPLEFOC_DEBUG("STM32-CS: timer has no repetition counter, ADC interrupt has to be used"); - } + bool tim_interrupt = _initTimerInterruptDownsampling(cs_params, driver_params, adc_interrupt_config[adc_index]); + if(tim_interrupt) { + // error in the timer interrupt initialization + SIMPLEFOC_DEBUG("STM32-CS: timer has no repetition counter, ADC interrupt has to be used"); } // set the trigger output event @@ -103,7 +73,7 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ HAL_ADCEx_Calibration_Start(cs_params->adc_handle, ADC_SINGLE_ENDED); // start the adc - if (use_adc_interrupt){ + if (adc_interrupt_config[adc_index].use_adc_interrupt){ // enable interrupt if(cs_params->adc_handle->Instance == ADC1) { // enable interrupt @@ -156,37 +126,14 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ // function reading an ADC value and returning the read voltage float _readADCVoltageLowSide(const int pin, const void* cs_params){ - uint8_t channel_no = 0; - for(int i=0; i < 3; i++){ - if( pin == ((Stm32CurrentSenseParams*)cs_params)->pins[i]){ // found in the buffer - if (use_adc_interrupt){ - return adc_val[_adcToIndex(((Stm32CurrentSenseParams*)cs_params)->adc_handle)][channel_no] * ((Stm32CurrentSenseParams*)cs_params)->adc_voltage_conv; - }else{ - // an optimized way to go from i to the channel i=0 -> channel 1, i=1 -> channel 2, i=2 -> channel 3 - uint32_t channel = _getADCInjectedRank(channel_no); - return HAL_ADCEx_InjectedGetValue(((Stm32CurrentSenseParams*)cs_params)->adc_handle, channel) * ((Stm32CurrentSenseParams*)cs_params)->adc_voltage_conv; - } - } - if(_isset(((Stm32CurrentSenseParams*)cs_params)->pins[i])) channel_no++; - } - return 0; + uint8_t adc_index = (uint8_t)_adcToIndex(((Stm32CurrentSenseParams*)cs_params)->adc_handle); + return _readADCInjectedChannelVoltage(pin, (void*)cs_params, adc_interrupt_config[adc_index], adc_val[adc_index]); } extern "C" { void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef *AdcHandle){ - // calculate the instance - int adc_index = _adcToIndex(AdcHandle); - - // if the timer han't repetition counter - downsample two times - if( needs_downsample[adc_index] && tim_downsample[adc_index]++ > 0) { - tim_downsample[adc_index] = 0; - return; - } - - adc_val[adc_index][0]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_1); - adc_val[adc_index][1]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_2); - adc_val[adc_index][2]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_3); - adc_val[adc_index][3]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_4); + uint8_t adc_index = (uint8_t)_adcToIndex(AdcHandle); + _handleInjectedConvCpltCallback(AdcHandle, adc_interrupt_config[adc_index], adc_val[adc_index]); } } diff --git a/src/current_sense/hardware_specific/stm32/stm32h7/stm32h7_hal.cpp b/src/current_sense/hardware_specific/stm32/stm32h7/stm32h7_hal.cpp index 32ddba65..0ad49cb9 100644 --- a/src/current_sense/hardware_specific/stm32/stm32h7/stm32h7_hal.cpp +++ b/src/current_sense/hardware_specific/stm32/stm32h7/stm32h7_hal.cpp @@ -86,7 +86,7 @@ int _adc_init(Stm32CurrentSenseParams* cs_params, const STM32DriverParams* drive // if ADC1 or ADC2 if(hadc.Instance == ADC1 || hadc.Instance == ADC2){ // more info here: https://github.com/stm32duino/Arduino_Core_STM32/blob/e156c32db24d69cb4818208ccc28894e2f427cfa/system/Drivers/STM32H7xx_HAL_Driver/Inc/stm32h7xx_hal_adc.h#L658 - sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_1CYCLE_5; + sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_2CYCLES_5; }else { // adc3 // https://github.com/stm32duino/Arduino_Core_STM32/blob/e156c32db24d69cb4818208ccc28894e2f427cfa/system/Drivers/STM32H7xx_HAL_Driver/Inc/stm32h7xx_hal_adc.h#L673 diff --git a/src/current_sense/hardware_specific/stm32/stm32h7/stm32h7_mcu.cpp b/src/current_sense/hardware_specific/stm32/stm32h7/stm32h7_mcu.cpp index e487b734..31f2c16b 100644 --- a/src/current_sense/hardware_specific/stm32/stm32h7/stm32h7_mcu.cpp +++ b/src/current_sense/hardware_specific/stm32/stm32h7/stm32h7_mcu.cpp @@ -15,19 +15,25 @@ #define _ADC_RESOLUTION 4096.0f -// array of values of 4 injected channels per adc instance (3) -uint32_t adc_val[3][4]={0}; -// does adc interrupt need a downsample - per adc (3) -bool needs_downsample[3] = {0}; -// downsampling variable - per adc (3) -uint8_t tim_downsample[3] = {0}; +// array of values of 4 injected channels per adc instance (5) +uint32_t adc_val[5][4]={0}; #ifdef SIMPLEFOC_STM32_ADC_INTERRUPT -uint8_t use_adc_interrupt = 1; +#define USE_ADC_INTERRUPT 1 #else -uint8_t use_adc_interrupt = 0; +#define USE_ADC_INTERRUPT 0 #endif +// structure containing the configuration of the adc interrupt +Stm32AdcInterruptConfig adc_interrupt_config[5] = { + {0, 0, USE_ADC_INTERRUPT}, // ADC1 + {0, 0, USE_ADC_INTERRUPT}, // ADC2 + {0, 0, USE_ADC_INTERRUPT}, // ADC3 + {0, 0, USE_ADC_INTERRUPT}, // ADC4 + {0, 0, USE_ADC_INTERRUPT} // ADC5 +}; + + void* _configureADCLowSide(const void* driver_params, const int pinA, const int pinB, const int pinC){ Stm32CurrentSenseParams* cs_params= new Stm32CurrentSenseParams { @@ -50,48 +56,14 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ // stop all the timers for the driver stm32_pause(driver_params); - // If DIR is 0 (upcounting), the next event is high-side active (PWM rising edge) - // If DIR is 1 (downcounting), the next event is low-side active (PWM falling edge) - bool next_event_high_side = (cs_params->timer_handle->Instance->CR1 & TIM_CR1_DIR) == 0; - - // if timer has repetition counter - it will downsample using it - // and it does not need the software downsample - if( IS_TIM_REPETITION_COUNTER_INSTANCE(cs_params->timer_handle->Instance) ){ - // adjust the initial timer state such that the trigger - // - only necessary for the timers that have repetition counters - // - basically make sure that the next trigger event is the one that is expected (high-side first then low-side) - - // set the direction and the - for(int i=0; i< 6; i++){ - if(driver_params->timers_handle[i] == NP) continue; // skip if not set - if(next_event_high_side){ - // Set DIR bit to 0 (downcounting) - driver_params->timers_handle[i]->Instance->CR1 |= TIM_CR1_DIR; - // Set CNT to ARR so it starts upcounting from the top - driver_params->timers_handle[i]->Instance->CNT = driver_params->timers_handle[i]->Instance->ARR; - }else{ - // Set DIR bit to 0 (upcounting) - driver_params->timers_handle[i]->Instance->CR1 &= ~TIM_CR1_DIR; - // Set CNT to ARR so it starts upcounting from zero - driver_params->timers_handle[i]->Instance->CNT = 0;// driver_params->timers_handle[i]->Instance->ARR; - } - } - }else{ - if(!use_adc_interrupt){ - // If the timer has no repetition counter, it needs to use the interrupt to downsample for low side sensing - use_adc_interrupt = 1; - uint8_t adc_index = _adcToIndex(cs_params->adc_handle); - // remember that this timer does not have the repetition counter - need to downasmple - needs_downsample[adc_index] = 1; - - if(next_event_high_side) // Next event is high-side active - tim_downsample[adc_index] = 0; // skip the next interrupt (and every second one) - else // Next event is low-side active - tim_downsample[adc_index] = 1; // read the next one (and every second one after) - - SIMPLEFOC_DEBUG("STM32-CS: timer has no repetition counter, ADC interrupt has to be used"); - } + // get the index of the adc + int adc_index = _adcToIndex(cs_params->adc_handle); + bool tim_interrupt = _initTimerInterruptDownsampling(cs_params, driver_params, adc_interrupt_config[adc_index]); + if(tim_interrupt) { + // error in the timer interrupt initialization + SIMPLEFOC_DEBUG("STM32-CS: timer has no repetition counter, ADC interrupt has to be used"); } + // set the trigger output event LL_TIM_SetTriggerOutput(cs_params->timer_handle->Instance, LL_TIM_TRGO_UPDATE); @@ -105,33 +77,33 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ } // start the adc - if(use_adc_interrupt){ + if(adc_interrupt_config[adc_index].use_adc_interrupt){ - if(cs_params->adc_handle->Instance == ADC1){ - // enable interrupt - HAL_NVIC_SetPriority(ADC_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(ADC_IRQn); - } - #ifdef ADC2 // if defined ADC2 - else if(cs_params->adc_handle->Instance == ADC2) { - // enable interrupt - HAL_NVIC_SetPriority(ADC3_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(ADC3_IRQn); + if(cs_params->adc_handle->Instance == ADC1){ + // enable interrupt + HAL_NVIC_SetPriority(ADC_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(ADC_IRQn); } - #endif - #ifdef ADC3 // if defined ADC3 - else if(cs_params->adc_handle->Instance == ADC3) { + #ifdef ADC2 // if defined ADC2 + else if(cs_params->adc_handle->Instance == ADC2) { // enable interrupt HAL_NVIC_SetPriority(ADC3_IRQn, 0, 0); HAL_NVIC_EnableIRQ(ADC3_IRQn); - } - #endif - if(HAL_ADCEx_InjectedStart_IT(cs_params->adc_handle) != HAL_OK){ - #ifdef SIMPLEFOC_STM32_DEBUG - SIMPLEFOC_DEBUG("STM32-CS: ERR: cannot start injected channels in interrupt mode!"); - #endif - return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; - } + } + #endif + #ifdef ADC3 // if defined ADC3 + else if(cs_params->adc_handle->Instance == ADC3) { + // enable interrupt + HAL_NVIC_SetPriority(ADC3_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(ADC3_IRQn); + } + #endif + if(HAL_ADCEx_InjectedStart_IT(cs_params->adc_handle) != HAL_OK){ + #ifdef SIMPLEFOC_STM32_DEBUG + SIMPLEFOC_DEBUG("STM32-CS: ERR: cannot start injected channels in interrupt mode!"); + #endif + return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED; + } }else{ if(HAL_ADCEx_InjectedStart(cs_params->adc_handle) != HAL_OK){ #ifdef SIMPLEFOC_STM32_DEBUG @@ -154,38 +126,14 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ // function reading an ADC value and returning the read voltage float _readADCVoltageLowSide(const int pin, const void* cs_params){ - uint8_t channel_no = 0; - for(int i=0; i < 3; i++){ - if( pin == ((Stm32CurrentSenseParams*)cs_params)->pins[i]){ // found in the buffer - if (use_adc_interrupt){ - return adc_val[_adcToIndex(((Stm32CurrentSenseParams*)cs_params)->adc_handle)][channel_no] * ((Stm32CurrentSenseParams*)cs_params)->adc_voltage_conv; - }else{ - // an optimized way to go from i to the channel i=0 -> channel 1, i=1 -> channel 2, i=2 -> channel 3 - uint32_t channel = _getADCInjectedRank(channel_no); - return HAL_ADCEx_InjectedGetValue(((Stm32CurrentSenseParams*)cs_params)->adc_handle, channel) * ((Stm32CurrentSenseParams*)cs_params)->adc_voltage_conv; - } - } - if(_isset(((Stm32CurrentSenseParams*)cs_params)->pins[i])) channel_no++; - } - return 0; + uint8_t adc_index = (uint8_t)_adcToIndex(((Stm32CurrentSenseParams*)cs_params)->adc_handle); + return _readADCInjectedChannelVoltage(pin, (void*)cs_params, adc_interrupt_config[adc_index], adc_val[adc_index]); } extern "C" { void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef *AdcHandle){ - - // calculate the instance - int adc_index = _adcToIndex(AdcHandle); - - // if the timer han't repetition counter - downsample two times - if( needs_downsample[adc_index] && tim_downsample[adc_index]++ > 0) { - tim_downsample[adc_index] = 0; - return; - } - - adc_val[adc_index][0]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_1); - adc_val[adc_index][1]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_2); - adc_val[adc_index][2]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_3); - adc_val[adc_index][3]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_4); + uint8_t adc_index = (uint8_t)_adcToIndex(AdcHandle); + _handleInjectedConvCpltCallback(AdcHandle, adc_interrupt_config[adc_index], adc_val[adc_index]); } } diff --git a/src/current_sense/hardware_specific/stm32/stm32l4/stm32l4_mcu.cpp b/src/current_sense/hardware_specific/stm32/stm32l4/stm32l4_mcu.cpp index fab44911..9bd4a33f 100644 --- a/src/current_sense/hardware_specific/stm32/stm32l4/stm32l4_mcu.cpp +++ b/src/current_sense/hardware_specific/stm32/stm32l4/stm32l4_mcu.cpp @@ -18,17 +18,22 @@ // array of values of 4 injected channels per adc instance (5) uint32_t adc_val[5][4]={0}; -// does adc interrupt need a downsample - per adc (5) -bool needs_downsample[5] = {0}; -// downsampling variable - per adc (5) -uint8_t tim_downsample[5] = {0}; #ifdef SIMPLEFOC_STM32_ADC_INTERRUPT -uint8_t use_adc_interrupt = 1; +#define USE_ADC_INTERRUPT 1 #else -uint8_t use_adc_interrupt = 0; +#define USE_ADC_INTERRUPT 0 #endif +// structure containing the configuration of the adc interrupt +Stm32AdcInterruptConfig adc_interrupt_config[5] = { + {0, 0, USE_ADC_INTERRUPT}, // ADC1 + {0, 0, USE_ADC_INTERRUPT}, // ADC2 + {0, 0, USE_ADC_INTERRUPT}, // ADC3 + {0, 0, USE_ADC_INTERRUPT}, // ADC4 + {0, 0, USE_ADC_INTERRUPT} // ADC5 +}; + void* _configureADCLowSide(const void* driver_params, const int pinA, const int pinB, const int pinC){ Stm32CurrentSenseParams* cs_params= new Stm32CurrentSenseParams { @@ -51,47 +56,13 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ // stop all the timers for the driver stm32_pause(driver_params); - // If DIR is 0 (upcounting), the next event is high-side active (PWM rising edge) - // If DIR is 1 (downcounting), the next event is low-side active (PWM falling edge) - bool next_event_high_side = (cs_params->timer_handle->Instance->CR1 & TIM_CR1_DIR) == 0; - - // if timer has repetition counter - it will downsample using it - // and it does not need the software downsample - if( IS_TIM_REPETITION_COUNTER_INSTANCE(cs_params->timer_handle->Instance) ){ - // adjust the initial timer state such that the trigger - // - only necessary for the timers that have repetition counters - // - basically make sure that the next trigger event is the one that is expected (high-side first then low-side) - - // set the direction and the - for(int i=0; i< 6; i++){ - if(driver_params->timers_handle[i] == NP) continue; // skip if not set - if(next_event_high_side){ - // Set DIR bit to 0 (downcounting) - driver_params->timers_handle[i]->Instance->CR1 |= TIM_CR1_DIR; - // Set CNT to ARR so it starts upcounting from the top - driver_params->timers_handle[i]->Instance->CNT = driver_params->timers_handle[i]->Instance->ARR; - }else{ - // Set DIR bit to 0 (upcounting) - driver_params->timers_handle[i]->Instance->CR1 &= ~TIM_CR1_DIR; - // Set CNT to ARR so it starts upcounting from zero - driver_params->timers_handle[i]->Instance->CNT = 0;// driver_params->timers_handle[i]->Instance->ARR; - } - } - }else{ - if(!use_adc_interrupt){ - // If the timer has no repetition counter, it needs to use the interrupt to downsample for low side sensing - use_adc_interrupt = 1; - uint8_t adc_index = _adcToIndex(cs_params->adc_handle); - // remember that this timer does not have the repetition counter - need to downasmple - needs_downsample[adc_index] = 1; - - if(next_event_high_side) // Next event is high-side active - tim_downsample[adc_index] = 0; // skip the next interrupt (and every second one) - else // Next event is low-side active - tim_downsample[adc_index] = 1; // read the next one (and every second one after) - - SIMPLEFOC_DEBUG("STM32-CS: timer has no repetition counter, ADC interrupt has to be used"); - } + // get the index of the adc + int adc_index = _adcToIndex(cs_params->adc_handle); + + bool tim_interrupt = _initTimerInterruptDownsampling(cs_params, driver_params, adc_interrupt_config[adc_index]); + if(tim_interrupt) { + // error in the timer interrupt initialization + SIMPLEFOC_DEBUG("STM32-CS: timer has no repetition counter, ADC interrupt has to be used"); } @@ -102,7 +73,7 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ HAL_ADCEx_Calibration_Start(cs_params->adc_handle,ADC_SINGLE_ENDED); // start the adc - if (use_adc_interrupt){ + if (adc_interrupt_config[adc_index].use_adc_interrupt){ if(cs_params->adc_handle->Instance == ADC1) { // enable interrupt HAL_NVIC_SetPriority(ADC1_2_IRQn, 0, 0); @@ -148,40 +119,17 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){ // TODO verify if success in future return _cs_params; } - // function reading an ADC value and returning the read voltage float _readADCVoltageLowSide(const int pin, const void* cs_params){ - uint8_t channel_no = 0; - for(int i=0; i < 3; i++){ - if( pin == ((Stm32CurrentSenseParams*)cs_params)->pins[i]){ // found in the buffer - if (use_adc_interrupt){ - return adc_val[_adcToIndex(((Stm32CurrentSenseParams*)cs_params)->adc_handle)][channel_no] * ((Stm32CurrentSenseParams*)cs_params)->adc_voltage_conv; - }else{ - // an optimized way to go from i to the channel i=0 -> channel 1, i=1 -> channel 2, i=2 -> channel 3 - uint32_t channel = _getADCInjectedRank(channel_no); - return HAL_ADCEx_InjectedGetValue(((Stm32CurrentSenseParams*)cs_params)->adc_handle,channel) * ((Stm32CurrentSenseParams*)cs_params)->adc_voltage_conv; - } - } - if(_isset(((Stm32CurrentSenseParams*)cs_params)->pins[i])) channel_no++; - } - return 0; + uint8_t adc_index = (uint8_t)_adcToIndex(((Stm32CurrentSenseParams*)cs_params)->adc_handle); + return _readADCInjectedChannelVoltage(pin, (void*)cs_params, adc_interrupt_config[adc_index], adc_val[adc_index]); } extern "C" { void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef *AdcHandle){ - // calculate the instance - int adc_index = _adcToIndex(AdcHandle); - - // if the timer han't repetition counter - downsample two times - if( needs_downsample[adc_index] && tim_downsample[adc_index]++ > 0) { - tim_downsample[adc_index] = 0; - return; - } - - adc_val[adc_index][0]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_1); - adc_val[adc_index][1]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_2); - adc_val[adc_index][2]=HAL_ADCEx_InjectedGetValue(AdcHandle, ADC_INJECTED_RANK_3); + uint8_t adc_index = (uint8_t)_adcToIndex(AdcHandle); + _handleInjectedConvCpltCallback(AdcHandle, adc_interrupt_config[adc_index], adc_val[adc_index]); } }