mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
156 lines
5.7 KiB
C
156 lines
5.7 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include "dac_priv_common.h"
|
|
#include "driver/dac_cosine.h"
|
|
#include "hal/clk_tree_ll.h"
|
|
#include "hal/dac_ll.h"
|
|
#include "esp_clk_tree.h"
|
|
#include "esp_check.h"
|
|
#include "esp_log.h"
|
|
|
|
struct dac_cosine_s {
|
|
dac_cosine_config_t cfg; /*!< Cosine mode configurations */
|
|
bool is_started; /*!< Flag: is the channel started(not cosine wave generator) */
|
|
};
|
|
|
|
/* Cosine wave generator reference count
|
|
* The cosine wave generator is shared by dac channels */
|
|
static uint32_t s_cwg_refer_cnt = 0;
|
|
|
|
/* The frequency of cosine wave generator */
|
|
static uint32_t s_cwg_freq = 0;
|
|
|
|
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 >= (130 / clk_ll_rc_fast_get_divider()), ESP_ERR_NOT_SUPPORTED, TAG, "The cosine wave frequency is too low");
|
|
ESP_RETURN_ON_FALSE((!s_cwg_freq) || cos_cfg->flags.force_set_freq || (cos_cfg->freq_hz == s_cwg_freq),
|
|
ESP_ERR_INVALID_STATE, TAG, "The cosine wave frequency has set already, not allowed to update unless `force_set_freq` is set");
|
|
|
|
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");
|
|
/* Assign configurations */
|
|
handle->cfg = *cos_cfg;
|
|
if (handle->cfg.clk_src == 0) {
|
|
handle->cfg.clk_src = DAC_COSINE_CLK_SRC_DEFAULT;
|
|
}
|
|
/* Register the handle */
|
|
ESP_GOTO_ON_ERROR(dac_priv_register_channel(handle->cfg.chan_id), err1, TAG, "register dac channel %d failed", handle->cfg.chan_id);
|
|
|
|
/* Get the cosine wave generator clock frequency */
|
|
uint32_t rtc_clk_freq = 0;
|
|
esp_clk_tree_src_get_freq_hz((soc_module_clk_t)handle->cfg.clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &rtc_clk_freq);
|
|
|
|
if (rtc_clk_freq == 0) {
|
|
ESP_LOGW(TAG, "RTC clock calibration failed, using the approximate value as default");
|
|
rtc_clk_freq = SOC_CLK_RC_FAST_FREQ_APPROX;
|
|
}
|
|
DAC_ENTER_CRITICAL();
|
|
/* Set coefficients for cosine wave generator */
|
|
if ((!s_cwg_freq) || handle->cfg.flags.force_set_freq) {
|
|
dac_ll_cw_set_freq(handle->cfg.freq_hz, rtc_clk_freq);
|
|
s_cwg_freq = handle->cfg.freq_hz;
|
|
}
|
|
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_dc_offset(handle->cfg.chan_id, handle->cfg.offset);
|
|
DAC_EXIT_CRITICAL();
|
|
|
|
*ret_handle = handle;
|
|
return ret;
|
|
|
|
err1:
|
|
free(handle);
|
|
return ret;
|
|
}
|
|
|
|
esp_err_t dac_cosine_del_channel(dac_cosine_handle_t handle)
|
|
{
|
|
DAC_NULL_POINTER_CHECK(handle);
|
|
ESP_RETURN_ON_FALSE(!handle->is_started, ESP_ERR_INVALID_STATE, TAG,
|
|
"the dac cosine generator is not stopped yet");
|
|
|
|
ESP_RETURN_ON_ERROR(dac_priv_deregister_channel(handle->cfg.chan_id), TAG,
|
|
"deregister dac channel %d failed", handle->cfg.chan_id);
|
|
/* Clear the frequency if no channel using it */
|
|
if (!s_cwg_refer_cnt) {
|
|
s_cwg_freq = 0;
|
|
}
|
|
free(handle);
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t dac_cosine_start(dac_cosine_handle_t handle)
|
|
{
|
|
esp_err_t ret = ESP_OK;
|
|
DAC_NULL_POINTER_CHECK(handle);
|
|
ESP_RETURN_ON_FALSE(!handle->is_started, ESP_ERR_INVALID_STATE, TAG,
|
|
"the dac channel has already started");
|
|
/* Acquire the cosine wave generator clock */
|
|
ESP_RETURN_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)handle->cfg.clk_src, true), TAG, "cosine clock enable failed");
|
|
/* Enabled DAC channel */
|
|
ESP_GOTO_ON_ERROR(dac_priv_enable_channel(handle->cfg.chan_id), err, TAG,
|
|
"enable dac channel %d failed", handle->cfg.chan_id);
|
|
/* Enabled the cosine wave generator if no channel using it before */
|
|
DAC_ENTER_CRITICAL();
|
|
if (s_cwg_refer_cnt == 0) {
|
|
dac_ll_cw_generator_enable();
|
|
}
|
|
/* Connect the DAC channel to the cosine wave generator */
|
|
dac_ll_cw_enable_channel(handle->cfg.chan_id, true);
|
|
s_cwg_refer_cnt++;
|
|
handle->is_started = true;
|
|
DAC_EXIT_CRITICAL();
|
|
|
|
return ESP_OK;
|
|
|
|
err:
|
|
esp_clk_tree_enable_src((soc_module_clk_t)handle->cfg.clk_src, false);
|
|
return ret;
|
|
}
|
|
|
|
esp_err_t dac_cosine_stop(dac_cosine_handle_t handle)
|
|
{
|
|
DAC_NULL_POINTER_CHECK(handle);
|
|
ESP_RETURN_ON_FALSE(handle->is_started, ESP_ERR_INVALID_STATE, TAG,
|
|
"the dac channel has already stopped");
|
|
|
|
/* Enabled DAC channel */
|
|
ESP_RETURN_ON_ERROR(dac_priv_disable_channel(handle->cfg.chan_id), TAG,
|
|
"disable dac channel %d failed", handle->cfg.chan_id);
|
|
DAC_ENTER_CRITICAL();
|
|
/* Disconnect the DAC channel from the cosine wave generator */
|
|
dac_ll_cw_enable_channel(handle->cfg.chan_id, false);
|
|
s_cwg_refer_cnt--;
|
|
/* Disable the cosine wave generator if no channel using it */
|
|
if (s_cwg_refer_cnt == 0) {
|
|
dac_ll_cw_generator_disable();
|
|
}
|
|
handle->is_started = false;
|
|
DAC_EXIT_CRITICAL();
|
|
/* Release the cosine wave generator clock */
|
|
ESP_RETURN_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)handle->cfg.clk_src, false), TAG, "cosine clock disable failed");
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
uint8_t dac_cosine_get_bitwidth(dac_cosine_handle_t handle)
|
|
{
|
|
if (!handle) {
|
|
return 0;
|
|
}
|
|
|
|
return SOC_DAC_RESOLUTION;
|
|
}
|