583 lines
21 KiB
C
583 lines
21 KiB
C
/*
|
|
* Copyright (c) 2022 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>
|
|
#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);
|
|
|
|
#define SOC_MCPWM_BASE_CLK_HZ (160000000U)
|
|
#ifdef CONFIG_PWM_CAPTURE
|
|
#define SKIP_IRQ_NUM 4U
|
|
#define MCPWM_INTR_CAP0 BIT(0)
|
|
#define MCPWM_INTR_CAP1 BIT(1)
|
|
#define MCPWM_INTR_CAP2 BIT(2)
|
|
#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;
|
|
mcpwm_hal_init_config_t init_config;
|
|
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;
|
|
uint32_t overflows;
|
|
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_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;
|
|
}
|
|
|
|
set_duty = mcpwm_ll_timer_get_peak(data->hal.dev, channel->timer_id, false) *
|
|
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_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);
|
|
int real_group_prescale = mcpwm_ll_group_get_clock_prescale(data->hal.dev);
|
|
uint32_t real_timer_clk_hz =
|
|
SOC_MCPWM_BASE_CLK_HZ / real_group_prescale /
|
|
mcpwm_ll_timer_get_clock_prescale(data->hal.dev, channel->timer_id);
|
|
mcpwm_ll_timer_set_peak(data->hal.dev, channel->timer_id, real_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];
|
|
|
|
if (!channel) {
|
|
LOG_ERR("Error getting channel %d", channel_idx);
|
|
return -EINVAL;
|
|
}
|
|
|
|
#ifdef CONFIG_PWM_CAPTURE
|
|
if (channel->idx >= CAPTURE_CHANNEL_IDX) {
|
|
*cycles = (uint64_t)APB_CLK_FREQ;
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_PWM_CAPTURE */
|
|
|
|
*cycles =
|
|
(uint64_t)SOC_MCPWM_BASE_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_hal_init(&data->hal, &data->init_config);
|
|
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_capture_status(data->hal.dev, 1 << 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;
|
|
}
|
|
|
|
/* 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_hal_init(&data->hal, &data->init_config);
|
|
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_intr_status = mcpwm_ll_intr_get_capture_status(data->hal.dev);
|
|
|
|
mcpwm_ll_intr_clear_capture_status(data->hal.dev, mcpwm_intr_status);
|
|
|
|
if (mcpwm_intr_status & MCPWM_INTR_CAP0) {
|
|
channel = &config->channel_config[CAPTURE_CHANNEL_IDX];
|
|
} else if (mcpwm_intr_status & MCPWM_INTR_CAP1) {
|
|
channel = &config->channel_config[CAPTURE_CHANNEL_IDX + 1];
|
|
} else if (mcpwm_intr_status & MCPWM_INTR_CAP2) {
|
|
channel = &config->channel_config[CAPTURE_CHANNEL_IDX + 2];
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
if (!channel) {
|
|
return;
|
|
}
|
|
|
|
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 const struct pwm_driver_api 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), \
|
|
}, \
|
|
.init_config = \
|
|
{ \
|
|
.group_id = 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)
|