diff --git a/components/esp_driver_dac/dac_common.c b/components/esp_driver_dac/dac_common.c index cb5537101d0..3478a839ffe 100644 --- a/components/esp_driver_dac/dac_common.c +++ b/components/esp_driver_dac/dac_common.c @@ -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) diff --git a/components/esp_driver_dac/dac_continuous.c b/components/esp_driver_dac/dac_continuous.c index 1708e5c0258..a70a5504d71 100644 --- a/components/esp_driver_dac/dac_continuous.c +++ b/components/esp_driver_dac/dac_continuous.c @@ -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); diff --git a/components/esp_driver_dac/dac_cosine.c b/components/esp_driver_dac/dac_cosine.c index 220b00f9047..ff297c532c4 100644 --- a/components/esp_driver_dac/dac_cosine.c +++ b/components/esp_driver_dac/dac_cosine.c @@ -4,26 +4,36 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include #include #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) diff --git a/components/esp_driver_dac/dac_oneshot.c b/components/esp_driver_dac/dac_oneshot.c index 8f1daf8d728..f6843d877fd 100644 --- a/components/esp_driver_dac/dac_oneshot.c +++ b/components/esp_driver_dac/dac_oneshot.c @@ -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; diff --git a/components/esp_driver_dac/dac_priv_common.h b/components/esp_driver_dac/dac_priv_common.h index c8e2841671e..91b1a69f905 100644 --- a/components/esp_driver_dac/dac_priv_common.h +++ b/components/esp_driver_dac/dac_priv_common.h @@ -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 } diff --git a/components/esp_driver_dac/esp32/dac_dma.c b/components/esp_driver_dac/esp32/dac_dma.c index b6e7fc6a8bb..49406c5fe70 100644 --- a/components/esp_driver_dac/esp32/dac_dma.c +++ b/components/esp_driver_dac/esp32/dac_dma.c @@ -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; } diff --git a/components/esp_driver_dac/esp32s2/dac_dma.c b/components/esp_driver_dac/esp32s2/dac_dma.c index a9095f73c87..7004dd1d429 100644 --- a/components/esp_driver_dac/esp32s2/dac_dma.c +++ b/components/esp_driver_dac/esp32s2/dac_dma.c @@ -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); } diff --git a/components/esp_hal_ana_conv/adc_oneshot_hal.c b/components/esp_hal_ana_conv/adc_oneshot_hal.c index 6fdad1e98e6..d9353411e09 100644 --- a/components/esp_hal_ana_conv/adc_oneshot_hal.c +++ b/components/esp_hal_ana_conv/adc_oneshot_hal.c @@ -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 diff --git a/components/esp_hal_ana_conv/esp32/include/hal/dac_ll.h b/components/esp_hal_ana_conv/esp32/include/hal/dac_ll.h index a3cd7058456..3e1b24e7d54 100644 --- a/components/esp_hal_ana_conv/esp32/include/hal/dac_ll.h +++ b/components/esp_hal_ana_conv/esp32/include/hal/dac_ll.h @@ -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; } diff --git a/components/esp_hal_ana_conv/esp32s2/include/hal/dac_ll.h b/components/esp_hal_ana_conv/esp32s2/include/hal/dac_ll.h index c5e8d97b8bd..3729c2dd943 100644 --- a/components/esp_hal_ana_conv/esp32s2/include/hal/dac_ll.h +++ b/components/esp_hal_ana_conv/esp32s2/include/hal/dac_ll.h @@ -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; diff --git a/components/esp_hal_ana_conv/include/hal/dac_types.h b/components/esp_hal_ana_conv/include/hal/dac_types.h index 0ddf0346594..14ebd56bf7a 100644 --- a/components/esp_hal_ana_conv/include/hal/dac_types.h +++ b/components/esp_hal_ana_conv/include/hal/dac_types.h @@ -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, diff --git a/components/esp_hal_ana_conv/include/hal/dac_types_private.h b/components/esp_hal_ana_conv/include/hal/dac_types_private.h new file mode 100644 index 00000000000..61cbe8ee855 --- /dev/null +++ b/components/esp_hal_ana_conv/include/hal/dac_types_private.h @@ -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