diff --git a/Documentation/platforms/risc-v/esp32c6/boards/esp32c6-devkitc/index.rst b/Documentation/platforms/risc-v/esp32c6/boards/esp32c6-devkitc/index.rst index fa5363ad3c6..0536d880713 100644 --- a/Documentation/platforms/risc-v/esp32c6/boards/esp32c6-devkitc/index.rst +++ b/Documentation/platforms/risc-v/esp32c6/boards/esp32c6-devkitc/index.rst @@ -105,15 +105,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 18 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 -------- @@ -154,6 +154,18 @@ You can scan for all I2C devices using the following command:: nsh> i2c dev 0x00 0x7f +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 (GPIO21 and GPIO22) 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 GPIO9), which can be used +for quick motor braking. All GPIOs are configurable in ``menuconfig``. + mcuboot_nsh -------------------- diff --git a/Documentation/platforms/risc-v/esp32c6/index.rst b/Documentation/platforms/risc-v/esp32c6/index.rst index 9642de6fcc5..72891f568dd 100644 --- a/Documentation/platforms/risc-v/esp32c6/index.rst +++ b/Documentation/platforms/risc-v/esp32c6/index.rst @@ -170,7 +170,7 @@ I2S No Int. Temp. No LED No LED_PWM Yes -MCPWM Yes (Capture) +MCPWM Yes Pulse Counter No RMT No RNG No diff --git a/arch/risc-v/src/common/espressif/Kconfig b/arch/risc-v/src/common/espressif/Kconfig index 85099a09b29..bc8c68c12c5 100644 --- a/arch/risc-v/src/common/espressif/Kconfig +++ b/arch/risc-v/src/common/espressif/Kconfig @@ -1309,9 +1309,105 @@ config ESPRESSIF_I2CTIMEOMS default 500 endmenu # I2C 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 20 if ESPRESSIF_ESP32C6 + default 10 if ESPRESSIF_ESP32H2 + ---help--- + Output pin assigned to channel 0 PWM output PWM_A. + +config ESP_MCPWM_MOTOR_CH0_PWMB_GPIO + int "Output Pin PWM_B" + default 21 if ESPRESSIF_ESP32C6 + default 11 if ESPRESSIF_ESP32H2 + ---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 9 + ---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 @@ -1372,6 +1468,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 "High Resolution Timer" diff --git a/arch/risc-v/src/common/espressif/esp_mcpwm.c b/arch/risc-v/src/common/espressif/esp_mcpwm.c index e4562e4a95d..e172490f457 100644 --- a/arch/risc-v/src/common/espressif/esp_mcpwm.c +++ b/arch/risc-v/src/common/espressif/esp_mcpwm.c @@ -33,7 +33,12 @@ #include #include #include +#ifdef CONFIG_ESP_MCPWM_CAPTURE #include +#endif +#ifdef CONFIG_ESP_MCPWM_MOTOR +#include +#endif #include "esp_gpio.h" #include "esp_irq.h" @@ -58,23 +63,122 @@ # define MCPWM_DEV_CLK_SOURCE SOC_MOD_CLK_PLL_F160M #endif -#define MCPWM_DEV_CLK_PRESCALE 4 +#define MCPWM_DEV_CLK_PRESCALE 1 #define MCPWM_CAPTURE_DEFAULT_GROUP 0 +#define GPIO_IN_FUNCTION INPUT_FUNCTION_2 +#define GPIO_OUT_FUNCTION OUTPUT_FUNCTION_2 + +#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. + */ +#ifndef CONFIG_ARCH_CHIP_ESP32H2 +# define PEAK_COUNTER 13330 +# define MCPWM_MAX_PWM_OUT_FREQ 3000 +# define MCPWM_MIN_PWM_OUT_FREQ 25 +#else +# define PEAK_COUNTER 9595 +# define MCPWM_MAX_PWM_OUT_FREQ 2500 +# define MCPWM_MIN_PWM_OUT_FREQ 20 +#endif +#endif +#ifdef CONFIG_ESP_MCPMW_MOTOR_CH0_FAULT +# define ESP_MCPMW_MOTOR_FAULT +#endif /**************************************************************************** * Private Types ****************************************************************************/ +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 { mcpwm_hal_init_config_t group; mcpwm_hal_context_t hal; spinlock_t mcpwm_spinlock; - bool initialized; /* MCPWM peripheral and HAL has been initialized */ - bool capture_initialized; /* Capture submodule has been initialized */ + bool initialized; /* MCPWM periph. and HAL has been initialized */ + bool isr_initialized; /* Shared ISR has been initialized */ int group_prescale; }; +#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 */ @@ -85,14 +189,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 */ -}; - /* Lowe-half data structure for a capture channel */ struct mcpwm_cap_channel_lowerhalf_s @@ -120,15 +216,13 @@ struct mcpwm_cap_channel_lowerhalf_s * Private Function Prototypes ****************************************************************************/ +/* General use for MCPWM peripheral */ + static void esp_mcpwm_group_start(void); #ifdef CONFIG_ESP_MCPWM_CAPTURE static int esp_mcpwm_capture_set_gpio( struct mcpwm_cap_channel_lowerhalf_s *lower); -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); /* Lower half methods required by capture driver */ @@ -140,18 +234,109 @@ 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_motor_pwm_config(struct mcpwm_motor_lowerhalf_s *lower); +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_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 ****************************************************************************/ -static struct mcpwm_dev_common_s mcpwm_common = +/* Common MCPWM data structure */ + +static struct mcpwm_dev_common_s g_mcpwm_common = { .group.group_id = MCPWM_CAPTURE_DEFAULT_GROUP, .initialized = false, - .capture_initialized = false, + .isr_initialized = false, .group_prescale = MCPWM_DEV_CLK_PRESCALE, }; +/* 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 the capture driver */ @@ -170,7 +355,7 @@ 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 = &mcpwm_common, + .common = &g_mcpwm_common, .data = &event_data_ch0, .channel_id = MCPWM_CAP_CHANNEL_0, .ready = false, @@ -182,7 +367,7 @@ 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 = &mcpwm_common, + .common = &g_mcpwm_common, .data = &event_data_ch1, .channel_id = MCPWM_CAP_CHANNEL_1, .ready = false, @@ -194,7 +379,7 @@ 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 = &mcpwm_common, + .common = &g_mcpwm_common, .data = &event_data_ch2, .channel_id = MCPWM_CAP_CHANNEL_2, .ready = false, @@ -206,6 +391,1011 @@ static struct mcpwm_cap_channel_lowerhalf_s mcpwm_cap_ch2_lowerhalf = * Private Functions ****************************************************************************/ +/**************************************************************************** + * 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(MCPWM_DEV_CLK_SOURCE, + 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); + + irqstate_t flags; + mcpwm_hal_context_t *hal = &lower->common->hal; + + 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: %lu\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 | GPIO_IN_FUNCTION); + 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], + GPIO_OUT_FUNCTION); + if (ret < 0) + { + mtrerr("Failed configuring PWM_A GPIO\n"); + return ret; + } + + ret = esp_configgpio(lower->generator_pins[MCPWM_GENERATOR_1], + GPIO_OUT_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 * @@ -226,15 +1416,17 @@ static struct mcpwm_cap_channel_lowerhalf_s mcpwm_cap_ch2_lowerhalf = #ifdef CONFIG_ESP_MCPWM_CAPTURE static int esp_capture_start(struct cap_lowerhalf_s *lower) { + DEBUGASSERT(lower != NULL); + struct mcpwm_cap_channel_lowerhalf_s *priv = ( struct mcpwm_cap_channel_lowerhalf_s *)lower; - - DEBUGASSERT(priv != NULL); - + irqstate_t flags; mcpwm_hal_context_t *hal = &priv->common->hal; + flags = spin_lock_irqsave(priv->common->mcpwm_spinlock); /* Enable channel and interruption for rising edge */ + mcpwm_ll_capture_enable_timer(g_mcpwm_common.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), @@ -252,6 +1444,7 @@ static int esp_capture_start(struct cap_lowerhalf_s *lower) priv->enabled = true; priv->ready = false; + spin_unlock_irqrestore(priv->common->mcpwm_spinlock, flags); cpinfo("Channel enabled: %d\n", priv->channel_id); return OK; } @@ -275,21 +1468,24 @@ static int esp_capture_start(struct cap_lowerhalf_s *lower) #ifdef CONFIG_ESP_MCPWM_CAPTURE static int esp_capture_stop(struct cap_lowerhalf_s *lower) { + DEBUGASSERT(lower != NULL); + struct mcpwm_cap_channel_lowerhalf_s *priv = ( struct mcpwm_cap_channel_lowerhalf_s *)lower; - - DEBUGASSERT(priv != NULL); - + irqstate_t flags; mcpwm_hal_context_t *hal = &priv->common->hal; + flags = spin_lock_irqsave(priv->common->mcpwm_spinlock); /* Disable channel and interrupts */ + mcpwm_ll_capture_enable_timer(g_mcpwm_common.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), false); priv->enabled = false; + spin_unlock_irqrestore(priv->common->mcpwm_spinlock, flags); cpinfo("Channel disabled: %d\n", priv->channel_id); return OK; } @@ -374,18 +1570,17 @@ static int esp_capture_getfreq(struct cap_lowerhalf_s *lower, static void esp_mcpwm_group_start(void) { - mcpwm_hal_context_t *hal = &mcpwm_common.hal; + mcpwm_hal_context_t *hal = &g_mcpwm_common.hal; /* HAL and MCPWM Initialization */ periph_module_enable(PERIPH_MCPWM0_MODULE); - mcpwm_hal_init(hal, &mcpwm_common.group); - mcpwm_hal_timer_reset(hal, 0); + mcpwm_hal_init(hal, &g_mcpwm_common.group); mcpwm_ll_group_set_clock_source(hal->dev, MCPWM_DEV_CLK_SOURCE); - mcpwm_ll_group_set_clock_prescale(hal->dev, 4); + mcpwm_ll_group_set_clock_prescale(hal->dev, g_mcpwm_common.group_prescale); mcpwm_ll_group_enable_clock(hal->dev, true); - mcpwm_common.initialized = true; + g_mcpwm_common.initialized = true; } /**************************************************************************** @@ -428,7 +1623,7 @@ static int esp_mcpwm_capture_set_gpio( #endif /**************************************************************************** - * Name: esp_mcpwm_capture_isr_register + * Name: esp_mcpwm_isr_register * * Description: * Registers a callback function for a channel interrupt request. @@ -443,8 +1638,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 *), +#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; @@ -460,8 +1655,8 @@ static int esp_mcpwm_capture_isr_register(int (*fn)(int, void *, void *), } ret = irq_attach(ESP_IRQ_MCPWM0, - mcpwm_capture_driver_isr_default, - &mcpwm_common); + fn, + &g_mcpwm_common); if (ret < 0) { cperr("Couldn't attach IRQ to handler.\n"); @@ -476,7 +1671,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. @@ -499,21 +1694,28 @@ 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; - irqstate_t flags; uint32_t cap_value; - uint32_t status; mcpwm_capture_edge_t cap_edge; +#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 capture interrupt for all 3 cap channels */ + if (status & MCPWM_LL_EVENT_CAPTURE(MCPWM_CAP_CHANNEL_0)) { #ifdef CONFIG_ESP_MCPWM_CAPTURE_CH0 @@ -532,9 +1734,44 @@ 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); + mtrinfo("enter: %lu\n", status); + } + + 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); + mtrinfo("exit: %lu\n", status); + } +#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, @@ -598,9 +1835,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 @@ -609,6 +1846,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; + uint32_t ref_clock; + int ret; + + 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("Ch %d initialized. GPIO: PWM_A: %d | PWM_B: %d | Freq: %lu\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 * @@ -636,17 +1962,16 @@ struct cap_lowerhalf_s *esp_mcpwm_capture_initialize(int channel, int pin) * and MCPWM Capture group. */ - if (!mcpwm_common.initialized) + if (!g_mcpwm_common.initialized) { esp_mcpwm_group_start(); } - if (!mcpwm_common.capture_initialized) + if (!g_mcpwm_common.isr_initialized) { - mcpwm_ll_capture_enable_timer(mcpwm_common.hal.dev, true); - esp_mcpwm_capture_isr_register(mcpwm_capture_driver_isr_default, - &mcpwm_common); - mcpwm_common.capture_initialized = true; + esp_mcpwm_isr_register(mcpwm_driver_isr_default, + &g_mcpwm_common); + g_mcpwm_common.isr_initialized = true; } switch (channel) @@ -678,7 +2003,7 @@ struct cap_lowerhalf_s *esp_mcpwm_capture_initialize(int channel, int pin) /* Set the clock to be used when calculating frequency */ lower->gpio_pin = pin; - lower->clock = group_clock / MCPWM_DEV_CLK_PRESCALE; + lower->clock = group_clock / g_mcpwm_common.group_prescale; /* Configure GPIO pin */ diff --git a/arch/risc-v/src/common/espressif/esp_mcpwm.h b/arch/risc-v/src/common/espressif/esp_mcpwm.h index 946f7310d76..6177905d63c 100644 --- a/arch/risc-v/src/common/espressif/esp_mcpwm.h +++ b/arch/risc-v/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/boards/risc-v/esp32c6/common/include/esp_board_mcpwm.h b/boards/risc-v/esp32c6/common/include/esp_board_mcpwm.h index 421fbffd3a5..191deab98a3 100644 --- a/boards/risc-v/esp32c6/common/include/esp_board_mcpwm.h +++ b/boards/risc-v/esp32c6/common/include/esp_board_mcpwm.h @@ -43,7 +43,24 @@ extern "C" * Public Function Prototypes ****************************************************************************/ -#ifdef CONFIG_ESP_MCPWM_CAPTURE +/**************************************************************************** + * 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 +int board_motor_initialize(void); +#endif /**************************************************************************** * Name: board_capture_initialize @@ -59,9 +76,10 @@ extern "C" * ****************************************************************************/ +#ifdef CONFIG_ESP_MCPWM_CAPTURE int board_capture_initialize(void); +#endif -#endif /* CONFIG_ESP_MCPWM_CAPTURE */ #undef EXTERN #ifdef __cplusplus } diff --git a/boards/risc-v/esp32c6/common/src/esp_board_mcpwm.c b/boards/risc-v/esp32c6/common/src/esp_board_mcpwm.c index 5229de617c4..43a059855ad 100644 --- a/boards/risc-v/esp32c6/common/src/esp_board_mcpwm.c +++ b/boards/risc-v/esp32c6/common/src/esp_board_mcpwm.c @@ -29,7 +29,12 @@ #include #include +#ifdef CONFIG_MOTOR +#include +#endif +#ifdef CONFIG_CAPTURE #include +#endif #include @@ -39,10 +44,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 +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 +113,7 @@ * ****************************************************************************/ +#ifdef CONFIG_ESP_MCPWM_CAPTURE int board_capture_initialize(void) { int ret; @@ -112,3 +169,4 @@ int board_capture_initialize(void) return OK; } +#endif diff --git a/boards/risc-v/esp32c6/esp32c6-devkitc/configs/motor/defconfig b/boards/risc-v/esp32c6/esp32c6-devkitc/configs/motor/defconfig new file mode 100644 index 00000000000..bf70eb967e7 --- /dev/null +++ b/boards/risc-v/esp32c6/esp32c6-devkitc/configs/motor/defconfig @@ -0,0 +1,52 @@ +# +# 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_NSH_ARGCAT is not set +# CONFIG_NSH_CMDOPT_HEXDUMP is not set +CONFIG_ARCH="risc-v" +CONFIG_ARCH_BOARD="esp32c6-devkitc" +CONFIG_ARCH_BOARD_COMMON=y +CONFIG_ARCH_BOARD_ESP32C6_DEVKITC=y +CONFIG_ARCH_CHIP="esp32c6" +CONFIG_ARCH_CHIP_ESP32C6=y +CONFIG_ARCH_CHIP_ESP32C6WROOM1=y +CONFIG_ARCH_INTERRUPTSTACK=2048 +CONFIG_ARCH_RISCV=y +CONFIG_ARCH_STACKDUMP=y +CONFIG_BOARDCTL_RESET=y +CONFIG_BOARD_LOOPSPERMSEC=15000 +CONFIG_BUILTIN=y +CONFIG_DEV_ZERO=y +CONFIG_ESPRESSIF_ESP32C6=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_IDLETHREAD_STACKSIZE=2048 +CONFIG_INIT_ENTRYPOINT="nsh_main" +CONFIG_INTELHEX_BINARY=y +CONFIG_LIBC_PERROR_STDOUT=y +CONFIG_LIBC_STRERROR=y +CONFIG_NFILE_DESCRIPTORS_PER_BLOCK=6 +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_READLINE=y +CONFIG_NSH_STRERROR=y +CONFIG_PREALLOC_TIMERS=0 +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_BACKTRACE=y +CONFIG_SCHED_WAITPID=y +CONFIG_START_DAY=29 +CONFIG_START_MONTH=11 +CONFIG_START_YEAR=2019 +CONFIG_SYSTEM_DUMPSTACK=y +CONFIG_SYSTEM_NSH=y +CONFIG_TESTING_GETPRIME=y +CONFIG_TESTING_OSTEST=y +CONFIG_UART0_SERIAL_CONSOLE=y diff --git a/boards/risc-v/esp32c6/esp32c6-devkitc/src/esp32c6_bringup.c b/boards/risc-v/esp32c6/esp32c6-devkitc/src/esp32c6_bringup.c index 96aadb503ba..c9384cc1627 100644 --- a/boards/risc-v/esp32c6/esp32c6-devkitc/src/esp32c6_bringup.c +++ b/boards/risc-v/esp32c6/esp32c6-devkitc/src/esp32c6_bringup.c @@ -348,6 +348,14 @@ int esp_bringup(void) } #endif +#ifdef CONFIG_ESP_MCPWM_MOTOR + 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. diff --git a/boards/risc-v/esp32h2/common/include/esp_board_mcpwm.h b/boards/risc-v/esp32h2/common/include/esp_board_mcpwm.h index 3572dfe2ce5..70cc0d2f886 100644 --- a/boards/risc-v/esp32h2/common/include/esp_board_mcpwm.h +++ b/boards/risc-v/esp32h2/common/include/esp_board_mcpwm.h @@ -43,7 +43,24 @@ extern "C" * Public Function Prototypes ****************************************************************************/ -#ifdef CONFIG_ESP_MCPWM_CAPTURE +/**************************************************************************** + * 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 +int board_motor_initialize(void); +#endif /**************************************************************************** * Name: board_capture_initialize @@ -59,9 +76,10 @@ extern "C" * ****************************************************************************/ +#ifdef CONFIG_ESP_MCPWM_CAPTURE int board_capture_initialize(void); +#endif -#endif /* CONFIG_ESP_MCPWM_CAPTURE */ #undef EXTERN #ifdef __cplusplus } diff --git a/boards/risc-v/esp32h2/common/src/esp_board_mcpwm.c b/boards/risc-v/esp32h2/common/src/esp_board_mcpwm.c index e5a080af079..3a24912107a 100644 --- a/boards/risc-v/esp32h2/common/src/esp_board_mcpwm.c +++ b/boards/risc-v/esp32h2/common/src/esp_board_mcpwm.c @@ -29,7 +29,12 @@ #include #include +#ifdef CONFIG_MOTOR +#include +#endif +#ifdef CONFIG_CAPTURE #include +#endif #include @@ -39,10 +44,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 +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 +113,7 @@ * ****************************************************************************/ +#ifdef CONFIG_ESP_MCPWM_CAPTURE int board_capture_initialize(void) { int ret; @@ -112,3 +169,4 @@ int board_capture_initialize(void) return OK; } +#endif diff --git a/boards/risc-v/esp32h2/esp32h2-devkit/configs/motor/defconfig b/boards/risc-v/esp32h2/esp32h2-devkit/configs/motor/defconfig new file mode 100644 index 00000000000..b83654b5877 --- /dev/null +++ b/boards/risc-v/esp32h2/esp32h2-devkit/configs/motor/defconfig @@ -0,0 +1,51 @@ +# +# 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_NSH_ARGCAT is not set +# CONFIG_NSH_CMDOPT_HEXDUMP is not set +CONFIG_ARCH="risc-v" +CONFIG_ARCH_BOARD="esp32h2-devkit" +CONFIG_ARCH_BOARD_COMMON=y +CONFIG_ARCH_BOARD_ESP32H2_DEVKIT=y +CONFIG_ARCH_CHIP="esp32h2" +CONFIG_ARCH_CHIP_ESP32H2=y +CONFIG_ARCH_INTERRUPTSTACK=2048 +CONFIG_ARCH_RISCV=y +CONFIG_ARCH_STACKDUMP=y +CONFIG_BOARDCTL_RESET=y +CONFIG_BOARD_LOOPSPERMSEC=15000 +CONFIG_BUILTIN=y +CONFIG_DEV_ZERO=y +CONFIG_ESPRESSIF_ESP32H2=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_IDLETHREAD_STACKSIZE=2048 +CONFIG_INIT_ENTRYPOINT="nsh_main" +CONFIG_INTELHEX_BINARY=y +CONFIG_LIBC_PERROR_STDOUT=y +CONFIG_LIBC_STRERROR=y +CONFIG_NFILE_DESCRIPTORS_PER_BLOCK=6 +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_READLINE=y +CONFIG_NSH_STRERROR=y +CONFIG_PREALLOC_TIMERS=0 +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_BACKTRACE=y +CONFIG_SCHED_WAITPID=y +CONFIG_START_DAY=29 +CONFIG_START_MONTH=11 +CONFIG_START_YEAR=2019 +CONFIG_SYSTEM_DUMPSTACK=y +CONFIG_SYSTEM_NSH=y +CONFIG_TESTING_GETPRIME=y +CONFIG_TESTING_OSTEST=y +CONFIG_UART0_SERIAL_CONSOLE=y diff --git a/boards/risc-v/esp32h2/esp32h2-devkit/src/esp32h2_bringup.c b/boards/risc-v/esp32h2/esp32h2-devkit/src/esp32h2_bringup.c index 499a17b5b52..f6afd1f4d6c 100644 --- a/boards/risc-v/esp32h2/esp32h2-devkit/src/esp32h2_bringup.c +++ b/boards/risc-v/esp32h2/esp32h2-devkit/src/esp32h2_bringup.c @@ -315,6 +315,14 @@ int esp_bringup(void) } #endif +#ifdef CONFIG_ESP_MCPWM_MOTOR + 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.