refactor(dac): rename LL APIs and introduce sintx manager

This commit is contained in:
Hu Rui
2026-09-17 15:35:58 +08:00
parent a8a5106421
commit 550734e7dc
12 changed files with 520 additions and 303 deletions
+143 -7
View File
@@ -36,7 +36,7 @@ static _Atomic dac_channel_fsm_t s_dac_chan_fsm[SOC_DAC_CHAN_NUM] = {
[0 ... SOC_DAC_CHAN_NUM - 1] = DAC_CHAN_FSM_IDLE,
};
esp_err_t dac_priv_register_channel(dac_channel_t chan_id)
esp_err_t dac_priv_channel_register(dac_channel_t chan_id)
{
ESP_RETURN_ON_FALSE(IS_VALID_DAC_CHANNEL(chan_id), ESP_ERR_INVALID_ARG, TAG, "channel id is invalid");
@@ -49,7 +49,7 @@ esp_err_t dac_priv_register_channel(dac_channel_t chan_id)
}
}
esp_err_t dac_priv_deregister_channel(dac_channel_t chan_id)
esp_err_t dac_priv_channel_deregister(dac_channel_t chan_id)
{
ESP_RETURN_ON_FALSE(IS_VALID_DAC_CHANNEL(chan_id), ESP_ERR_INVALID_ARG, TAG, "channel id is invalid");
@@ -62,7 +62,7 @@ esp_err_t dac_priv_deregister_channel(dac_channel_t chan_id)
}
}
esp_err_t dac_priv_enable_channel(dac_channel_t chan_id)
esp_err_t dac_priv_channel_enable(dac_channel_t chan_id, dac_data_source_t source)
{
ESP_RETURN_ON_FALSE(IS_VALID_DAC_CHANNEL(chan_id), ESP_ERR_INVALID_ARG, TAG, "channel id is invalid");
@@ -71,8 +71,9 @@ esp_err_t dac_priv_enable_channel(dac_channel_t chan_id)
gpio_num_t gpio_num = (gpio_num_t)dac_periph_signal.dac_channel_io_num[chan_id];
gpio_config_as_analog(gpio_num);
DAC_ENTER_CRITICAL();
dac_ll_power_on(chan_id);
dac_ll_rtc_sync_by_adc(false);
dac_ll_pad_set_data_source(chan_id, source);
dac_ll_sync_by_adc(false);
dac_ll_pad_power_on(chan_id);
DAC_EXIT_CRITICAL();
atomic_store(&s_dac_chan_fsm[chan_id], DAC_CHAN_FSM_ENABLED);
return ESP_OK;
@@ -82,14 +83,14 @@ esp_err_t dac_priv_enable_channel(dac_channel_t chan_id)
}
}
esp_err_t dac_priv_disable_channel(dac_channel_t chan_id)
esp_err_t dac_priv_channel_disable(dac_channel_t chan_id)
{
ESP_RETURN_ON_FALSE(IS_VALID_DAC_CHANNEL(chan_id), ESP_ERR_INVALID_ARG, TAG, "channel id is invalid");
dac_channel_fsm_t expected_fsm = DAC_CHAN_FSM_ENABLED;
if (atomic_compare_exchange_strong(&s_dac_chan_fsm[chan_id], &expected_fsm, DAC_CHAN_FSM_WAIT)) {
DAC_ENTER_CRITICAL();
dac_ll_power_down(chan_id);
dac_ll_pad_power_down(chan_id);
DAC_EXIT_CRITICAL();
atomic_store(&s_dac_chan_fsm[chan_id], DAC_CHAN_FSM_REGISTERED);
return ESP_OK;
@@ -99,6 +100,141 @@ esp_err_t dac_priv_disable_channel(dac_channel_t chan_id)
}
}
/*---------------------------------------------------------------
Cosine (Sintx) generator management
---------------------------------------------------------------*/
/**
* The cosine wave generator is a single shared resource: every channel that outputs a cosine wave
* uses the same generator, and therefore the same frequency. On some targets, the same Sintx generator
* also serves the direct (DC) software output. The tone and DC modes are mutually exclusive there.
* This manager reference counts the generator, enforces the mode exclusion and the single shared
* frequency.
*/
typedef enum {
DAC_SINTX_MODE_TONE = 0, /* cosine wave output */
#if SOC_DAC_DC_VIA_SINTX
DAC_SINTX_MODE_DC, /* direct (DC) software output */
#endif
} dac_sintx_mode_t;
typedef struct {
dac_sintx_mode_t mode;
uint32_t ref_cnt;
uint32_t freq;
} dac_sintx_state_t;
static dac_sintx_state_t s_dac_sintx_state;
typedef struct {
#if SOC_DAC_SINTX_HAS_TIMER_TARGET
uint32_t timer_target;
#endif
uint32_t fstep;
} dac_sintx_freq_hw_t;
/* Due to frequency aliasing, fstep values above the Nyquist limit (2^16)/2, are effectively invalid */
#define DAC_SINTX_FSTEP_MAX 0x8000
/**
* @brief Compute Sintx hardware frequency parameters from the requested tone frequency.
*/
static __attribute__((const)) dac_sintx_freq_hw_t dac_priv_sintx_calc_freq(uint32_t freq_hz, uint32_t clk_freq_hz)
{
#if SOC_DAC_SINTX_HAS_TIMER_TARGET
#error "Not implemented"
#else
/* freq = clk * fstep / 2^16 */
uint64_t fstep = DAC_DIV_ROUND((uint64_t)freq_hz << 16, clk_freq_hz);
return (dac_sintx_freq_hw_t) {
.fstep = DAC_CLAMP(fstep, 1, DAC_SINTX_FSTEP_MAX),
};
#endif
}
static inline void dac_priv_sintx_apply_freq(dac_sintx_freq_hw_t hw)
{
#if SOC_DAC_SINTX_HAS_TIMER_TARGET
dac_ll_cw_set_timer_target(hw.timer_target);
#endif
dac_ll_cw_set_fstep(hw.fstep);
}
esp_err_t dac_priv_sintx_acquire_tone(uint32_t freq_hz, uint32_t clk_freq_hz, bool force_set_freq)
{
dac_sintx_freq_hw_t hw_freq = dac_priv_sintx_calc_freq(freq_hz, clk_freq_hz);
esp_err_t ret = ESP_OK;
DAC_ENTER_CRITICAL();
if (s_dac_sintx_state.ref_cnt == 0) {
// We are the first user. Lock the generator in tone mode and start the generator.
s_dac_sintx_state.ref_cnt = 1;
s_dac_sintx_state.mode = DAC_SINTX_MODE_TONE;
s_dac_sintx_state.freq = freq_hz;
dac_priv_sintx_apply_freq(hw_freq);
dac_ll_cw_enable_tone();
} else if (s_dac_sintx_state.mode != DAC_SINTX_MODE_TONE) {
// The generator is busy in other mode. Conflict.
ret = ESP_ERR_INVALID_STATE;
} else if (s_dac_sintx_state.freq == freq_hz) {
// Other users already requested the same frequency. Just ride it.
s_dac_sintx_state.ref_cnt++;
} else if (force_set_freq) {
// Re-program the frequency.
s_dac_sintx_state.ref_cnt++;
s_dac_sintx_state.freq = freq_hz;
dac_ll_cw_disable();
dac_priv_sintx_apply_freq(hw_freq);
dac_ll_cw_enable_tone();
} else {
// Other users already requested a different frequency. Conflict.
ret = ESP_ERR_INVALID_STATE;
}
DAC_EXIT_CRITICAL();
return ret;
}
#if SOC_DAC_DC_VIA_SINTX
esp_err_t dac_priv_sintx_acquire_dc(void)
{
esp_err_t ret = ESP_OK;
DAC_ENTER_CRITICAL();
if (s_dac_sintx_state.ref_cnt == 0) {
// We are the first user. Lock the generator in DC mode and start the generator.
s_dac_sintx_state.ref_cnt = 1;
s_dac_sintx_state.mode = DAC_SINTX_MODE_DC;
dac_ll_cw_enable_dc();
} else if (s_dac_sintx_state.mode == DAC_SINTX_MODE_DC) {
// Other users already requested the DC mode. Just ride it.
s_dac_sintx_state.ref_cnt++;
} else {
// Other users already requested the cosine mode. Conflict.
ret = ESP_ERR_INVALID_STATE;
}
DAC_EXIT_CRITICAL();
return ret;
}
#endif // SOC_DAC_DC_VIA_SINTX
esp_err_t dac_priv_sintx_release(void)
{
esp_err_t ret = ESP_OK;
DAC_ENTER_CRITICAL();
if (s_dac_sintx_state.ref_cnt > 0) {
s_dac_sintx_state.ref_cnt--;
if (s_dac_sintx_state.ref_cnt == 0) {
// We are the last user. Stop the generator.
dac_ll_cw_disable();
}
} else {
ret = ESP_ERR_INVALID_STATE;
}
DAC_EXIT_CRITICAL();
return ret;
}
#if CONFIG_DAC_ENABLE_DEBUG_LOG
__attribute__((constructor))
static void dac_override_default_log_level(void)
+5 -23
View File
@@ -219,7 +219,7 @@ esp_err_t dac_continuous_new_channels(const dac_continuous_config_t *cont_cfg, d
/* Register the channels */
dac_channel_mask_t registered_chan_mask = 0;
DAC_CHANNEL_MASK_FOREACH(chan, cont_cfg->chan_mask) {
ESP_GOTO_ON_ERROR(dac_priv_register_channel(chan), err_dereg, TAG, "register dac channel %"PRIu32" failed", chan);
ESP_GOTO_ON_ERROR(dac_priv_channel_register(chan), err_dereg, TAG, "register dac channel %"PRIu32" failed", chan);
registered_chan_mask |= BIT(chan);
}
@@ -258,11 +258,6 @@ esp_err_t dac_continuous_new_channels(const dac_continuous_config_t *cont_cfg, d
ESP_GOTO_ON_ERROR(dac_priv_dma_init(handle->cfg.clk_src, handle->cfg.freq_hz, handle->cfg.chan_mode == DAC_CHANNEL_MODE_ALTER, &cbs, handle),
err_desc, TAG, "Failed to initialize DAC DMA peripheral");
/* Connect DAC module to the DMA peripheral */
DAC_ENTER_CRITICAL();
dac_ll_digi_enable_dma(true);
DAC_EXIT_CRITICAL();
/* FSM: WAIT -> REGISTERED */
atomic_store(&s_dac_cont_fsm, DAC_CONT_FSM_REGISTERED);
@@ -287,7 +282,7 @@ err_free:
err_dereg:
/* Deregister registered channels */
DAC_CHANNEL_MASK_FOREACH(chan, registered_chan_mask) {
dac_priv_deregister_channel(chan);
dac_priv_channel_deregister(chan);
}
/* FSM: WAIT -> IDLE */
atomic_store(&s_dac_cont_fsm, DAC_CONT_FSM_IDLE);
@@ -308,11 +303,6 @@ esp_err_t dac_continuous_del_channels(dac_continuous_handle_t handle)
/* Deinitialize DMA peripheral */
ESP_RETURN_ON_ERROR(dac_priv_dma_deinit(), TAG, "Failed to deinitialize DAC DMA peripheral");
/* Disconnect DAC module from the DMA peripheral */
DAC_ENTER_CRITICAL();
dac_ll_digi_enable_dma(false);
DAC_EXIT_CRITICAL();
/* Free allocated resources */
s_dac_free_dma_desc(handle);
@@ -334,7 +324,7 @@ esp_err_t dac_continuous_del_channels(dac_continuous_handle_t handle)
/* Deregister the channels */
DAC_CHANNEL_MASK_FOREACH(chan, handle->cfg.chan_mask) {
dac_priv_deregister_channel(chan);
dac_priv_channel_deregister(chan);
}
free(handle);
@@ -386,14 +376,10 @@ esp_err_t dac_continuous_enable(dac_continuous_handle_t handle)
#endif
DAC_CHANNEL_MASK_FOREACH(chan, handle->cfg.chan_mask) {
dac_priv_enable_channel(chan);
dac_priv_channel_enable(chan, DAC_DATA_SOURCE_DMA);
}
dac_priv_dma_enable();
DAC_ENTER_CRITICAL();
dac_ll_digi_enable_dma(true);
DAC_EXIT_CRITICAL();
/* FSM: WAIT -> ENABLED */
atomic_store(&s_dac_cont_fsm, DAC_CONT_FSM_ENABLED);
return ESP_OK;
@@ -421,12 +407,8 @@ esp_err_t dac_continuous_disable(dac_continuous_handle_t handle)
dac_priv_dma_disable();
DAC_ENTER_CRITICAL();
dac_ll_digi_enable_dma(false);
DAC_EXIT_CRITICAL();
DAC_CHANNEL_MASK_FOREACH(chan, handle->cfg.chan_mask) {
dac_priv_disable_channel(chan);
dac_priv_channel_disable(chan);
}
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_release(handle->pm_lock);
+96 -77
View File
@@ -4,26 +4,36 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdatomic.h>
#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_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 */
bool is_started; /*!< Flag: is the channel started(not cosine wave generator) */
dac_cosine_config_t cfg; /*!< Cosine mode configurations */
_Atomic dac_cosine_fsm_t fsm; /*!< FSM state */
};
/* 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;
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)
{
@@ -31,45 +41,68 @@ esp_err_t dac_cosine_new_channel(const dac_cosine_config_t *cos_cfg, dac_cosine_
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_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");
/* 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);
handle->cfg.clk_src = clk_src;
atomic_store(&handle->fsm, DAC_COS_FSM_REGISTERED);
/* 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);
/* Acquire the generator clock and resolve its frequency */
uint32_t clk_freq = 0;
ESP_GOTO_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)handle->cfg.clk_src, true), 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");
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;
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();
/* 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_ll_cw_set_offset(handle->cfg.chan_id, offset);
DAC_EXIT_CRITICAL();
*ret_handle = handle;
return ret;
return ESP_OK;
err1:
err_dereg:
dac_priv_channel_deregister(cos_cfg->chan_id);
err_clk:
esp_clk_tree_enable_src((soc_module_clk_t)handle->cfg.clk_src, false);
err_handle:
free(handle);
return ret;
}
@@ -77,15 +110,15 @@ err1:
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,
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);
/* Clear the frequency if no channel using it */
if (!s_cwg_refer_cnt) {
s_cwg_freq = 0;
}
ESP_RETURN_ON_ERROR(dac_priv_sintx_release(), TAG, "release dac sintx generator failed");
ESP_RETURN_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)handle->cfg.clk_src, false), TAG, "disable clock failed");
free(handle);
return ESP_OK;
@@ -93,56 +126,42 @@ esp_err_t dac_cosine_del_channel(dac_cosine_handle_t handle)
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();
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:
esp_clk_tree_enable_src((soc_module_clk_t)handle->cfg.clk_src, false);
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);
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");
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)
+6 -6
View File
@@ -29,14 +29,14 @@ esp_err_t dac_oneshot_new_channel(const dac_oneshot_config_t *oneshot_cfg, dac_o
handle->cfg = *oneshot_cfg;
/* Register and enable the dac channel */
ESP_GOTO_ON_ERROR(dac_priv_register_channel(oneshot_cfg->chan_id), err2, TAG, "register dac channel %d failed", oneshot_cfg->chan_id);
ESP_GOTO_ON_ERROR(dac_priv_enable_channel(oneshot_cfg->chan_id), err1, TAG, "enable dac channel %d failed", oneshot_cfg->chan_id);
ESP_GOTO_ON_ERROR(dac_priv_channel_register(oneshot_cfg->chan_id), err2, TAG, "register dac channel %d failed", oneshot_cfg->chan_id);
ESP_GOTO_ON_ERROR(dac_priv_channel_enable(oneshot_cfg->chan_id, DAC_DATA_SOURCE_DIRECT), err1, TAG, "enable dac channel %d failed", oneshot_cfg->chan_id);
*ret_handle = handle;
return ret;
err1:
dac_priv_deregister_channel(oneshot_cfg->chan_id);
dac_priv_channel_deregister(oneshot_cfg->chan_id);
err2:
free(handle);
return ret;
@@ -47,8 +47,8 @@ esp_err_t dac_oneshot_del_channel(dac_oneshot_handle_t handle)
DAC_NULL_POINTER_CHECK(handle);
/* Disable and deregister the channel */
ESP_RETURN_ON_ERROR(dac_priv_disable_channel(handle->cfg.chan_id), TAG, "disable dac channel %d failed", handle->cfg.chan_id);
ESP_RETURN_ON_ERROR(dac_priv_deregister_channel(handle->cfg.chan_id), TAG, "deregister dac channel %d failed", handle->cfg.chan_id);
ESP_RETURN_ON_ERROR(dac_priv_channel_disable(handle->cfg.chan_id), TAG, "disable dac channel %d failed", handle->cfg.chan_id);
ESP_RETURN_ON_ERROR(dac_priv_channel_deregister(handle->cfg.chan_id), TAG, "deregister dac channel %d failed", handle->cfg.chan_id);
/* Free resources */
free(handle);
@@ -64,7 +64,7 @@ esp_err_t dac_oneshot_output_voltage(dac_oneshot_handle_t handle, uint8_t digi_v
/* Set the voltage by the digital value */
DAC_ENTER_CRITICAL_SAFE();
dac_ll_update_output_value(handle->cfg.chan_id, digi_value);
dac_ll_pad_set_output_code(handle->cfg.chan_id, digi_value);
DAC_EXIT_CRITICAL_SAFE();
return ESP_OK;
+61 -4
View File
@@ -14,6 +14,8 @@
#endif
#include "freertos/FreeRTOS.h"
#include "hal/dac_types.h"
#include "hal/dac_types_private.h"
#include "hal/dac_ll.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_err.h"
@@ -56,6 +58,11 @@ extern portMUX_TYPE dac_priv_spinlock;
#define DAC_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#endif
#define DAC_DIV_ROUND(n, d) (((n) + (d) / 2) / (d))
#define DAC_DIV_CEIL(n, d) (((n) + (d) - 1) / (d))
#define DAC_MAX(a, b) ((a) > (b) ? (a) : (b))
#define DAC_CLAMP(x, min, max) ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x)))
/**
* @brief Register dac channel in the driver, in case a same channel is reused by different modes
*
@@ -65,7 +72,7 @@ extern portMUX_TYPE dac_priv_spinlock;
* - ESP_ERR_INVALID_ARG The channel id is incorrect
* - ESP_OK Register the channel success
*/
esp_err_t dac_priv_register_channel(dac_channel_t chan_id);
esp_err_t dac_priv_channel_register(dac_channel_t chan_id);
/**
* @brief Deregister dac channel in the driver
@@ -76,18 +83,19 @@ esp_err_t dac_priv_register_channel(dac_channel_t chan_id);
* - ESP_ERR_INVALID_ARG The channel id is incorrect
* - ESP_OK Deregister the channel success
*/
esp_err_t dac_priv_deregister_channel(dac_channel_t chan_id);
esp_err_t dac_priv_channel_deregister(dac_channel_t chan_id);
/**
* @brief Enable the DAC channel and turn on its power
*
* @param chan_id DAC channel id
* @param source Channel data source
* @return
* - ESP_ERR_INVALID_STATE The channel has not been registered or already enabled
* - ESP_ERR_INVALID_ARG The channel id is incorrect
* - ESP_OK Enable the channel success
*/
esp_err_t dac_priv_enable_channel(dac_channel_t chan_id);
esp_err_t dac_priv_channel_enable(dac_channel_t chan_id, dac_data_source_t source);
/**
* @brief Disable the DAC channel and turn off its power
@@ -98,7 +106,56 @@ esp_err_t dac_priv_enable_channel(dac_channel_t chan_id);
* - ESP_ERR_INVALID_ARG The channel id is incorrect
* - ESP_OK Disable the channel success
*/
esp_err_t dac_priv_disable_channel(dac_channel_t chan_id);
esp_err_t dac_priv_channel_disable(dac_channel_t chan_id);
/**
* @brief Acquire the shared cosine wave (Sintx/tone) generator (reference counted)
*
* @note The cosine wave generator is a single shared resource: all DAC channels that output a
* cosine wave share one generator (and thus one frequency). This claims the generator in tone
* mode, programs the wave frequency, and starts the generator on the first acquire. The first
* acquirer (or any acquirer passing `force_set_freq`) programs the frequency; a later acquirer
* requesting a different frequency without `force_set_freq` is rejected. On targets where the
* tone generator and the direct (DC) output share one Sintx generator, a tone acquire is also
* rejected while the DC path is in use.
*
* @param[in] freq_hz The cosine wave frequency in Hz
* @param[in] clk_freq_hz The clock frequency that drives the generator in Hz
* @param[in] force_set_freq Force (re)programming the frequency even if the generator is in use
* @return
* - ESP_ERR_INVALID_STATE The generator is busy on a conflicting mode or frequency
* - ESP_OK Success
*/
esp_err_t dac_priv_sintx_acquire_tone(uint32_t freq_hz, uint32_t clk_freq_hz, bool force_set_freq);
#if SOC_DAC_DC_VIA_SINTX
/**
* @brief Acquire the shared Sintx generator for the direct (DC) software output (reference counted)
*
* @note On some targets, the direct (DC) software output runs through the same Sintx generator as
* the cosine wave output, so the two are mutually exclusive. This claims the generator in DC
* mode and, on the first acquire, starts the Sintx timer that pushes the written DC value to
* the pad.
*
* @return
* - ESP_ERR_INVALID_STATE The generator is busy on the cosine path
* - ESP_OK Success
*/
esp_err_t dac_priv_sintx_acquire_dc(void);
#endif // SOC_DAC_DC_VIA_SINTX
/**
* @brief Release the shared Sintx generator (reference counted)
*
* @note Releases one reference acquired by `dac_priv_sintx_acquire_tone()` or
* `dac_priv_sintx_acquire_dc()`. The generator is stopped once the last reference is
* released.
*
* @return
* - ESP_ERR_INVALID_STATE The generator is not in use
* - ESP_OK Success
*/
esp_err_t dac_priv_sintx_release(void);
#ifdef __cplusplus
}
@@ -16,6 +16,7 @@
#include "freertos/FreeRTOS.h"
#include "sdkconfig.h"
#include "hal/adc_ll.h"
#include "hal/dac_ll.h"
#include "hal/i2s_hal.h"
#include "hal/i2s_types.h"
#include "hal/clk_tree_ll.h"
@@ -148,6 +149,7 @@ esp_err_t dac_priv_dma_init(soc_periph_dac_digi_clk_src_t clk_src, uint32_t freq
ESP_GOTO_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)clk_src, true), err, TAG, "enable DAC digital clock source failed");
s_ddp->clk_src = clk_src;
dac_ll_dma_clk_inv(true);
ESP_GOTO_ON_ERROR(s_dac_priv_dma_set_clock(clk_src, freq_hz), err, TAG, "Failed to set clock of DMA peripheral");
i2s_ll_enable_builtin_adc_dac(s_ddp->periph_dev, true);
@@ -188,6 +190,7 @@ esp_err_t dac_priv_dma_deinit(void)
}
if (s_ddp->clk_src) {
dac_ll_dma_clk_inv(false);
ESP_RETURN_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)s_ddp->clk_src, false), TAG, "disable DAC digital clock source failed");
s_ddp->clk_src = 0;
}
+9 -5
View File
@@ -139,8 +139,8 @@ static esp_err_t s_dac_priv_dma_set_clock(soc_periph_dac_digi_clk_src_t clk_src,
hal_utils_calc_clk_div_frac_accurate(&adc_clk_info, &adc_clk_div);
/* Step 4: Set the clock coefficients */
dac_ll_digi_clk_inv(true);
dac_ll_digi_set_trigger_interval(interval); // secondary clock division
dac_ll_dma_clk_inv(true);
dac_ll_dma_set_timer_target(interval); // secondary clock division
adc_ll_digi_controller_clk_div(adc_clk_div.integer - 1, adc_clk_div.denominator, adc_clk_div.numerator);
adc_ll_digi_clk_sel((adc_continuous_clk_src_t)clk_src);
return ESP_OK;
@@ -174,7 +174,7 @@ esp_err_t dac_priv_dma_init(soc_periph_dac_digi_clk_src_t clk_src, uint32_t freq
err, TAG, "Failed to allocate dma peripheral channel");
s_ddp->dma_chan = spi_bus_get_dma_ctx(DAC_DMA_PERIPH_SPI_HOST)->rx_dma_chan.chan_id;
spi_ll_enable_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_DONE | SPI_LL_INTR_OUT_TOTAL_EOF);
dac_ll_digi_set_convert_mode(is_alternate);
dac_ll_dma_enable_alternate_mode(is_alternate);
s_ddp->cbs = *cbs;
s_ddp->ctx = ctx;
@@ -211,6 +211,7 @@ esp_err_t dac_priv_dma_deinit(void)
}
if (s_ddp->clk_src) {
dac_ll_dma_clk_inv(false);
ESP_RETURN_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)s_ddp->clk_src, false), TAG, "disable DAC digital clock source failed");
s_ddp->clk_src = 0;
}
@@ -224,12 +225,14 @@ static void s_dac_priv_dma_reset(void)
{
spi_ll_dma_tx_reset(s_ddp->periph_dev, s_ddp->dma_chan);
spi_ll_dma_tx_fifo_reset(s_ddp->periph_dev);
dac_ll_dma_reset_fifo();
dac_ll_dma_reset_fsm();
}
void dac_priv_dma_enable(void)
{
s_dac_priv_dma_reset();
dac_ll_digi_trigger_output(true);
dac_ll_dma_enable_timer(true);
esp_intr_enable(s_ddp->intr_handle);
}
@@ -237,7 +240,7 @@ void dac_priv_dma_disable(void)
{
s_dac_priv_dma_reset();
spi_ll_dma_tx_stop(s_ddp->periph_dev, s_ddp->dma_chan);
dac_ll_digi_trigger_output(false);
dac_ll_dma_enable_timer(false);
esp_intr_disable(s_ddp->intr_handle);
}
@@ -245,6 +248,7 @@ void dac_priv_dma_trans_start(uintptr_t desc_addr)
{
spi_ll_dma_tx_reset(s_ddp->periph_dev, s_ddp->dma_chan);
spi_ll_dma_tx_fifo_reset(s_ddp->periph_dev);
dac_ll_dma_reset_fifo();
spi_ll_dma_tx_start(s_ddp->periph_dev, s_ddp->dma_chan, (lldesc_t *)desc_addr);
}