mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-03 03:31:41 +03:00
refactor(dac): rename LL APIs and introduce sintx manager
This commit is contained in:
@@ -38,7 +38,7 @@ static _Atomic dac_channel_fsm_t s_dac_chan_fsm[SOC_DAC_CHAN_NUM] = {
|
||||
/* Global dac spin lock for the whole DAC driver */
|
||||
portMUX_TYPE dac_spinlock = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
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");
|
||||
|
||||
@@ -51,7 +51,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");
|
||||
|
||||
@@ -64,7 +64,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");
|
||||
|
||||
@@ -73,8 +73,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;
|
||||
@@ -84,14 +85,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;
|
||||
@@ -101,6 +102,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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#endif
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "hal/dac_types.h"
|
||||
#include "hal/dac_types_private.h"
|
||||
#include "hal/dac_ll.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
@@ -56,6 +58,11 @@ extern portMUX_TYPE dac_priv_spinlock;
|
||||
#define DAC_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
|
||||
#endif
|
||||
|
||||
#define DAC_DIV_ROUND(n, d) (((n) + (d) / 2) / (d))
|
||||
#define DAC_DIV_CEIL(n, d) (((n) + (d) - 1) / (d))
|
||||
#define DAC_MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define DAC_CLAMP(x, min, max) ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x)))
|
||||
|
||||
/**
|
||||
* @brief Register dac channel in the driver, in case a same channel is reused by different modes
|
||||
*
|
||||
@@ -65,7 +72,7 @@ extern portMUX_TYPE dac_priv_spinlock;
|
||||
* - ESP_ERR_INVALID_ARG The channel id is incorrect
|
||||
* - ESP_OK Register the channel success
|
||||
*/
|
||||
esp_err_t dac_priv_register_channel(dac_channel_t chan_id);
|
||||
esp_err_t dac_priv_channel_register(dac_channel_t chan_id);
|
||||
|
||||
/**
|
||||
* @brief Deregister dac channel in the driver
|
||||
@@ -76,18 +83,19 @@ esp_err_t dac_priv_register_channel(dac_channel_t chan_id);
|
||||
* - ESP_ERR_INVALID_ARG The channel id is incorrect
|
||||
* - ESP_OK Deregister the channel success
|
||||
*/
|
||||
esp_err_t dac_priv_deregister_channel(dac_channel_t chan_id);
|
||||
esp_err_t dac_priv_channel_deregister(dac_channel_t chan_id);
|
||||
|
||||
/**
|
||||
* @brief Enable the DAC channel and turn on its power
|
||||
*
|
||||
* @param chan_id DAC channel id
|
||||
* @param source Channel data source
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_STATE The channel has not been registered or already enabled
|
||||
* - ESP_ERR_INVALID_ARG The channel id is incorrect
|
||||
* - ESP_OK Enable the channel success
|
||||
*/
|
||||
esp_err_t dac_priv_enable_channel(dac_channel_t chan_id);
|
||||
esp_err_t dac_priv_channel_enable(dac_channel_t chan_id, dac_data_source_t source);
|
||||
|
||||
/**
|
||||
* @brief Disable the DAC channel and turn off its power
|
||||
@@ -98,7 +106,56 @@ esp_err_t dac_priv_enable_channel(dac_channel_t chan_id);
|
||||
* - ESP_ERR_INVALID_ARG The channel id is incorrect
|
||||
* - ESP_OK Disable the channel success
|
||||
*/
|
||||
esp_err_t dac_priv_disable_channel(dac_channel_t chan_id);
|
||||
esp_err_t dac_priv_channel_disable(dac_channel_t chan_id);
|
||||
|
||||
/**
|
||||
* @brief Acquire the shared cosine wave (Sintx/tone) generator (reference counted)
|
||||
*
|
||||
* @note The cosine wave generator is a single shared resource: all DAC channels that output a
|
||||
* cosine wave share one generator (and thus one frequency). This claims the generator in tone
|
||||
* mode, programs the wave frequency, and starts the generator on the first acquire. The first
|
||||
* acquirer (or any acquirer passing `force_set_freq`) programs the frequency; a later acquirer
|
||||
* requesting a different frequency without `force_set_freq` is rejected. On targets where the
|
||||
* tone generator and the direct (DC) output share one Sintx generator, a tone acquire is also
|
||||
* rejected while the DC path is in use.
|
||||
*
|
||||
* @param[in] freq_hz The cosine wave frequency in Hz
|
||||
* @param[in] clk_freq_hz The clock frequency that drives the generator in Hz
|
||||
* @param[in] force_set_freq Force (re)programming the frequency even if the generator is in use
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_STATE The generator is busy on a conflicting mode or frequency
|
||||
* - ESP_OK Success
|
||||
*/
|
||||
esp_err_t dac_priv_sintx_acquire_tone(uint32_t freq_hz, uint32_t clk_freq_hz, bool force_set_freq);
|
||||
|
||||
#if SOC_DAC_DC_VIA_SINTX
|
||||
/**
|
||||
* @brief Acquire the shared Sintx generator for the direct (DC) software output (reference counted)
|
||||
*
|
||||
* @note On some targets, the direct (DC) software output runs through the same Sintx generator as
|
||||
* the cosine wave output, so the two are mutually exclusive. This claims the generator in DC
|
||||
* mode and, on the first acquire, starts the Sintx timer that pushes the written DC value to
|
||||
* the pad.
|
||||
*
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_STATE The generator is busy on the cosine path
|
||||
* - ESP_OK Success
|
||||
*/
|
||||
esp_err_t dac_priv_sintx_acquire_dc(void);
|
||||
#endif // SOC_DAC_DC_VIA_SINTX
|
||||
|
||||
/**
|
||||
* @brief Release the shared Sintx generator (reference counted)
|
||||
*
|
||||
* @note Releases one reference acquired by `dac_priv_sintx_acquire_tone()` or
|
||||
* `dac_priv_sintx_acquire_dc()`. The generator is stopped once the last reference is
|
||||
* released.
|
||||
*
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_STATE The generator is not in use
|
||||
* - ESP_OK Success
|
||||
*/
|
||||
esp_err_t dac_priv_sintx_release(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "hal/adc_ll.h"
|
||||
#include "hal/dac_ll.h"
|
||||
#include "hal/i2s_hal.h"
|
||||
#include "hal/i2s_types.h"
|
||||
#include "hal/clk_tree_ll.h"
|
||||
@@ -148,6 +149,7 @@ esp_err_t dac_priv_dma_init(soc_periph_dac_digi_clk_src_t clk_src, uint32_t freq
|
||||
|
||||
ESP_GOTO_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)clk_src, true), err, TAG, "enable DAC digital clock source failed");
|
||||
s_ddp->clk_src = clk_src;
|
||||
dac_ll_dma_clk_inv(true);
|
||||
ESP_GOTO_ON_ERROR(s_dac_priv_dma_set_clock(clk_src, freq_hz), err, TAG, "Failed to set clock of DMA peripheral");
|
||||
|
||||
i2s_ll_enable_builtin_adc_dac(s_ddp->periph_dev, true);
|
||||
@@ -188,6 +190,7 @@ esp_err_t dac_priv_dma_deinit(void)
|
||||
}
|
||||
|
||||
if (s_ddp->clk_src) {
|
||||
dac_ll_dma_clk_inv(false);
|
||||
ESP_RETURN_ON_ERROR(esp_clk_tree_enable_src((soc_module_clk_t)s_ddp->clk_src, false), TAG, "disable DAC digital clock source failed");
|
||||
s_ddp->clk_src = 0;
|
||||
}
|
||||
|
||||
@@ -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_dma_ll_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_dma_ll_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_dma_ll_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_dma_ll_tx_start(s_ddp->periph_dev, s_ddp->dma_chan, (lldesc_t *)desc_addr);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user