From 7feddeab79b3f1393dd9f6d29e63895d81d15cf4 Mon Sep 17 00:00:00 2001 From: runger1101001 Date: Thu, 3 Oct 2024 21:09:37 +0200 Subject: [PATCH 1/5] testing silabs ERF32 gecko support --- .../hardware_specific/silabs/silabs_mcu.cpp | 143 ++++++++++++++++++ .../hardware_specific/silabs/silabs_mcu.h | 20 +++ 2 files changed, 163 insertions(+) create mode 100644 src/drivers/hardware_specific/silabs/silabs_mcu.cpp create mode 100644 src/drivers/hardware_specific/silabs/silabs_mcu.h diff --git a/src/drivers/hardware_specific/silabs/silabs_mcu.cpp b/src/drivers/hardware_specific/silabs/silabs_mcu.cpp new file mode 100644 index 00000000..63d4606b --- /dev/null +++ b/src/drivers/hardware_specific/silabs/silabs_mcu.cpp @@ -0,0 +1,143 @@ + +#include "./silabs_mcu.h" + + +#if defined(ARDUINO_ARCH_SILABS) + +#include "em_device.h" +#include "em_chip.h" +#include "em_cmu.h" +#include "em_emu.h" +#include "em_gpio.h" +#include "em_timer.h" + +#pragma message("") +#pragma message("SimpleFOC: compiling for SiliconLabs") +#pragma message("") + +void setupPWM(int pin_nr, long pwm_frequency, bool active_high, SilabsDriverParams* params, uint8_t index) { + uint32_t timerFreq = 0; + uint32_t topValue = 0; + //CMU_ClockEnable(cmuClock_GPIO, true); assume this is done by Arduino core + CMU_ClockEnable(cmuClock_TIMER0, true); // enable timer clock + + TIMER_Init_TypeDef timerInit = TIMER_INIT_DEFAULT; + timerInit.enable = false; + // TODO adjust pre-scaler as needed to get the desired frequency + timerFreq = CMU_ClockFreqGet(cmuClock_TIMER0) / (timerInit.prescale + 1); + topValue = (timerFreq / pwm_frequency); + TIMER_TopSet(TIMER0, topValue); + + TIMER_InitCC_TypeDef timerCCInit = TIMER_INITCC_DEFAULT; + timerCCInit.mode = timerCCModePWM; + GPIO->TIMERROUTE[0].ROUTEEN = GPIO_TIMER_ROUTEEN_CC0PEN; + GPIO->TIMERROUTE[0].CC0ROUTE = (gpioPortA << _GPIO_TIMER_CC0ROUTE_PORT_SHIFT) + | (6 << _GPIO_TIMER_CC0ROUTE_PIN_SHIFT); + TIMER_InitCC(TIMER0, 0, &timerCCInit); + + // TODO enable all the timers at the same time? + TIMER_Enable(TIMER0, true); +} + + + // if( !pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; + // else pwm_frequency = _constrain(pwm_frequency, _PWM_FREQUENCY_MIN, _PWM_FREQUENCY_MAX); + // params->pwm_frequency = pwm_frequency; + +void* _configure1PWM(long pwm_frequency, const int pinA) { + SilabsDriverParams* params = new SilabsDriverParams(); + setupPWM(pinA, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 0); + return params; +} + + + +void* _configure2PWM(long pwm_frequency, const int pinA, const int pinB) { + SilabsDriverParams* params = new SilabsDriverParams(); + setupPWM(pinA, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 0); + setupPWM(pinB, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 1); + return params; +} + + + +void* _configure3PWM(long pwm_frequency, const int pinA, const int pinB, const int pinC) { + SilabsDriverParams* params = new SilabsDriverParams(); + setupPWM(pinA, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 0); + setupPWM(pinB, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 1); + setupPWM(pinC, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 2); + return params; +} + + + + +void* _configure4PWM(long pwm_frequency, const int pin1A, const int pin1B, const int pin2A, const int pin2B) { + SilabsDriverParams* params = new SilabsDriverParams(); + setupPWM(pin1A, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 0); + setupPWM(pin1B, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 1); + setupPWM(pin2A, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 2); + setupPWM(pin2B, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 3); + return params; +} + + +void* _configure6PWM(long pwm_frequency, float dead_zone, const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l) { + // non-PIO solution... + SilabsDriverParams* params = new SilabsDriverParams(); + params->dead_zone = dead_zone; + setupPWM(pinA_h, pwm_frequency, SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH, params, 0); + setupPWM(pinB_h, pwm_frequency, SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH, params, 2); + setupPWM(pinC_h, pwm_frequency, SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH, params, 4); + setupPWM(pinA_l, pwm_frequency, SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH, params, 1); + setupPWM(pinB_l, pwm_frequency, SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH, params, 3); + setupPWM(pinC_l, pwm_frequency, SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH, params, 5); + return params; +} + + + + + +void writeDutyCycle(float val, uint8_t slice, uint8_t chan) { + //pwm_set_chan_level(slice, chan, (wrapvalues[slice]+1) * val); +} + + + + +void _writeDutyCycle1PWM(float dc_a, void* params) { + writeDutyCycle(dc_a, ((SilabsDriverParams*)params)->timer[0], ((SilabsDriverParams*)params)->channel[0]); +} + + + + +void _writeDutyCycle2PWM(float dc_a, float dc_b, void* params) { + writeDutyCycle(dc_a, ((SilabsDriverParams*)params)->timer[0], ((SilabsDriverParams*)params)->channel[0]); + writeDutyCycle(dc_b, ((SilabsDriverParams*)params)->timer[1], ((SilabsDriverParams*)params)->channel[1]); +} + + + +void _writeDutyCycle3PWM(float dc_a, float dc_b, float dc_c, void* params) { + writeDutyCycle(dc_a, ((SilabsDriverParams*)params)->timer[0], ((SilabsDriverParams*)params)->channel[0]); + writeDutyCycle(dc_b, ((SilabsDriverParams*)params)->timer[1], ((SilabsDriverParams*)params)->channel[1]); + writeDutyCycle(dc_c, ((SilabsDriverParams*)params)->timer[2], ((SilabsDriverParams*)params)->channel[2]); +} + + + +void _writeDutyCycle4PWM(float dc_1a, float dc_1b, float dc_2a, float dc_2b, void* params) { + writeDutyCycle(dc_1a, ((SilabsDriverParams*)params)->timer[0], ((SilabsDriverParams*)params)->channel[0]); + writeDutyCycle(dc_1b, ((SilabsDriverParams*)params)->timer[1], ((SilabsDriverParams*)params)->channel[1]); + writeDutyCycle(dc_2a, ((SilabsDriverParams*)params)->timer[2], ((SilabsDriverParams*)params)->channel[2]); + writeDutyCycle(dc_2b, ((SilabsDriverParams*)params)->timer[3], ((SilabsDriverParams*)params)->channel[3]); +} + + + + + +#endif + diff --git a/src/drivers/hardware_specific/silabs/silabs_mcu.h b/src/drivers/hardware_specific/silabs/silabs_mcu.h new file mode 100644 index 00000000..eb1df5c7 --- /dev/null +++ b/src/drivers/hardware_specific/silabs/silabs_mcu.h @@ -0,0 +1,20 @@ + +#pragma once + +#include "../../hardware_api.h" +#include + +#if defined(ARDUINO_ARCH_SILABS) + + +typedef struct SilabsDriverParams { + int pins[6]; + uint8_t timer[6]; + uint8_t channel[6]; + long pwm_frequency; + float dead_zone; +}; + + + +#endif \ No newline at end of file From 2326f9814cf6a941268bfad24336006fc0de52e3 Mon Sep 17 00:00:00 2001 From: runger1101001 Date: Fri, 4 Oct 2024 22:16:05 +0200 Subject: [PATCH 2/5] working on silabs support --- .../hardware_specific/silabs/silabs_mcu.cpp | 32 +++++++++++-------- .../hardware_specific/silabs/silabs_mcu.h | 3 +- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/drivers/hardware_specific/silabs/silabs_mcu.cpp b/src/drivers/hardware_specific/silabs/silabs_mcu.cpp index 63d4606b..cd50f4d7 100644 --- a/src/drivers/hardware_specific/silabs/silabs_mcu.cpp +++ b/src/drivers/hardware_specific/silabs/silabs_mcu.cpp @@ -16,6 +16,9 @@ #pragma message("") void setupPWM(int pin_nr, long pwm_frequency, bool active_high, SilabsDriverParams* params, uint8_t index) { + GPIO_Port_TypeDef port = getSilabsPortFromArduinoPin(pin_nr); + uint32_t pin = getSilabsPinFromArduinoPin(pin_nr); + TIMER_TypeDef* timer = TIMER0; // TODO determine correct timer uint32_t timerFreq = 0; uint32_t topValue = 0; //CMU_ClockEnable(cmuClock_GPIO, true); assume this is done by Arduino core @@ -23,20 +26,23 @@ void setupPWM(int pin_nr, long pwm_frequency, bool active_high, SilabsDriverPara TIMER_Init_TypeDef timerInit = TIMER_INIT_DEFAULT; timerInit.enable = false; + timerInit.mode = timerModeUpDown; // TODO adjust pre-scaler as needed to get the desired frequency + uint32_t max = TIMER_MaxCount(timer); timerFreq = CMU_ClockFreqGet(cmuClock_TIMER0) / (timerInit.prescale + 1); topValue = (timerFreq / pwm_frequency); - TIMER_TopSet(TIMER0, topValue); + TIMER_TopSet(timer, topValue); TIMER_InitCC_TypeDef timerCCInit = TIMER_INITCC_DEFAULT; timerCCInit.mode = timerCCModePWM; + timerCCInit.outInvert = !active_high; GPIO->TIMERROUTE[0].ROUTEEN = GPIO_TIMER_ROUTEEN_CC0PEN; - GPIO->TIMERROUTE[0].CC0ROUTE = (gpioPortA << _GPIO_TIMER_CC0ROUTE_PORT_SHIFT) - | (6 << _GPIO_TIMER_CC0ROUTE_PIN_SHIFT); - TIMER_InitCC(TIMER0, 0, &timerCCInit); + GPIO->TIMERROUTE[0].CC0ROUTE = (port << _GPIO_TIMER_CC0ROUTE_PORT_SHIFT) + | (pin << _GPIO_TIMER_CC0ROUTE_PIN_SHIFT); + TIMER_InitCC(timer, 0, &timerCCInit); // TODO enable all the timers at the same time? - TIMER_Enable(TIMER0, true); + TIMER_Enable(timer, true); } @@ -83,15 +89,15 @@ void* _configure4PWM(long pwm_frequency, const int pin1A, const int pin1B, const void* _configure6PWM(long pwm_frequency, float dead_zone, const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l) { - // non-PIO solution... SilabsDriverParams* params = new SilabsDriverParams(); params->dead_zone = dead_zone; - setupPWM(pinA_h, pwm_frequency, SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH, params, 0); - setupPWM(pinB_h, pwm_frequency, SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH, params, 2); - setupPWM(pinC_h, pwm_frequency, SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH, params, 4); - setupPWM(pinA_l, pwm_frequency, SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH, params, 1); - setupPWM(pinB_l, pwm_frequency, SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH, params, 3); - setupPWM(pinC_l, pwm_frequency, SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH, params, 5); + // TODO init using DTI if posssible + // setupPWM(pinA_h, pwm_frequency, SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH, params, 0); + // setupPWM(pinB_h, pwm_frequency, SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH, params, 2); + // setupPWM(pinC_h, pwm_frequency, SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH, params, 4); + // setupPWM(pinA_l, pwm_frequency, SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH, params, 1); + // setupPWM(pinB_l, pwm_frequency, SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH, params, 3); + // setupPWM(pinC_l, pwm_frequency, SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH, params, 5); return params; } @@ -99,7 +105,7 @@ void* _configure6PWM(long pwm_frequency, float dead_zone, const int pinA_h, cons -void writeDutyCycle(float val, uint8_t slice, uint8_t chan) { +void writeDutyCycle(float val, TIMER_TypeDef* timer, uint8_t chan) { //pwm_set_chan_level(slice, chan, (wrapvalues[slice]+1) * val); } diff --git a/src/drivers/hardware_specific/silabs/silabs_mcu.h b/src/drivers/hardware_specific/silabs/silabs_mcu.h index eb1df5c7..560be998 100644 --- a/src/drivers/hardware_specific/silabs/silabs_mcu.h +++ b/src/drivers/hardware_specific/silabs/silabs_mcu.h @@ -6,10 +6,11 @@ #if defined(ARDUINO_ARCH_SILABS) +#include "em_timer.h" typedef struct SilabsDriverParams { int pins[6]; - uint8_t timer[6]; + TIMER_TypeDef* timer[6]; uint8_t channel[6]; long pwm_frequency; float dead_zone; From 01a5edf161c1d42f9e2a6bcd233e4043a4fafc16 Mon Sep 17 00:00:00 2001 From: runger1101001 Date: Sun, 6 Oct 2024 21:50:27 +0200 Subject: [PATCH 3/5] working on silabs support --- .../hardware_specific/silabs/silabs_mcu.cpp | 15 +++-- .../hardware_specific/silabs/silabs_mcu.h | 65 ++++++++++++++++++- 2 files changed, 74 insertions(+), 6 deletions(-) diff --git a/src/drivers/hardware_specific/silabs/silabs_mcu.cpp b/src/drivers/hardware_specific/silabs/silabs_mcu.cpp index cd50f4d7..96d51bd6 100644 --- a/src/drivers/hardware_specific/silabs/silabs_mcu.cpp +++ b/src/drivers/hardware_specific/silabs/silabs_mcu.cpp @@ -19,18 +19,17 @@ void setupPWM(int pin_nr, long pwm_frequency, bool active_high, SilabsDriverPara GPIO_Port_TypeDef port = getSilabsPortFromArduinoPin(pin_nr); uint32_t pin = getSilabsPinFromArduinoPin(pin_nr); TIMER_TypeDef* timer = TIMER0; // TODO determine correct timer - uint32_t timerFreq = 0; - uint32_t topValue = 0; //CMU_ClockEnable(cmuClock_GPIO, true); assume this is done by Arduino core CMU_ClockEnable(cmuClock_TIMER0, true); // enable timer clock TIMER_Init_TypeDef timerInit = TIMER_INIT_DEFAULT; timerInit.enable = false; timerInit.mode = timerModeUpDown; - // TODO adjust pre-scaler as needed to get the desired frequency uint32_t max = TIMER_MaxCount(timer); - timerFreq = CMU_ClockFreqGet(cmuClock_TIMER0) / (timerInit.prescale + 1); - topValue = (timerFreq / pwm_frequency); + timerInit.prescale = pwm_frequency / max; + // TODO adjust pre-scaler as needed to get the desired frequency + uint32_t timerFreq = CMU_ClockFreqGet(cmuClock_TIMER0) / (timerInit.prescale + 1); + uint32_t topValue = (timerFreq / pwm_frequency / 2); TIMER_TopSet(timer, topValue); TIMER_InitCC_TypeDef timerCCInit = TIMER_INITCC_DEFAULT; @@ -41,6 +40,12 @@ void setupPWM(int pin_nr, long pwm_frequency, bool active_high, SilabsDriverPara | (pin << _GPIO_TIMER_CC0ROUTE_PIN_SHIFT); TIMER_InitCC(timer, 0, &timerCCInit); + params->timer[index] = timer; + params->channel[index] = 0; + params->pins[index] = pin_nr; + params->pwm_frequency = pwm_frequency; + params->resolution = topValue; + // TODO enable all the timers at the same time? TIMER_Enable(timer, true); } diff --git a/src/drivers/hardware_specific/silabs/silabs_mcu.h b/src/drivers/hardware_specific/silabs/silabs_mcu.h index 560be998..5acb855e 100644 --- a/src/drivers/hardware_specific/silabs/silabs_mcu.h +++ b/src/drivers/hardware_specific/silabs/silabs_mcu.h @@ -1,3 +1,65 @@ +/** + * Silabs support for SimpleFOC library + * + * The Silabs Gecko EFR32 procossors as used in the Arduino Nano Matter have the following + * features: + * - 2 × 32-bit Timer/Counter with 3 Compare/Capture/PWM channels (TIMER0, TIMER1) + * - 3 × 16-bit Timer/Counter with 3 Compare/Capture/PWM channels (TIMER2, TIMER3, TIMER4) + * - All timers support dead-time insertion (DTI) + * - Centre-aligned PWM mode + * - Buffered compare register to ensure glitch-free update of compare values + * - Timers can trigger ADC and DMA via reflex system + * - Timers can be started/stopped synchronously by other timers + * + * The MCU is very flexible regarding its pin assigments. The following is the possible + * assignments: + * + * GPIO port A: TIMER0, TIMER1, TIMER2, TIMER4 + * GPIO port B: TIMER0, TIMER1, TIMER2, TIMER4 + * GPIO port C: TIMER0, TIMER1, TIMER3 + * GPIO port D: TIMER0, TIMER1, TIMER3 + * + * Any pin of these ports can be used with any of the 3 channels or the inverted + * channels of the possible timers. + * + * This suggests the following usage paradigm: + * - 3-PWM or 6-PWM use a single timer, all 3 channels + * - 2-PWM uses two channels of a single timer + * - 1-PWM uses a single channel + * - 4-PWM uses two timers, two channels each (we could try to sync them) + * - TIMER0 and TIMER1 are preferred, since they can be used with all GPIO ports + * + * Nano Matter Pin/Port assignments: + * D0 TX: PA04 + * D1 RX: PA05 + * D3: PC06 + * D4 SDA1: PC07 + * D5 SCL1: PC08 + * D6: PC09 + * D7: PD02 + * D8: PD03 + * D9: PTI_DATA: PD04 + * D10 SS PTI_SYNC: PD05 + * D11 MOSI: PA09 + * D12 MISO: PA08 + * D13 CLK: PB04 + * A0: PB00 + * A1: PB02 + * A2: PB05 + * A3: PC00 + * A4 SDA: PA06 + * A5 SCL: PA07 + * A6: PB01 + * A7: PB03 + * RGB_R: PC01 + * RGB_G: PC02 + * RGB_B: PC03 + * + * So the Nano Matter could use any of TIMER0, TIMER1, or TIMER3 with + * the SimpleFOC Nano Shield (3-PWM mode). + * + */ + #pragma once @@ -12,7 +74,8 @@ typedef struct SilabsDriverParams { int pins[6]; TIMER_TypeDef* timer[6]; uint8_t channel[6]; - long pwm_frequency; + uint32_t pwm_frequency; + uint32_t resolution; float dead_zone; }; From 218070d7d7092c663f242e8be9e8bdd17a145e69 Mon Sep 17 00:00:00 2001 From: runger1101001 Date: Mon, 14 Oct 2024 12:18:43 +0200 Subject: [PATCH 4/5] silabs driver working for 3-PWM --- .../hardware_specific/silabs/silabs_mcu.cpp | 208 +++++++++++++++--- .../hardware_specific/silabs/silabs_mcu.h | 9 +- 2 files changed, 178 insertions(+), 39 deletions(-) diff --git a/src/drivers/hardware_specific/silabs/silabs_mcu.cpp b/src/drivers/hardware_specific/silabs/silabs_mcu.cpp index 96d51bd6..352b62ba 100644 --- a/src/drivers/hardware_specific/silabs/silabs_mcu.cpp +++ b/src/drivers/hardware_specific/silabs/silabs_mcu.cpp @@ -15,49 +15,141 @@ #pragma message("SimpleFOC: compiling for SiliconLabs") #pragma message("") -void setupPWM(int pin_nr, long pwm_frequency, bool active_high, SilabsDriverParams* params, uint8_t index) { - GPIO_Port_TypeDef port = getSilabsPortFromArduinoPin(pin_nr); - uint32_t pin = getSilabsPinFromArduinoPin(pin_nr); - TIMER_TypeDef* timer = TIMER0; // TODO determine correct timer - //CMU_ClockEnable(cmuClock_GPIO, true); assume this is done by Arduino core - CMU_ClockEnable(cmuClock_TIMER0, true); // enable timer clock - TIMER_Init_TypeDef timerInit = TIMER_INIT_DEFAULT; - timerInit.enable = false; - timerInit.mode = timerModeUpDown; - uint32_t max = TIMER_MaxCount(timer); - timerInit.prescale = pwm_frequency / max; - // TODO adjust pre-scaler as needed to get the desired frequency - uint32_t timerFreq = CMU_ClockFreqGet(cmuClock_TIMER0) / (timerInit.prescale + 1); - uint32_t topValue = (timerFreq / pwm_frequency / 2); - TIMER_TopSet(timer, topValue); - +#ifndef SIMPLEFOC_SILABS_MAX_MOTORS +#define SIMPLEFOC_SILABS_MAX_MOTORS 5 +#endif + +SilabsDriverParams* configured_motors[SIMPLEFOC_SILABS_MAX_MOTORS] = {NULL}; +uint8_t num_configured_motors = 0; + +void printPortLetter(GPIO_Port_TypeDef port); +int8_t getTimerNumber(TIMER_TypeDef* timer); +CMU_Clock_TypeDef getTimerClock(TIMER_TypeDef* timer); + + +void setupPWM(int pin_nr, long pwm_frequency, bool active_high, SilabsDriverParams* params, uint8_t index, TIMER_TypeDef* timer, uint8_t channel) { + PinName pin_n = pinToPinName(pin_nr); + GPIO_Port_TypeDef port = getSilabsPortFromArduinoPin(pin_n); + uint32_t pin = getSilabsPinFromArduinoPin(pin_n); + int8_t timer_nr = getTimerNumber(timer); TIMER_InitCC_TypeDef timerCCInit = TIMER_INITCC_DEFAULT; timerCCInit.mode = timerCCModePWM; timerCCInit.outInvert = !active_high; - GPIO->TIMERROUTE[0].ROUTEEN = GPIO_TIMER_ROUTEEN_CC0PEN; - GPIO->TIMERROUTE[0].CC0ROUTE = (port << _GPIO_TIMER_CC0ROUTE_PORT_SHIFT) - | (pin << _GPIO_TIMER_CC0ROUTE_PIN_SHIFT); - TIMER_InitCC(timer, 0, &timerCCInit); + switch(channel) { + case 0: + GPIO->TIMERROUTE[timer_nr].ROUTEEN |= GPIO_TIMER_ROUTEEN_CC0PEN; + GPIO->TIMERROUTE[timer_nr].CC0ROUTE = (port << _GPIO_TIMER_CC0ROUTE_PORT_SHIFT) + | (pin << _GPIO_TIMER_CC0ROUTE_PIN_SHIFT); + break; + case 1: + GPIO->TIMERROUTE[timer_nr].ROUTEEN |= GPIO_TIMER_ROUTEEN_CC1PEN; + GPIO->TIMERROUTE[timer_nr].CC1ROUTE = (port << _GPIO_TIMER_CC1ROUTE_PORT_SHIFT) + | (pin << _GPIO_TIMER_CC1ROUTE_PIN_SHIFT); + break; + case 2: + GPIO->TIMERROUTE[timer_nr].ROUTEEN |= GPIO_TIMER_ROUTEEN_CC2PEN; + GPIO->TIMERROUTE[timer_nr].CC2ROUTE = (port << _GPIO_TIMER_CC2ROUTE_PORT_SHIFT) + | (pin << _GPIO_TIMER_CC2ROUTE_PIN_SHIFT); + break; + } + TIMER_InitCC(timer, channel, &timerCCInit); - params->timer[index] = timer; - params->channel[index] = 0; params->pins[index] = pin_nr; + params->timer[index] = timer; + params->channel[index] = channel; + + SimpleFOCDebug::print("DRV (Silabs): Pin "); + SimpleFOCDebug::print(pin_nr); + SimpleFOCDebug::print(" (P"); + printPortLetter(port); + SimpleFOCDebug::print((int)pin); + SimpleFOCDebug::print(") on TIMER"); + SimpleFOCDebug::print(getTimerNumber(timer)); + SimpleFOCDebug::print(" CH"); + SimpleFOCDebug::print(channel); + SimpleFOCDebug::print(" top "); + SimpleFOCDebug::println((int)params->resolution); +} + + + +bool isTimerUsed(TIMER_TypeDef* timer) { + for (int i=0;itimer[j] == timer) return true; + } + } + return false; +} + + + +TIMER_TypeDef* findFreeTimer(int* pins, uint8_t num_pins) { + TIMER_TypeDef* max_timer = NULL; + for (int i=0;iTOP = topValue; params->pwm_frequency = pwm_frequency; params->resolution = topValue; - - // TODO enable all the timers at the same time? - TIMER_Enable(timer, true); + params->timer[0] = timer; + configured_motors[num_configured_motors++] = params; } + + // if( !pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // else pwm_frequency = _constrain(pwm_frequency, _PWM_FREQUENCY_MIN, _PWM_FREQUENCY_MAX); // params->pwm_frequency = pwm_frequency; void* _configure1PWM(long pwm_frequency, const int pinA) { SilabsDriverParams* params = new SilabsDriverParams(); - setupPWM(pinA, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 0); + int pins[1] = {pinA}; + TIMER_TypeDef* timer = findFreeTimer(pins, 1); + initTimer(timer, pwm_frequency, params); + setupPWM(pinA, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 0, timer, 0); + TIMER_Enable(timer, true); return params; } @@ -65,8 +157,12 @@ void* _configure1PWM(long pwm_frequency, const int pinA) { void* _configure2PWM(long pwm_frequency, const int pinA, const int pinB) { SilabsDriverParams* params = new SilabsDriverParams(); - setupPWM(pinA, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 0); - setupPWM(pinB, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 1); + int pins[2] = {pinA, pinB}; + TIMER_TypeDef* timer = findFreeTimer(pins, 2); + initTimer(timer, pwm_frequency, params); + setupPWM(pinA, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 0, timer, 0); + setupPWM(pinB, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 1, timer, 1); + TIMER_Enable(timer, true); return params; } @@ -74,9 +170,13 @@ void* _configure2PWM(long pwm_frequency, const int pinA, const int pinB) { void* _configure3PWM(long pwm_frequency, const int pinA, const int pinB, const int pinC) { SilabsDriverParams* params = new SilabsDriverParams(); - setupPWM(pinA, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 0); - setupPWM(pinB, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 1); - setupPWM(pinC, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 2); + int pins[3] = {pinA, pinB, pinC}; + TIMER_TypeDef* timer = findFreeTimer(pins, 3); + initTimer(timer, pwm_frequency, params); + setupPWM(pinA, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 0, timer, 0); + setupPWM(pinB, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 1, timer, 1); + setupPWM(pinC, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 2, timer, 2); + TIMER_Enable(timer, true); return params; } @@ -85,10 +185,18 @@ void* _configure3PWM(long pwm_frequency, const int pinA, const int pinB, const i void* _configure4PWM(long pwm_frequency, const int pin1A, const int pin1B, const int pin2A, const int pin2B) { SilabsDriverParams* params = new SilabsDriverParams(); - setupPWM(pin1A, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 0); - setupPWM(pin1B, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 1); - setupPWM(pin2A, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 2); - setupPWM(pin2B, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 3); + int pins[2] = { pin1A, pin1B }; + TIMER_TypeDef* timer0 = findFreeTimer(pins, 2); + initTimer(timer0, pwm_frequency, params); + pins[0] = pin2A; pins[1] = pin2B; + TIMER_TypeDef* timer1 = findFreeTimer(pins, 2); + initTimer(timer1, pwm_frequency, params); + setupPWM(pin1A, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 0, timer0, 0); + setupPWM(pin1B, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 1, timer0, 1); + setupPWM(pin2A, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 2, timer1, 0); + setupPWM(pin2B, pwm_frequency, SIMPLEFOC_PWM_ACTIVE_HIGH, params, 3, timer1, 1); + TIMER_Enable(timer0, true); + TIMER_Enable(timer1, true); return params; } @@ -111,7 +219,8 @@ void* _configure6PWM(long pwm_frequency, float dead_zone, const int pinA_h, cons void writeDutyCycle(float val, TIMER_TypeDef* timer, uint8_t chan) { - //pwm_set_chan_level(slice, chan, (wrapvalues[slice]+1) * val); + timer->CC[chan].OC = val * timer->TOP; + //TIMER_CompareSet(timer, chan, val * timer->TOP); } @@ -148,6 +257,33 @@ void _writeDutyCycle4PWM(float dc_1a, float dc_1b, float dc_2a, float dc_2b, vo +CMU_Clock_TypeDef getTimerClock(TIMER_TypeDef* timer) { + if(timer == TIMER0) return cmuClock_TIMER0; + if(timer == TIMER1) return cmuClock_TIMER1; + if(timer == TIMER2) return cmuClock_TIMER2; + if(timer == TIMER3) return cmuClock_TIMER3; + if(timer == TIMER4) return cmuClock_TIMER4; + return cmuClock_TIMER0; +} + + + +int8_t getTimerNumber(TIMER_TypeDef* timer) { + if(timer == TIMER0) return 0; + if(timer == TIMER1) return 1; + if(timer == TIMER2) return 2; + if(timer == TIMER3) return 3; + if(timer == TIMER4) return 4; + return -1; +} + +void printPortLetter(GPIO_Port_TypeDef port) { + if(port == gpioPortA) SimpleFOCDebug::print("A"); + else if(port == gpioPortB) SimpleFOCDebug::print("B"); + else if(port == gpioPortC) SimpleFOCDebug::print("C"); + else if(port == gpioPortD) SimpleFOCDebug::print("D"); + else SimpleFOCDebug::print("?"); +} #endif diff --git a/src/drivers/hardware_specific/silabs/silabs_mcu.h b/src/drivers/hardware_specific/silabs/silabs_mcu.h index 5acb855e..0ca2c41f 100644 --- a/src/drivers/hardware_specific/silabs/silabs_mcu.h +++ b/src/drivers/hardware_specific/silabs/silabs_mcu.h @@ -70,14 +70,17 @@ #include "em_timer.h" +#define SIMPLEFOC_SILABS_DEFAULT_PWM_FREQUENCY 25000 + + typedef struct SilabsDriverParams { - int pins[6]; - TIMER_TypeDef* timer[6]; + int pins[6] = { -1 }; + TIMER_TypeDef* timer[6] = { NULL }; uint8_t channel[6]; uint32_t pwm_frequency; uint32_t resolution; float dead_zone; -}; +} SilabsDriverParams; From 3c8e287020fe57625f11c7cc9d3072bf4322fd44 Mon Sep 17 00:00:00 2001 From: runger1101001 Date: Tue, 15 Oct 2024 01:59:27 +0200 Subject: [PATCH 5/5] 6-PWM is working --- .../hardware_specific/silabs/silabs_mcu.cpp | 101 ++++++++++++++++-- 1 file changed, 90 insertions(+), 11 deletions(-) diff --git a/src/drivers/hardware_specific/silabs/silabs_mcu.cpp b/src/drivers/hardware_specific/silabs/silabs_mcu.cpp index 352b62ba..742218ed 100644 --- a/src/drivers/hardware_specific/silabs/silabs_mcu.cpp +++ b/src/drivers/hardware_specific/silabs/silabs_mcu.cpp @@ -35,6 +35,7 @@ void setupPWM(int pin_nr, long pwm_frequency, bool active_high, SilabsDriverPara int8_t timer_nr = getTimerNumber(timer); TIMER_InitCC_TypeDef timerCCInit = TIMER_INITCC_DEFAULT; timerCCInit.mode = timerCCModePWM; + timerCCInit.coist = !active_high; timerCCInit.outInvert = !active_high; switch(channel) { case 0: @@ -65,7 +66,7 @@ void setupPWM(int pin_nr, long pwm_frequency, bool active_high, SilabsDriverPara printPortLetter(port); SimpleFOCDebug::print((int)pin); SimpleFOCDebug::print(") on TIMER"); - SimpleFOCDebug::print(getTimerNumber(timer)); + SimpleFOCDebug::print(timer_nr); SimpleFOCDebug::print(" CH"); SimpleFOCDebug::print(channel); SimpleFOCDebug::print(" top "); @@ -74,6 +75,50 @@ void setupPWM(int pin_nr, long pwm_frequency, bool active_high, SilabsDriverPara +void setupComplementaryPWM(int pin_nr, SilabsDriverParams* params, uint8_t index, TIMER_TypeDef* timer, uint8_t channel){ + int8_t timer_nr = getTimerNumber(timer); + PinName pin_n = pinToPinName(pin_nr); + GPIO_Port_TypeDef port = getSilabsPortFromArduinoPin(pin_n); + uint32_t pin = getSilabsPinFromArduinoPin(pin_n); + switch(channel) { + case 0: + GPIO->TIMERROUTE[timer_nr].ROUTEEN |= GPIO_TIMER_ROUTEEN_CCC0PEN; + GPIO->TIMERROUTE[timer_nr].CDTI0ROUTE = (port << _GPIO_TIMER_CDTI0ROUTE_PORT_SHIFT) + | (pin << _GPIO_TIMER_CDTI0ROUTE_PIN_SHIFT); + break; + case 1: + GPIO->TIMERROUTE[timer_nr].ROUTEEN |= GPIO_TIMER_ROUTEEN_CCC1PEN; + GPIO->TIMERROUTE[timer_nr].CDTI1ROUTE = (port << _GPIO_TIMER_CDTI1ROUTE_PORT_SHIFT) + | (pin << _GPIO_TIMER_CDTI1ROUTE_PIN_SHIFT); + break; + case 2: + GPIO->TIMERROUTE[timer_nr].ROUTEEN |= GPIO_TIMER_ROUTEEN_CCC2PEN; + GPIO->TIMERROUTE[timer_nr].CDTI2ROUTE = (port << _GPIO_TIMER_CDTI2ROUTE_PORT_SHIFT) + | (pin << _GPIO_TIMER_CDTI2ROUTE_PIN_SHIFT); + break; + } + + params->pins[index] = pin_nr; + params->timer[index] = timer; + params->channel[index] = channel; + + SimpleFOCDebug::print("DRV (Silabs): Pin "); + SimpleFOCDebug::print(pin_nr); + SimpleFOCDebug::print(" (P"); + printPortLetter(port); + SimpleFOCDebug::print((int)pin); + SimpleFOCDebug::print(") on TIMER"); + SimpleFOCDebug::print(timer_nr); + SimpleFOCDebug::print(" CH"); + SimpleFOCDebug::print(channel); + SimpleFOCDebug::print("COMP top "); + SimpleFOCDebug::println((int)params->resolution); +} + + + + + bool isTimerUsed(TIMER_TypeDef* timer) { for (int i=0;ipwm_frequency = pwm_frequency; + +void initDeadTimeInsertion(TIMER_TypeDef* timer, long pwm_frequency, bool highside_active_high, bool lowside_active_high, SilabsDriverParams* params){ + TIMER_InitDTI_TypeDef dtiInit = TIMER_INITDTI_DEFAULT; + dtiInit.enable = false; // or true? need to check first PWM cycle for this + dtiInit.activeLowOut = !highside_active_high; + dtiInit.invertComplementaryOut = !lowside_active_high; + dtiInit.riseTime = params->dead_zone * params->resolution; // divided by 2 for symmetric dead-time, times 2 due to up-down counting + if (dtiInit.riseTime > 64) dtiInit.riseTime = 64; // max dead-time is 64 counts (each side) + dtiInit.fallTime = dtiInit.riseTime; + dtiInit.autoRestart = true; + SimpleFOCDebug::print("DRV (Silabs): Dead time "); + Serial.println(dtiInit.riseTime); + dtiInit.outputsEnableMask = TIMER_DTOGEN_DTOGCC0EN | TIMER_DTOGEN_DTOGCDTI0EN + | TIMER_DTOGEN_DTOGCC1EN | TIMER_DTOGEN_DTOGCDTI1EN + | TIMER_DTOGEN_DTOGCC2EN | TIMER_DTOGEN_DTOGCDTI2EN; + unsigned long long max = TIMER_MaxCount(timer) + 1; + CMU_Clock_TypeDef timer_clock = getTimerClock(timer); + dtiInit.prescale = (TIMER_Prescale_TypeDef)(CMU_ClockFreqGet(timer_clock) / pwm_frequency / 2 / max); + TIMER_InitDTI(timer, &dtiInit); +} + + + void* _configure1PWM(long pwm_frequency, const int pinA) { SilabsDriverParams* params = new SilabsDriverParams(); @@ -203,14 +268,20 @@ void* _configure4PWM(long pwm_frequency, const int pin1A, const int pin1B, const void* _configure6PWM(long pwm_frequency, float dead_zone, const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l) { SilabsDriverParams* params = new SilabsDriverParams(); + int pins[6] = {pinA_h, pinA_l, pinB_h, pinB_l, pinC_h, pinC_l}; + TIMER_TypeDef* timer = findFreeTimer(pins, 6); + initTimer(timer, pwm_frequency, params); + // init using DTI params->dead_zone = dead_zone; - // TODO init using DTI if posssible - // setupPWM(pinA_h, pwm_frequency, SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH, params, 0); - // setupPWM(pinB_h, pwm_frequency, SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH, params, 2); - // setupPWM(pinC_h, pwm_frequency, SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH, params, 4); - // setupPWM(pinA_l, pwm_frequency, SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH, params, 1); - // setupPWM(pinB_l, pwm_frequency, SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH, params, 3); - // setupPWM(pinC_l, pwm_frequency, SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH, params, 5); + setupPWM(pinA_h, pwm_frequency, SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH, params, 0, timer, 0); + setupPWM(pinB_h, pwm_frequency, SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH, params, 2, timer, 1); + setupPWM(pinC_h, pwm_frequency, SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH, params, 4, timer, 2); + initDeadTimeInsertion(timer, pwm_frequency, SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH, SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH, params); + setupComplementaryPWM(pinA_l, params, 1, timer, 0); + setupComplementaryPWM(pinB_l, params, 3, timer, 1); + setupComplementaryPWM(pinC_l, params, 5, timer, 2); + TIMER_EnableDTI(timer, true); + TIMER_Enable(timer, true); return params; } @@ -256,6 +327,14 @@ void _writeDutyCycle4PWM(float dc_1a, float dc_1b, float dc_2a, float dc_2b, vo } +void _writeDutyCycle6PWM(float dc_a, float dc_b, float dc_c, PhaseState *phase_state, void* params) { + // TODO: handle phase state + writeDutyCycle(dc_a, ((SilabsDriverParams*)params)->timer[0], ((SilabsDriverParams*)params)->channel[0]); + writeDutyCycle(dc_b, ((SilabsDriverParams*)params)->timer[2], ((SilabsDriverParams*)params)->channel[2]); + writeDutyCycle(dc_c, ((SilabsDriverParams*)params)->timer[4], ((SilabsDriverParams*)params)->channel[4]); +} + + CMU_Clock_TypeDef getTimerClock(TIMER_TypeDef* timer) { if(timer == TIMER0) return cmuClock_TIMER0;