Skip to content

Commit 3ea40a8

Browse files
authored
Merge branch 'dev' into Candas1-autotuning
2 parents 3b2499f + 5caa643 commit 3ea40a8

File tree

20 files changed

+714
-236
lines changed

20 files changed

+714
-236
lines changed

src/common/base_classes/FOCMotor.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ enum MotionControlType : uint8_t {
4646
torque = 0x00, //!< Torque control
4747
velocity = 0x01, //!< Velocity motion control
4848
angle = 0x02, //!< Position/angle motion control
49-
velocity_openloop = 0x03, //!< Open loop velocity control
50-
angle_openloop = 0x04 //!< Open loop position/angle control
49+
velocity_openloop = 0x03,
50+
angle_openloop = 0x04,
51+
angle_nocascade = 0x05 //!< Position/angle motion control without velocity cascade
5152
};
5253

5354
/**

src/communication/Commander.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,9 @@ void Commander::motion(FOCMotor* motor, char* user_cmd, char* separator){
443443
case MotionControlType::angle_openloop:
444444
println(F("angle open"));
445445
break;
446+
case MotionControlType::angle_nocascade:
447+
println(F("angle nocascade"));
448+
break;
446449
}
447450
break;
448451
}
@@ -552,6 +555,7 @@ void Commander::target(FOCMotor* motor, char* user_cmd, char* separator){
552555
float pos, vel, torque;
553556
char* next_value;
554557
switch(motor->controller){
558+
case MotionControlType::angle_nocascade:
555559
case MotionControlType::torque: // setting torque target
556560
torque = atof(strtok (user_cmd, separator));
557561
motor->target = torque;

src/current_sense/hardware_specific/esp32/esp32_mcpwm_mcu.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,8 @@ void* IRAM_ATTR _driverSyncLowSide(void* driver_params, void* cs_params){
186186
// if comparator creation failed, fall back to on_full callback
187187
if (cs->pretrig_comparator){
188188
// Calculate pwm duty cycle ticks for pre-trigger channel
189-
uint32_t pwm_duty_cycle = p->mcpwm_period * (0.75 - ((float)p->pwm_frequency*SIMPLEFOC_CS_PRETRIGGER_US)/1e6/2.0);
189+
// TODO: verify the timing it seems to be correct between 15 and 20kHz (but needs better testing)
190+
uint32_t pwm_duty_cycle = p->mcpwm_period * (0.75 - ((float)p->pwm_frequency*SIMPLEFOC_CS_PRETRIGGER_US)/1e6/2.0);
190191
// set up the comparator duty cycle
191192
CHECK_CS_ERR(mcpwm_comparator_set_compare_value((mcpwm_cmpr_handle_t)cs->pretrig_comparator, pwm_duty_cycle),
192193
"Failed to set pretrigger compare value");
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#ifndef STM32_ADC_INCLUDE_HAL
2+
3+
#include "Arduino.h"
4+
5+
#if defined(_STM32_DEF_)
6+
7+
#include "stm32_mcu.h"
8+
9+
// for searching the best ADCs, we need to know the number of ADCs
10+
// it might be better to use some HAL variable for example ADC_COUNT
11+
// here I've assumed the maximum number of ADCs is 5
12+
#define ADC_COUNT 5
13+
14+
// pointer to the ADC handles used in the project
15+
ADC_HandleTypeDef* _get_adc_handles();
16+
17+
int _adc_init_regular(ADC_TypeDef* adc_instance);
18+
int _adc_init(Stm32CurrentSenseParams* cs_params, const STM32DriverParams* driver_params);
19+
int _adc_gpio_init(Stm32CurrentSenseParams* cs_params, const int pinA, const int pinB, const int pinC);
20+
21+
#endif
22+
23+
#endif

src/current_sense/hardware_specific/stm32/stm32_adc_utils.cpp

Lines changed: 178 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
#include "stm32_adc_utils.h"
22
#include "stm32_mcu.h"
3+
#include "stm32_adc_hal.h"
34

45
#if defined(_STM32_DEF_)
56

7+
#ifdef STM32F1xx
8+
#include "stm32f1xx_ll_adc.h"
9+
#endif
10+
#ifdef STM32F7xx
11+
#include "stm32f7xx_ll_adc.h"
12+
#endif
613

714

15+
extern ADC_HandleTypeDef hadc[];
16+
817
int _adcToIndex(ADC_TypeDef *AdcHandle){
918
if(AdcHandle == ADC1) return 0;
1019
#ifdef ADC2 // if ADC2 exists
@@ -110,14 +119,39 @@ int _findIndexOfLastPinMapADCEntry(int pin) {
110119
return _findIndexOfLastEntry(pinName);
111120
}
112121

122+
// find the best ADC for the given pin
123+
// returns the ADC_TypeDef pointer or nullptr if not found
124+
// It returns already configured ADC if possible
125+
// otherwise it returns the first available unconfigured ADC
126+
ADC_TypeDef* _findBestADCForRegularPin(int pin, ADC_HandleTypeDef adc_handles[]) {
127+
PinName pinName = digitalPinToPinName(pin);
128+
int index = _findIndexOfFirstPinMapADCEntry(pin);
129+
int last_index = _findIndexOfLastPinMapADCEntry(pin);
130+
if (index == -1) {
131+
return nullptr;
132+
}
133+
for (int j = index; j <= last_index; j++) {
134+
if (PinMap_ADC[j].pin == NC) {
135+
break;
136+
}
137+
int adcIndex = _adcToIndex((ADC_TypeDef*)PinMap_ADC[j].peripheral);
138+
if (adc_handles[adcIndex].Instance != NP) {
139+
// if ADC is already configured, return it
140+
return (ADC_TypeDef*)PinMap_ADC[j].peripheral;
141+
}
142+
}
143+
// return the first available ADC
144+
return (ADC_TypeDef*)PinMap_ADC[index].peripheral;
145+
}
146+
113147
// find the best ADC combination for the given pins
114148
// returns the index of the best ADC
115149
// each pin can be connected to multiple ADCs
116150
// the function will try to find a single ADC that can be used for all pins
117151
// if not possible it will return nullptr
118-
ADC_TypeDef* _findBestADCForPins(int numPins, int pins[]) {
152+
ADC_TypeDef* _findBestADCForInjectedPins(int numPins, int pins[], ADC_HandleTypeDef adc_handles[]) {
119153

120-
// assuning that there is less than 8 ADCs
154+
// assuning that there is at most 5 ADCs
121155
uint8_t pins_at_adc[ADC_COUNT] = {0};
122156

123157
// check how many pins are there and are not set
@@ -152,11 +186,23 @@ ADC_TypeDef* _findBestADCForPins(int numPins, int pins[]) {
152186
SimpleFOCDebug::print(i+1);
153187
SimpleFOCDebug::print(" pins: ");
154188
SimpleFOCDebug::println(pins_at_adc[i]);
189+
if (adc_handles[i].Instance != NP) {
190+
// check if ADC injeted is already in use
191+
if(!LL_ADC_INJ_IsTriggerSourceSWStart(adc_handles[i].Instance)) {
192+
SimpleFOCDebug::print("STM32-CS: ADC");
193+
SimpleFOCDebug::print(i+1);
194+
SimpleFOCDebug::println(" already in use for injected channels!");
195+
}
196+
}
155197
}
156198
#endif
157199

158200
// now take the first ADC that has all pins connected
159201
for (int i = 0; i < ADC_COUNT; i++) {
202+
if (adc_handles[i].Instance != NP) {
203+
if (!LL_ADC_INJ_IsTriggerSourceSWStart(adc_handles[i].Instance))
204+
continue; // ADC already in use for injected
205+
}
160206
if (pins_at_adc[i] == no_pins) {
161207
return _indexToADC(i);
162208
}
@@ -363,7 +409,6 @@ uint32_t _getADCChannel(PinName pin, ADC_TypeDef *AdcHandle )
363409
for (int i = first_ind; i <= last_ind; i++) {
364410
if (PinMap_ADC[i].peripheral == AdcHandle) {
365411
channel =_getADCChannelFromPinMap(PinMap_ADC[i].pin);
366-
SIMPLEFOC_DEBUG("STM32-CS: ADC channel: ", (int)STM_PIN_CHANNEL(pinmap_function(PinMap_ADC[i].pin, PinMap_ADC)));
367412
break;
368413
}
369414
}
@@ -494,4 +539,134 @@ float _readADCInjectedChannelVoltage(int pin, void* cs_params, Stm32AdcInterrupt
494539
#endif
495540
}
496541

542+
543+
544+
int last_pin[ADC_COUNT] = {-1,-1,-1,-1,-1};
545+
uint32_t last_channel[ADC_COUNT] = {0,0,0,0,0};
546+
547+
/**
548+
* Read a regular ADC channel while injected channels are running for current sensing.
549+
*
550+
* This function performs a one-shot regular conversion on the same ADC that is being
551+
* used for injected current sensing. Injected conversions have hardware priority and
552+
* will pre-empt regular conversions, so this function may experience some latency.
553+
*
554+
* The function will retry a few times if the ADC returns HAL_BUSY, making it suitable
555+
* for reading auxiliary sensors (temperature, voltage, potentiometers, etc.) while
556+
* motor control is active.
557+
*
558+
* @param pin - Arduino pin number to read (must be on the same ADC as current sensing)
559+
* @return float - Voltage reading in volts, or -1.0f on error
560+
*/
561+
float _readRegularADCVoltage(const int pin){
562+
563+
ADC_HandleTypeDef* hadc = _get_adc_handles();
564+
565+
int adc_index = NOT_SET;
566+
for(int i = 0; i < ADC_COUNT; i++){
567+
if(last_pin[i] == pin){
568+
adc_index = i;
569+
break;
570+
}
571+
}
572+
// avoid re-configuring the channel if reading the same pin as last time
573+
if(!_isset(adc_index)){
574+
ADC_TypeDef* adc_instance = _findBestADCForRegularPin(pin, hadc);
575+
if(adc_instance == NP){
576+
#ifdef SIMPLEFOC_STM32_DEBUG
577+
SIMPLEFOC_DEBUG("STM32-CS: ERR: Pin does not belong to any ADC!");
578+
#endif
579+
return -1.0f;
580+
}
581+
adc_index = _adcToIndex(adc_instance);
582+
583+
ADC_HandleTypeDef adc_handle = hadc[adc_index];
584+
if (adc_handle.Instance == NP) {
585+
#ifdef SIMPLEFOC_STM32_DEBUG
586+
SIMPLEFOC_DEBUG("STM32-CS: WARN: ADC not configured, need to configure it: ADC", adc_index+1);
587+
#endif
588+
if(_adc_init_regular(adc_instance) != 0){
589+
#ifdef SIMPLEFOC_STM32_DEBUG
590+
SIMPLEFOC_DEBUG("STM32-CS: ERR: Failed to initialize ADC for pin ", pin);
591+
#endif
592+
return -1.0f;
593+
}
594+
}
595+
596+
597+
last_pin[adc_index] = pin;
598+
// Configure the regular channel for this pin
599+
PinName pinName = analogInputToPinName(pin);
600+
uint32_t channel = _getADCChannel(pinName, adc_instance);
601+
602+
last_channel[adc_index] = channel;
603+
}
604+
605+
606+
ADC_ChannelConfTypeDef sConfig = {0};
607+
sConfig.Channel = last_channel[adc_index];
608+
// the shortes possible sampling time
609+
// this seems to be a constant in HAL - the shortest time enum is equal to 0
610+
// G4 - 2.5 cycles
611+
// F1, H7 - 1.5 cycles
612+
// L4 - 2.5 cycles
613+
// F4, F7 - 3 cycles
614+
sConfig.SamplingTime = 0;
615+
616+
#ifdef ADC_REGULAR_RANK_1
617+
sConfig.Rank = ADC_REGULAR_RANK_1;
618+
#else
619+
sConfig.Rank = 1;
620+
#endif
621+
#ifdef ADC_SINGLE_ENDED
622+
sConfig.SingleDiff = ADC_SINGLE_ENDED;
623+
#endif
624+
#ifdef ADC_OFFSET_NONE
625+
sConfig.OffsetNumber = ADC_OFFSET_NONE;
626+
#endif
627+
#ifndef STM32F1xx
628+
sConfig.Offset = 0;
629+
#endif
630+
631+
if (HAL_ADC_ConfigChannel(&hadc[adc_index], &sConfig) != HAL_OK) {
632+
#ifdef SIMPLEFOC_STM32_DEBUG
633+
SIMPLEFOC_DEBUG("STM32-CS: ERR: Failed to configure regular channel");
634+
#endif
635+
return -1.0f;
636+
}
637+
638+
// Try to start conversion, with retries for HAL_BUSY
639+
// (ADC may be busy with injected conversion)
640+
HAL_StatusTypeDef status;
641+
int retries = 5;
642+
643+
do {
644+
status = HAL_ADC_Start(&hadc[adc_index]);
645+
if (status == HAL_BUSY) {
646+
// Wait a bit for injected conversion to complete
647+
delayMicroseconds(1);
648+
retries--;
649+
}
650+
} while (status == HAL_BUSY && retries > 0);
651+
652+
if (status != HAL_OK) {
653+
#ifdef SIMPLEFOC_STM32_DEBUG
654+
SIMPLEFOC_DEBUG("STM32-CS: ERR: ADC busy or failed to start");
655+
#endif
656+
return -1.0f;
657+
}
658+
659+
// Wait for conversion to complete
660+
// Timeout of 1ms should be more than enough
661+
if (HAL_ADC_PollForConversion(&hadc[adc_index], 1) == HAL_OK) {
662+
uint32_t raw = HAL_ADC_GetValue(&hadc[adc_index]);
663+
return raw * 3.3f / 4096.0f; // assuming 12-bit ADC and 3.3V reference
664+
}
665+
666+
#ifdef SIMPLEFOC_STM32_DEBUG
667+
SIMPLEFOC_DEBUG("STM32-CS: ERR: Regular conversion timeout");
668+
#endif
669+
return -1.0f;
670+
}
671+
497672
#endif

