esp-idf/components/driver/mcpwm/mcpwm_sync.c
2023-03-01 00:43:14 +00:00

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;
}