Merge branch 'refactor/dac_ll_sintx' into 'master'

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

See merge request espressif/esp-idf!51995
This commit is contained in:
Hu Rui
2026-09-20 15:27:43 +08:00
12 changed files with 520 additions and 303 deletions

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)

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

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)

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;

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
}

View File

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

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

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -181,25 +181,25 @@ static void s_disable_dac(adc_oneshot_hal_ctx_t *hal, adc_channel_t channel)
* If enabled(default), ADC RTC controller sampling will cause the DAC channel output voltage.
*/
if (hal->unit == ADC_UNIT_1) {
dac_ll_rtc_sync_by_adc(false);
dac_ll_sync_by_adc(false);
}
#if SOC_IS(ESP32)
if (hal->unit == ADC_UNIT_2) {
if (channel == ADC_CHANNEL_8) {
dac_ll_power_down(DAC_CHAN_0); // the same as DAC channel 0
dac_ll_pad_power_down(DAC_CHAN_0); // the same as DAC channel 0
}
if (channel == ADC_CHANNEL_9) {
dac_ll_power_down(DAC_CHAN_1);
dac_ll_pad_power_down(DAC_CHAN_1);
}
}
#elif SOC_IS(ESP32S2)
if (hal->unit == ADC_UNIT_2) {
if (channel == ADC_CHANNEL_6) {
dac_ll_power_down(DAC_CHAN_0); // the same as DAC channel 0
dac_ll_pad_power_down(DAC_CHAN_0); // the same as DAC channel 0
}
if (channel == ADC_CHANNEL_7) {
dac_ll_power_down(DAC_CHAN_1);
dac_ll_pad_power_down(DAC_CHAN_1);
}
}
#else

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -19,56 +19,84 @@
#include "soc/rtc_io_struct.h"
#include "soc/sens_struct.h"
#include "hal/dac_types.h"
#include "hal/dac_types_private.h"
#define SOC_DAC_DC_VIA_SINTX 0
#define SOC_DAC_SINTX_HAS_TIMER_TARGET 0
#define SOC_DAC_SINTX_LUT_SIGNED 0
#ifdef __cplusplus
extern "C" {
#endif
#define DAC_LL_CW_PHASE_0 0x02
#define DAC_LL_CW_PHASE_180 0x03
/*---------------------------------------------------------------
DAC pad setting
---------------------------------------------------------------*/
/**
* Power on dac module and start output voltage.
* @brief Power on the DAC pad and start outputting voltage.
*
* @note Before powering up, make sure the DAC PAD is set to RTC PAD and floating status.
* @param channel DAC channel num.
*/
static inline void dac_ll_power_on(dac_channel_t channel)
static inline void dac_ll_pad_power_on(dac_channel_t channel)
{
RTCIO.pad_dac[channel].dac_xpd_force = 1;
RTCIO.pad_dac[channel].xpd_dac = 1;
}
/**
* Power done dac module and stop output voltage.
* @brief Power down the DAC pad and stop outputting voltage.
*
* @param channel DAC channel num.
*/
static inline void dac_ll_power_down(dac_channel_t channel)
static inline void dac_ll_pad_power_down(dac_channel_t channel)
{
RTCIO.pad_dac[channel].dac_xpd_force = 0;
RTCIO.pad_dac[channel].xpd_dac = 0;
}
/**
* Output voltage with value (8 bit).
* @brief Select the internal data source that drives a DAC channel output.
*
* @note Since dac_dig_force is shared between the two channels, either both channels must use DMA as their data source, or neither can.
* @note When selecting the DMA source, the DAC output data comes from the I2S DMA.
*
* @param channel DAC channel num.
* @param value Output value. Value range: 0 ~ 255.
* The corresponding range of voltage is 0v ~ VDD3P3_RTC.
* @param source Data source, see `dac_data_source_t`
*/
__attribute__((always_inline))
static inline void dac_ll_update_output_value(dac_channel_t channel, uint8_t value)
static inline void dac_ll_pad_set_data_source(dac_channel_t channel, dac_data_source_t source)
{
if (channel == DAC_CHAN_0) {
SENS.sar_dac_ctrl2.dac_cw_en1 = 0;
HAL_FORCE_MODIFY_U32_REG_FIELD(RTCIO.pad_dac[channel], dac, value);
} else if (channel == DAC_CHAN_1) {
SENS.sar_dac_ctrl2.dac_cw_en2 = 0;
HAL_FORCE_MODIFY_U32_REG_FIELD(RTCIO.pad_dac[channel], dac, value);
if (source == DAC_DATA_SOURCE_DMA) {
SENS.sar_dac_ctrl1.dac_dig_force = true;
} else {
bool cw_en = (source == DAC_DATA_SOURCE_COSINE);
if (channel == DAC_CHAN_0) {
SENS.sar_dac_ctrl2.dac_cw_en1 = cw_en;
} else if (channel == DAC_CHAN_1) {
SENS.sar_dac_ctrl2.dac_cw_en2 = cw_en;
}
SENS.sar_dac_ctrl1.dac_dig_force = false;
}
}
/**
* @brief Set the DAC output code (8 bit).
*
* @param channel DAC channel num.
* @param code Output code. Range: 0 ~ 255.
* The corresponding voltage range is 0 V ~ VDD3P3_RTC.
*/
__attribute__((always_inline))
static inline void dac_ll_pad_set_output_code(dac_channel_t channel, uint8_t code)
{
HAL_FORCE_MODIFY_U32_REG_FIELD(RTCIO.pad_dac[channel], dac, code);
}
/*---------------------------------------------------------------
DAC controller setting
---------------------------------------------------------------*/
/**
* Enable/disable the synchronization operation function of ADC1 and DAC.
*
@@ -76,56 +104,39 @@ static inline void dac_ll_update_output_value(dac_channel_t channel, uint8_t val
*
* @param enable Enable or disable adc and dac synchronization function.
*/
static inline void dac_ll_rtc_sync_by_adc(bool enable)
static inline void dac_ll_sync_by_adc(bool enable)
{
SENS.sar_meas_ctrl2.sar1_dac_xpd_fsm = enable;
}
/************************************/
/* DAC cosine wave generator API's */
/************************************/
/*---------------------------------------------------------------
Cosine wave generator setting
---------------------------------------------------------------*/
/**
* Enable cosine wave generator output.
* @brief Enable the cosine wave generator phase accumulator.
*/
static inline void dac_ll_cw_generator_enable(void)
static inline void dac_ll_cw_enable_tone(void)
{
SENS.sar_dac_ctrl1.sw_tone_en = 1;
}
/**
* Disable cosine wave generator output.
* @brief Disable the cosine wave generator
*/
static inline void dac_ll_cw_generator_disable(void)
static inline void dac_ll_cw_disable(void)
{
SENS.sar_dac_ctrl1.sw_tone_en = 0;
}
/**
* Enable the cosine wave generator of DAC channel.
* Set the step increment of the cosine wave generator.
*
* @param channel DAC channel num.
* @param enable
* @note cosine wave frequency = (dig_clk_rtc_freq * fstep) / 2^16
*/
static inline void dac_ll_cw_enable_channel(dac_channel_t channel, bool enable)
static inline void dac_ll_cw_set_fstep(uint16_t fstep)
{
if (channel == DAC_CHAN_0) {
SENS.sar_dac_ctrl2.dac_cw_en1 = enable;
} else if (channel == DAC_CHAN_1) {
SENS.sar_dac_ctrl2.dac_cw_en2 = enable;
}
}
/**
* Set frequency of cosine wave generator output.
*
* @note We know that CLK8M is about 8M, but don't know the actual value. so this freq have limited error.
* @param freq_hz CW generator frequency. Range: >= 130Hz, no exact ceiling limitation, but will distort when reach several MHz
* @param rtc8m_freq the calibrated RTC 8M clock frequency
*/
static inline void dac_ll_cw_set_freq(uint32_t freq, uint32_t rtc8m_freq)
{
uint32_t sw_freq = (uint32_t)(((uint64_t)freq << 16) / rtc8m_freq);
HAL_FORCE_MODIFY_U32_REG_FIELD(SENS.sar_dac_ctrl1, sw_fstep, (sw_freq > 0xFFFF) ? 0xFFFF : sw_freq);
HAL_FORCE_MODIFY_U32_REG_FIELD(SENS.sar_dac_ctrl1, sw_fstep, fstep);
}
/**
@@ -163,38 +174,33 @@ static inline void dac_ll_cw_set_phase(dac_channel_t channel, dac_cosine_phase_t
}
/**
* Set the voltage value of the DC component of the cosine wave generator output.
* @brief Set the DC offset of the cosine wave generator output.
*
* @note The DC offset setting should be after phase setting.
* @note Unreasonable settings can cause the signal to be oversaturated.
* @note On ESP32, dac_inv also inverts the DC component. The caller must
* compensate (e.g. negate) the offset for 180° phase before calling this.
* @param channel DAC channel num.
* @param offset DC value. Range: -128 ~ 127.
* @param offset DC offset. Range: -128 ~ 127.
*/
static inline void dac_ll_cw_set_dc_offset(dac_channel_t channel, int8_t offset)
static inline void dac_ll_cw_set_offset(dac_channel_t channel, int8_t offset)
{
if (channel == DAC_CHAN_0) {
if (SENS.sar_dac_ctrl2.dac_inv1 == DAC_LL_CW_PHASE_180) {
offset = -offset;
}
HAL_FORCE_MODIFY_U32_REG_FIELD(SENS.sar_dac_ctrl2, dac_dc1, offset);
} else if (channel == DAC_CHAN_1) {
if (SENS.sar_dac_ctrl2.dac_inv2 == DAC_LL_CW_PHASE_180) {
offset = -offset;
}
HAL_FORCE_MODIFY_U32_REG_FIELD(SENS.sar_dac_ctrl2, dac_dc2, offset);
}
}
/************************************/
/* DAC DMA API's */
/************************************/
/*---------------------------------------------------------------
DAC DMA setting
---------------------------------------------------------------*/
/**
* Enable/disable DAC output data from I2S DMA.
* I2S_CLK connect to DAC_CLK, I2S_DATA_OUT connect to DAC_DATA.
* @brief Enable/disable invert the DAC DMA clock signal.
*
* @param enable true to invert, false otherwise
*/
static inline void dac_ll_digi_enable_dma(bool enable)
static inline void dac_ll_dma_clk_inv(bool enable)
{
SENS.sar_dac_ctrl1.dac_dig_force = enable;
SENS.sar_dac_ctrl1.dac_clk_inv = enable;
}

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -17,28 +17,30 @@
#include "hal/misc.h"
#include "hal/dac_periph.h"
#include "hal/dac_types.h"
#include "hal/dac_types_private.h"
#include "soc/apb_saradc_struct.h"
#include "soc/sens_struct.h"
#include "soc/rtc_io_struct.h"
#include "soc/apb_saradc_reg.h"
#define SOC_DAC_DC_VIA_SINTX 0
#define SOC_DAC_SINTX_HAS_TIMER_TARGET 0
#define SOC_DAC_SINTX_LUT_SIGNED 0
#ifdef __cplusplus
extern "C" {
#endif
#define DAC_LL_CW_PHASE_0 0x02
#define DAC_LL_CW_PHASE_180 0x03
/*---------------------------------------------------------------
DAC common setting
DAC pad setting
---------------------------------------------------------------*/
/**
* Power on dac module and start output voltage.
* @brief Power on the DAC pad and start outputting voltage.
*
* @note Before powering up, make sure the DAC PAD is set to RTC PAD and floating status.
* @param channel DAC channel num.
*/
static inline void dac_ll_power_on(dac_channel_t channel)
static inline void dac_ll_pad_power_on(dac_channel_t channel)
{
SENS.sar_dac_ctrl1.dac_clkgate_en = 1;
RTCIO.pad_dac[channel].dac_xpd_force = 1;
@@ -46,11 +48,11 @@ static inline void dac_ll_power_on(dac_channel_t channel)
}
/**
* Power done dac module and stop output voltage.
* @brief Power down the DAC pad and stop outputting voltage.
*
* @param channel DAC channel num.
*/
static inline void dac_ll_power_down(dac_channel_t channel)
static inline void dac_ll_pad_power_down(dac_channel_t channel)
{
RTCIO.pad_dac[channel].dac_xpd_force = 0;
RTCIO.pad_dac[channel].xpd_dac = 0;
@@ -59,32 +61,51 @@ static inline void dac_ll_power_down(dac_channel_t channel)
}
}
/*---------------------------------------------------------------
RTC controller setting
---------------------------------------------------------------*/
/**
* Output voltage with value (8 bit).
* @brief Select the internal data source that drives a DAC channel output.
*
* @note Since dac_dig_force is shared between the two channels, either both channels must use DMA as their data source, or neither can.
*
* @param channel DAC channel num.
* @param value Output value. Value range: 0 ~ 255.
* The corresponding range of voltage is 0v ~ VDD3P3_RTC.
* @param source Data source, see `dac_data_source_t`
*/
__attribute__((always_inline))
static inline void dac_ll_update_output_value(dac_channel_t channel, uint8_t value)
static inline void dac_ll_pad_set_data_source(dac_channel_t channel, dac_data_source_t source)
{
if (channel == DAC_CHAN_0) {
SENS.sar_dac_ctrl2.dac_cw_en1 = 0;
HAL_FORCE_MODIFY_U32_REG_FIELD(RTCIO.pad_dac[channel], dac, value);
} else if (channel == DAC_CHAN_1) {
SENS.sar_dac_ctrl2.dac_cw_en2 = 0;
HAL_FORCE_MODIFY_U32_REG_FIELD(RTCIO.pad_dac[channel], dac, value);
if (source == DAC_DATA_SOURCE_DMA) {
SENS.sar_dac_ctrl1.dac_dig_force = true;
APB_SARADC.apb_dac_ctrl.apb_dac_trans = true;
} else {
bool cw_en = (source == DAC_DATA_SOURCE_COSINE);
if (channel == DAC_CHAN_0) {
SENS.sar_dac_ctrl2.dac_cw_en1 = cw_en;
} else if (channel == DAC_CHAN_1) {
SENS.sar_dac_ctrl2.dac_cw_en2 = cw_en;
}
SENS.sar_dac_ctrl1.dac_dig_force = false;
APB_SARADC.apb_dac_ctrl.apb_dac_trans = false;
}
}
/**
* @brief Set the DAC output code (8 bit).
*
* @param channel DAC channel num.
* @param code Output code. Range: 0 ~ 255.
* The corresponding voltage range is 0 V ~ VDD3P3_RTC.
*/
__attribute__((always_inline))
static inline void dac_ll_pad_set_output_code(dac_channel_t channel, uint8_t code)
{
HAL_FORCE_MODIFY_U32_REG_FIELD(RTCIO.pad_dac[channel], dac, code);
}
/*---------------------------------------------------------------
DAC controller setting
---------------------------------------------------------------*/
/**
* Reset dac by software.
*/
static inline void dac_ll_rtc_reset(void)
static inline void dac_ll_reset(void)
{
SENS.sar_dac_ctrl1.dac_reset = 1;
SENS.sar_dac_ctrl1.dac_reset = 0;
@@ -97,56 +118,38 @@ static inline void dac_ll_rtc_reset(void)
*
* @param enable Enable or disable adc and dac synchronization function.
*/
static inline void dac_ll_rtc_sync_by_adc(bool enable)
static inline void dac_ll_sync_by_adc(bool enable)
{
SENS.sar_amp_ctrl3.sar1_dac_xpd_fsm = enable;
}
/************************************/
/* DAC cosine wave generator API's */
/************************************/
/*---------------------------------------------------------------
DAC cosine wave generator setting
---------------------------------------------------------------*/
/**
* Enable cosine wave generator output.
* @brief Enable the cosine wave generator phase accumulator.
*/
static inline void dac_ll_cw_generator_enable(void)
static inline void dac_ll_cw_enable_tone(void)
{
SENS.sar_dac_ctrl1.sw_tone_en = 1;
}
/**
* Disable cosine wave generator output.
* @brief Disable the cosine wave generator
*/
static inline void dac_ll_cw_generator_disable(void)
static inline void dac_ll_cw_disable(void)
{
SENS.sar_dac_ctrl1.sw_tone_en = 0;
}
/**
* Enable the cosine wave generator of DAC channel.
* Set the step increment of the cosine wave generator.
*
* @param channel DAC channel num.
* @param enable
* @note cosine wave frequency = (dig_clk_rtc_freq * fstep) / 2^16
*/
static inline void dac_ll_cw_enable_channel(dac_channel_t channel, bool enable)
static inline void dac_ll_cw_set_fstep(uint16_t fstep)
{
if (channel == DAC_CHAN_0) {
SENS.sar_dac_ctrl2.dac_cw_en1 = enable;
} else if (channel == DAC_CHAN_1) {
SENS.sar_dac_ctrl2.dac_cw_en2 = enable;
}
}
/**
* Set frequency of cosine wave generator output.
*
* @note We know that CLK8M is about 8M, but don't know the actual value. so this freq have limited error.
* @param freq_hz CW generator frequency. Range: >= 130Hz, no exact ceiling limitation, but will distort when reach several MHz
* @param rtc8m_freq the calibrated RTC 8M clock frequency
*/
static inline void dac_ll_cw_set_freq(uint32_t freq, uint32_t rtc8m_freq)
{
uint32_t sw_freq = (uint32_t)(((uint64_t)freq << 16) / rtc8m_freq);
HAL_FORCE_MODIFY_U32_REG_FIELD(SENS.sar_dac_ctrl1, sw_fstep, (sw_freq > 0xFFFF) ? 0xFFFF : sw_freq);
HAL_FORCE_MODIFY_U32_REG_FIELD(SENS.sar_dac_ctrl1, sw_fstep, fstep);
}
/**
@@ -184,101 +187,84 @@ static inline void dac_ll_cw_set_phase(dac_channel_t channel, dac_cosine_phase_t
}
/**
* Set the voltage value of the DC component of the cosine wave generator output.
* @brief Set the DC offset of the cosine wave generator output.
*
* @note The DC offset setting should be after phase setting.
* @note Unreasonable settings can cause the signal to be oversaturated.
* @note On ESP32-S2, dac_inv also inverts the DC component. The caller must
* compensate (e.g. negate) the offset for 180° phase before calling this.
* @param channel DAC channel num.
* @param offset DC value. Range: -128 ~ 127.
* @param offset DC offset. Range: -128 ~ 127.
*/
static inline void dac_ll_cw_set_dc_offset(dac_channel_t channel, int8_t offset)
static inline void dac_ll_cw_set_offset(dac_channel_t channel, int8_t offset)
{
if (channel == DAC_CHAN_0) {
if (SENS.sar_dac_ctrl2.dac_inv1 == DAC_LL_CW_PHASE_180) {
offset = -offset;
}
HAL_FORCE_MODIFY_U32_REG_FIELD(SENS.sar_dac_ctrl2, dac_dc1, offset);
} else if (channel == DAC_CHAN_1) {
if (SENS.sar_dac_ctrl2.dac_inv2 == DAC_LL_CW_PHASE_180) {
offset = -offset;
}
HAL_FORCE_MODIFY_U32_REG_FIELD(SENS.sar_dac_ctrl2, dac_dc2, offset);
}
}
/*---------------------------------------------------------------
Digital controller setting
DAC DMA setting
---------------------------------------------------------------*/
/************************************/
/* DAC DMA API's */
/************************************/
/**
* Enable/disable invert the DAC digital controller clock signal.
* @brief Enable/disable invert the DAC DMA clock signal.
*
* @param enable true or false.
* @param enable true to invert, false otherwise
*
* @note On ESP32-S2, when the DAC is driven through DMA, enabling inv_clk is necessary to avoid glitches in the output waveform.
*/
static inline void dac_ll_digi_clk_inv(bool enable)
static inline void dac_ll_dma_clk_inv(bool enable)
{
SENS.sar_dac_ctrl1.dac_clk_inv = enable;
}
/**
* Enable/disable DAC-DMA mode for dac digital controller.
* @brief Set the DMA path timer target.
*
* @note The clocks of the DAC digital controller use the ADC digital controller clock divider.
* @note DMA output frequency = controller_clk / timer_target.
*
* @param timer_target Number of divided-clock cycles between DAC outputs.
*/
static inline void dac_ll_digi_enable_dma(bool enable)
static inline void dac_ll_dma_set_timer_target(uint32_t timer_target)
{
SENS.sar_dac_ctrl1.dac_dig_force = enable;
APB_SARADC.apb_dac_ctrl.apb_dac_trans = enable;
APB_SARADC.apb_dac_ctrl.dac_timer_target = timer_target;
}
/**
* Sets the number of interval clock cycles for the digital controller to trigger the DAC output.
* Expression: `dac_output_freq` = `controller_clk` / interval.
* @brief Enable/disable the DAC DMA output timer.
*
* @note The clocks of the DAC digital controller use the ADC digital controller clock divider.
*
* @param cycle The number of clock cycles for the trigger output interval. The unit is the divided clock.
* @param enable true to enable, false to disable
*/
static inline void dac_ll_digi_set_trigger_interval(uint32_t cycle)
{
APB_SARADC.apb_dac_ctrl.dac_timer_target = cycle;
}
/**
* Enable/disable DAC digital controller to trigger the DAC output.
*
* @param enable true or false.
*/
static inline void dac_ll_digi_trigger_output(bool enable)
static inline void dac_ll_dma_enable_timer(bool enable)
{
APB_SARADC.apb_dac_ctrl.dac_timer_en = enable;
}
/**
* Set DAC conversion mode for digital controller.
* @brief Enable/disable the alternate (ping-pong) output mode of the DMA path.
*
* @param mode Conversion mode select. See ``dac_digi_convert_mode_t``.
* @param enable true to route consecutive samples alternately to the two channels
*/
static inline void dac_ll_digi_set_convert_mode(bool is_alternate)
static inline void dac_ll_dma_enable_alternate_mode(bool enable)
{
APB_SARADC.apb_dac_ctrl.apb_dac_alter_mode = is_alternate;
APB_SARADC.apb_dac_ctrl.apb_dac_alter_mode = enable;
}
/**
* Reset FIFO of DAC digital controller.
* @brief Reset the DAC DMA FIFO.
*/
static inline void dac_ll_digi_fifo_reset(void)
static inline void dac_ll_dma_reset_fifo(void)
{
APB_SARADC.apb_dac_ctrl.dac_reset_fifo = 1;
APB_SARADC.apb_dac_ctrl.dac_reset_fifo = 0;
}
/**
* Reset DAC digital controller.
* @brief Reset the DAC DMA FSM, i.e. the DAC-side consumer of DMA samples (timer, alter-mode demux).
*/
static inline void dac_ll_digi_reset(void)
static inline void dac_ll_dma_reset_fsm(void)
{
APB_SARADC.apb_dac_ctrl.apb_dac_rst = 1;
APB_SARADC.apb_dac_ctrl.apb_dac_rst = 0;

View File

@@ -15,15 +15,6 @@ extern "C" {
#if SOC_DAC_SUPPORTED
/**
* ESP32:
* - DAC channel 0: GPIO25
* - DAC channel 1: GPIO26
* ESP32S2:
* - DAC channel 0: GPIO17
* - DAC channel 1: GPIO18
*/
typedef enum {
DAC_CHAN_0 = 0,
DAC_CHAN_1 = 1,

View File

@@ -0,0 +1,33 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "soc/soc_caps.h"
#ifdef __cplusplus
extern "C" {
#endif
#if SOC_DAC_SUPPORTED
/**
* @brief The internal data source that drives a DAC channel output
*
* @note The number of distinct sources differs per chip:
* - ESP32/ESP32-S2: cosine generator, DMA, and direct register output are three separate states.
* - ESP32-S31: the Sintx path serves both the cosine generator and the direct software output.
*/
typedef enum {
DAC_DATA_SOURCE_COSINE = 0, /*!< Channel output driven by the on-chip cosine wave generator (Sintx) */
DAC_DATA_SOURCE_DMA = 1, /*!< Channel output driven by the digital DMA path */
DAC_DATA_SOURCE_DIRECT = 2, /*!< Channel output driven by the direct register value */
} dac_data_source_t;
#endif // SOC_DAC_SUPPORTED
#ifdef __cplusplus
}
#endif