src/current_sense/hardware_specific/stm32/stm32_adc_utils.h

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,12 @@
77

88
#define _TRGO_NOT_AVAILABLE 12345
99

10-
// for searching the best ADCs, we need to know the number of ADCs
11-
// it might be better to use some HAL variable for example ADC_COUNT
12-
// here I've assumed the maximum number of ADCs is 5
13-
#define ADC_COUNT 5
14-
1510

1611
#include "../../../common/foc_utils.h"
1712
#include "../../../communication/SimpleFOCDebug.h"
1813
#include "../../../drivers/hardware_specific/stm32/stm32_mcu.h"
1914
#include "stm32_mcu.h"
2015

21-
22-
2316
/* Exported Functions */
2417
/**
2518
* @brief Return ADC HAL channel linked to a PinName
@@ -43,8 +36,8 @@ int _adcToIndex(ADC_TypeDef *AdcHandle);
4336
// functions helping to find the best ADC channel
4437
int _findIndexOfFirstPinMapADCEntry(int pin);
4538
int _findIndexOfLastPinMapADCEntry(int pin);
46-
ADC_TypeDef* _findBestADCForPins(int num_pins, int pins[]);
47-
39+
ADC_TypeDef* _findBestADCForInjectedPins(int num_pins, int pins[], ADC_HandleTypeDef adc_handles[]);
40+
ADC_TypeDef* _findBestADCForRegularPin(int pin, ADC_HandleTypeDef adc_handles[]);
4841

4942
// Structure to hold ADC interrupt configuration per ADC instance
5043
struct Stm32AdcInterruptConfig {
@@ -61,5 +54,7 @@ uint8_t _handleInjectedConvCpltCallback(ADC_HandleTypeDef *AdcHandle, Stm32AdcIn
6154
// returns the voltage
6255
// if the pin is not found in the current sense parameters, returns 0
6356
float _readADCInjectedChannelVoltage(int pin, void* cs_params, Stm32AdcInterruptConfig& adc_interrupt_config, uint32_t adc_val[4]);
57+
58+
6459
#endif
6560
#endif

src/current_sense/hardware_specific/stm32/stm32_mcu.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#define _ADC_VOLTAGE 3.3f
99
#define _ADC_RESOLUTION 1024.0f
1010

11+
extern ADC_HandleTypeDef hadc[];
12+
1113
// function reading an ADC value and returning the read voltage
1214
void* _configureADCInline(const void* driver_params, const int pinA,const int pinB,const int pinC){
1315
_UNUSED(driver_params);
@@ -30,5 +32,4 @@ __attribute__((weak)) float _readADCVoltageInline(const int pinA, const void* c
3032
return raw_adc * ((Stm32CurrentSenseParams*)cs_params)->adc_voltage_conv;
3133
}
3234

33-
3435
#endif

src/current_sense/hardware_specific/stm32/stm32_mcu.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,26 @@ typedef struct Stm32CurrentSenseParams {
2020
} Stm32CurrentSenseParams;
2121

2222

23+
/**
24+
* Read a regular ADC channel while injected channels are running for current sensing.
25+
* Injected conversions have hardware priority and will pre-empt regular conversions.
26+
*
27+
* This funciton performs a one-shot regular conversion on either on the same ADC as the one
28+
* used for injected current sensing, or on another ADC if the pin belongs to a different ADC.
29+
* The funciton will initialize the ADC for regular conversion if not already initialized.
30+
*
31+
* NOTE:
32+
* The low-side current sensing code already initializes the ADC for injected conversions and regullar conversions
33+
* so if the same ADC is used for regular reading, no re-configuration is needed.
34+
*
35+
* NOTE:
36+
* This function will be relatively slow >10us in comparision to the injected reading.
37+
* But it is much better than analogRead though.
38+
*
39+
* @param pin - the Arduino pin to be read (must be an ADC pin on the same ADC)
40+
* @return float - the voltage read from the pin, or -1.0f on error
41+
*/
42+
float _readRegularADCVoltage(const int pin);
43+
2344
#endif
2445
#endif

0 commit comments

Comments
 (0)