mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
297 lines
11 KiB
C
297 lines
11 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <sys/cdefs.h>
|
|
#include "sdkconfig.h"
|
|
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
|
// The local log level must be defined before including esp_log.h
|
|
// Set the maximum log level for this source file
|
|
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
|
#endif
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "esp_attr.h"
|
|
#include "esp_check.h"
|
|
#include "esp_err.h"
|
|
#include "esp_log.h"
|
|
#include "esp_memory_utils.h"
|
|
#include "soc/soc_caps.h"
|
|
#include "soc/mcpwm_periph.h"
|
|
#include "hal/mcpwm_ll.h"
|
|
#include "driver/mcpwm_sync.h"
|
|
#include "driver/gpio.h"
|
|
#include "mcpwm_private.h"
|
|
|
|
static const char *TAG = "mcpwm";
|
|
|
|
static esp_err_t mcpwm_del_timer_sync_src(mcpwm_sync_t *sync_src);
|
|
static esp_err_t mcpwm_del_gpio_sync_src(mcpwm_sync_t *sync_src);
|
|
static esp_err_t mcpwm_del_soft_sync_src(mcpwm_sync_t *sync_src);
|
|
|
|
static esp_err_t mcpwm_timer_sync_src_register_to_timer(mcpwm_timer_sync_src_t *timer_sync_src, mcpwm_timer_t *timer)
|
|
{
|
|
bool new_sync = false;
|
|
portENTER_CRITICAL(&timer->spinlock);
|
|
if (!timer->sync_src) {
|
|
new_sync = true;
|
|
timer->sync_src = timer_sync_src;
|
|
}
|
|
portEXIT_CRITICAL(&timer->spinlock);
|
|
ESP_RETURN_ON_FALSE(new_sync, ESP_ERR_INVALID_STATE, TAG, "timer sync_src already installed for timer (%d,%d)",
|
|
timer->group->group_id, timer->timer_id);
|
|
|
|
timer_sync_src->timer = timer;
|
|
return ESP_OK;
|
|
}
|
|
|
|
static void mcpwm_timer_sync_src_unregister_from_timer(mcpwm_timer_sync_src_t *timer_sync_src)
|
|
{
|
|
mcpwm_timer_t *timer = timer_sync_src->timer;
|
|
|
|
portENTER_CRITICAL(&timer->spinlock);
|
|
timer->sync_src = NULL;
|
|
portEXIT_CRITICAL(&timer->spinlock);
|
|
}
|
|
|
|
static esp_err_t mcpwm_timer_sync_src_destroy(mcpwm_timer_sync_src_t *timer_sync_src)
|
|
{
|
|
if (timer_sync_src->timer) {
|
|
mcpwm_timer_sync_src_unregister_from_timer(timer_sync_src);
|
|
}
|
|
free(timer_sync_src);
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t mcpwm_new_timer_sync_src(mcpwm_timer_handle_t timer, const mcpwm_timer_sync_src_config_t *config, mcpwm_sync_handle_t *ret_sync)
|
|
{
|
|
esp_err_t ret = ESP_OK;
|
|
mcpwm_timer_sync_src_t *timer_sync_src = NULL;
|
|
ESP_GOTO_ON_FALSE(timer && config && ret_sync, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
|
timer_sync_src = heap_caps_calloc(1, sizeof(mcpwm_timer_sync_src_t), MCPWM_MEM_ALLOC_CAPS);
|
|
ESP_GOTO_ON_FALSE(timer_sync_src, ESP_ERR_NO_MEM, err, TAG, "no mem for timer sync_src");
|
|
|
|
ESP_GOTO_ON_ERROR(mcpwm_timer_sync_src_register_to_timer(timer_sync_src, timer), err, TAG, "register timer sync_src failed");
|
|
mcpwm_group_t *group = timer->group;
|
|
mcpwm_hal_context_t *hal = &group->hal;
|
|
int timer_id = timer->timer_id;
|
|
|
|
if (config->flags.propagate_input_sync) {
|
|
mcpwm_ll_timer_propagate_input_sync(hal->dev, timer_id);
|
|
} else {
|
|
switch (config->timer_event) {
|
|
case MCPWM_TIMER_EVENT_EMPTY:
|
|
mcpwm_ll_timer_sync_out_on_timer_event(hal->dev, timer_id, MCPWM_TIMER_EVENT_EMPTY);
|
|
break;
|
|
case MCPWM_TIMER_EVENT_FULL:
|
|
mcpwm_ll_timer_sync_out_on_timer_event(hal->dev, timer_id, MCPWM_TIMER_EVENT_FULL);
|
|
break;
|
|
default:
|
|
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "unknown timer sync event:%d", config->timer_event);
|
|
break;
|
|
}
|
|
}
|
|
|
|
timer_sync_src->base.group = group;
|
|
timer_sync_src->base.type = MCPWM_SYNC_TYPE_TIMER;
|
|
timer_sync_src->base.del = mcpwm_del_timer_sync_src;
|
|
*ret_sync = &timer_sync_src->base;
|
|
ESP_LOGD(TAG, "new timer sync_src at %p in timer (%d,%d), event:%c", timer_sync_src, group->group_id, timer_id, "EP?"[config->timer_event]);
|
|
return ESP_OK;
|
|
|
|
err:
|
|
if (timer_sync_src) {
|
|
mcpwm_timer_sync_src_destroy(timer_sync_src);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static esp_err_t mcpwm_del_timer_sync_src(mcpwm_sync_t *sync_src)
|
|
{
|
|
mcpwm_timer_sync_src_t *timer_sync_src = __containerof(sync_src, mcpwm_timer_sync_src_t, base);
|
|
mcpwm_timer_t *timer = timer_sync_src->timer;
|
|
int timer_id = timer->timer_id;
|
|
mcpwm_group_t *group = sync_src->group;
|
|
|
|
mcpwm_ll_timer_disable_sync_out(group->hal.dev, timer_id);
|
|
ESP_LOGD(TAG, "del timer sync_src in timer (%d,%d)", group->group_id, timer_id);
|
|
ESP_RETURN_ON_ERROR(mcpwm_timer_sync_src_destroy(timer_sync_src), TAG, "destroy timer sync_src failed");
|
|
return ESP_OK;
|
|
}
|
|
|
|
static esp_err_t mcpwm_gpio_sync_src_register_to_group(mcpwm_gpio_sync_src_t *gpio_sync_src, int group_id)
|
|
{
|
|
mcpwm_group_t *group = mcpwm_acquire_group_handle(group_id);
|
|
ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "no mem for group (%d)", group_id);
|
|
|
|
int sync_id = -1;
|
|
portENTER_CRITICAL(&group->spinlock);
|
|
for (int i = 0; i < SOC_MCPWM_GPIO_SYNCHROS_PER_GROUP; i++) {
|
|
if (!group->gpio_sync_srcs[i]) {
|
|
sync_id = i;
|
|
group->gpio_sync_srcs[i] = gpio_sync_src;
|
|
break;
|
|
}
|
|
}
|
|
portEXIT_CRITICAL(&group->spinlock);
|
|
|
|
if (sync_id < 0) {
|
|
mcpwm_release_group_handle(group);
|
|
group = NULL;
|
|
} else {
|
|
gpio_sync_src->base.group = group;
|
|
gpio_sync_src->sync_id = sync_id;
|
|
}
|
|
ESP_RETURN_ON_FALSE(sync_id >= 0, ESP_ERR_NOT_FOUND, TAG, "no free gpio sync_src in group (%d)", group_id);
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
static void mcpwm_gpio_sync_src_unregister_from_group(mcpwm_gpio_sync_src_t *gpio_sync_src)
|
|
{
|
|
mcpwm_group_t *group = gpio_sync_src->base.group;
|
|
int sync_id = gpio_sync_src->sync_id;
|
|
|
|
portENTER_CRITICAL(&group->spinlock);
|
|
group->gpio_sync_srcs[sync_id] = NULL;
|
|
portEXIT_CRITICAL(&group->spinlock);
|
|
|
|
// sync_src has a reference on group, release it now
|
|
mcpwm_release_group_handle(group);
|
|
}
|
|
|
|
static esp_err_t mcpwm_gpio_sync_src_destroy(mcpwm_gpio_sync_src_t *gpio_sync_src)
|
|
{
|
|
if (gpio_sync_src->base.group) {
|
|
mcpwm_gpio_sync_src_unregister_from_group(gpio_sync_src);
|
|
}
|
|
free(gpio_sync_src);
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t mcpwm_new_gpio_sync_src(const mcpwm_gpio_sync_src_config_t *config, mcpwm_sync_handle_t *ret_sync)
|
|
{
|
|
#if CONFIG_MCPWM_ENABLE_DEBUG_LOG
|
|
esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
|
#endif
|
|
esp_err_t ret = ESP_OK;
|
|
mcpwm_gpio_sync_src_t *gpio_sync_src = NULL;
|
|
ESP_GOTO_ON_FALSE(config && ret_sync, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
|
ESP_GOTO_ON_FALSE(config->group_id < SOC_MCPWM_GROUPS && config->group_id >= 0, ESP_ERR_INVALID_ARG,
|
|
err, TAG, "invalid group ID:%d", config->group_id);
|
|
|
|
gpio_sync_src = heap_caps_calloc(1, sizeof(mcpwm_gpio_sync_src_t), MCPWM_MEM_ALLOC_CAPS);
|
|
ESP_GOTO_ON_FALSE(gpio_sync_src, ESP_ERR_NO_MEM, err, TAG, "no mem for gpio sync_src");
|
|
|
|
ESP_GOTO_ON_ERROR(mcpwm_gpio_sync_src_register_to_group(gpio_sync_src, config->group_id), err, TAG, "register gpio sync_src failed");
|
|
mcpwm_group_t *group = gpio_sync_src->base.group;
|
|
int group_id = group->group_id;
|
|
int sync_id = gpio_sync_src->sync_id;
|
|
|
|
// GPIO configuration
|
|
gpio_config_t gpio_conf = {
|
|
.intr_type = GPIO_INTR_DISABLE,
|
|
.mode = GPIO_MODE_INPUT | (config->flags.io_loop_back ? GPIO_MODE_OUTPUT : 0), // also enable the output path if `io_loop_back` is enabled
|
|
.pin_bit_mask = (1ULL << config->gpio_num),
|
|
.pull_down_en = config->flags.pull_down,
|
|
.pull_up_en = config->flags.pull_up,
|
|
};
|
|
ESP_GOTO_ON_ERROR(gpio_config(&gpio_conf), err, TAG, "config sync GPIO failed");
|
|
esp_rom_gpio_connect_in_signal(config->gpio_num, mcpwm_periph_signals.groups[group_id].gpio_synchros[sync_id].sync_sig, 0);
|
|
|
|
// different ext sync share the same config register, using a group level spin lock
|
|
portENTER_CRITICAL(&group->spinlock);
|
|
mcpwm_ll_invert_gpio_sync_input(group->hal.dev, sync_id, config->flags.active_neg);
|
|
portEXIT_CRITICAL(&group->spinlock);
|
|
|
|
// fill in other operator members
|
|
gpio_sync_src->base.type = MCPWM_SYNC_TYPE_GPIO;
|
|
gpio_sync_src->gpio_num = config->gpio_num;
|
|
gpio_sync_src->base.del = mcpwm_del_gpio_sync_src;
|
|
*ret_sync = &gpio_sync_src->base;
|
|
ESP_LOGD(TAG, "new gpio sync_src (%d,%d) at %p, GPIO:%d", group_id, sync_id, gpio_sync_src, config->gpio_num);
|
|
return ESP_OK;
|
|
|
|
err:
|
|
if (gpio_sync_src) {
|
|
mcpwm_gpio_sync_src_destroy(gpio_sync_src);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static esp_err_t mcpwm_del_gpio_sync_src(mcpwm_sync_t *sync_src)
|
|
{
|
|
mcpwm_gpio_sync_src_t *gpio_sync_src = __containerof(sync_src, mcpwm_gpio_sync_src_t, base);
|
|
mcpwm_group_t *group = sync_src->group;
|
|
|
|
ESP_LOGD(TAG, "del gpio sync_src (%d,%d)", group->group_id, gpio_sync_src->sync_id);
|
|
gpio_reset_pin(gpio_sync_src->gpio_num);
|
|
|
|
// recycle memory resource
|
|
ESP_RETURN_ON_ERROR(mcpwm_gpio_sync_src_destroy(gpio_sync_src), TAG, "destroy GPIO sync_src failed");
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t mcpwm_new_soft_sync_src(const mcpwm_soft_sync_config_t *config, mcpwm_sync_handle_t *ret_sync)
|
|
{
|
|
esp_err_t ret = ESP_OK;
|
|
mcpwm_soft_sync_src_t *soft_sync = NULL;
|
|
ESP_GOTO_ON_FALSE(config && ret_sync, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
|
soft_sync = heap_caps_calloc(1, sizeof(mcpwm_soft_sync_src_t), MCPWM_MEM_ALLOC_CAPS);
|
|
ESP_GOTO_ON_FALSE(soft_sync, ESP_ERR_NO_MEM, err, TAG, "no mem for soft sync");
|
|
|
|
// fill in other sync member
|
|
soft_sync->soft_sync_from = MCPWM_SOFT_SYNC_FROM_NONE;
|
|
soft_sync->base.type = MCPWM_SYNC_TYPE_SOFT;
|
|
soft_sync->base.del = mcpwm_del_soft_sync_src;
|
|
*ret_sync = &soft_sync->base;
|
|
ESP_LOGD(TAG, "new soft sync at %p", soft_sync);
|
|
return ESP_OK;
|
|
|
|
err:
|
|
// soft_sync must be NULL in the error handling path, and it's a determined behaviour to free a NULL pointer in esp-idf
|
|
free(soft_sync);
|
|
return ret;
|
|
}
|
|
|
|
static esp_err_t mcpwm_del_soft_sync_src(mcpwm_sync_t *sync_src)
|
|
{
|
|
mcpwm_soft_sync_src_t *soft_sync = __containerof(sync_src, mcpwm_soft_sync_src_t, base);
|
|
ESP_LOGD(TAG, "del soft sync %p", soft_sync);
|
|
free(soft_sync);
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t mcpwm_del_sync_src(mcpwm_sync_handle_t sync_src)
|
|
{
|
|
ESP_RETURN_ON_FALSE(sync_src, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
|
return sync_src->del(sync_src);
|
|
}
|
|
|
|
esp_err_t mcpwm_soft_sync_activate(mcpwm_sync_handle_t sync_src)
|
|
{
|
|
ESP_RETURN_ON_FALSE(sync_src, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
|
ESP_RETURN_ON_FALSE(sync_src->type == MCPWM_SYNC_TYPE_SOFT, ESP_ERR_INVALID_ARG, TAG, "not a valid soft sync");
|
|
mcpwm_group_t *group = sync_src->group;
|
|
mcpwm_soft_sync_src_t *soft_sync = __containerof(sync_src, mcpwm_soft_sync_src_t, base);
|
|
|
|
switch (soft_sync->soft_sync_from) {
|
|
case MCPWM_SOFT_SYNC_FROM_TIMER: {
|
|
mcpwm_timer_t *timer = soft_sync->timer;
|
|
mcpwm_ll_timer_trigger_soft_sync(group->hal.dev, timer->timer_id);
|
|
break;
|
|
}
|
|
case MCPWM_SOFT_SYNC_FROM_CAP: {
|
|
mcpwm_ll_capture_trigger_sw_sync(group->hal.dev);
|
|
break;
|
|
}
|
|
default:
|
|
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_STATE, TAG, "no soft sync generator is assigned");
|
|
break;
|
|
}
|
|
return ESP_OK;
|
|
}
|