You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
582 lines
21 KiB
582 lines
21 KiB
/* |
|
* Copyright (c) 2024-2025 Espressif Systems (Shanghai) Co., Ltd. |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#define DT_DRV_COMPAT espressif_esp32_mcpwm |
|
|
|
#include <hal/mcpwm_hal.h> |
|
#include <hal/mcpwm_ll.h> |
|
#include <driver/mcpwm.h> |
|
|
|
#include <soc.h> |
|
#include <errno.h> |
|
#include <string.h> |
|
#include <zephyr/drivers/pwm.h> |
|
#include <zephyr/kernel.h> |
|
#include <zephyr/drivers/pinctrl.h> |
|
#include <zephyr/drivers/clock_control.h> |
|
#include <esp_clk_tree.h> |
|
#ifdef CONFIG_PWM_CAPTURE |
|
#include <zephyr/drivers/interrupt_controller/intc_esp32.h> |
|
#endif /* CONFIG_PWM_CAPTURE */ |
|
#include <zephyr/logging/log.h> |
|
LOG_MODULE_REGISTER(mcpwm_esp32, CONFIG_PWM_LOG_LEVEL); |
|
|
|
#ifdef CONFIG_PWM_CAPTURE |
|
#define SKIP_IRQ_NUM 4U |
|
#define CAP_INT_MASK 7U |
|
#define CAP_INT_BASE_BIT 27U |
|
#define MCPWM_CHANNEL_NUM 8U |
|
#define CAPTURE_CHANNEL_IDX 6U |
|
#else |
|
#define MCPWM_CHANNEL_NUM 6U |
|
#endif /* CONFIG_PWM_CAPTURE */ |
|
|
|
struct mcpwm_esp32_data { |
|
mcpwm_hal_context_t hal; |
|
uint32_t mcpwm_clk_hz; |
|
struct k_sem cmd_sem; |
|
}; |
|
|
|
#ifdef CONFIG_PWM_CAPTURE |
|
struct capture_data { |
|
uint32_t value; |
|
mcpwm_capture_on_edge_t edge; |
|
}; |
|
|
|
struct mcpwm_esp32_capture_config { |
|
uint8_t capture_signal; |
|
pwm_capture_callback_handler_t callback; |
|
void *user_data; |
|
uint32_t period; |
|
uint32_t pulse; |
|
uint8_t skip_irq; |
|
bool capture_period; |
|
bool capture_pulse; |
|
bool continuous; |
|
struct capture_data capture_data[SKIP_IRQ_NUM]; |
|
}; |
|
#endif /* CONFIG_PWM_CAPTURE */ |
|
|
|
struct mcpwm_esp32_channel_config { |
|
uint8_t idx; |
|
uint8_t timer_id; |
|
uint8_t operator_id; |
|
uint8_t generator_id; |
|
uint32_t freq; |
|
uint32_t duty; |
|
uint8_t prescale; |
|
bool inverted; |
|
#ifdef CONFIG_PWM_CAPTURE |
|
struct mcpwm_esp32_capture_config capture; |
|
#endif /* CONFIG_PWM_CAPTURE */ |
|
}; |
|
|
|
struct mcpwm_esp32_config { |
|
const uint8_t index; |
|
const struct pinctrl_dev_config *pincfg; |
|
const struct device *clock_dev; |
|
const clock_control_subsys_t clock_subsys; |
|
uint8_t prescale; |
|
uint8_t prescale_timer0; |
|
uint8_t prescale_timer1; |
|
uint8_t prescale_timer2; |
|
struct mcpwm_esp32_channel_config channel_config[MCPWM_CHANNEL_NUM]; |
|
#ifdef CONFIG_PWM_CAPTURE |
|
int (*irq_config_func)(const struct device *dev); |
|
#endif /* CONFIG_PWM_CAPTURE */ |
|
}; |
|
|
|
static void mcpwm_esp32_duty_set(const struct device *dev, |
|
struct mcpwm_esp32_channel_config *channel) |
|
{ |
|
struct mcpwm_esp32_config *config = (struct mcpwm_esp32_config *)dev->config; |
|
struct mcpwm_esp32_data *data = (struct mcpwm_esp32_data *const)(dev)->data; |
|
mcpwm_duty_type_t duty_type; |
|
uint32_t set_duty; |
|
|
|
if (channel->inverted) { |
|
duty_type = channel->duty == 0 ? |
|
MCPWM_HAL_GENERATOR_MODE_FORCE_HIGH : channel->duty == 100 ? |
|
MCPWM_HAL_GENERATOR_MODE_FORCE_LOW : MCPWM_DUTY_MODE_1; |
|
} else { |
|
duty_type = channel->duty == 0 ? |
|
MCPWM_HAL_GENERATOR_MODE_FORCE_LOW : channel->duty == 100 ? |
|
MCPWM_HAL_GENERATOR_MODE_FORCE_HIGH : MCPWM_DUTY_MODE_0; |
|
} |
|
|
|
uint32_t timer_clk_hz = data->mcpwm_clk_hz / config->prescale / channel->prescale; |
|
|
|
set_duty = (timer_clk_hz / channel->freq) * channel->duty / 100; |
|
mcpwm_ll_operator_connect_timer(data->hal.dev, channel->operator_id, channel->timer_id); |
|
mcpwm_ll_operator_set_compare_value(data->hal.dev, channel->operator_id, |
|
channel->generator_id, set_duty); |
|
mcpwm_ll_operator_enable_update_compare_on_tez(data->hal.dev, channel->operator_id, |
|
channel->generator_id, true); |
|
|
|
if (duty_type == MCPWM_DUTY_MODE_0) { |
|
mcpwm_ll_generator_set_action_on_timer_event( |
|
data->hal.dev, channel->operator_id, channel->generator_id, |
|
MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH); |
|
mcpwm_ll_generator_set_action_on_timer_event( |
|
data->hal.dev, channel->operator_id, channel->generator_id, |
|
MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_FULL, MCPWM_GEN_ACTION_KEEP); |
|
mcpwm_ll_generator_set_action_on_compare_event( |
|
data->hal.dev, channel->operator_id, channel->generator_id, |
|
MCPWM_TIMER_DIRECTION_UP, channel->generator_id, MCPWM_ACTION_FORCE_LOW); |
|
} else if (duty_type == MCPWM_DUTY_MODE_1) { |
|
mcpwm_ll_generator_set_action_on_timer_event( |
|
data->hal.dev, channel->operator_id, channel->generator_id, |
|
MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_LOW); |
|
mcpwm_ll_generator_set_action_on_timer_event( |
|
data->hal.dev, channel->operator_id, channel->generator_id, |
|
MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_FULL, MCPWM_ACTION_NO_CHANGE); |
|
mcpwm_ll_generator_set_action_on_compare_event( |
|
data->hal.dev, channel->operator_id, channel->generator_id, |
|
MCPWM_TIMER_DIRECTION_UP, channel->generator_id, MCPWM_ACTION_FORCE_HIGH); |
|
} else if (duty_type == MCPWM_HAL_GENERATOR_MODE_FORCE_LOW) { |
|
mcpwm_ll_generator_set_action_on_timer_event( |
|
data->hal.dev, channel->operator_id, channel->generator_id, |
|
MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_ACTION_FORCE_LOW); |
|
mcpwm_ll_generator_set_action_on_timer_event( |
|
data->hal.dev, channel->operator_id, channel->generator_id, |
|
MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_FULL, MCPWM_ACTION_FORCE_LOW); |
|
mcpwm_ll_generator_set_action_on_compare_event( |
|
data->hal.dev, channel->operator_id, channel->generator_id, |
|
MCPWM_TIMER_DIRECTION_UP, channel->generator_id, MCPWM_ACTION_FORCE_LOW); |
|
} else if (duty_type == MCPWM_HAL_GENERATOR_MODE_FORCE_HIGH) { |
|
mcpwm_ll_generator_set_action_on_timer_event( |
|
data->hal.dev, channel->operator_id, channel->generator_id, |
|
MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_ACTION_FORCE_HIGH); |
|
mcpwm_ll_generator_set_action_on_timer_event( |
|
data->hal.dev, channel->operator_id, channel->generator_id, |
|
MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_FULL, MCPWM_ACTION_FORCE_HIGH); |
|
mcpwm_ll_generator_set_action_on_compare_event( |
|
data->hal.dev, channel->operator_id, channel->generator_id, |
|
MCPWM_TIMER_DIRECTION_UP, channel->generator_id, MCPWM_ACTION_FORCE_HIGH); |
|
} |
|
} |
|
|
|
static int mcpwm_esp32_configure_pinctrl(const struct device *dev) |
|
{ |
|
int ret; |
|
struct mcpwm_esp32_config *config = (struct mcpwm_esp32_config *)dev->config; |
|
|
|
ret = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); |
|
if (ret < 0) { |
|
LOG_ERR("PWM pinctrl setup failed (%d)", ret); |
|
return ret; |
|
} |
|
return 0; |
|
} |
|
|
|
static int mcpwm_esp32_timer_set(const struct device *dev, |
|
struct mcpwm_esp32_channel_config *channel) |
|
{ |
|
struct mcpwm_esp32_config *config = (struct mcpwm_esp32_config *)dev->config; |
|
struct mcpwm_esp32_data *data = (struct mcpwm_esp32_data *const)(dev)->data; |
|
|
|
__ASSERT_NO_MSG(channel->freq > 0); |
|
|
|
mcpwm_ll_timer_set_clock_prescale(data->hal.dev, channel->timer_id, channel->prescale); |
|
mcpwm_ll_timer_set_count_mode(data->hal.dev, channel->timer_id, MCPWM_TIMER_COUNT_MODE_UP); |
|
mcpwm_ll_timer_update_period_at_once(data->hal.dev, channel->timer_id); |
|
|
|
uint32_t timer_clk_hz = data->mcpwm_clk_hz / config->prescale / channel->prescale; |
|
|
|
mcpwm_ll_timer_set_peak(data->hal.dev, channel->timer_id, timer_clk_hz / channel->freq, |
|
false); |
|
|
|
return 0; |
|
} |
|
|
|
static int mcpwm_esp32_get_cycles_per_sec(const struct device *dev, uint32_t channel_idx, |
|
uint64_t *cycles) |
|
{ |
|
struct mcpwm_esp32_config *config = (struct mcpwm_esp32_config *)dev->config; |
|
struct mcpwm_esp32_channel_config *channel = &config->channel_config[channel_idx]; |
|
struct mcpwm_esp32_data *data = (struct mcpwm_esp32_data *const)(dev)->data; |
|
|
|
if (!channel) { |
|
LOG_ERR("Error getting channel %d", channel_idx); |
|
return -EINVAL; |
|
} |
|
|
|
#ifdef CONFIG_PWM_CAPTURE |
|
if (channel->idx >= CAPTURE_CHANNEL_IDX) { |
|
#if SOC_MCPWM_CAPTURE_CLK_FROM_GROUP |
|
/* Capture prescaler is disabled by default (equals 1) */ |
|
*cycles = (uint64_t)data->mcpwm_clk_hz / (config->prescale + 1) / 1; |
|
#else |
|
*cycles = (uint64_t)APB_CLK_FREQ; |
|
#endif |
|
return 0; |
|
} |
|
#endif /* CONFIG_PWM_CAPTURE */ |
|
|
|
*cycles = |
|
(uint64_t)data->mcpwm_clk_hz / (config->prescale + 1) / (channel->prescale + 1); |
|
|
|
return 0; |
|
} |
|
|
|
static int mcpwm_esp32_set_cycles(const struct device *dev, uint32_t channel_idx, |
|
uint32_t period_cycles, uint32_t pulse_cycles, pwm_flags_t flags) |
|
{ |
|
int ret = 0; |
|
uint64_t clk_freq; |
|
struct mcpwm_esp32_config *config = (struct mcpwm_esp32_config *)dev->config; |
|
struct mcpwm_esp32_data *data = (struct mcpwm_esp32_data *const)(dev)->data; |
|
struct mcpwm_esp32_channel_config *channel = &config->channel_config[channel_idx]; |
|
|
|
if (!channel) { |
|
LOG_ERR("Error getting channel %d", channel_idx); |
|
return -EINVAL; |
|
} |
|
|
|
/* Update PWM frequency according to period_cycles */ |
|
mcpwm_esp32_get_cycles_per_sec(dev, channel_idx, &clk_freq); |
|
|
|
channel->freq = (uint32_t)(clk_freq / period_cycles); |
|
if (!channel->freq) { |
|
return -EINVAL; |
|
} |
|
|
|
k_sem_take(&data->cmd_sem, K_FOREVER); |
|
|
|
ret = mcpwm_esp32_timer_set(dev, channel); |
|
if (ret < 0) { |
|
k_sem_give(&data->cmd_sem); |
|
return ret; |
|
} |
|
|
|
double duty_cycle = (double)pulse_cycles * 100 / (double)period_cycles; |
|
|
|
channel->duty = (uint32_t)duty_cycle; |
|
|
|
channel->inverted = (flags & PWM_POLARITY_INVERTED); |
|
|
|
mcpwm_esp32_duty_set(dev, channel); |
|
|
|
ret = mcpwm_esp32_configure_pinctrl(dev); |
|
if (ret < 0) { |
|
k_sem_give(&data->cmd_sem); |
|
return ret; |
|
} |
|
|
|
mcpwm_ll_timer_set_start_stop_command(data->hal.dev, channel->timer_id, |
|
MCPWM_TIMER_START_NO_STOP); |
|
|
|
k_sem_give(&data->cmd_sem); |
|
|
|
return ret; |
|
} |
|
|
|
#ifdef CONFIG_PWM_CAPTURE |
|
static int mcpwm_esp32_configure_capture(const struct device *dev, uint32_t channel_idx, |
|
pwm_flags_t flags, pwm_capture_callback_handler_t cb, |
|
void *user_data) |
|
{ |
|
struct mcpwm_esp32_config *config = (struct mcpwm_esp32_config *)dev->config; |
|
struct mcpwm_esp32_data *data = (struct mcpwm_esp32_data *const)(dev)->data; |
|
struct mcpwm_esp32_channel_config *channel = &config->channel_config[channel_idx]; |
|
struct mcpwm_esp32_capture_config *capture = &channel->capture; |
|
|
|
if (!channel) { |
|
LOG_ERR("Error getting channel %d", channel_idx); |
|
return -EINVAL; |
|
} |
|
|
|
if ((channel->idx < CAPTURE_CHANNEL_IDX) || (channel->idx > CAPTURE_CHANNEL_IDX + 2)) { |
|
LOG_ERR("PWM capture only supported on channels 6, 7 and 8"); |
|
return -EINVAL; |
|
} |
|
|
|
if (data->hal.dev->cap_chn_cfg[capture->capture_signal].capn_en) { |
|
LOG_ERR("PWM Capture already in progress"); |
|
return -EBUSY; |
|
} |
|
|
|
if (!(flags & PWM_CAPTURE_TYPE_MASK)) { |
|
LOG_ERR("No PWM capture type specified"); |
|
return -EINVAL; |
|
} |
|
|
|
channel->inverted = (flags & PWM_POLARITY_INVERTED); |
|
capture->capture_signal = channel->idx - CAPTURE_CHANNEL_IDX; |
|
capture->callback = cb; |
|
capture->user_data = user_data; |
|
capture->capture_period = (flags & PWM_CAPTURE_TYPE_PERIOD); |
|
capture->capture_pulse = (flags & PWM_CAPTURE_TYPE_PULSE); |
|
capture->continuous = (flags & PWM_CAPTURE_MODE_CONTINUOUS); |
|
|
|
return 0; |
|
} |
|
|
|
static int mcpwm_esp32_disable_capture(const struct device *dev, uint32_t channel_idx) |
|
{ |
|
struct mcpwm_esp32_config *config = (struct mcpwm_esp32_config *)dev->config; |
|
struct mcpwm_esp32_data *data = (struct mcpwm_esp32_data *const)(dev)->data; |
|
struct mcpwm_esp32_channel_config *channel = &config->channel_config[channel_idx]; |
|
struct mcpwm_esp32_capture_config *capture = &channel->capture; |
|
|
|
if (!channel) { |
|
LOG_ERR("Error getting channel %d", channel_idx); |
|
return -EINVAL; |
|
} |
|
|
|
if ((channel->idx < CAPTURE_CHANNEL_IDX) || (channel->idx > CAPTURE_CHANNEL_IDX + 2)) { |
|
LOG_ERR("PWM capture only supported on channels 6, 7 and 8"); |
|
return -EINVAL; |
|
} |
|
|
|
mcpwm_ll_capture_enable_channel(data->hal.dev, capture->capture_signal, false); |
|
mcpwm_ll_intr_enable(data->hal.dev, MCPWM_LL_EVENT_CAPTURE(capture->capture_signal), false); |
|
|
|
return 0; |
|
} |
|
|
|
static int mcpwm_esp32_enable_capture(const struct device *dev, uint32_t channel_idx) |
|
{ |
|
struct mcpwm_esp32_config *config = (struct mcpwm_esp32_config *)dev->config; |
|
struct mcpwm_esp32_data *data = (struct mcpwm_esp32_data *const)(dev)->data; |
|
struct mcpwm_esp32_channel_config *channel = &config->channel_config[channel_idx]; |
|
struct mcpwm_esp32_capture_config *capture = &channel->capture; |
|
|
|
if (!channel) { |
|
LOG_ERR("Error getting channel %d", channel_idx); |
|
return -EINVAL; |
|
} |
|
|
|
if (!capture->callback) { |
|
LOG_ERR("Capture not configured"); |
|
return -EINVAL; |
|
} |
|
|
|
if ((channel->idx < CAPTURE_CHANNEL_IDX) || (channel->idx > CAPTURE_CHANNEL_IDX + 2)) { |
|
LOG_ERR("PWM capture only supported on channels 6, 7 and 8"); |
|
return -EINVAL; |
|
} |
|
|
|
if (data->hal.dev->cap_chn_cfg[capture->capture_signal].capn_en) { |
|
LOG_ERR("PWM Capture already in progress"); |
|
return -EBUSY; |
|
} |
|
|
|
/** |
|
* Capture prescale is different from other modules as it is applied to the input |
|
* signal, not the timer source. It is disabled by default. |
|
*/ |
|
mcpwm_capture_config_t cap_conf = { |
|
.cap_edge = MCPWM_BOTH_EDGE, |
|
.cap_prescale = 1, |
|
}; |
|
|
|
mcpwm_ll_group_set_clock_prescale(data->hal.dev, config->prescale); |
|
mcpwm_ll_group_enable_shadow_mode(data->hal.dev); |
|
mcpwm_ll_group_flush_shadow(data->hal.dev); |
|
|
|
mcpwm_ll_capture_enable_timer(data->hal.dev, true); |
|
mcpwm_ll_capture_enable_channel(data->hal.dev, capture->capture_signal, true); |
|
mcpwm_ll_capture_enable_negedge(data->hal.dev, capture->capture_signal, |
|
cap_conf.cap_edge & MCPWM_NEG_EDGE); |
|
mcpwm_ll_capture_enable_posedge(data->hal.dev, capture->capture_signal, |
|
cap_conf.cap_edge & MCPWM_POS_EDGE); |
|
mcpwm_ll_capture_set_prescale(data->hal.dev, capture->capture_signal, |
|
cap_conf.cap_prescale); |
|
|
|
mcpwm_ll_intr_enable(data->hal.dev, MCPWM_LL_EVENT_CAPTURE(capture->capture_signal), true); |
|
mcpwm_ll_intr_clear_status(data->hal.dev, MCPWM_LL_EVENT_CAPTURE(capture->capture_signal)); |
|
|
|
capture->skip_irq = 0; |
|
|
|
return 0; |
|
} |
|
#endif /* CONFIG_PWM_CAPTURE */ |
|
|
|
static void channel_init(const struct device *dev) |
|
{ |
|
struct mcpwm_esp32_config *config = (struct mcpwm_esp32_config *)dev->config; |
|
struct mcpwm_esp32_channel_config *channel; |
|
|
|
for (uint8_t i = 0; i < MCPWM_CHANNEL_NUM; i++) { |
|
channel = &config->channel_config[i]; |
|
channel->idx = i; |
|
channel->timer_id = i < 2 ? 0 : i < 4 ? 1 : 2; |
|
channel->operator_id = i < 2 ? 0 : i < 4 ? 1 : 2; |
|
channel->generator_id = i % 2 ? 1 : 0; |
|
channel->prescale = i < 2 ? config->prescale_timer0 |
|
: i < 4 ? config->prescale_timer1 |
|
: config->prescale_timer2; |
|
} |
|
} |
|
|
|
int mcpwm_esp32_init(const struct device *dev) |
|
{ |
|
int ret; |
|
struct mcpwm_esp32_config *config = (struct mcpwm_esp32_config *)dev->config; |
|
struct mcpwm_esp32_data *data = (struct mcpwm_esp32_data *const)(dev)->data; |
|
|
|
if (!device_is_ready(config->clock_dev)) { |
|
LOG_ERR("clock control device not ready"); |
|
return -ENODEV; |
|
} |
|
|
|
mcpwm_ll_group_set_clock_source(data->hal.dev, MCPWM_TIMER_CLK_SRC_DEFAULT); |
|
esp_clk_tree_src_get_freq_hz(MCPWM_TIMER_CLK_SRC_DEFAULT, |
|
ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &data->mcpwm_clk_hz); |
|
|
|
/* Enable peripheral */ |
|
ret = clock_control_on(config->clock_dev, config->clock_subsys); |
|
if (ret < 0) { |
|
LOG_ERR("Could not initialize clock (%d)", ret); |
|
return ret; |
|
} |
|
|
|
channel_init(dev); |
|
|
|
mcpwm_ll_group_set_clock_prescale(data->hal.dev, config->prescale); |
|
mcpwm_ll_group_enable_shadow_mode(data->hal.dev); |
|
mcpwm_ll_group_flush_shadow(data->hal.dev); |
|
|
|
#ifdef CONFIG_PWM_CAPTURE |
|
ret = config->irq_config_func(dev); |
|
|
|
if (ret != 0) { |
|
LOG_ERR("could not allocate interrupt (err %d)", ret); |
|
} |
|
#endif /* CONFIG_PWM_CAPTURE */ |
|
return ret; |
|
} |
|
|
|
#ifdef CONFIG_PWM_CAPTURE |
|
static void IRAM_ATTR mcpwm_esp32_isr(const struct device *dev) |
|
{ |
|
struct mcpwm_esp32_config *config = (struct mcpwm_esp32_config *)dev->config; |
|
struct mcpwm_esp32_data *data = (struct mcpwm_esp32_data *const)(dev)->data; |
|
struct mcpwm_esp32_channel_config *channel; |
|
struct mcpwm_esp32_capture_config *capture; |
|
uint32_t mcpwm_intr_status, mcpwm_cap_intr_status; |
|
uint8_t cap_id; |
|
|
|
mcpwm_intr_status = mcpwm_ll_intr_get_status(data->hal.dev); |
|
mcpwm_cap_intr_status = (mcpwm_intr_status >> CAP_INT_BASE_BIT) & CAP_INT_MASK; |
|
|
|
if (!mcpwm_cap_intr_status) { |
|
return; |
|
} |
|
|
|
cap_id = __builtin_ctz(mcpwm_cap_intr_status); |
|
|
|
mcpwm_ll_intr_clear_status(data->hal.dev, |
|
mcpwm_intr_status & MCPWM_LL_EVENT_CAPTURE(cap_id)); |
|
|
|
channel = &config->channel_config[CAPTURE_CHANNEL_IDX + cap_id]; |
|
capture = &channel->capture; |
|
|
|
/* We need to wait at least 4 (2 positive edges and 2 negative edges) interrupts to |
|
* calculate the period |
|
*/ |
|
if (capture->skip_irq < SKIP_IRQ_NUM) { |
|
capture->capture_data[capture->skip_irq].value = |
|
mcpwm_ll_capture_get_value(data->hal.dev, capture->capture_signal); |
|
capture->capture_data[capture->skip_irq].edge = |
|
mcpwm_ll_capture_get_edge(data->hal.dev, capture->capture_signal) == |
|
MCPWM_CAP_EDGE_NEG |
|
? MCPWM_NEG_EDGE |
|
: MCPWM_POS_EDGE; |
|
capture->skip_irq++; |
|
|
|
} else { |
|
/** |
|
* The capture timer is a 32-bit counter incrementing continuously, once enabled. |
|
* On the input it has an APB clock running typically at 80 MHz |
|
*/ |
|
capture->period = channel->inverted ? |
|
capture->capture_data[0].edge == MCPWM_NEG_EDGE |
|
? (capture->capture_data[2].value - capture->capture_data[0].value) |
|
: (capture->capture_data[3].value - capture->capture_data[1].value) |
|
: capture->capture_data[0].edge == MCPWM_POS_EDGE |
|
? (capture->capture_data[2].value - capture->capture_data[0].value) |
|
: (capture->capture_data[3].value - capture->capture_data[1].value); |
|
|
|
capture->pulse = channel->inverted ? |
|
capture->capture_data[0].edge == MCPWM_NEG_EDGE |
|
? (capture->capture_data[1].value - capture->capture_data[0].value) |
|
: (capture->capture_data[2].value - capture->capture_data[1].value) |
|
: capture->capture_data[0].edge == MCPWM_POS_EDGE |
|
? (capture->capture_data[1].value - capture->capture_data[0].value) |
|
: (capture->capture_data[2].value - capture->capture_data[1].value); |
|
|
|
capture->skip_irq = 0; |
|
if (!capture->continuous) { |
|
mcpwm_esp32_disable_capture(dev, channel->idx); |
|
} |
|
|
|
if (capture->callback) { |
|
capture->callback(dev, capture->capture_signal + CAPTURE_CHANNEL_IDX, |
|
capture->capture_period ? capture->period : 0u, |
|
capture->capture_pulse ? capture->pulse : 0u, 0u, |
|
capture->user_data); |
|
} |
|
} |
|
} |
|
#endif /* CONFIG_PWM_CAPTURE */ |
|
|
|
static DEVICE_API(pwm, mcpwm_esp32_api) = { |
|
.set_cycles = mcpwm_esp32_set_cycles, |
|
.get_cycles_per_sec = mcpwm_esp32_get_cycles_per_sec, |
|
#ifdef CONFIG_PWM_CAPTURE |
|
.configure_capture = mcpwm_esp32_configure_capture, |
|
.enable_capture = mcpwm_esp32_enable_capture, |
|
.disable_capture = mcpwm_esp32_disable_capture, |
|
#endif /* CONFIG_PWM_CAPTURE */ |
|
}; |
|
|
|
#ifdef CONFIG_PWM_CAPTURE |
|
#define IRQ_CONFIG_FUNC(idx) \ |
|
static int mcpwm_esp32_irq_config_func_##idx(const struct device *dev) \ |
|
{ \ |
|
int ret; \ |
|
ret = esp_intr_alloc(DT_INST_IRQ_BY_IDX(idx, 0, irq), \ |
|
ESP_PRIO_TO_FLAGS(DT_INST_IRQ_BY_IDX(idx, 0, priority)) | \ |
|
ESP_INT_FLAGS_CHECK(DT_INST_IRQ_BY_IDX(idx, 0, flags)) | \ |
|
ESP_INTR_FLAG_IRAM, \ |
|
(intr_handler_t)mcpwm_esp32_isr, (void *)dev, NULL); \ |
|
return ret; \ |
|
} |
|
#define CAPTURE_INIT(idx) .irq_config_func = mcpwm_esp32_irq_config_func_##idx |
|
#else |
|
#define IRQ_CONFIG_FUNC(idx) |
|
#define CAPTURE_INIT(idx) |
|
#endif /* CONFIG_PWM_CAPTURE */ |
|
|
|
#define ESP32_MCPWM_INIT(idx) \ |
|
PINCTRL_DT_INST_DEFINE(idx); \ |
|
IRQ_CONFIG_FUNC(idx); \ |
|
static struct mcpwm_esp32_data mcpwm_esp32_data_##idx = { \ |
|
.hal = \ |
|
{ \ |
|
.dev = (mcpwm_dev_t *)DT_INST_REG_ADDR(idx), \ |
|
}, \ |
|
.cmd_sem = Z_SEM_INITIALIZER(mcpwm_esp32_data_##idx.cmd_sem, 1, 1), \ |
|
}; \ |
|
\ |
|
static struct mcpwm_esp32_config mcpwm_esp32_config_##idx = { \ |
|
.index = idx, \ |
|
.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(idx), \ |
|
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(idx)), \ |
|
.clock_subsys = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(idx, offset), \ |
|
.prescale = DT_INST_PROP(idx, prescale), \ |
|
.prescale_timer0 = DT_INST_PROP_OR(idx, prescale_timer0, 0), \ |
|
.prescale_timer1 = DT_INST_PROP_OR(idx, prescale_timer1, 0), \ |
|
.prescale_timer2 = DT_INST_PROP_OR(idx, prescale_timer2, 0), \ |
|
CAPTURE_INIT(idx)}; \ |
|
\ |
|
DEVICE_DT_INST_DEFINE(idx, &mcpwm_esp32_init, NULL, &mcpwm_esp32_data_##idx, \ |
|
&mcpwm_esp32_config_##idx, POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, \ |
|
&mcpwm_esp32_api); |
|
|
|
DT_INST_FOREACH_STATUS_OKAY(ESP32_MCPWM_INIT)
|
|
|