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.