Skip to content

Commit b7c7136

Browse files
committed
FEAT initial low side current sensing for stm32f4
1 parent c2068a3 commit b7c7136

File tree

12 files changed

+305
-18
lines changed

12 files changed

+305
-18
lines changed

src/common/base_classes/CurrentSense.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ float CurrentSense::getDCCurrent(float motor_electrical_angle){
1616
// if only two measured currents
1717
i_alpha = current.a;
1818
i_beta = _1_SQRT3 * current.a + _2_SQRT3 * current.b;
19+
}if(!current.a){
20+
// if only two measured currents
21+
float a = -current.c - current.b;
22+
i_alpha = a;
23+
i_beta = _1_SQRT3 * a + _2_SQRT3 * current.b;
24+
}if(!current.b){
25+
// if only two measured currents
26+
float b = -current.a - current.c;
27+
i_alpha = current.a;
28+
i_beta = _1_SQRT3 * current.a + _2_SQRT3 * b;
1929
}else{
2030
// signal filtering using identity a + b + c = 0. Assumes measurement error is normally distributed.
2131
float mid = (1.f/3) * (current.a + current.b + current.c);
@@ -48,6 +58,16 @@ DQCurrent_s CurrentSense::getFOCCurrents(float angle_el){
4858
// if only two measured currents
4959
i_alpha = current.a;
5060
i_beta = _1_SQRT3 * current.a + _2_SQRT3 * current.b;
61+
}if(!current.a){
62+
// if only two measured currents
63+
float a = -current.c - current.b;
64+
i_alpha = a;
65+
i_beta = _1_SQRT3 * a + _2_SQRT3 * current.b;
66+
}if(!current.b){
67+
// if only two measured currents
68+
float b = -current.a - current.c;
69+
i_alpha = current.a;
70+
i_beta = _1_SQRT3 * current.a + _2_SQRT3 * b;
5171
} else {
5272
// signal filtering using identity a + b + c = 0. Assumes measurement error is normally distributed.
5373
float mid = (1.f/3) * (current.a + current.b + current.c);

src/common/foc_utils.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#define _HIGH_IMPEDANCE 0
3030
#define _HIGH_Z _HIGH_IMPEDANCE
3131
#define _ACTIVE 1
32+
#define _NC (NOT_SET)
3233

3334
// dq current structure
3435
struct DQCurrent_s

src/current_sense/InlineCurrentSense.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,22 +40,22 @@ void InlineCurrentSense::calibrateOffsets(){
4040
offset_ic = 0;
4141
// read the adc voltage 1000 times ( arbitrary number )
4242
for (int i = 0; i < calibration_rounds; i++) {
43-
offset_ia += _readADCVoltageInline(pinA, params);
44-
offset_ib += _readADCVoltageInline(pinB, params);
43+
if(_isset(pinA)) offset_ia += _readADCVoltageInline(pinA, params);
44+
if(_isset(pinB)) offset_ib += _readADCVoltageInline(pinB, params);
4545
if(_isset(pinC)) offset_ic += _readADCVoltageInline(pinC, params);
4646
_delay(1);
4747
}
4848
// calculate the mean offsets
49-
offset_ia = offset_ia / calibration_rounds;
50-
offset_ib = offset_ib / calibration_rounds;
49+
if(_isset(pinA)) offset_ia = offset_ia / calibration_rounds;
50+
if(_isset(pinB)) offset_ib = offset_ib / calibration_rounds;
5151
if(_isset(pinC)) offset_ic = offset_ic / calibration_rounds;
5252
}
5353

5454
// read all three phase currents (if possible 2 or 3)
5555
PhaseCurrent_s InlineCurrentSense::getPhaseCurrents(){
5656
PhaseCurrent_s current;
57-
current.a = (_readADCVoltageInline(pinA, params) - offset_ia)*gain_a;// amps
58-
current.b = (_readADCVoltageInline(pinB, params) - offset_ib)*gain_b;// amps
57+
current.a = (!_isset(pinA)) ? 0 : (_readADCVoltageInline(pinA, params) - offset_ia)*gain_a;// amps
58+
current.b = (!_isset(pinB)) ? 0 : (_readADCVoltageInline(pinB, params) - offset_ib)*gain_b;// amps
5959
current.c = (!_isset(pinC)) ? 0 : (_readADCVoltageInline(pinC, params) - offset_ic)*gain_c; // amps
6060
return current;
6161
}

src/current_sense/LowsideCurrentSense.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,23 +43,23 @@ void LowsideCurrentSense::calibrateOffsets(){
4343
// read the adc voltage 1000 times ( arbitrary number )
4444
for (int i = 0; i < calibration_rounds; i++) {
4545
_startADC3PinConversionLowSide();
46-
offset_ia += _readADCVoltageLowSide(pinA, params);
47-
offset_ib += _readADCVoltageLowSide(pinB, params);
46+
if(_isset(pinA)) offset_ia += _readADCVoltageLowSide(pinA, params);
47+
if(_isset(pinB)) offset_ib += _readADCVoltageLowSide(pinB, params);
4848
if(_isset(pinC)) offset_ic += _readADCVoltageLowSide(pinC, params);
4949
_delay(1);
5050
}
5151
// calculate the mean offsets
52-
offset_ia = offset_ia / calibration_rounds;
53-
offset_ib = offset_ib / calibration_rounds;
52+
if(_isset(pinA)) offset_ia = offset_ia / calibration_rounds;
53+
if(_isset(pinB)) offset_ib = offset_ib / calibration_rounds;
5454
if(_isset(pinC)) offset_ic = offset_ic / calibration_rounds;
5555
}
5656

5757
// read all three phase currents (if possible 2 or 3)
5858
PhaseCurrent_s LowsideCurrentSense::getPhaseCurrents(){
5959
PhaseCurrent_s current;
6060
_startADC3PinConversionLowSide();
61-
current.a = (_readADCVoltageLowSide(pinA, params) - offset_ia)*gain_a;// amps
62-
current.b = (_readADCVoltageLowSide(pinB, params) - offset_ib)*gain_b;// amps
61+
current.a = (!_isset(pinA)) ? 0 : (_readADCVoltageLowSide(pinA, params) - offset_ia)*gain_a;// amps
62+
current.b = (!_isset(pinB)) ? 0 : (_readADCVoltageLowSide(pinB, params) - offset_ib)*gain_b;// amps
6363
current.c = (!_isset(pinC)) ? 0 : (_readADCVoltageLowSide(pinC, params) - offset_ic)*gain_c; // amps
6464
return current;
6565
}

src/current_sense/LowsideCurrentSense.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class LowsideCurrentSense: public CurrentSense{
2121
@param phB B phase adc pin
2222
@param phC C phase adc pin (optional)
2323
*/
24-
LowsideCurrentSense(float shunt_resistor, float gain, int pinA, int pinB, int pinC = NOT_SET);
24+
LowsideCurrentSense(float shunt_resistor, float gain, int pinA, int pinB, int pinC = _NC);
2525

2626
// CurrentSense interface implementing functions
2727
int init() override;

src/current_sense/hardware_api.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// will be returned as a void pointer from the _configureADCx functions
1313
// will be provided to the _readADCVoltageX() as a void pointer
1414
typedef struct GenericCurrentSenseParams {
15-
int pins[3];
15+
int pins[3]={(int)NOT_SET};
1616
float adc_voltage_conv;
1717
} GenericCurrentSenseParams;
1818

src/current_sense/hardware_specific/stm32/b_g431/b_g431_mcu.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
#include "../../../hardware_api.h"
2+
3+
#if defined(ARDUINO_B_G431B_ESC1)
4+
25
#include "b_g431_hal.h"
36
#include "Arduino.h"
7+
#include "../stm32_mcu.h"
48

5-
#if defined(ARDUINO_B_G431B_ESC1)
69
#define _ADC_VOLTAGE 3.3f
710
#define _ADC_RESOLUTION 4096.0f
811
#define ADC_BUF_LEN_1 2
@@ -32,7 +35,7 @@ float _readADCVoltageInline(const int pin, const void* cs_params){
3235
else if(pin == PB1) // = ADC1_IN12 = phase W (OP3_OUT) on B-G431B-ESC1
3336
raw_adc = adcBuffer1[0];
3437
#endif
35-
return raw_adc * ((GenericCurrentSenseParams*)cs_params)->adc_voltage_conv;
38+
return raw_adc * ((Stm32CurrentSenseParams*)cs_params)->adc_voltage_conv;
3639
}
3740

3841
void _configureOPAMP(OPAMP_HandleTypeDef *hopamp, OPAMP_TypeDef *OPAMPx_Def){
@@ -110,7 +113,7 @@ void* _configureADCInline(const void* driver_params, const int pinA,const int pi
110113
return SIMPLEFOC_CURRENT_SENSE_INIT_FAILED;
111114
}
112115

113-
GenericCurrentSenseParams* params = new GenericCurrentSenseParams {
116+
Stm32CurrentSenseParams* params = new Stm32CurrentSenseParams {
114117
.pins = { pinA, pinB, pinC },
115118
.adc_voltage_conv = (_ADC_VOLTAGE) / (_ADC_RESOLUTION)
116119
};
@@ -128,4 +131,10 @@ void DMA1_Channel2_IRQHandler(void) {
128131
}
129132
}
130133

134+
void _driverSyncLowSide(void* driver_params, void* cs_params){
135+
_UNUSED(cs_params);
136+
// Set Trigger out for DMA transfer
137+
LL_TIM_SetTriggerOutput(((STM32DriverParams*)driver_params)->timers[0]->getHandle()->Instance, LL_TIM_TRGO_UPDATE);
138+
}
139+
131140
#endif

src/current_sense/hardware_specific/stm32/stm32_mcu.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
#if defined(_STM32_DEF_) and !defined(ARDUINO_B_G431B_ESC1)
55

6+
#include "stm32_mcu.h"
7+
68
#define _ADC_VOLTAGE 3.3f
79
#define _ADC_RESOLUTION 1024.0f
810

@@ -14,12 +16,18 @@ void* _configureADCInline(const void* driver_params, const int pinA,const int pi
1416
pinMode(pinB, INPUT);
1517
if( _isset(pinC) ) pinMode(pinC, INPUT);
1618

17-
GenericCurrentSenseParams* params = new GenericCurrentSenseParams {
19+
Stm32CurrentSenseParams* params = new Stm32CurrentSenseParams {
1820
.pins = { pinA, pinB, pinC },
1921
.adc_voltage_conv = (_ADC_VOLTAGE)/(_ADC_RESOLUTION)
2022
};
2123

2224
return params;
2325
}
2426

27+
// function reading an ADC value and returning the read voltage
28+
__attribute__((weak)) float _readADCVoltageInline(const int pinA, const void* cs_params){
29+
uint32_t raw_adc = analogRead(pinA);
30+
return raw_adc * ((Stm32CurrentSenseParams*)cs_params)->adc_voltage_conv;
31+
}
32+
2533
#endif
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
#ifndef STM32_CURRENTSENSE_MCU_DEF
3+
#define STM32_CURRENTSENSE_MCU_DEF
4+
#include "../../hardware_api.h"
5+
#include "../../../common/foc_utils.h"
6+
7+
#if defined(_STM32_DEF_) and !defined(ARDUINO_B_G431B_ESC1)
8+
9+
#define _ADC_VOLTAGE 3.3f
10+
#define _ADC_RESOLUTION 1024.0f
11+
12+
// generic implementation of the hardware specific structure
13+
// containing all the necessary current sense parameters
14+
// will be returned as a void pointer from the _configureADCx functions
15+
// will be provided to the _readADCVoltageX() as a void pointer
16+
typedef struct Stm32CurrentSenseParams {
17+
int pins[3] = {(int)NOT_SET};
18+
float adc_voltage_conv;
19+
ADC_HandleTypeDef* adc_handle = NULL;
20+
HardwareTimer* timer_handle = NULL;
21+
} Stm32CurrentSenseParams;
22+
23+
#endif
24+
#endif
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
#include "stm32f4_hal.h"
2+
3+
#if defined(STM32F4xx)
4+
5+
6+
// timer to injected TRGO
7+
// https://github.com/stm32duino/Arduino_Core_STM32/blob/e156c32db24d69cb4818208ccc28894e2f427cfa/system/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_adc_ex.h#L179
8+
uint32_t _timerToInjectedTRGO(HardwareTimer* timer){
9+
if(timer->getHandle()->Instance == TIM1)
10+
return ADC_EXTERNALTRIGINJECCONV_T1_TRGO;
11+
else if(timer->getHandle()->Instance == TIM2)
12+
return ADC_EXTERNALTRIGINJECCONV_T2_TRGO;
13+
else if(timer->getHandle()->Instance == TIM4)
14+
return ADC_EXTERNALTRIGINJECCONV_T4_TRGO;
15+
else if(timer->getHandle()->Instance == TIM5)
16+
return ADC_EXTERNALTRIGINJECCONV_T5_TRGO;
17+
else
18+
return 0;
19+
}
20+
21+
// timer to regular TRGO
22+
// https://github.com/stm32duino/Arduino_Core_STM32/blob/e156c32db24d69cb4818208ccc28894e2f427cfa/system/Drivers/STM32F4xx_HAL_Driver/Inc/stm32f4xx_hal_adc.h#L331
23+
uint32_t _timerToRegularTRGO(HardwareTimer* timer){
24+
if(timer->getHandle()->Instance == TIM2)
25+
return ADC_EXTERNALTRIGCONV_T2_TRGO;
26+
else if(timer->getHandle()->Instance == TIM3)
27+
return ADC_EXTERNALTRIGCONV_T3_TRGO;
28+
else if(timer->getHandle()->Instance == TIM8)
29+
return ADC_EXTERNALTRIGCONV_T8_TRGO;
30+
else
31+
return 0;
32+
}
33+
34+
ADC_HandleTypeDef hadc;
35+
36+
void _adc_init(Stm32CurrentSenseParams* cs_params, const STM32DriverParams* driver_params)
37+
{
38+
ADC_ChannelConfTypeDef sConfig;
39+
ADC_InjectionConfTypeDef sConfigInjected;
40+
41+
/**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
42+
*/
43+
hadc.Instance = (ADC_TypeDef *)pinmap_peripheral(analogInputToPinName(cs_params->pins[0]), PinMap_ADC);
44+
45+
if(hadc.Instance == ADC1) __HAL_RCC_ADC1_CLK_ENABLE();
46+
else if(hadc.Instance == ADC2) __HAL_RCC_ADC2_CLK_ENABLE();
47+
else if(hadc.Instance == ADC3) __HAL_RCC_ADC3_CLK_ENABLE();
48+
49+
hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
50+
hadc.Init.Resolution = ADC_RESOLUTION_12B;
51+
hadc.Init.ScanConvMode = ENABLE;
52+
hadc.Init.ContinuousConvMode = ENABLE;
53+
hadc.Init.DiscontinuousConvMode = DISABLE;
54+
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
55+
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
56+
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
57+
hadc.Init.NbrOfConversion = 1;
58+
hadc.Init.DMAContinuousRequests = DISABLE;
59+
hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
60+
HAL_ADC_Init(&hadc);
61+
/**Configure for the selected ADC regular channel to be converted.
62+
*/
63+
64+
/**Configures for the selected ADC injected channel its corresponding rank in the sequencer and its sample time
65+
*/
66+
sConfigInjected.InjectedNbrOfConversion = _isset(cs_params->pins[2]) ? 3 : 2;
67+
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_3CYCLES;
68+
sConfigInjected.ExternalTrigInjecConvEdge = ADC_EXTERNALTRIGINJECCONVEDGE_RISING;
69+
70+
// automating TRGO flag finding - hardware specific
71+
// sConfigInjected.ExternalTrigInjecConv = ADC_EXTERNALTRIGINJECCONV_T1_TRGO;
72+
uint8_t tim_num = 0;
73+
while(driver_params->timers[tim_num] && tim_num < 6){
74+
uint32_t trigger_flag = _timerToInjectedTRGO(driver_params->timers[tim_num++]);
75+
if(!trigger_flag) continue; // timer does not have valid trgo for injected channels
76+
77+
// timer does have trgo flag for injuected channels
78+
sConfigInjected.ExternalTrigInjecConv = trigger_flag;
79+
80+
// this will be the timer with which the ADC will sync
81+
cs_params->timer_handle = driver_params->timers[tim_num-1];
82+
}
83+
if(!cs_params->timer_handle){
84+
// not possible to use these timers for low-side current sense
85+
return;
86+
};
87+
sConfigInjected.AutoInjectedConv = DISABLE;
88+
sConfigInjected.InjectedDiscontinuousConvMode = DISABLE;
89+
sConfigInjected.InjectedOffset = 0;
90+
91+
// first channel
92+
sConfigInjected.InjectedRank = 1;
93+
sConfigInjected.InjectedChannel = STM_PIN_CHANNEL(pinmap_function(analogInputToPinName(cs_params->pins[0]), PinMap_ADC));
94+
HAL_ADCEx_InjectedConfigChannel(&hadc, &sConfigInjected);
95+
// second channel
96+
sConfigInjected.InjectedRank = 2;
97+
sConfigInjected.InjectedChannel = STM_PIN_CHANNEL(pinmap_function(analogInputToPinName(cs_params->pins[1]), PinMap_ADC));
98+
HAL_ADCEx_InjectedConfigChannel(&hadc, &sConfigInjected);
99+
100+
// third channel - if exists
101+
if(_isset(cs_params->pins[2])){
102+
sConfigInjected.InjectedRank = 3;
103+
sConfigInjected.InjectedChannel = STM_PIN_CHANNEL(pinmap_function(analogInputToPinName(cs_params->pins[2]), PinMap_ADC));
104+
HAL_ADCEx_InjectedConfigChannel(&hadc, &sConfigInjected);
105+
}
106+
107+
// enable interrupt
108+
HAL_NVIC_SetPriority(ADC_IRQn, 0, 0);
109+
HAL_NVIC_EnableIRQ(ADC_IRQn);
110+
111+
cs_params->adc_handle = &hadc;
112+
}
113+
114+
void _adc_gpio_init(Stm32CurrentSenseParams* cs_params, const int pinA, const int pinB, const int pinC)
115+
{
116+
uint8_t cnt = 0;
117+
if(_isset(pinA)){
118+
pinmap_pinout(analogInputToPinName(pinA), PinMap_ADC);
119+
cs_params->pins[cnt++] = pinA;
120+
}
121+
if(_isset(pinB)){
122+
pinmap_pinout(analogInputToPinName(pinB), PinMap_ADC);
123+
cs_params->pins[cnt++] = pinB;
124+
}
125+
if(_isset(pinC)){
126+
pinmap_pinout(analogInputToPinName(pinC), PinMap_ADC);
127+
cs_params->pins[cnt] = pinC;
128+
}
129+
}
130+
131+
extern "C" {
132+
void ADC_IRQHandler(void)
133+
{
134+
HAL_ADC_IRQHandler(&hadc);
135+
}
136+
}
137+
138+
#endif

0 commit comments

Comments
 (0)