mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-01 18:50:34 +03:00
175 lines
6.6 KiB
C
175 lines
6.6 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <stdatomic.h>
|
|
#include <string.h>
|
|
#include "dac_priv_common.h"
|
|
#include "driver/dac_cosine.h"
|
|
#include "hal/dac_ll.h"
|
|
#include "esp_private/esp_clk_tree_common.h"
|
|
#include "esp_check.h"
|
|
#include "esp_log.h"
|
|
|
|
typedef enum {
|
|
DAC_COS_FSM_REGISTERED,
|
|
DAC_COS_FSM_ENABLED,
|
|
DAC_COS_FSM_WAIT,
|
|
} dac_cosine_fsm_t;
|
|
|
|
struct dac_cosine_s {
|
|
dac_cosine_config_t cfg; /*!< Cosine mode configurations */
|
|
_Atomic dac_cosine_fsm_t fsm; /*!< FSM state */
|
|
};
|
|
|
|
static bool dac_cosine_clk_src_is_supported(dac_cosine_clk_src_t clk_src)
|
|
{
|
|
const dac_cosine_clk_src_t supported[] = SOC_DAC_COSINE_CLKS;
|
|
for (size_t i = 0; i < sizeof(supported) / sizeof(supported[0]); i++) {
|
|
if (clk_src == supported[i]) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
esp_err_t dac_cosine_new_channel(const dac_cosine_config_t *cos_cfg, dac_cosine_handle_t *ret_handle)
|
|
{
|
|
/* Parameters validation */
|
|
DAC_NULL_POINTER_CHECK(cos_cfg);
|
|
DAC_NULL_POINTER_CHECK(ret_handle);
|
|
ESP_RETURN_ON_FALSE(IS_VALID_DAC_CHANNEL(cos_cfg->chan_id), ESP_ERR_INVALID_ARG, TAG, "invalid dac channel id");
|
|
ESP_RETURN_ON_FALSE(cos_cfg->freq_hz > 0, ESP_ERR_INVALID_ARG, TAG, "invalid cosine wave frequency");
|
|
|
|
dac_cosine_clk_src_t clk_src = cos_cfg->clk_src ? : DAC_COSINE_CLK_SRC_DEFAULT;
|
|
ESP_RETURN_ON_FALSE(dac_cosine_clk_src_is_supported(clk_src), ESP_ERR_INVALID_ARG, TAG, "invalid DAC cosine clock source");
|
|
|
|
esp_err_t ret = ESP_OK;
|
|
|
|
/* Allocate cosine handle */
|
|
dac_cosine_handle_t handle = heap_caps_calloc(1, sizeof(struct dac_cosine_s), DAC_MEM_ALLOC_CAPS);
|
|
ESP_RETURN_ON_FALSE(handle, ESP_ERR_NO_MEM, TAG, "no memory for the dac cosine handle");
|
|
handle->cfg = *cos_cfg;
|
|
handle->cfg.clk_src = clk_src;
|
|
atomic_store(&handle->fsm, DAC_COS_FSM_REGISTERED);
|
|
|
|
/* Acquire the generator clock and resolve its frequency */
|
|
uint32_t clk_freq = 0;
|
|
ESP_GOTO_ON_ERROR(esp_clk_tree_acquire_src((soc_module_clk_t)handle->cfg.clk_src), err_handle, TAG, "enable clock failed");
|
|
ESP_GOTO_ON_ERROR(esp_clk_tree_src_get_freq_hz((soc_module_clk_t)handle->cfg.clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_freq),
|
|
err_clk, TAG, "get clock frequency failed");
|
|
|
|
ESP_GOTO_ON_ERROR(dac_priv_channel_register(cos_cfg->chan_id), err_clk, TAG, "register dac channel %d failed", cos_cfg->chan_id);
|
|
|
|
/* Claim the shared cosine wave generator in tone mode and program the wave frequency (this also
|
|
* starts the generator). Rejected if the direct-output path is in use or a different frequency
|
|
* is already set. */
|
|
ESP_GOTO_ON_ERROR(dac_priv_sintx_acquire_tone(cos_cfg->freq_hz, clk_freq, cos_cfg->flags.force_set_freq), err_dereg, TAG, "acquire cosine wave generator failed");
|
|
|
|
int16_t offset = cos_cfg->offset;
|
|
if (cos_cfg->phase == DAC_COSINE_PHASE_180) {
|
|
offset = -offset;
|
|
}
|
|
#if SOC_DAC_SINTX_LUT_SIGNED
|
|
#error "not implemented"
|
|
#else
|
|
const int16_t base_offset = 0;
|
|
/* Hardware DC register is signed: -128~127 */
|
|
const int16_t min_offset = -128, max_offset = 127;
|
|
#endif
|
|
if (offset < min_offset || offset > max_offset) {
|
|
/* User-facing range: phase 0° → [min-B, max-B]; phase 180° → [B-max, B-min] */
|
|
const int16_t min_user = (cos_cfg->phase == DAC_COSINE_PHASE_0) ? (min_offset - base_offset) : (base_offset - max_offset);
|
|
const int16_t max_user = min_user + (max_offset - min_offset);
|
|
ESP_LOGW(TAG, "DAC cosine DC offset %d out of range [%d, %d], saturating",
|
|
cos_cfg->offset, min_user, max_user);
|
|
offset = (offset < min_offset) ? min_offset : max_offset;
|
|
}
|
|
|
|
/* Set the per-channel cosine wave parameters. */
|
|
DAC_ENTER_CRITICAL();
|
|
dac_ll_cw_set_atten(handle->cfg.chan_id, handle->cfg.atten);
|
|
dac_ll_cw_set_phase(handle->cfg.chan_id, handle->cfg.phase);
|
|
dac_ll_cw_set_offset(handle->cfg.chan_id, offset);
|
|
DAC_EXIT_CRITICAL();
|
|
|
|
*ret_handle = handle;
|
|
return ESP_OK;
|
|
|
|
err_dereg:
|
|
dac_priv_channel_deregister(cos_cfg->chan_id);
|
|
err_clk:
|
|
esp_clk_tree_release_src((soc_module_clk_t)handle->cfg.clk_src);
|
|
err_handle:
|
|
free(handle);
|
|
return ret;
|
|
}
|
|
|
|
esp_err_t dac_cosine_del_channel(dac_cosine_handle_t handle)
|
|
{
|
|
DAC_NULL_POINTER_CHECK(handle);
|
|
|
|
dac_cosine_fsm_t expected_fsm = DAC_COS_FSM_REGISTERED;
|
|
ESP_RETURN_ON_FALSE(atomic_compare_exchange_strong(&handle->fsm, &expected_fsm, DAC_COS_FSM_WAIT),
|
|
ESP_ERR_INVALID_STATE, TAG, "dac cosine is running");
|
|
|
|
ESP_RETURN_ON_ERROR(dac_priv_channel_deregister(handle->cfg.chan_id), TAG,
|
|
"deregister dac channel %d failed", handle->cfg.chan_id);
|
|
ESP_RETURN_ON_ERROR(dac_priv_sintx_release(), TAG, "release dac sintx generator failed");
|
|
ESP_RETURN_ON_ERROR(esp_clk_tree_release_src((soc_module_clk_t)handle->cfg.clk_src), TAG, "disable clock failed");
|
|
free(handle);
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t dac_cosine_start(dac_cosine_handle_t handle)
|
|
{
|
|
DAC_NULL_POINTER_CHECK(handle);
|
|
|
|
dac_cosine_fsm_t expected_fsm = DAC_COS_FSM_REGISTERED;
|
|
ESP_RETURN_ON_FALSE(atomic_compare_exchange_strong(&handle->fsm, &expected_fsm, DAC_COS_FSM_WAIT),
|
|
ESP_ERR_INVALID_STATE, TAG, "dac cosine already started");
|
|
|
|
esp_err_t ret = ESP_OK;
|
|
/* The generator is already running; starting only powers on the pad so the wave reaches the output. */
|
|
ESP_GOTO_ON_ERROR(dac_priv_channel_enable(handle->cfg.chan_id, DAC_DATA_SOURCE_COSINE), err,
|
|
TAG, "enable dac channel %d failed", handle->cfg.chan_id);
|
|
|
|
atomic_store(&handle->fsm, DAC_COS_FSM_ENABLED);
|
|
return ESP_OK;
|
|
err:
|
|
atomic_store(&handle->fsm, DAC_COS_FSM_REGISTERED);
|
|
return ret;
|
|
}
|
|
|
|
esp_err_t dac_cosine_stop(dac_cosine_handle_t handle)
|
|
{
|
|
DAC_NULL_POINTER_CHECK(handle);
|
|
|
|
dac_cosine_fsm_t expected_fsm = DAC_COS_FSM_ENABLED;
|
|
ESP_RETURN_ON_FALSE(atomic_compare_exchange_strong(&handle->fsm, &expected_fsm, DAC_COS_FSM_WAIT),
|
|
ESP_ERR_INVALID_STATE, TAG, "dac cosine already stopped");
|
|
|
|
esp_err_t ret = ESP_OK;
|
|
/* Power off the pad; the generator keeps running */
|
|
ESP_GOTO_ON_ERROR(dac_priv_channel_disable(handle->cfg.chan_id), err,
|
|
TAG, "disable dac channel %d failed", handle->cfg.chan_id);
|
|
|
|
atomic_store(&handle->fsm, DAC_COS_FSM_REGISTERED);
|
|
return ESP_OK;
|
|
err:
|
|
atomic_store(&handle->fsm, DAC_COS_FSM_ENABLED);
|
|
return ret;
|
|
}
|
|
|
|
uint8_t dac_cosine_get_bitwidth(dac_cosine_handle_t handle)
|
|
{
|
|
if (!handle) {
|
|
return 0;
|
|
}
|
|
|
|
return SOC_DAC_RESOLUTION;
|
|
}
|