From 91dfd20eafd6cfef338692d20a0159ff25f77b3d Mon Sep 17 00:00:00 2001 From: Filipe Cavalcanti Date: Wed, 3 Jul 2024 09:49:11 -0300 Subject: [PATCH] arch/xtensa: add support for brushed DC motor control arch/xtensa: add support for capture driver on ESP32 and ESP32|S3| Squashed: Initial settings for MCPWM Capture on board level Created lower half files - compilation ok Using capture debug features. Simple example on fops works Successful duty and freq calculation Documentation update Fixed and added interupt capabilities for all 3 capture channels Cleaned defconfig Renamed macros, added S3 options and moved arch source to common dir Added support for ESP32S3 Added capture example to defconfig and renamed Basic bring up ready. New Kconfig options for motor. Good motor registration Working on enabling pwm generators Working on enabling pwm generators Added fops functions Success on PWM 50% stable pwm operation for bdc Added loopback option for testing Improved rules on fsm Working motor direction control Testing new ISR for fault handling Issues on fault ISR Removed fault implementation (not working) Added support for esp32s3 Documentation improvements Added default motor spin direction Added parameter change while running Review fixes arch/xtensa: add support for fault signal on motor control Squashed: Initial settings for MCPWM Capture on board level Created lower half files - compilation ok Using capture debug features. Simple example on fops works Successful duty and freq calculation Documentation update Fixed and added interupt capabilities for all 3 capture channels Cleaned defconfig Renamed macros, added S3 options and moved arch source to common dir Added support for ESP32S3 Added capture example to defconfig and renamed Basic bring up ready. New Kconfig options for motor. Good motor registration Working on enabling pwm generators Working on enabling pwm generators Added fops functions Success on PWM 50% stable pwm operation for bdc Added loopback option for testing Improved rules on fsm Working motor direction control Testing new ISR for fault handling Issues on fault ISR Removed fault implementation (not working) Added support for esp32s3 Documentation improvements Added default motor spin direction Added parameter change while running Got responsive fault indicator Working brakes - still need to work on starting isr Fixed single-time ISR initializiation Working soft brake on fault Improved KConfig for BDC and BLDC Kconfig fixed at board level --- .../esp32/boards/esp32-devkitc/index.rst | 29 +- .../platforms/xtensa/esp32/index.rst | 2 +- .../esp32s3/boards/esp32s3-devkit/index.rst | 28 +- .../platforms/xtensa/esp32s3/index.rst | 2 +- arch/xtensa/src/common/espressif/esp_mcpwm.c | 1412 ++++++++++++++++- arch/xtensa/src/common/espressif/esp_mcpwm.h | 25 + arch/xtensa/src/esp32/Kconfig | 104 +- arch/xtensa/src/esp32s3/Kconfig | 102 ++ .../esp32/common/include/esp32_board_mcpwm.h | 19 + .../esp32/common/src/esp32_board_mcpwm.c | 50 + .../esp32-devkitc/configs/capture/defconfig | 1 + .../esp32-devkitc/configs/motor/defconfig | 50 + .../esp32/esp32-devkitc/src/esp32_bringup.c | 8 + .../common/include/esp32s3_board_mcpwm.h | 26 +- .../esp32s3/common/src/esp32s3_board_mcpwm.c | 54 + .../esp32s3-devkit/configs/motor/defconfig | 49 + .../esp32s3-devkit/src/esp32s3_bringup.c | 8 + 17 files changed, 1897 insertions(+), 72 deletions(-) create mode 100644 boards/xtensa/esp32/esp32-devkitc/configs/motor/defconfig create mode 100644 boards/xtensa/esp32s3/esp32s3-devkit/configs/motor/defconfig diff --git a/Documentation/platforms/xtensa/esp32/boards/esp32-devkitc/index.rst b/Documentation/platforms/xtensa/esp32/boards/esp32-devkitc/index.rst index 733be697d89..c171e87bd43 100644 --- a/Documentation/platforms/xtensa/esp32/boards/esp32-devkitc/index.rst +++ b/Documentation/platforms/xtensa/esp32/boards/esp32-devkitc/index.rst @@ -300,15 +300,15 @@ capture The capture configuration enables the capture driver and the capture example, allowing the user to measure duty cycle and frequency of a signal. Default pin is GPIO 14 with an internal pull-up resistor enabled. When connecting a 50 Hz pulse with 50% duty cycle, -the following output is expected: +the following output is expected:: -nsh> cap -cap_main: Hardware initialized. Opening the capture device: /dev/capture0 -cap_main: Number of samples: 0 -pwm duty cycle: 50 % -pwm frequence: 50 Hz -pwm duty cycle: 50 % -pwm frequence: 50 Hz + nsh> cap + cap_main: Hardware initialized. Opening the capture device: /dev/capture0 + cap_main: Number of samples: 0 + pwm duty cycle: 50 % + pwm frequence: 50 Hz + pwm duty cycle: 50 % + pwm frequence: 50 Hz coremark -------- @@ -561,6 +561,19 @@ module This config is to run apps/examples/module. +motor +------- + +The motor configuration enables the MCPWM peripheral with support to brushed DC motor +control. + +It creates a ``/dev/motor0`` device with speed and direction control capabilities +by using two GPIOs (GPIO15 and GPIO16) for PWM output. PWM frequency is configurable +from 25 Hz to 3 kHz, however it defaults to 1 kHz. +There is also support for an optional fault GPIO (defaults to GPIO10), which can be used +for quick motor braking. All GPIOs are configurable in ``menuconfig``. + + mqttc ----- diff --git a/Documentation/platforms/xtensa/esp32/index.rst b/Documentation/platforms/xtensa/esp32/index.rst index 42bb03189c5..d8559a71b13 100644 --- a/Documentation/platforms/xtensa/esp32/index.rst +++ b/Documentation/platforms/xtensa/esp32/index.rst @@ -189,7 +189,7 @@ GPIO Yes I2C Yes I2S Yes LED_PWM Yes -MCPWM Yes Capture +MCPWM Yes Pulse_CNT No RMT Yes RNG Yes diff --git a/Documentation/platforms/xtensa/esp32s3/boards/esp32s3-devkit/index.rst b/Documentation/platforms/xtensa/esp32s3/boards/esp32s3-devkit/index.rst index a7afcadd829..092fb36f599 100644 --- a/Documentation/platforms/xtensa/esp32s3/boards/esp32s3-devkit/index.rst +++ b/Documentation/platforms/xtensa/esp32s3/boards/esp32s3-devkit/index.rst @@ -152,15 +152,15 @@ capture The capture configuration enables the capture driver and the capture example, allowing the user to measure duty cycle and frequency of a signal. Default pin is GPIO 12 with an internal pull-up resistor enabled. When connecting a 50 Hz pulse with 50% duty cycle, -the following output is expected: +the following output is expected:: -nsh> cap -cap_main: Hardware initialized. Opening the capture device: /dev/capture0 -cap_main: Number of samples: 0 -pwm duty cycle: 50 % -pwm frequence: 50 Hz -pwm duty cycle: 50 % -pwm frequence: 50 Hz + nsh> cap + cap_main: Hardware initialized. Opening the capture device: /dev/capture0 + cap_main: Number of samples: 0 + pwm duty cycle: 50 % + pwm frequence: 50 Hz + pwm duty cycle: 50 % + pwm frequence: 50 Hz coremark -------- @@ -257,6 +257,18 @@ Flash and PSRAM). .. warning:: The World Controller and Permission Control **do not** prevent the application from accessing CPU System Registers. +motor +------- + +The motor configuration enables the MCPWM peripheral with support to brushed DC motor +control. + +It creates a ``/dev/motor0`` device with speed and direction control capabilities +by using two GPIOs (GPIO15 and GPIO16) for PWM output. PWM frequency is configurable +from 25 Hz to 3 kHz, however it defaults to 1 kHz. +There is also support for an optional fault GPIO (defaults to GPIO10), which can be used +for quick motor braking. All GPIOs are configurable in ``menuconfig``. + mcuboot_nsh ----------- diff --git a/Documentation/platforms/xtensa/esp32s3/index.rst b/Documentation/platforms/xtensa/esp32s3/index.rst index 9aada315f55..a16c2659ac2 100644 --- a/Documentation/platforms/xtensa/esp32s3/index.rst +++ b/Documentation/platforms/xtensa/esp32s3/index.rst @@ -178,7 +178,7 @@ I2C No I2S Yes LCD No LED_PWM No -MCPWM Yes Capture +MCPWM Yes Pulse_CNT No RMT No RNG No diff --git a/arch/xtensa/src/common/espressif/esp_mcpwm.c b/arch/xtensa/src/common/espressif/esp_mcpwm.c index 935bf28f84a..1760c573901 100644 --- a/arch/xtensa/src/common/espressif/esp_mcpwm.c +++ b/arch/xtensa/src/common/espressif/esp_mcpwm.c @@ -34,7 +34,12 @@ #include #include #include +#ifdef CONFIG_ESP_MCPWM_CAPTURE #include +#endif +#ifdef CONFIG_ESP_MCPWM_MOTOR +#include +#endif #include "xtensa.h" #ifdef CONFIG_ARCH_CHIP_ESP32 @@ -49,8 +54,10 @@ #include "hal/mcpwm_hal.h" #include "hal/mcpwm_ll.h" +#include "hal/mcpwm_types.h" #include "soc/mcpwm_periph.h" #include "periph_ctrl.h" +#include "esp_clk_tree.h" #include "hal/clk_tree_hal.h" #ifdef CONFIG_ESP_MCPWM @@ -63,22 +70,80 @@ #ifdef CONFIG_ARCH_CHIP_ESP32 # define esp_configgpio esp32_configgpio # define esp_gpio_matrix_in esp32_gpio_matrix_in +# define esp_gpio_matrix_out esp32_gpio_matrix_out # define esp_setup_irq esp32_setup_irq # define esp_teardown_irq esp32_teardown_irq # define ESP_CPUINT_LEVEL ESP32_CPUINT_LEVEL #elif CONFIG_ARCH_CHIP_ESP32S3 # define esp_configgpio esp32s3_configgpio # define esp_gpio_matrix_in esp32s3_gpio_matrix_in +# define esp_gpio_matrix_out esp32s3_gpio_matrix_out # define esp_setup_irq esp32s3_setup_irq # define esp_teardown_irq esp32s3_teardown_irq # define ESP_CPUINT_LEVEL ESP32S3_CPUINT_LEVEL #endif +#ifdef CONFIG_ESP_MCPWM_MOTOR_BDC +/* Peak counter at 13330 in up-down mode allows frequencies at a prescale + * of: 2 kHz @ 2; 1.5 kHz @ 3; 1.2 kHz @ 4; 1 kHz @ 5. + */ +# define PEAK_COUNTER 13330 +# define MCPWM_MAX_PWM_OUT_FREQ 3000 +# define MCPWM_MIN_PWM_OUT_FREQ 25 +#endif +#ifdef CONFIG_ESP_MCPMW_MOTOR_CH0_FAULT +# define ESP_MCPMW_MOTOR_FAULT +#endif /**************************************************************************** * Private Types ****************************************************************************/ -/* Capture event data. The 'last_' value is used to calculate frequency */ +typedef enum +{ + MCPWM_GENERATOR_0, + MCPWM_GENERATOR_1, + MCPWM_GENERATOR_MAX +} mcpwm_generator_e; + +typedef enum +{ + MCPWM_OPERATOR_0, + MCPWM_OPERATOR_1, + MCPWM_OPERATOR_2, + MCPWM_OPERATOR_MAX +} mcpwm_operator_e; + +typedef enum +{ + MCPWM_TIMER_0, + MCPWM_TIMER_1, + MCPWM_TIMER_2, + MCPWM_TIMER_MAX +} mcpwm_timer_e; + +typedef enum +{ + MCPWM_FAULT_0, + MCPWM_FAULT_1, + MCPWM_FAULT_2, + MCPWM_FAULT_MAX +} mcpwm_fault_e; + +typedef enum +{ + MCPWM_MOTOR_CHANNEL_0, + MCPWM_MOTOR_CHANNEL_1, + MCPWM_MOTOR_CHANNEL_2, + MCPWM_MOTOR_CHANNEL_MAX +} mcpwm_motor_channel_e; + +enum mcpwm_capture_channel_e +{ + MCPWM_CAP_CHANNEL_0, + MCPWM_CAP_CHANNEL_1, + MCPWM_CAP_CHANNEL_2, + MCPWM_CAP_CHANNEL_MAX +}; struct mcpwm_dev_common_s { @@ -86,12 +151,43 @@ struct mcpwm_dev_common_s mcpwm_hal_context_t hal; spinlock_t mcpwm_spinlock; bool initialized; /* MCPWM periph. and HAL has been initialized */ -#ifdef CONFIG_ESP_MCPWM_CAPTURE - bool capture_initialized; /* Capture submodule has been initialized */ -#endif + bool isr_initialized; /* Shared ISR has been initialized */ }; +#ifdef CONFIG_ESP_MCPWM_MOTOR + +struct mcpwm_motor_lowerhalf_s +{ + /* The following block is part of the upper-half device struct */ + + FAR const struct motor_ops_s *ops; /* Arch-specific operations */ + uint8_t opmode; /* Motor operation mode */ + uint8_t opflags; /* Motor operation flags */ + struct motor_limits_s limits; /* Motor absolute limits */ + struct motor_params_s param; /* Motor settings */ + struct motor_state_s state; /* Motor state */ + FAR void *priv; /* Private data */ + + /* The following is private to the ESP MCPWM driver */ + + struct mcpwm_dev_common_s *common; + mcpwm_timer_e timer_id; + mcpwm_motor_channel_e channel_id; + mcpwm_operator_e operator_id; + uint32_t pwm_frequency; + uint16_t counter_peak; + uint8_t timer_prescale; + int fault_pin; /* GPIO Pin for fault detection */ + int generator_pins[MCPWM_GENERATOR_MAX]; +#ifdef ESP_MCPMW_MOTOR_FAULT + mcpwm_fault_e fault_id; +#endif +}; +#endif /* CONFIG_ESP_MCPWM_MOTOR */ + #ifdef CONFIG_ESP_MCPWM_CAPTURE +/* Capture event data. The 'last_' value is used to calculate frequency */ + struct mcpwm_capture_event_data_s { uint32_t pos_edge_count; @@ -99,14 +195,6 @@ struct mcpwm_capture_event_data_s uint32_t last_pos_edge_count; }; -enum mcpwm_capture_channel_e -{ - MCPWM_CAP_CHANNEL_0, /* MCPWM capture channel number 0 */ - MCPWM_CAP_CHANNEL_1, /* MCPWM capture channel number 1 */ - MCPWM_CAP_CHANNEL_2, /* MCPWM capture channel number 2 */ - MCPWM_CAP_CHANNEL_MAX /* Number of MCPWM capture channels */ -}; - struct mcpwm_cap_channel_lowerhalf_s { /* The following block is part of the upper-half device struct */ @@ -132,17 +220,17 @@ struct mcpwm_cap_channel_lowerhalf_s * Private Function Prototypes ****************************************************************************/ +/* General use for MCPWM peripheral */ + static void esp_mcpwm_group_start(void); + +/* MCPWM Capture specific */ + #ifdef CONFIG_ESP_MCPWM_CAPTURE -static int esp_mcpwm_capture_isr_register(int (*fn)(int, void *, void *), - void *arg); -static int IRAM_ATTR mcpwm_capture_driver_isr_default(int irq, void *context, - void *arg); static int esp_mcpwm_capture_set_gpio( struct mcpwm_cap_channel_lowerhalf_s *lower); -#endif -/* Lower half methods required by capture driver */ +/* Upper-half functions required by capture driver */ static int esp_capture_start(struct cap_lowerhalf_s *lower); static int esp_capture_stop(struct cap_lowerhalf_s *lower); @@ -150,18 +238,110 @@ static int esp_capture_getduty(struct cap_lowerhalf_s *lower, uint8_t *duty); static int esp_capture_getfreq(struct cap_lowerhalf_s *lower, uint32_t *freq); +#endif + +/* MCPWM Motor Control */ + +#ifdef CONFIG_ESP_MCPWM_MOTOR + +/* Upper-half functions required by motor driver */ + +static int esp_motor_setup(struct motor_lowerhalf_s *dev); +static int esp_motor_shutdown(struct motor_lowerhalf_s *dev); +static int esp_motor_stop(struct motor_lowerhalf_s *dev); +static int esp_motor_start(struct motor_lowerhalf_s *dev); +static int esp_motor_mode_set(struct motor_lowerhalf_s *dev, uint8_t mode); +static int esp_motor_fault_set(struct motor_lowerhalf_s *dev, uint8_t fault); +static int esp_motor_fault_get(struct motor_lowerhalf_s *dev, + uint8_t *fault); +static int esp_motor_params_set(struct motor_lowerhalf_s *dev, + struct motor_params_s *param); +static int esp_motor_state_get(struct motor_lowerhalf_s *dev, + struct motor_state_s *state); +static int esp_motor_limits_set(struct motor_lowerhalf_s *dev, + struct motor_limits_s *limits); +static int esp_motor_fault_clear(struct motor_lowerhalf_s *dev, + uint8_t fault); +static int esp_motor_ioctl(struct motor_lowerhalf_s *dev, int cmd, + unsigned long arg); + +/* Lower-half motor functions */ + +static int esp_mcpwm_motor_set_gpio( + struct mcpwm_motor_lowerhalf_s *lower, bool enable); +static int esp_motor_set_duty_cycle( + struct mcpwm_motor_lowerhalf_s *lower, float duty); +static int esp_motor_pwm_config(struct mcpwm_motor_lowerhalf_s *lower); +static int esp_motor_bdc_set_direction( + struct mcpwm_motor_lowerhalf_s *lower); +#endif + +/* MCPWM Fault Control */ + +#ifdef ESP_MCPMW_MOTOR_FAULT +static int esp_mcpwm_fault_gpio_config( + struct mcpwm_motor_lowerhalf_s *lower, bool enable); +static int esp_motor_fault_configure( + struct mcpwm_motor_lowerhalf_s *lower, bool enable); +#endif + +/* MCPWM Interrupt */ + +#if defined(CONFIG_ESP_MCPWM_CAPTURE) || defined(ESP_MCPMW_MOTOR_FAULT) +static int esp_mcpwm_isr_register(int (*fn)(int, void *, void *), void *arg); +static int IRAM_ATTR mcpwm_driver_isr_default(int irq, void *context, + void *arg); +#endif /**************************************************************************** * Private Data ****************************************************************************/ +/* Common MCPWM data structure */ + static struct mcpwm_dev_common_s g_mcpwm_common = { - .group.group_id = 0, - .initialized = false, - .capture_initialized = false, + .group.group_id = 0, + .initialized = false, + .isr_initialized = false, }; +/* Motor specific data structures */ + +#ifdef CONFIG_ESP_MCPWM_MOTOR +static const struct motor_ops_s mcpwm_motor_ops = +{ + .setup = esp_motor_setup, + .shutdown = esp_motor_shutdown, + .stop = esp_motor_stop, + .start = esp_motor_start, + .params_set = esp_motor_params_set, + .mode_set = esp_motor_mode_set, + .limits_set = esp_motor_limits_set, + .fault_set = esp_motor_fault_set, + .state_get = esp_motor_state_get, + .fault_get = esp_motor_fault_get, + .fault_clear = esp_motor_fault_clear, + .ioctl = esp_motor_ioctl, +}; + +#if defined(CONFIG_ESP_MCPWM_MOTOR_CH0) &&\ + defined(CONFIG_ESP_MCPWM_MOTOR_BDC) +static struct mcpwm_motor_lowerhalf_s mcpwm_bdc_ch0_lowerhalf = +{ + .ops = &mcpwm_motor_ops, + .common = &g_mcpwm_common, + .channel_id = MCPWM_MOTOR_CHANNEL_0, + .timer_id = MCPWM_TIMER_0, + .operator_id = MCPWM_OPERATOR_0, + .counter_peak = PEAK_COUNTER, +#ifdef ESP_MCPMW_MOTOR_FAULT + .fault_id = MCPWM_FAULT_0, +#endif +}; +#endif /* CONFIG_ESP_MCPWM_MOTOR_BDC_CH0 && CONFIG_ESP_MCPWM_MOTOR_BDC */ +#endif /* CONFIG_ESP_MCPWM_MOTOR */ + #ifdef CONFIG_ESP_MCPWM_CAPTURE /* Lower half methods required by capture driver */ @@ -179,11 +359,11 @@ static const struct cap_ops_s mcpwm_cap_ops = static struct mcpwm_capture_event_data_s event_data_ch0; static struct mcpwm_cap_channel_lowerhalf_s mcpwm_cap_ch0_lowerhalf = { - .ops = &mcpwm_cap_ops, - .common = &g_mcpwm_common, - .data = &event_data_ch0, + .ops = &mcpwm_cap_ops, + .common = &g_mcpwm_common, + .data = &event_data_ch0, .channel_id = MCPWM_CAP_CHANNEL_0, - .ready = false, + .ready = false, }; #endif @@ -191,11 +371,11 @@ static struct mcpwm_cap_channel_lowerhalf_s mcpwm_cap_ch0_lowerhalf = static struct mcpwm_capture_event_data_s event_data_ch1; static struct mcpwm_cap_channel_lowerhalf_s mcpwm_cap_ch1_lowerhalf = { - .ops = &mcpwm_cap_ops, - .common = &g_mcpwm_common, - .data = &event_data_ch1, + .ops = &mcpwm_cap_ops, + .common = &g_mcpwm_common, + .data = &event_data_ch1, .channel_id = MCPWM_CAP_CHANNEL_1, - .ready = false, + .ready = false, }; #endif @@ -203,11 +383,11 @@ static struct mcpwm_cap_channel_lowerhalf_s mcpwm_cap_ch1_lowerhalf = static struct mcpwm_capture_event_data_s event_data_ch2; static struct mcpwm_cap_channel_lowerhalf_s mcpwm_cap_ch2_lowerhalf = { - .ops = &mcpwm_cap_ops, - .common = &g_mcpwm_common, - .data = &event_data_ch2, + .ops = &mcpwm_cap_ops, + .common = &g_mcpwm_common, + .data = &event_data_ch2, .channel_id = MCPWM_CAP_CHANNEL_2, - .ready = false, + .ready = false, }; #endif #endif /* CONFIG_ESP_MCPWM_CAPTURE */ @@ -235,11 +415,1013 @@ static void esp_mcpwm_group_start(void) { periph_module_enable(PERIPH_PWM0_MODULE); mcpwm_hal_init(&g_mcpwm_common.hal, &g_mcpwm_common.group); - mcpwm_hal_timer_reset(&g_mcpwm_common.hal, 0); - g_mcpwm_common.initialized = true; } +/**************************************************************************** + * Name: esp_motor_setup + * + * Description: + * Configures the MCPWM operator and generator, setting the PWM clock and + * output pins. + * If required, also configures fault detection. When done, sets the the + * motor state to IDLE. + * + * Input Parameters: + * dev - Pointer to the motor channel lower-half data structure. + * + * Returned Value: + * OK on success, otherwise a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP_MCPWM_MOTOR +static int esp_motor_setup(struct motor_lowerhalf_s *dev) +{ + DEBUGASSERT(dev != NULL); + + struct mcpwm_motor_lowerhalf_s *priv = ( + struct mcpwm_motor_lowerhalf_s *)dev; + mcpwm_hal_context_t *hal = &priv->common->hal; + uint32_t base_clock; + irqstate_t flags; + int ret; + + flags = spin_lock_irqsave(&g_mcpwm_common.mcpwm_spinlock); + if ((priv->state.state == MOTOR_STATE_FAULT) || + (priv->state.state == MOTOR_STATE_CRITICAL)) + { + mtrerr("Motor is in fault state. Clear faults first\n"); + return ERROR; + } + + mtrinfo("State: %d\n", priv->state.state); + + esp_clk_tree_src_get_freq_hz(SOC_MOD_CLK_PLL_F160M, + ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, + &base_clock); + + mcpwm_hal_timer_reset(hal, priv->timer_id); + mcpwm_hal_operator_reset(hal, priv->operator_id); + + /* Setup control in UP-DOWN mode for duty cycle control + * Peak value modifies the maximum and minimum possible frequencies. + * Important: the HAL subtracts 1 from the target value. + */ + + priv->timer_prescale = \ + base_clock / ((2 * priv->counter_peak + 1) * priv->pwm_frequency); + + mcpwm_ll_timer_set_peak(hal->dev, priv->timer_id, + priv->counter_peak, true); + mcpwm_ll_timer_set_clock_prescale(hal->dev, priv->timer_id, + priv->timer_prescale); + mcpwm_ll_timer_update_period_at_once(hal->dev, priv->timer_id); + mcpwm_ll_timer_enable_update_period_on_tez(hal->dev, priv->timer_id, true); + mcpwm_ll_timer_set_count_mode(hal->dev, priv->timer_id, + MCPWM_TIMER_COUNT_MODE_UP_DOWN); + + mcpwm_ll_operator_flush_shadow(hal->dev, priv->operator_id); + mcpwm_ll_operator_connect_timer(hal->dev, priv->operator_id, + priv->timer_id); + mcpwm_ll_operator_enable_update_compare_on_tez(hal->dev, priv->operator_id, + MCPWM_GENERATOR_0, true); + +#ifdef ESP_MCPMW_MOTOR_FAULT + esp_motor_fault_configure(priv, true); +#endif + priv->state.state = MOTOR_STATE_IDLE; + + spin_unlock_irqrestore(&g_mcpwm_common.mcpwm_spinlock, flags); + mtrinfo("Channel %d starts: prescale %d | freq: %"PRIu32"\n", + priv->channel_id, priv->timer_prescale - 1, priv->pwm_frequency); + return OK; +} +#endif + +/**************************************************************************** + * Name: esp_motor_shutdown + * + * Description: + * Stop the PWM timer and disable output on GPIO matrix. + * + * Input Parameters: + * dev - Pointer to the motor channel lower-half data structure. + * + * Returned Value: + * Returns OK on success. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP_MCPWM_MOTOR +static int esp_motor_shutdown(struct motor_lowerhalf_s *dev) +{ + DEBUGASSERT(dev != NULL); + + struct mcpwm_motor_lowerhalf_s *priv = ( + struct mcpwm_motor_lowerhalf_s *)dev; + mcpwm_hal_context_t *hal = &priv->common->hal; + irqstate_t flags; + + flags = spin_lock_irqsave(&g_mcpwm_common.mcpwm_spinlock); + + /* Stop the motor */ + + esp_motor_stop(dev); + + /* Stop the PWM timer */ + + mcpwm_ll_timer_set_count_mode(hal->dev, priv->timer_id, + MCPWM_TIMER_COUNT_MODE_PAUSE); + mcpwm_ll_timer_set_start_stop_command(hal->dev, priv->timer_id, + MCPWM_TIMER_STOP_EMPTY); + + /* Disable fault detection */ + +#ifdef ESP_MCPMW_MOTOR_FAULT + esp_motor_fault_configure(priv, false); +#endif + + spin_unlock_irqrestore(&g_mcpwm_common.mcpwm_spinlock, flags); + return OK; +} +#endif + +/**************************************************************************** + * Name: esp_motor_stop + * + * Description: + * Holds the motor at a stand-still. PWM_A and PWM_B are kept low. + * + * Input Parameters: + * dev - Pointer to the motor channel lower-half data structure. + * + * Returned Value: + * OK on success, otherwise a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP_MCPWM_MOTOR +static int esp_motor_stop(struct motor_lowerhalf_s *dev) +{ + DEBUGASSERT(dev != NULL); + + struct mcpwm_motor_lowerhalf_s *priv = ( + struct mcpwm_motor_lowerhalf_s *)dev; + mcpwm_hal_context_t *hal = &priv->common->hal; + irqstate_t flags; + int ret; + + flags = spin_lock_irqsave(&g_mcpwm_common.mcpwm_spinlock); + if (priv->state.state == MOTOR_STATE_IDLE) + { + mtrerr("Motor already stopped\n"); + return -EPERM; + } + + mcpwm_ll_timer_set_start_stop_command(hal->dev, priv->timer_id, + MCPWM_TIMER_STOP_EMPTY); + mcpwm_ll_gen_set_continue_force_level(hal->dev, + priv->operator_id, + MCPWM_GENERATOR_0, 0); + mcpwm_ll_gen_set_continue_force_level(hal->dev, + priv->operator_id, + MCPWM_GENERATOR_1, 0); + + ret = esp_motor_set_duty_cycle(priv, 0.0); + if (ret < 0) + { + mtrerr("Failed setting duty cycle to 0 on stop: %d\n", ret); + return ret; + } + + if ((priv->state.state == MOTOR_STATE_FAULT) || + (priv->state.state == MOTOR_STATE_CRITICAL)) + { + mtrinfo("Channel %d stopped in fault state\n", priv->channel_id); + } + else + { + priv->state.state = MOTOR_STATE_IDLE; + } + + priv->param.lock = false; + spin_unlock_irqrestore(&g_mcpwm_common.mcpwm_spinlock, flags); + mtrinfo("Channel %d stopped\n", priv->channel_id); + + return OK; +} +#endif + +/**************************************************************************** + * Name: esp_motor_start + * + * Description: + * Start the motor by disabling forced actions and setting the duty cycle. + * The motor parameters must have been set before calling this function. + * + * Input Parameters: + * dev - Pointer to the motor channel lower-half data structure. + * + * Returned Value: + * OK on success, otherwise a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP_MCPWM_MOTOR +static int esp_motor_start(struct motor_lowerhalf_s *dev) +{ + DEBUGASSERT(dev != NULL); + + struct mcpwm_motor_lowerhalf_s *priv = ( + struct mcpwm_motor_lowerhalf_s *)dev; + + mcpwm_hal_context_t *hal = &priv->common->hal; + irqstate_t flags; + int ret; + float duty; + + flags = spin_lock_irqsave(&g_mcpwm_common.mcpwm_spinlock); + if (priv->state.state == MOTOR_STATE_RUN) + { + mtrerr("Motor already running\n"); + return -EINVAL; + } + + if ((priv->state.state == MOTOR_STATE_CRITICAL) || + (priv->state.state == MOTOR_STATE_FAULT)) + { + mtrerr("Motor is in fault state\n"); + return -EINVAL; + } + + ret = esp_motor_pwm_config(priv); + if (ret < 0) + { + mtrerr("Failed setting PWM configuration\n"); + return ret; + } + + /* Set motor direction */ + + esp_motor_bdc_set_direction(priv); + mcpwm_ll_timer_set_start_stop_command(hal->dev, priv->timer_id, + MCPWM_TIMER_START_NO_STOP); + + /* Set duty cycle based on motor parameter and limits */ + +#ifdef CONFIG_MOTOR_UPPER_HAVE_SPEED + if (priv->opmode == MOTOR_OPMODE_SPEED) + { + duty = priv->param.speed / priv->limits.speed; + ret = esp_motor_set_duty_cycle(priv, duty); + if (ret < 0) + { + mtrerr("Failed starting motor\n"); + return ret; + } + } +#endif + + priv->state.state = MOTOR_STATE_RUN; + spin_unlock_irqrestore(&g_mcpwm_common.mcpwm_spinlock, flags); + mtrinfo("Motor start\n"); + return OK; +} +#endif + +/**************************************************************************** + * Name: esp_motor_params_set + * + * Description: + * Set parameters to run the motor. + * + * Input Parameters: + * dev - Pointer to the motor channel lower-half data structure. + * param - Pointer to the motor parameter structure. + * + * Returned Value: + * Returns OK on success. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP_MCPWM_MOTOR +static int esp_motor_params_set(struct motor_lowerhalf_s *dev, + struct motor_params_s *param) +{ + DEBUGASSERT(dev != NULL); + DEBUGASSERT(param != NULL); + + struct mcpwm_motor_lowerhalf_s *priv = ( + struct mcpwm_motor_lowerhalf_s *) dev; + +#ifdef CONFIG_MOTOR_UPPER_HAVE_POSITION + priv->param.position = param->position; +#endif +#ifdef CONFIG_MOTOR_UPPER_HAVE_SPEED + priv->param.speed = param->speed; +#endif +#ifdef CONFIG_MOTOR_UPPER_HAVE_TORQUE + priv->param.torque = param->torque; +#endif +#ifdef CONFIG_MOTOR_UPPER_HAVE_FORCE + priv->param.force = param->force; +#endif +#ifdef CONFIG_MOTOR_UPPER_HAVE_ACCELERATION + priv->param.acceleration = param->acceleration; +#endif +#ifdef CONFIG_MOTOR_UPPER_HAVE_DECELERATION + priv->param.deceleration = param->deceleration; +#endif +#ifdef CONFIG_MOTOR_UPPER_HAVE_DIRECTION + priv->param.direction = param->direction; +#endif + priv->param.lock = false; + + /* Refresh duty and direction on the go */ + + if (priv->state.state == MOTOR_STATE_RUN) + { + float duty = priv->param.speed / priv->limits.speed; + esp_motor_set_duty_cycle(priv, duty); + esp_motor_bdc_set_direction(priv); + } + + mtrinfo("Motor parameters set\n"); + return OK; +} +#endif + +/**************************************************************************** + * Name: esp_motor_mode_set + * + * Description: + * Sets the motor operating mode. + * + * Input Parameters: + * dev - Pointer to the motor channel lower-half data structure. + * mode - Must be one of the motor_opmode_e enum. + * + * Returned Value: + * OK on success, otherwise a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP_MCPWM_MOTOR +static int esp_motor_mode_set(struct motor_lowerhalf_s *dev, uint8_t mode) +{ + DEBUGASSERT(dev != NULL); + + struct mcpwm_motor_lowerhalf_s *priv = ( + struct mcpwm_motor_lowerhalf_s *) dev; + + if (mode > MOTOR_OPMODE_PATTERN) + { + mtrerr("Invalid operation mode: %u\n", mode); + return -EINVAL; + } + + priv->opmode = mode; + mtrinfo("Mode set: %u\n", priv->opmode); + + return OK; +} +#endif + +/**************************************************************************** + * Name: esp_motor_limits_set + * + * Description: + * Set motor limits. Must be called before start the motor. + * + * Input Parameters: + * dev - Pointer to the motor channel lower-half data structure. + * limits - Pointer to the motor limits data structure. + * + * Returned Value: + * Returns OK on success. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP_MCPWM_MOTOR +static int esp_motor_limits_set(struct motor_lowerhalf_s *dev, + struct motor_limits_s *limits) +{ + DEBUGASSERT(dev != NULL); + DEBUGASSERT(limits != NULL); + + struct mcpwm_motor_lowerhalf_s *priv = ( + struct mcpwm_motor_lowerhalf_s *) dev; + +#ifdef CONFIG_MOTOR_UPPER_HAVE_POSITION + priv->limits.position = limits->position; +#endif +#ifdef CONFIG_MOTOR_UPPER_HAVE_SPEED + priv->limits.speed = limits->speed; +#endif +#ifdef CONFIG_MOTOR_UPPER_HAVE_TORQUE + priv->limits.torque = limits->torque; +#endif +#ifdef CONFIG_MOTOR_UPPER_HAVE_FORCE + priv->limits.force = limits->force; +#endif +#ifdef CONFIG_MOTOR_UPPER_HAVE_ACCELERATION + priv->limits.acceleration = limits->acceleration; +#endif +#ifdef CONFIG_MOTOR_UPPER_HAVE_DECELERATION + priv->limits.deceleration = limits->deceleration; +#endif +#ifdef CONFIG_MOTOR_UPPER_HAVE_INPUT_VOLTAGE + priv->limits.v_in = limits->v_in; +#endif +#ifdef CONFIG_MOTOR_UPPER_HAVE_INPUT_CURRENT + priv->limits.i_in = limits->i_in; +#endif +#ifdef CONFIG_MOTOR_UPPER_HAVE_INPUT_POWER + priv->limits.p_in = limits->p_in; +#endif + priv->limits.lock = true; + mtrinfo("limits set and locked %d\n", priv->limits.lock); + return OK; +} +#endif + +/**************************************************************************** + * Name: esp_motor_fault_set + * + * Description: + * Sets the fault state for the motor. + * + * Input Parameters: + * lower - Pointer to the motor channel lower-half data structure. + * fault - Fault value. Must be one of motor_fault_e enum. + * + * Returned Value: + * OK on success, otherwise a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP_MCPWM_MOTOR +static int esp_motor_fault_set(struct motor_lowerhalf_s *dev, uint8_t fault) +{ + DEBUGASSERT(dev != NULL); + + struct mcpwm_motor_lowerhalf_s *priv = ( + struct mcpwm_motor_lowerhalf_s *) dev; + + if (fault > MOTOR_FAULT_OTHER) + { + mtrerr("Invalid fault value: %u\n", fault); + return -EINVAL; + } + + priv->state.state = MOTOR_STATE_FAULT; + priv->state.fault |= fault; + mtrinfo("%u\n", priv->state.fault); + return OK; +} +#endif + +/**************************************************************************** + * Name: esp_motor_state_get + * + * Description: + * Get the current motor state. + * + * Input Parameters: + * dev - Pointer to the motor channel lower-half data structure. + * state - Pointer to the motor state data structure. + * + * Returned Value: + * Returns OK on success. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP_MCPWM_MOTOR +static int esp_motor_state_get(struct motor_lowerhalf_s *dev, + struct motor_state_s *state) +{ + DEBUGASSERT(dev != NULL); + DEBUGASSERT(state != NULL); + + struct mcpwm_motor_lowerhalf_s *priv = ( + struct mcpwm_motor_lowerhalf_s *) dev; + + state->state = priv->state.state; + state->fault = priv->state.fault; + memcpy(&state->state, &priv->state, sizeof(struct motor_feedback_s)); + return OK; +} +#endif + +/**************************************************************************** + * Name: esp_motor_fault_get + * + * Description: + * Get current motor fault state. + * + * Input Parameters: + * dev - Pointer to the motor channel lower-half data structure. + * fault - Fault state value. Must be one of motor_fault_e enum. + * + * Returned Value: + * Returns OK on success. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP_MCPWM_MOTOR +static int esp_motor_fault_get(struct motor_lowerhalf_s *dev, uint8_t *fault) +{ + DEBUGASSERT(dev != NULL); + DEBUGASSERT(fault != NULL); + + struct mcpwm_motor_lowerhalf_s *priv = ( + struct mcpwm_motor_lowerhalf_s *) dev; + + *fault = priv->state.fault; + mtrinfo("%u\n", priv->state.fault); + return OK; +} +#endif + +/**************************************************************************** + * Name: esp_motor_fault_clear + * + * Description: + * Clears a motor fault. + * + * Input Parameters: + * lower - Pointer to the capture channel lower-half data structure. + * fault - Fault state to clear (one of motor_fault_e enum). + * + * Returned Value: + * OK on success, otherwise a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP_MCPWM_MOTOR +static int esp_motor_fault_clear(struct motor_lowerhalf_s *dev, + uint8_t fault) +{ + DEBUGASSERT(dev != NULL); + + struct mcpwm_motor_lowerhalf_s *priv = ( + struct mcpwm_motor_lowerhalf_s *) dev; + + if (fault > MOTOR_FAULT_OTHER) + { + mtrerr("Invalid fault value: %u\n", fault); + return -EINVAL; + } + + priv->state.fault &= ~fault; + if (priv->state.fault == 0) + { + priv->state.state = MOTOR_STATE_IDLE; + mtrinfo("All faults clear\n"); + return OK; + } + + mtrinfo("Fault clear: %u\n", fault); + return OK; +} +#endif + +/**************************************************************************** + * Name: esp_motor_ioctl + * + * Description: + * Unused but required for upper-half motor driver. + * + * Input Parameters: + * dev - Pointer to the motor channel lower-half data structure. + * cmd - Custom IOCTL. + * arg - Argument to be passed for custom IOCTL. + * + * Returned Value: + * Returns 1. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP_MCPWM_MOTOR +static int esp_motor_ioctl(struct motor_lowerhalf_s *dev, int cmd, + unsigned long arg) +{ + return 1; +} +#endif + +/**************************************************************************** + * Name: esp_motor_fault_configure + * + * Description: + * Configure fault detection for motor channel. Enables the interrupt + * handling to set and clear fault state and sets brake action on fault + * (holds PWM at low state) in one-shot mode. + * + * Input Parameters: + * dev - Pointer to the motor channel lower-half data structure. + * enable - True to setup motor fault. False to disable fault detection. + * + * Returned Value: + * OK on success, otherwise a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +#ifdef ESP_MCPMW_MOTOR_FAULT +static int esp_motor_fault_configure(struct mcpwm_motor_lowerhalf_s *lower, + bool enable) +{ + DEBUGASSERT(lower != NULL); + + mcpwm_hal_context_t *hal = &lower->common->hal; + irqstate_t flags; + + flags = spin_lock_irqsave(&g_mcpwm_common.mcpwm_spinlock); + if (!enable) + { + mcpwm_ll_fault_enable_detection(hal->dev, lower->fault_id, false); + mcpwm_ll_intr_enable(hal->dev, + MCPWM_LL_EVENT_FAULT_ENTER(lower->fault_id), + false); + mcpwm_ll_intr_enable(hal->dev, + MCPWM_LL_EVENT_FAULT_EXIT(lower->fault_id), + false); + return OK; + } + + /* Detect fault when signal is high and also enable software trigger */ + + mcpwm_ll_fault_set_active_level(hal->dev, lower->fault_id, true); + mcpwm_ll_fault_enable_detection(hal->dev, lower->fault_id, true); + mcpwm_ll_brake_enable_oneshot_mode(hal->dev, lower->operator_id, + lower->fault_id, true); + mcpwm_ll_brake_enable_soft_ost(hal->dev, lower->operator_id, true); + + /* Make sure the brake event can be triggered when the timer is + * counting up AND down because it is running in up-down mode. + */ + + mcpwm_ll_generator_set_action_on_brake_event(hal->dev, + lower->operator_id, + MCPWM_GENERATOR_0, + MCPWM_TIMER_DIRECTION_UP, + MCPWM_OPER_BRAKE_MODE_OST, + 1); + mcpwm_ll_generator_set_action_on_brake_event(hal->dev, + lower->operator_id, + MCPWM_GENERATOR_0, + MCPWM_TIMER_DIRECTION_DOWN, + MCPWM_OPER_BRAKE_MODE_OST, + 1); + mcpwm_ll_generator_set_action_on_brake_event(hal->dev, + lower->operator_id, + MCPWM_GENERATOR_1, + MCPWM_TIMER_DIRECTION_UP, + MCPWM_OPER_BRAKE_MODE_OST, + 1); + mcpwm_ll_generator_set_action_on_brake_event(hal->dev, + lower->operator_id, + MCPWM_GENERATOR_1, + MCPWM_TIMER_DIRECTION_DOWN, + MCPWM_OPER_BRAKE_MODE_OST, + 1); + + /* Enable interrupt requests for motor fault */ + + mcpwm_ll_brake_clear_ost(hal->dev, lower->operator_id); + mcpwm_ll_intr_enable(hal->dev, + MCPWM_LL_EVENT_FAULT_ENTER(lower->fault_id), + true); + mcpwm_ll_intr_enable(hal->dev, + MCPWM_LL_EVENT_FAULT_EXIT(lower->fault_id), + true); + mcpwm_ll_intr_clear_status(hal->dev, + MCPWM_LL_EVENT_FAULT_ENTER(lower->fault_id)); + mcpwm_ll_intr_clear_status(hal->dev, + MCPWM_LL_EVENT_FAULT_EXIT(lower->fault_id)); + + spin_unlock_irqrestore(&g_mcpwm_common.mcpwm_spinlock, flags); + mtrinfo("Brake configured for motor channel %d on fault id %d", + lower->channel_id, lower->fault_id); + return OK; +} +#endif + +/**************************************************************************** + * Name: esp_motor_set_duty_cycle + * + * Description: + * Sets the duty cycle on output PWM_A and PWM_B. + * + * Input Parameters: + * lower - Pointer to the motor channel lower-half data structure. + * duty - Duty-cycle value from 0.0 to 1.0. + * + * Returned Value: + * OK on success, otherwise a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP_MCPWM_MOTOR +static int esp_motor_set_duty_cycle(struct mcpwm_motor_lowerhalf_s *lower, + float duty) +{ + DEBUGASSERT(lower != NULL); + + mcpwm_hal_context_t *hal = &lower->common->hal; + + if (duty < 0.0 || duty > 1.0) + { + mtrerr("Invalid duty cycle value: %f\n", duty); + return -EINVAL; + } + + uint32_t pwm_count = -1 * lower->counter_peak * (duty - 1.0); + mcpwm_ll_operator_set_compare_value(hal->dev, lower->operator_id, + MCPWM_GENERATOR_0, pwm_count); + mtrinfo("Duty %f compare value set: %u\n", duty, pwm_count); + + return OK; +} +#endif + +/**************************************************************************** + * Name: esp_motor_bdc_set_direction + * + * Description: + * Sets direction of motor spin by disabling PWM_A or PWM_B. If + * CONFIG_MOTOR_UPPER_HAVE_DIRECTION is not defined, defaults to CW. + * + * Input Parameters: + * lower - Pointer to the motor channel lower-half data structure. + * + * Returned Value: + * OK on success, otherwise a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP_MCPWM_MOTOR +static int esp_motor_bdc_set_direction(struct mcpwm_motor_lowerhalf_s *lower) +{ + DEBUGASSERT(lower != NULL); + + mcpwm_hal_context_t *hal = &lower->common->hal; + int8_t direction; + + if (lower->opmode == MOTOR_OPMODE_SPEED) + { +#ifdef CONFIG_MOTOR_UPPER_HAVE_DIRECTION + if (lower->param.direction == MOTOR_DIR_CW) + { + mcpwm_ll_gen_disable_continue_force_action(hal->dev, + lower->operator_id, + MCPWM_GENERATOR_0); + mcpwm_ll_gen_set_continue_force_level(hal->dev, + lower->operator_id, + MCPWM_GENERATOR_1, 0); + } + else + { + mcpwm_ll_gen_disable_continue_force_action(hal->dev, + lower->operator_id, + MCPWM_GENERATOR_1); + mcpwm_ll_gen_set_continue_force_level(hal->dev, + lower->operator_id, + MCPWM_GENERATOR_0, 0); + direction = lower->param.direction; + } +#else + mcpwm_ll_gen_disable_continue_force_action(hal->dev, + lower->operator_id, + MCPWM_GENERATOR_0); + mcpwm_ll_gen_set_continue_force_level(hal->dev, + lower->operator_id, + MCPWM_GENERATOR_1, 0); + direction = MOTOR_DIR_CW; +#endif + } + + mtrinfo("Motor direction set: %d\n", direction); + return OK; +} +#endif + +/**************************************************************************** + * Name: esp_motor_pwm_config + * + * Description: + * Configures the control waveform by setting some configurations for + * timer events. Included configurations are: speed control. + * + * Input Parameters: + * lower - Pointer to the motor channel lower-half data structure. + * + * Returned Value: + * Returns OK on success. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP_MCPWM_MOTOR +static int esp_motor_pwm_config(struct mcpwm_motor_lowerhalf_s *lower) +{ + mcpwm_hal_context_t *hal = &lower->common->hal; + + /* Configure PWM_A and PWM_B as complementary */ + + if (lower->opmode == MOTOR_OPMODE_SPEED) + { + /* PWM_A Output */ + + mcpwm_ll_generator_set_action_on_compare_event(hal->dev, + lower->operator_id, + MCPWM_GENERATOR_0, + MCPWM_TIMER_DIRECTION_UP, + MCPWM_GENERATOR_0, + MCPWM_GEN_ACTION_HIGH); + mcpwm_ll_generator_set_action_on_compare_event( + hal->dev, + lower->operator_id, + MCPWM_GENERATOR_0, + MCPWM_TIMER_DIRECTION_DOWN, + MCPWM_GENERATOR_0, + MCPWM_GEN_ACTION_LOW); + + /* PWM_B Output */ + + mcpwm_ll_generator_set_action_on_compare_event(hal->dev, + lower->operator_id, + MCPWM_GENERATOR_1, + MCPWM_TIMER_DIRECTION_UP, + MCPWM_GENERATOR_0, + MCPWM_GEN_ACTION_HIGH); + mcpwm_ll_generator_set_action_on_compare_event(hal->dev, + lower->operator_id, + MCPWM_GENERATOR_1, + MCPWM_TIMER_DIRECTION_DOWN, + MCPWM_GENERATOR_0, + MCPWM_GEN_ACTION_LOW); + } + else + { + mtrerr("Invalid operation mode\n"); + return -EPERM; + } + + return OK; +} +#endif + +/**************************************************************************** + * Name: esp_mcpwm_fault_configure + * + * Description: + * Configures the fault GPIO. + * + * Input Parameters: + * lower - Pointer to the motor channel lower-half data structure. + * enable - True to configure motor fault. False to disable. + * + * Returned Value: + * OK on success, otherwise a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +#ifdef ESP_MCPMW_MOTOR_FAULT +static int esp_mcpwm_fault_gpio_config(struct mcpwm_motor_lowerhalf_s *lower, + bool enable) +{ + int ret; + + if (!enable) + { + esp_gpio_matrix_in(0x3a, + mcpwm_periph_signals.groups[MCPWM_CAPTURE_DEFAULT_GROUP].\ + gpio_faults[lower->fault_id].fault_sig, + false); + return OK; + } + + ret = esp_configgpio(lower->fault_pin, INPUT_PULLDOWN); + if (ret < 0) + { + mtrerr("Failed configuring fault GPIO\n"); + return ret; + } + + esp_gpio_matrix_in( + lower->fault_pin, + mcpwm_periph_signals.groups[MCPWM_CAPTURE_DEFAULT_GROUP].\ + gpio_faults[lower->fault_id].fault_sig, + false); + + mtrinfo("Fault signal configured for GPIO %d in channel %d\n", + lower->fault_pin, lower->channel_id); + + return ret; +} +#endif + +/**************************************************************************** + * Name: esp_mcpwm_motor_set_gpio + * + * Description: + * Configures the GPIO pins to be used as motor PWM output and the fault + * GPIO (if enabled). + * + * Input Parameters: + * lower - Pointer to the motor channel lower-half data structure. + * enable - True to configure GPIO for motor PWM. False to disable. + * + * Returned Value: + * OK on success, otherwise a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP_MCPWM_MOTOR +static int esp_mcpwm_motor_set_gpio(struct mcpwm_motor_lowerhalf_s *lower, + bool enable) +{ + int ret; + + if (!enable) + { + esp_gpio_matrix_out(lower->generator_pins[MCPWM_GENERATOR_0], 0x100, + false, false); + + esp_gpio_matrix_out(lower->generator_pins[MCPWM_GENERATOR_1], 0x100, + false, false); + return OK; + } + + ret = esp_configgpio(lower->generator_pins[MCPWM_GENERATOR_0], + OUTPUT_FUNCTION); + if (ret < 0) + { + mtrerr("Failed configuring PWM_A GPIO\n"); + return ret; + } + + ret = esp_configgpio(lower->generator_pins[MCPWM_GENERATOR_1], + OUTPUT_FUNCTION); + if (ret < 0) + { + mtrerr("Failed configuring PWM_B GPIO\n"); + return ret; + } + + esp_gpio_matrix_out( + lower->generator_pins[MCPWM_GENERATOR_0], + mcpwm_periph_signals.groups[MCPWM_CAPTURE_DEFAULT_GROUP].\ + operators[lower->channel_id].generators[MCPWM_GENERATOR_0].pwm_sig, + false, false); + + esp_gpio_matrix_out( + lower->generator_pins[MCPWM_GENERATOR_1], + mcpwm_periph_signals.groups[MCPWM_CAPTURE_DEFAULT_GROUP].\ + operators[lower->channel_id].generators[MCPWM_GENERATOR_1].pwm_sig, + false, false); + + /* Connects the PWM output to the Capture input */ + +#ifdef CONFIG_ESP_MCPWM_TEST_LOOPBACK + esp_gpio_matrix_out(CONFIG_ESP_MCPWM_CAPTURE_CH0_GPIO, + mcpwm_periph_signals.\ + groups[MCPWM_CAPTURE_DEFAULT_GROUP].\ + operators[lower->channel_id].\ + generators[MCPWM_GENERATOR_0].pwm_sig, + 0, 0); + esp_gpio_matrix_out(CONFIG_ESP_MCPWM_CAPTURE_CH1_GPIO, + mcpwm_periph_signals.\ + groups[MCPWM_CAPTURE_DEFAULT_GROUP].\ + operators[lower->channel_id].\ + generators[MCPWM_GENERATOR_1].pwm_sig, + 0, 0); + mtrinfo("Loopback for capture device is enabled\n"); +#endif + + mtrinfo("GPIO: %d PWM_A configured for channel %d\n", + lower->generator_pins[MCPWM_GENERATOR_0], + lower->channel_id); + mtrinfo("GPIO: %d PWM_B configured for channel %d\n", + lower->generator_pins[MCPWM_GENERATOR_1], + lower->channel_id); + + return ret; +} +#endif + /**************************************************************************** * Name: esp_capture_start * @@ -270,6 +1452,7 @@ static int esp_capture_start(struct cap_lowerhalf_s *lower) /* Enable channel and interruption for rising edge */ + mcpwm_ll_capture_enable_timer(hal->dev, true); mcpwm_ll_capture_enable_channel(hal->dev, priv->channel_id, true); mcpwm_ll_intr_enable(hal->dev, MCPWM_LL_EVENT_CAPTURE(priv->channel_id), @@ -320,6 +1503,7 @@ static int esp_capture_stop(struct cap_lowerhalf_s *lower) /* Disable channel and interrupts */ + mcpwm_ll_capture_enable_timer(hal->dev, false); mcpwm_ll_capture_enable_channel(hal->dev, priv->channel_id, false); mcpwm_ll_intr_enable(hal->dev, MCPWM_LL_EVENT_CAPTURE(priv->channel_id), @@ -446,9 +1630,8 @@ static int esp_mcpwm_capture_set_gpio( * ****************************************************************************/ -#ifdef CONFIG_ESP_MCPWM_CAPTURE -static int esp_mcpwm_capture_isr_register(int (*fn)(int, void *, void *), - void *arg) +#if defined(CONFIG_ESP_MCPWM_CAPTURE) || defined(ESP_MCPMW_MOTOR_FAULT) +static int esp_mcpwm_isr_register(int (*fn)(int, void *, void *), void *arg) { int cpuint; int ret; @@ -478,7 +1661,7 @@ static int esp_mcpwm_capture_isr_register(int (*fn)(int, void *, void *), #endif /**************************************************************************** - * Name: mcpwm_capture_driver_isr_default + * Name: mcpwm_driver_isr_default * * Description: * Default function called when a capture interrupt occurs. @@ -501,22 +1684,27 @@ static int esp_mcpwm_capture_isr_register(int (*fn)(int, void *, void *), * ****************************************************************************/ -#ifdef CONFIG_ESP_MCPWM_CAPTURE -static int IRAM_ATTR mcpwm_capture_driver_isr_default(int irq, void *context, - void *arg) +#if defined(CONFIG_ESP_MCPWM_CAPTURE) || defined(ESP_MCPMW_MOTOR_FAULT) +static int IRAM_ATTR mcpwm_driver_isr_default(int irq, void *context, + void *arg) { struct mcpwm_dev_common_s *common = (struct mcpwm_dev_common_s *)arg; + uint32_t status; + irqstate_t flags; +#ifdef CONFIG_ESP_MCPWM_CAPTURE struct mcpwm_cap_channel_lowerhalf_s *lower = NULL; struct mcpwm_capture_event_data_s *data = NULL; - uint32_t status; uint32_t cap_value; mcpwm_capture_edge_t cap_edge; - irqstate_t flags; +#endif +#ifdef CONFIG_ESP_MCPWM_MOTOR + struct mcpwm_motor_lowerhalf_s *priv = NULL; +#endif flags = spin_lock_irqsave(common->mcpwm_spinlock); status = mcpwm_ll_intr_get_status(common->hal.dev); - /* Evaluate which channel triggered the capture interrupt */ + /* Evaluate capture interrupt for all 3 cap channels */ if (status & MCPWM_LL_EVENT_CAPTURE(MCPWM_CAP_CHANNEL_0)) { @@ -536,9 +1724,42 @@ static int IRAM_ATTR mcpwm_capture_driver_isr_default(int irq, void *context, lower = &mcpwm_cap_ch2_lowerhalf; } #endif - else + + /* Evaluate fault interrupt which can be of the ENTER or EXIT type */ + +#ifdef CONFIG_ESP_MCPMW_MOTOR_CH0_FAULT + if (status & MCPWM_LL_EVENT_FAULT_ENTER(MCPWM_FAULT_0)) { - return -ERANGE; + priv = &mcpwm_bdc_ch0_lowerhalf; + mcpwm_ll_intr_clear_status(common->hal.dev, + status & + MCPWM_LL_EVENT_FAULT_ENTER(MCPWM_FAULT_0)); + esp_motor_fault_set((struct motor_lowerhalf_s *)priv, + MOTOR_FAULT_OTHER); + mcpwm_ll_brake_trigger_soft_ost(common->hal.dev, priv->operator_id); + } + + if (status & MCPWM_LL_EVENT_FAULT_EXIT(MCPWM_FAULT_0)) + { + priv = &mcpwm_bdc_ch0_lowerhalf; + mcpwm_ll_intr_clear_status(common->hal.dev, + status & + MCPWM_LL_EVENT_FAULT_EXIT(MCPWM_FAULT_0)); + esp_motor_fault_clear((struct motor_lowerhalf_s *)priv, + MOTOR_FAULT_OTHER); + mcpwm_ll_brake_clear_ost(common->hal.dev, MCPWM_FAULT_0); + } +#endif + + /* If capture is disabled or the interrupt was not related to it, + * simply return. Otherwise, continue executing capture math + */ + +#ifdef CONFIG_ESP_MCPWM_CAPTURE + if (lower == NULL) + { + spin_unlock_irqrestore(common->mcpwm_spinlock, flags); + return OK; } mcpwm_ll_intr_clear_status(common->hal.dev, @@ -600,9 +1821,9 @@ static int IRAM_ATTR mcpwm_capture_driver_isr_default(int irq, void *context, lower->channel_id, true); } +#endif spin_unlock_irqrestore(common->mcpwm_spinlock, flags); - return OK; } #endif @@ -611,6 +1832,95 @@ static int IRAM_ATTR mcpwm_capture_driver_isr_default(int irq, void *context, * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: esp_motor_bdc_initialize + * + * Description: + * This function initializes the MCPWM peripheral and configures the + * motor control driver. + * + * Input Parameters: + * channel - Channel to be initialized [0-3]. + * frequency - PWM output frequency in Hz. + * pwm_a_pin - GPIO pin for PWM_A output. + * pwm_b_pin - GPIO pin for PWM_B output (complements PWM_A). + * fault_pin - Indicates input pin to detect fault (to be implemented). + * + * Returned Value: + * On success, this function returns a valid pointer to the Capture device + * structure. If the initialization fails, it returns NULL. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP_MCPWM_MOTOR_BDC +struct motor_lowerhalf_s *esp_motor_bdc_initialize(int channel, + uint32_t frequency, int pwm_a_pin, int pwm_b_pin, int fault_pin) +{ + struct mcpwm_motor_lowerhalf_s *lower = NULL; + int ret; + uint32_t ref_clock; + + if ((frequency > MCPWM_MAX_PWM_OUT_FREQ) || + (frequency < MCPWM_MIN_PWM_OUT_FREQ)) + { + mtrerr("Invalid frequency set. Must be between %d and %d Hz\n", + MCPWM_MIN_PWM_OUT_FREQ, MCPWM_MAX_PWM_OUT_FREQ); + return NULL; + } + + if (!g_mcpwm_common.initialized) + { + esp_mcpwm_group_start(); + } + +#ifdef ESP_MCPMW_MOTOR_FAULT + if (!g_mcpwm_common.isr_initialized) + { + esp_mcpwm_isr_register(mcpwm_driver_isr_default, &g_mcpwm_common); + g_mcpwm_common.isr_initialized = true; + } +#endif + + switch (channel) + { + case 0: + lower = &mcpwm_bdc_ch0_lowerhalf; + lower->pwm_frequency = frequency; + break; + default: + mtrerr("Invalid channel selection: %d\n", channel); + return NULL; + } + + lower->generator_pins[MCPWM_GENERATOR_0] = pwm_a_pin; + lower->generator_pins[MCPWM_GENERATOR_1] = pwm_b_pin; + lower->fault_pin = fault_pin; + + /* Configure GPIOs before starting */ + +#ifdef ESP_MCPMW_MOTOR_FAULT + ret = esp_mcpwm_fault_gpio_config(lower, true); + if (ret < 0) + { + mtrerr("Failed configuring motor fault GPIOs\n"); + return NULL; + } +#endif + + ret = esp_mcpwm_motor_set_gpio(lower, true); + if (ret < 0) + { + mtrerr("Failed configuring motor PWM GPIOs\n"); + return NULL; + } + + mtrinfo("Channel %d initialized. GPIO: PWM_A: %d | PWM_B: %d | Freq: %d\n", + lower->channel_id, lower->generator_pins[MCPWM_GENERATOR_0], + lower->generator_pins[MCPWM_GENERATOR_1], lower->pwm_frequency); + return (struct motor_lowerhalf_s *) lower; +} +#endif + /**************************************************************************** * Name: esp_mcpwm_capture_initialize * @@ -638,11 +1948,11 @@ struct cap_lowerhalf_s *esp_mcpwm_capture_initialize(int channel, int pin) esp_mcpwm_group_start(); } - if (!g_mcpwm_common.capture_initialized) + if (!g_mcpwm_common.isr_initialized) { - esp_mcpwm_capture_isr_register(mcpwm_capture_driver_isr_default, + esp_mcpwm_isr_register(mcpwm_driver_isr_default, &g_mcpwm_common); - mcpwm_ll_capture_enable_timer(g_mcpwm_common.hal.dev, true); + g_mcpwm_common.isr_initialized = true; } switch (channel) diff --git a/arch/xtensa/src/common/espressif/esp_mcpwm.h b/arch/xtensa/src/common/espressif/esp_mcpwm.h index 4bcdfbdf368..56e4cb35f8f 100644 --- a/arch/xtensa/src/common/espressif/esp_mcpwm.h +++ b/arch/xtensa/src/common/espressif/esp_mcpwm.h @@ -56,6 +56,31 @@ extern "C" * Public Function Prototypes ****************************************************************************/ +/**************************************************************************** + * Name: esp_motor_bdc_initialize + * + * Description: + * This function initializes the MCPWM peripheral and configures the + * motor control driver. + * + * Input Parameters: + * channel - Channel to be initialized (only 0 available for now). + * frequency - PWM output frequency in Hertz. + * pwm_a_pin - GPIO pin number for PWM0_A output. + * pwm_b_pin - GPIO pin number for PWM0_B output. + * fault_pin - GPIO pin number for fault signal input. + * + * Returned Value: + * On success, this function returns a valid pointer to the Capture device + * structure. If the initialization fails, it returns NULL. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP_MCPWM_MOTOR_BDC +struct motor_lowerhalf_s *esp_motor_bdc_initialize(int channel, + uint32_t frequency, int pwm_a_pin, int pwm_b_pin, int fault_pin); +#endif + /**************************************************************************** * Name: esp_mcpwm_capture_initialize * diff --git a/arch/xtensa/src/esp32/Kconfig b/arch/xtensa/src/esp32/Kconfig index 9135ee65621..e371c46bc76 100644 --- a/arch/xtensa/src/esp32/Kconfig +++ b/arch/xtensa/src/esp32/Kconfig @@ -2496,6 +2496,99 @@ endmenu # LEDC configuration menu "MCPWM Configuration" depends on ESP_MCPWM +config ESP_MCPWM_MOTOR + bool "MCPWM Motor Support" + default n + +menu "MCPWM Motor Configuration" + depends on ESP_MCPWM_MOTOR + +config ESP_MCPWM_MOTOR_BDC + bool "Brushed DC Motor Control" + depends on ESP_MCPWM + depends on ESP_MCPWM_MOTOR + select MOTOR + select MOTOR_UPPER + select MOTOR_UPPER_HAVE_SPEED + default n + ---help--- + Enables the use of the MCPWM submodule for control of brushed DC + motor. + +if ESP_MCPWM_MOTOR_BDC + +config ESP_MCPWM_MOTOR_CH0 + bool "Motor Control Channel 0" + default n + ---help--- + Enables motor control on channel 0. + +if ESP_MCPWM_MOTOR_CH0 + +config ESP_MCPWM_MOTOR_CH0_PWMA_GPIO + int "Output Pin PWM_A" + default 15 + ---help--- + Output pin assigned to channel 0 PWM output PWM_A. + +config ESP_MCPWM_MOTOR_CH0_PWMB_GPIO + int "Output Pin PWM_B" + default 16 + ---help--- + Output pin assigned to channel 0 PWM output PWM_B. + +config ESP_MCPWM_MOTOR_CH0_PWM_FREQ + int "PWM output frequency for channel 0 [Hz]" + default 1000 + ---help--- + Select PWM frequency for channel 0. + Minimum is 25 Hz and maximum is 3000 Hz. + +config ESP_MCPMW_MOTOR_CH0_FAULT + bool "Enable fault for channel 0" + default n + ---help--- + Enables the use of a fault pin to quickly stop the motor when + a GPIO pin pulled high. + +if ESP_MCPMW_MOTOR_CH0_FAULT + +config ESP_MCPMW_MOTOR_CH0_FAULT_GPIO + int "GPIO Pin for fault detection" + default 10 + ---help--- + Input pin assigned to channel 0 fault indicator. + +endif # ESP_MCPMW_MOTOR_CH0_FAULT + +endif # ESP_MCPWM_MOTOR_CH0 + +config ESP_MCPWM_MOTOR_CH1 + bool "Motor Control Channel 1" + default n + ---help--- + Enables motor control on channel 1. + +if ESP_MCPWM_MOTOR_CH1 + +config ESP_MCPWM_MOTOR_CH1_PWMA_GPIO + int "Output Pin CH1 PWM_A" + default 15 + ---help--- + Output pin assigned to channel 1 PWM output PWM_A. + +config ESP_MCPWM_MOTOR_CH1_PWMB_GPIO + int "Output Pin CH1 PWM_B" + default 16 + ---help--- + Output pin assigned to channel 1 PWM output PWM_B. + +endif # ESP_MCPWM_MOTOR_CH1 + +endif # ESP_MCPWM_MOTOR_BDC + +endmenu # MCPWM Motor Settings + config ESP_MCPWM_CAPTURE bool "MCPWM Capture Submodule" depends on ESP_MCPWM @@ -2516,7 +2609,7 @@ if ESP_MCPWM_CAPTURE_CH0 config ESP_MCPWM_CAPTURE_CH0_GPIO int "GPIO Pin" - default 14 + default 12 ---help--- GPIO pin assigned to capture channel 0. @@ -2556,6 +2649,15 @@ endif # ESP_MCPWM_CAPTURE_CH2 endif # ESP_MCPWM_CAPTURE +config ESP_MCPWM_TEST_LOOPBACK + bool "MCPWM loopback test mode" + depends on EXPERIMENTAL + default n + ---help--- + This enables a lower-half driver-specific loopback test + mode that attaches a capture device to the PWM output on + motor tests. + endmenu # MCPWM Configuration config ESP32_HAVE_OTA_PARTITION diff --git a/arch/xtensa/src/esp32s3/Kconfig b/arch/xtensa/src/esp32s3/Kconfig index 4a980f45400..efdb7bd99df 100644 --- a/arch/xtensa/src/esp32s3/Kconfig +++ b/arch/xtensa/src/esp32s3/Kconfig @@ -2120,6 +2120,99 @@ endmenu # LEDC configuration menu "MCPWM Configuration" depends on ESP_MCPWM +config ESP_MCPWM_MOTOR + bool "MCPWM Motor Support" + default n + +menu "MCPWM Motor Configuration" + depends on ESP_MCPWM_MOTOR + +config ESP_MCPWM_MOTOR_BDC + bool "Brushed DC Motor Control" + depends on ESP_MCPWM + depends on ESP_MCPWM_MOTOR + select MOTOR + select MOTOR_UPPER + select MOTOR_UPPER_HAVE_SPEED + default n + ---help--- + Enables the use of the MCPWM submodule for control of brushed DC + motor. + +if ESP_MCPWM_MOTOR_BDC + +config ESP_MCPWM_MOTOR_CH0 + bool "Motor Control Channel 0" + default n + ---help--- + Enables motor control on channel 0. + +if ESP_MCPWM_MOTOR_CH0 + +config ESP_MCPWM_MOTOR_CH0_PWMA_GPIO + int "Output Pin PWM_A" + default 15 + ---help--- + Output pin assigned to channel 0 PWM output PWM_A. + +config ESP_MCPWM_MOTOR_CH0_PWMB_GPIO + int "Output Pin PWM_B" + default 16 + ---help--- + Output pin assigned to channel 0 PWM output PWM_B. + +config ESP_MCPWM_MOTOR_CH0_PWM_FREQ + int "PWM output frequency for channel 0 [Hz]" + default 1000 + ---help--- + Select PWM frequency for channel 0. + Minimum is 25 Hz and maximum is 3000 Hz. + +config ESP_MCPMW_MOTOR_CH0_FAULT + bool "Enable fault for channel 0" + default n + ---help--- + Enables the use of a fault pin to quickly stop the motor when + a GPIO pin pulled high. + +if ESP_MCPMW_MOTOR_CH0_FAULT + +config ESP_MCPMW_MOTOR_CH0_FAULT_GPIO + int "GPIO Pin for fault detection" + default 10 + ---help--- + Input pin assigned to channel 0 fault indicator. + +endif # ESP_MCPMW_MOTOR_CH0_FAULT + +endif # ESP_MCPWM_MOTOR_CH0 + +config ESP_MCPWM_MOTOR_CH1 + bool "Motor Control Channel 1" + default n + ---help--- + Enables motor control on channel 1. + +if ESP_MCPWM_MOTOR_CH1 + +config ESP_MCPWM_MOTOR_CH1_PWMA_GPIO + int "Output Pin CH1 PWM_A" + default 15 + ---help--- + Output pin assigned to channel 1 PWM output PWM_A. + +config ESP_MCPWM_MOTOR_CH1_PWMB_GPIO + int "Output Pin CH1 PWM_B" + default 16 + ---help--- + Output pin assigned to channel 1 PWM output PWM_B. + +endif # ESP_MCPWM_MOTOR_CH1 + +endif # ESP_MCPWM_MOTOR_BDC + +endmenu # MCPWM Motor Settings + config ESP_MCPWM_CAPTURE bool "MCPWM Capture Submodule" depends on ESP_MCPWM @@ -2180,6 +2273,15 @@ endif # ESP_MCPWM_CAPTURE_CH2 endif # ESP_MCPWM_CAPTURE +config ESP_MCPWM_TEST_LOOPBACK + bool "MCPWM loopback test mode" + depends on EXPERIMENTAL + default n + ---help--- + This enables a lower-half driver-specific loopback test + mode that attaches a capture device to the PWM output on + motor tests. + endmenu # MCPWM Configuration menu "USB OTG Configuration" diff --git a/boards/xtensa/esp32/common/include/esp32_board_mcpwm.h b/boards/xtensa/esp32/common/include/esp32_board_mcpwm.h index fa8ec257e12..ecb402120a0 100644 --- a/boards/xtensa/esp32/common/include/esp32_board_mcpwm.h +++ b/boards/xtensa/esp32/common/include/esp32_board_mcpwm.h @@ -43,6 +43,25 @@ extern "C" * Public Function Prototypes ****************************************************************************/ +/**************************************************************************** + * Name: esp32_motor_initialize + * + * Description: + * Initialize MCPWM peripheral for motor control and register the motor + * driver. + * + * Input Parameters: + * None. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP_MCPWM_MOTOR_BDC +int board_motor_initialize(void); +#endif + #ifdef CONFIG_ESP_MCPWM_CAPTURE /**************************************************************************** diff --git a/boards/xtensa/esp32/common/src/esp32_board_mcpwm.c b/boards/xtensa/esp32/common/src/esp32_board_mcpwm.c index 0b6992bf5fe..09d1b2728aa 100644 --- a/boards/xtensa/esp32/common/src/esp32_board_mcpwm.c +++ b/boards/xtensa/esp32/common/src/esp32_board_mcpwm.c @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -39,10 +40,59 @@ * Pre-processor Definitions ****************************************************************************/ +#ifdef CONFIG_ESP_MCPMW_MOTOR_CH0_FAULT +# define MCPWM_FAULT_GPIO CONFIG_ESP_MCPMW_MOTOR_CH0_FAULT_GPIO +#else +# define MCPWM_FAULT_GPIO 0 +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: board_motor_initialize + * + * Description: + * Initialize MCPWM peripheral for motor control and register the motor + * driver. + * + * Input Parameters: + * None. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP_MCPWM_MOTOR_BDC +int board_motor_initialize(void) +{ + int ret; + struct motor_lowerhalf_s *motor; + + motor = esp_motor_bdc_initialize(0, + CONFIG_ESP_MCPWM_MOTOR_CH0_PWM_FREQ, + CONFIG_ESP_MCPWM_MOTOR_CH0_PWMA_GPIO, + CONFIG_ESP_MCPWM_MOTOR_CH0_PWMB_GPIO, + MCPWM_FAULT_GPIO); + if (!motor) + { + syslog(LOG_ERR, "ERROR: Failed to start MCPWM BDC Motor: CH0\n"); + return -ENODEV; + } + + ret = motor_register("/dev/motor0", motor); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: motor_register failed: %d\n", ret); + return ret; + } + + return OK; +} +#endif + /**************************************************************************** * Name: board_capture_initialize * diff --git a/boards/xtensa/esp32/esp32-devkitc/configs/capture/defconfig b/boards/xtensa/esp32/esp32-devkitc/configs/capture/defconfig index b277878e61b..27974533c1f 100644 --- a/boards/xtensa/esp32/esp32-devkitc/configs/capture/defconfig +++ b/boards/xtensa/esp32/esp32-devkitc/configs/capture/defconfig @@ -23,6 +23,7 @@ CONFIG_ESP32_UART0=y CONFIG_ESP_MCPWM=y CONFIG_ESP_MCPWM_CAPTURE=y CONFIG_ESP_MCPWM_CAPTURE_CH0=y +CONFIG_ESP_MCPWM_CAPTURE_CH0_GPIO=14 CONFIG_EXAMPLES_CAPTURE=y CONFIG_FS_PROCFS=y CONFIG_HAVE_CXX=y diff --git a/boards/xtensa/esp32/esp32-devkitc/configs/motor/defconfig b/boards/xtensa/esp32/esp32-devkitc/configs/motor/defconfig new file mode 100644 index 00000000000..d5ab54b94f0 --- /dev/null +++ b/boards/xtensa/esp32/esp32-devkitc/configs/motor/defconfig @@ -0,0 +1,50 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_ARCH_LEDS is not set +# CONFIG_NSH_ARGCAT is not set +# CONFIG_NSH_CMDOPT_HEXDUMP is not set +CONFIG_ARCH="xtensa" +CONFIG_ARCH_BOARD="esp32-devkitc" +CONFIG_ARCH_BOARD_COMMON=y +CONFIG_ARCH_BOARD_ESP32_DEVKITC=y +CONFIG_ARCH_CHIP="esp32" +CONFIG_ARCH_CHIP_ESP32=y +CONFIG_ARCH_CHIP_ESP32WROVER=y +CONFIG_ARCH_STACKDUMP=y +CONFIG_ARCH_XTENSA=y +CONFIG_BOARD_LOOPSPERMSEC=16717 +CONFIG_BUILTIN=y +CONFIG_ESP32_UART0=y +CONFIG_ESP_MCPWM=y +CONFIG_ESP_MCPWM_MOTOR=y +CONFIG_ESP_MCPWM_MOTOR_BDC=y +CONFIG_ESP_MCPWM_MOTOR_CH0=y +CONFIG_FS_PROCFS=y +CONFIG_HAVE_CXX=y +CONFIG_HAVE_CXXINITIALIZE=y +CONFIG_IDLETHREAD_STACKSIZE=3072 +CONFIG_INIT_ENTRYPOINT="nsh_main" +CONFIG_INIT_STACKSIZE=3072 +CONFIG_INTELHEX_BINARY=y +CONFIG_MM_REGIONS=3 +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_LINELEN=64 +CONFIG_NSH_READLINE=y +CONFIG_PREALLOC_TIMERS=4 +CONFIG_RAM_SIZE=114688 +CONFIG_RAM_START=0x20000000 +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_WAITPID=y +CONFIG_START_DAY=6 +CONFIG_START_MONTH=12 +CONFIG_START_YEAR=2011 +CONFIG_SYSLOG_BUFFER=y +CONFIG_SYSTEM_NSH=y +CONFIG_UART0_SERIAL_CONSOLE=y diff --git a/boards/xtensa/esp32/esp32-devkitc/src/esp32_bringup.c b/boards/xtensa/esp32/esp32-devkitc/src/esp32_bringup.c index 0d4ed6cda7d..3e1ebee0b53 100644 --- a/boards/xtensa/esp32/esp32-devkitc/src/esp32_bringup.c +++ b/boards/xtensa/esp32/esp32-devkitc/src/esp32_bringup.c @@ -295,6 +295,14 @@ int esp32_bringup(void) } #endif +#ifdef CONFIG_ESP_MCPWM_MOTOR_BDC + ret = board_motor_initialize(); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: board_motor_initialize failed: %d\n", ret); + } +#endif + #ifdef CONFIG_SENSORS_MAX6675 ret = board_max6675_initialize(0, 2); if (ret < 0) diff --git a/boards/xtensa/esp32s3/common/include/esp32s3_board_mcpwm.h b/boards/xtensa/esp32s3/common/include/esp32s3_board_mcpwm.h index 75cf53e4262..46e266a90b1 100644 --- a/boards/xtensa/esp32s3/common/include/esp32s3_board_mcpwm.h +++ b/boards/xtensa/esp32s3/common/include/esp32s3_board_mcpwm.h @@ -43,7 +43,28 @@ extern "C" * Public Function Prototypes ****************************************************************************/ -#ifdef CONFIG_ESP_MCPWM_CAPTURE +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32_motor_initialize + * + * Description: + * Initialize MCPWM peripheral for motor control and register the motor + * driver. + * + * Input Parameters: + * None. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP_MCPWM_MOTOR_BDC +int board_motor_initialize(void); +#endif /**************************************************************************** * Name: board_capture_initialize @@ -59,9 +80,10 @@ extern "C" * ****************************************************************************/ +#ifdef CONFIG_ESP_MCPWM_CAPTURE int board_capture_initialize(void); - #endif /* CONFIG_ESP_MCPWM_CAPTURE */ + #undef EXTERN #ifdef __cplusplus } diff --git a/boards/xtensa/esp32s3/common/src/esp32s3_board_mcpwm.c b/boards/xtensa/esp32s3/common/src/esp32s3_board_mcpwm.c index 84be2be059c..0d25b91f0de 100644 --- a/boards/xtensa/esp32s3/common/src/esp32s3_board_mcpwm.c +++ b/boards/xtensa/esp32s3/common/src/esp32s3_board_mcpwm.c @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -39,10 +40,61 @@ * Pre-processor Definitions ****************************************************************************/ +#ifdef CONFIG_ESP_MCPMW_MOTOR_CH0_FAULT +# define MCPWM_FAULT_GPIO CONFIG_ESP_MCPMW_MOTOR_CH0_FAULT_GPIO +#else +# define MCPWM_FAULT_GPIO 0 +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: board_motor_initialize + * + * Description: + * Initialize MCPWM peripheral for motor control and register the motor + * driver. + * + * Input Parameters: + * None. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP_MCPWM_MOTOR_BDC +int board_motor_initialize(void) +{ + int ret; + struct motor_lowerhalf_s *motor; + +#ifdef CONFIG_ESP_MCPWM_MOTOR_CH0 + motor = esp_motor_bdc_initialize(0, + CONFIG_ESP_MCPWM_MOTOR_CH0_PWM_FREQ, + CONFIG_ESP_MCPWM_MOTOR_CH0_PWMA_GPIO, + CONFIG_ESP_MCPWM_MOTOR_CH0_PWMB_GPIO, + MCPWM_FAULT_GPIO); + if (motor == NULL) + { + syslog(LOG_ERR, "ERROR: Failed to start MCPWM BDC Motor: CH0\n"); + return -ENODEV; + } + + ret = motor_register("/dev/motor0", motor); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: motor_register failed: %d\n", ret); + return ret; + } +#endif + + return OK; +} +#endif + /**************************************************************************** * Name: board_capture_initialize * @@ -57,6 +109,7 @@ * ****************************************************************************/ +#ifdef CONFIG_ESP_MCPWM_CAPTURE int board_capture_initialize(void) { int ret; @@ -116,3 +169,4 @@ int board_capture_initialize(void) return OK; } +#endif diff --git a/boards/xtensa/esp32s3/esp32s3-devkit/configs/motor/defconfig b/boards/xtensa/esp32s3/esp32s3-devkit/configs/motor/defconfig new file mode 100644 index 00000000000..98c8e5eb536 --- /dev/null +++ b/boards/xtensa/esp32s3/esp32s3-devkit/configs/motor/defconfig @@ -0,0 +1,49 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_ARCH_LEDS is not set +# CONFIG_NSH_ARGCAT is not set +# CONFIG_NSH_CMDOPT_HEXDUMP is not set +CONFIG_ARCH="xtensa" +CONFIG_ARCH_BOARD="esp32s3-devkit" +CONFIG_ARCH_BOARD_COMMON=y +CONFIG_ARCH_BOARD_ESP32S3_DEVKIT=y +CONFIG_ARCH_CHIP="esp32s3" +CONFIG_ARCH_CHIP_ESP32S3=y +CONFIG_ARCH_CHIP_ESP32S3WROOM1N4=y +CONFIG_ARCH_INTERRUPTSTACK=2048 +CONFIG_ARCH_STACKDUMP=y +CONFIG_ARCH_XTENSA=y +CONFIG_BOARD_LOOPSPERMSEC=16717 +CONFIG_BUILTIN=y +CONFIG_ESP32S3_UART0=y +CONFIG_ESP_MCPWM=y +CONFIG_ESP_MCPWM_MOTOR=y +CONFIG_ESP_MCPWM_MOTOR_BDC=y +CONFIG_ESP_MCPWM_MOTOR_CH0=y +CONFIG_FS_PROCFS=y +CONFIG_HAVE_CXX=y +CONFIG_HAVE_CXXINITIALIZE=y +CONFIG_IDLETHREAD_STACKSIZE=3072 +CONFIG_INIT_ENTRYPOINT="nsh_main" +CONFIG_INTELHEX_BINARY=y +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_LINELEN=64 +CONFIG_NSH_READLINE=y +CONFIG_PREALLOC_TIMERS=4 +CONFIG_RAM_SIZE=114688 +CONFIG_RAM_START=0x20000000 +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_WAITPID=y +CONFIG_START_DAY=6 +CONFIG_START_MONTH=12 +CONFIG_START_YEAR=2011 +CONFIG_SYSLOG_BUFFER=y +CONFIG_SYSTEM_NSH=y +CONFIG_UART0_SERIAL_CONSOLE=y diff --git a/boards/xtensa/esp32s3/esp32s3-devkit/src/esp32s3_bringup.c b/boards/xtensa/esp32s3/esp32s3-devkit/src/esp32s3_bringup.c index eb210bcc6b0..d7a8fc47a4d 100644 --- a/boards/xtensa/esp32s3/esp32s3-devkit/src/esp32s3_bringup.c +++ b/boards/xtensa/esp32s3/esp32s3-devkit/src/esp32s3_bringup.c @@ -482,6 +482,14 @@ int esp32s3_bringup(void) } #endif +#ifdef CONFIG_ESP_MCPWM_MOTOR_BDC + ret = board_motor_initialize(); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: board_motor_initialize failed: %d\n", ret); + } +#endif + /* If we got here then perhaps not all initialization was successful, but * at least enough succeeded to bring-up NSH with perhaps reduced * capabilities.