/* * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #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; }