Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 88 additions & 5 deletions src/current_sense/hardware_specific/stm32/stm32_adc_utils.cpp
Original file line number Diff line number Diff line change
@@ -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){
Expand Down Expand Up @@ -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
27 changes: 27 additions & 0 deletions src/current_sense/hardware_specific/stm32/stm32_adc_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
/**
Expand All @@ -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
82 changes: 28 additions & 54 deletions src/current_sense/hardware_specific/stm32/stm32f1/stm32f1_mcu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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] = {1};
// 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 {
Expand All @@ -48,34 +54,23 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){
// stop all the timers for the driver
stm32_pause(driver_params);

// 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;
}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
}
// 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 calibration
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);

Expand All @@ -97,37 +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);
uint8_t adc_index = (uint8_t)_adcToIndex(AdcHandle);
_handleInjectedConvCpltCallback(AdcHandle, adc_interrupt_config[adc_index], adc_val[adc_index]);
}
}


#endif
81 changes: 26 additions & 55 deletions src/current_sense/hardware_specific/stm32/stm32f4/stm32f4_mcu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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] = {1};
// 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 {
Expand All @@ -50,31 +54,20 @@ void* _driverSyncLowSide(void* _driver_params, void* _cs_params){
// stop all the timers for the driver
stm32_pause(driver_params);

// 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;
}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
// 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");
#endif
}
}

// 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);
Expand All @@ -96,36 +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);
uint8_t adc_index = (uint8_t)_adcToIndex(AdcHandle);
_handleInjectedConvCpltCallback(AdcHandle, adc_interrupt_config[adc_index], adc_val[adc_index]);
}
}

Expand Down
Loading