mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-01 18:50:34 +03:00
Merge branch 'refactor/driver_dac_common' into 'master'
refactor(dac): improve the DAC driver See merge request espressif/esp-idf!48212
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
idf_build_get_property(target IDF_TARGET)
|
||||
|
||||
set(srcs)
|
||||
set(priv_req esp_pm esp_driver_gpio esp_hal_clock)
|
||||
set(priv_req esp_pm esp_driver_gpio esp_hal_clock esp_driver_dma)
|
||||
|
||||
if(${target} STREQUAL "linux")
|
||||
return() # This component is not supported by the POSIX/Linux simulator
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "stdatomic.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "hal/dac_periph.h"
|
||||
#include "hal/dac_types.h"
|
||||
#include "hal/dac_ll.h"
|
||||
@@ -15,77 +15,78 @@
|
||||
#include "esp_check.h"
|
||||
#include "dac_priv_common.h"
|
||||
|
||||
typedef struct {
|
||||
bool in_use;
|
||||
bool is_enabled;
|
||||
const char *mode;
|
||||
} dac_channel_info_t;
|
||||
typedef enum {
|
||||
DAC_CHAN_FSM_IDLE,
|
||||
DAC_CHAN_FSM_REGISTERED,
|
||||
DAC_CHAN_FSM_ENABLED,
|
||||
DAC_CHAN_FSM_WAIT, // transition state
|
||||
} dac_channel_fsm_t;
|
||||
|
||||
static dac_channel_info_t s_dac_chan[SOC_DAC_CHAN_NUM] = {
|
||||
[0 ... SOC_DAC_CHAN_NUM - 1] = {
|
||||
.in_use = false,
|
||||
.is_enabled = false,
|
||||
.mode = NULL,
|
||||
}
|
||||
static _Atomic dac_channel_fsm_t s_dac_chan_fsm[SOC_DAC_CHAN_NUM] = {
|
||||
[0 ... SOC_DAC_CHAN_NUM - 1] = DAC_CHAN_FSM_IDLE,
|
||||
};
|
||||
|
||||
static const char *TAG = "dac_common";
|
||||
|
||||
esp_err_t dac_priv_register_channel(dac_channel_t chan_id, const char *mode_name)
|
||||
esp_err_t dac_priv_register_channel(dac_channel_t chan_id)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(chan_id < SOC_DAC_CHAN_NUM, ESP_ERR_INVALID_ARG, TAG, "channel id is invalid");
|
||||
DAC_NULL_POINTER_CHECK(mode_name);
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (!s_dac_chan[chan_id].in_use) {
|
||||
s_dac_chan[chan_id].in_use = true;
|
||||
s_dac_chan[chan_id].mode = mode_name;
|
||||
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_IDLE;
|
||||
if (atomic_compare_exchange_strong(&s_dac_chan_fsm[chan_id], &expected_fsm, DAC_CHAN_FSM_REGISTERED)) {
|
||||
return ESP_OK;
|
||||
} else {
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
ESP_LOGE(TAG, "dac channel %d has been registered", chan_id);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "dac channel %d has been registered by %s", chan_id, s_dac_chan[chan_id].mode);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t dac_priv_deregister_channel(dac_channel_t chan_id)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(chan_id < SOC_DAC_CHAN_NUM, ESP_ERR_INVALID_ARG, TAG, "channel id is invalid");
|
||||
ESP_RETURN_ON_FALSE(!s_dac_chan[chan_id].is_enabled, ESP_ERR_INVALID_STATE, TAG, "the channel is still enabled");
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (s_dac_chan[chan_id].in_use) {
|
||||
s_dac_chan[chan_id].in_use = false;
|
||||
s_dac_chan[chan_id].mode = NULL;
|
||||
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_REGISTERED;
|
||||
if (atomic_compare_exchange_strong(&s_dac_chan_fsm[chan_id], &expected_fsm, DAC_CHAN_FSM_IDLE)) {
|
||||
return ESP_OK;
|
||||
} else {
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
ESP_LOGE(TAG, "dac channel %d is still enabled or not registered", chan_id);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t dac_priv_enable_channel(dac_channel_t chan_id)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(chan_id < SOC_DAC_CHAN_NUM, ESP_ERR_INVALID_ARG, TAG, "channel id is invalid");
|
||||
ESP_RETURN_ON_FALSE(s_dac_chan[chan_id].in_use, ESP_ERR_INVALID_STATE, TAG, "the channel is not registered");
|
||||
ESP_RETURN_ON_FALSE(IS_VALID_DAC_CHANNEL(chan_id), ESP_ERR_INVALID_ARG, TAG, "channel id is invalid");
|
||||
|
||||
gpio_num_t gpio_num = (gpio_num_t)dac_periph_signal.dac_channel_io_num[chan_id];
|
||||
gpio_config_as_analog(gpio_num);
|
||||
DAC_RTC_ENTER_CRITICAL();
|
||||
dac_ll_power_on(chan_id);
|
||||
dac_ll_rtc_sync_by_adc(false);
|
||||
DAC_RTC_EXIT_CRITICAL();
|
||||
s_dac_chan[chan_id].is_enabled = true;
|
||||
return ESP_OK;
|
||||
dac_channel_fsm_t expected_fsm = DAC_CHAN_FSM_REGISTERED;
|
||||
if (atomic_compare_exchange_strong(&s_dac_chan_fsm[chan_id], &expected_fsm, DAC_CHAN_FSM_WAIT)) {
|
||||
gpio_num_t gpio_num = (gpio_num_t)dac_periph_signal.dac_channel_io_num[chan_id];
|
||||
gpio_config_as_analog(gpio_num);
|
||||
DAC_RTC_ENTER_CRITICAL();
|
||||
dac_ll_power_on(chan_id);
|
||||
dac_ll_rtc_sync_by_adc(false);
|
||||
DAC_RTC_EXIT_CRITICAL();
|
||||
atomic_store(&s_dac_chan_fsm[chan_id], DAC_CHAN_FSM_ENABLED);
|
||||
return ESP_OK;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "dac channel %d is already enabled or not registered", chan_id);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t dac_priv_disable_channel(dac_channel_t chan_id)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(chan_id < SOC_DAC_CHAN_NUM, ESP_ERR_INVALID_ARG, TAG, "channel id is invalid");
|
||||
ESP_RETURN_ON_FALSE(s_dac_chan[chan_id].in_use, ESP_ERR_INVALID_STATE, TAG, "the channel is not registered");
|
||||
ESP_RETURN_ON_FALSE(IS_VALID_DAC_CHANNEL(chan_id), ESP_ERR_INVALID_ARG, TAG, "channel id is invalid");
|
||||
|
||||
DAC_RTC_ENTER_CRITICAL();
|
||||
dac_ll_power_down(chan_id);
|
||||
DAC_RTC_EXIT_CRITICAL();
|
||||
s_dac_chan[chan_id].is_enabled = false;
|
||||
|
||||
return ESP_OK;
|
||||
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_RTC_ENTER_CRITICAL();
|
||||
dac_ll_power_down(chan_id);
|
||||
DAC_RTC_EXIT_CRITICAL();
|
||||
atomic_store(&s_dac_chan_fsm[chan_id], DAC_CHAN_FSM_REGISTERED);
|
||||
return ESP_OK;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "dac channel %d is not enabled", chan_id);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "soc/soc_caps.h"
|
||||
#include "driver/dac_cosine.h"
|
||||
#include "hal/clk_tree_ll.h"
|
||||
#include "dac_priv_common.h"
|
||||
@@ -43,7 +42,7 @@ esp_err_t dac_cosine_new_channel(const dac_cosine_config_t *cos_cfg, dac_cosine_
|
||||
/* Parameters validation */
|
||||
DAC_NULL_POINTER_CHECK(cos_cfg);
|
||||
DAC_NULL_POINTER_CHECK(ret_handle);
|
||||
ESP_RETURN_ON_FALSE(cos_cfg->chan_id < SOC_DAC_CHAN_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid dac channel id");
|
||||
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");
|
||||
@@ -53,9 +52,9 @@ esp_err_t dac_cosine_new_channel(const dac_cosine_config_t *cos_cfg, dac_cosine_
|
||||
dac_cosine_handle_t handle = heap_caps_calloc(1, sizeof(struct dac_cosine_s), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
ESP_RETURN_ON_FALSE(handle, ESP_ERR_NO_MEM, TAG, "no memory for the dac cosine handle");
|
||||
/* Assign configurations */
|
||||
memcpy(&handle->cfg, cos_cfg, sizeof(dac_cosine_config_t));
|
||||
handle->cfg = *cos_cfg;
|
||||
/* Register the handle */
|
||||
ESP_GOTO_ON_ERROR(dac_priv_register_channel(cos_cfg->chan_id, "dac cosine"), err1, TAG, "register dac channel %d failed", cos_cfg->chan_id);
|
||||
ESP_GOTO_ON_ERROR(dac_priv_register_channel(cos_cfg->chan_id), err1, TAG, "register dac channel %d failed", cos_cfg->chan_id);
|
||||
|
||||
/* Cosine wave generator uses RTC_FAST clock which is divided from RC_FAST */
|
||||
uint32_t rtc_clk_freq = 0;
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "soc/soc_caps.h"
|
||||
#include "dac_priv_common.h"
|
||||
#include "driver/dac_oneshot.h"
|
||||
|
||||
@@ -33,16 +32,16 @@ esp_err_t dac_oneshot_new_channel(const dac_oneshot_config_t *oneshot_cfg, dac_o
|
||||
/* Parameters validation */
|
||||
DAC_NULL_POINTER_CHECK(oneshot_cfg);
|
||||
DAC_NULL_POINTER_CHECK(ret_handle);
|
||||
ESP_RETURN_ON_FALSE(oneshot_cfg->chan_id < SOC_DAC_CHAN_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid dac channel id");
|
||||
ESP_RETURN_ON_FALSE(IS_VALID_DAC_CHANNEL(oneshot_cfg->chan_id), ESP_ERR_INVALID_ARG, TAG, "invalid dac channel id");
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
/* Resources allocation */
|
||||
dac_oneshot_handle_t handle = heap_caps_calloc(1, sizeof(struct dac_oneshot_s), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
||||
ESP_RETURN_ON_FALSE(handle, ESP_ERR_NO_MEM, TAG, "no memory for the dac oneshot handle");
|
||||
memcpy(&handle->cfg, oneshot_cfg, sizeof(dac_oneshot_config_t));
|
||||
handle->cfg = *oneshot_cfg;
|
||||
|
||||
/* Register and enable the dac channel */
|
||||
ESP_GOTO_ON_ERROR(dac_priv_register_channel(oneshot_cfg->chan_id, "dac oneshot"), err2, TAG, "register dac channel %d failed", oneshot_cfg->chan_id);
|
||||
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);
|
||||
|
||||
*ret_handle = handle;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -30,20 +30,19 @@ extern portMUX_TYPE rtc_spinlock; /*!< Extern global rtc spinlock */
|
||||
* @brief Register dac channel in the driver, in case a same channel is reused by different modes
|
||||
*
|
||||
* @param[in] chan_id DAC channel id
|
||||
* @param[in] mode_name The const string of mode name
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_STATE The channel has been occupied
|
||||
* - 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, const char *mode_name);
|
||||
esp_err_t dac_priv_register_channel(dac_channel_t chan_id);
|
||||
|
||||
/**
|
||||
* @brief Deregister dac channel in the driver
|
||||
*
|
||||
* @param[in] chan_id DAC channel id
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_STATE The channel has been freed
|
||||
* - ESP_ERR_INVALID_STATE The channel has been freed or not disabled
|
||||
* - ESP_ERR_INVALID_ARG The channel id is incorrect
|
||||
* - ESP_OK Deregister the channel success
|
||||
*/
|
||||
@@ -54,9 +53,9 @@ esp_err_t dac_priv_deregister_channel(dac_channel_t chan_id);
|
||||
*
|
||||
* @param chan_id DAC channel id
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_STATE The channel has not been registered
|
||||
* - ESP_ERR_INVALID_STATE The channel has not been registered or already enabled
|
||||
* - ESP_ERR_INVALID_ARG The channel id is incorrect
|
||||
* - ESP_OK Deregister the channel success
|
||||
* - ESP_OK Enable the channel success
|
||||
*/
|
||||
esp_err_t dac_priv_enable_channel(dac_channel_t chan_id);
|
||||
|
||||
@@ -65,9 +64,9 @@ esp_err_t dac_priv_enable_channel(dac_channel_t chan_id);
|
||||
*
|
||||
* @param chan_id DAC channel id
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_STATE The channel has not been registered
|
||||
* - ESP_ERR_INVALID_STATE The channel is not enabled
|
||||
* - ESP_ERR_INVALID_ARG The channel id is incorrect
|
||||
* - ESP_OK Deregister the channel success
|
||||
* - ESP_OK Disable the channel success
|
||||
*/
|
||||
esp_err_t dac_priv_disable_channel(dac_channel_t chan_id);
|
||||
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -7,14 +7,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_bit_defs.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define DAC_DMA_EOF_INTR 0x01
|
||||
#define DAC_DMA_TEOF_INTR 0x02
|
||||
// one node in the descriptor chain is finished
|
||||
#define DAC_DMA_DONE_INTR BIT(0)
|
||||
// all nodes in the descriptor chain are finished
|
||||
#define DAC_DMA_TEOF_INTR BIT(1)
|
||||
|
||||
/**
|
||||
* @brief Initialize DAC DMA peripheral
|
||||
@@ -61,20 +65,12 @@ void dac_dma_periph_enable(void);
|
||||
void dac_dma_periph_disable(void);
|
||||
|
||||
/**
|
||||
* @brief Whether the TX_EOF interrupt is triggered
|
||||
* @brief Get the mask of the triggered interrupt
|
||||
*
|
||||
* @return
|
||||
* - uint32_t Mask of the triggered interrupt: DAC_DMA_EOF_INTR, DAC_DMA_EOF_INTR
|
||||
* - uint32_t Mask of the triggered interrupt: DAC_DMA_DONE_INTR, DAC_DMA_TEOF_INTR
|
||||
*/
|
||||
uint32_t dac_dma_periph_intr_is_triggered(void);
|
||||
|
||||
/**
|
||||
* @brief Get the descriptor that just finished sending data
|
||||
*
|
||||
* @return
|
||||
* - uint32_t The address of the EOF descriptor
|
||||
*/
|
||||
uint32_t dac_dma_periph_intr_get_eof_desc(void);
|
||||
uint32_t dac_dma_periph_intr_get_mask(void);
|
||||
|
||||
/**
|
||||
* @brief Start a DMA transaction
|
||||
@@ -82,7 +78,20 @@ uint32_t dac_dma_periph_intr_get_eof_desc(void);
|
||||
*
|
||||
* @param[in] desc_addr Descriptor address
|
||||
*/
|
||||
void dac_dma_periph_dma_trans_start(uint32_t desc_addr);
|
||||
void dac_dma_periph_trans_start(uintptr_t desc_addr);
|
||||
|
||||
/**
|
||||
* @brief Stop the current DMA transaction immediately
|
||||
*/
|
||||
void dac_dma_periph_trans_stop(void);
|
||||
|
||||
#if !SOC_IS(ESP32)
|
||||
/**
|
||||
* @brief Append the newly linked DMA descriptors to the current transaction
|
||||
* @note The caller should link new descriptors to the current tail before calling this function.
|
||||
*/
|
||||
void dac_dma_periph_trans_append(void);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -138,8 +138,6 @@ esp_err_t dac_dma_periph_init(uint32_t freq_hz, bool is_alternate, bool is_apll)
|
||||
/* Should always enable fifo */
|
||||
i2s_ll_tx_force_enable_fifo_mod(s_ddp->periph_dev, true);
|
||||
i2s_ll_dma_enable_auto_write_back(s_ddp->periph_dev, true);
|
||||
/* Enable the interrupts */
|
||||
i2s_ll_enable_intr(s_ddp->periph_dev, I2S_LL_EVENT_TX_EOF | I2S_LL_EVENT_TX_TEOF, true);
|
||||
|
||||
return ret;
|
||||
err:
|
||||
@@ -155,7 +153,6 @@ esp_err_t dac_dma_periph_deinit(void)
|
||||
|
||||
ESP_RETURN_ON_FALSE(s_ddp->intr_handle == NULL, ESP_ERR_INVALID_STATE, TAG, "The interrupt is not deregistered yet");
|
||||
ESP_RETURN_ON_ERROR(i2s_platform_release_occupation(I2S_CTLR_HP, DAC_DMA_PERIPH_I2S_NUM), TAG, "Failed to release DAC DMA peripheral");
|
||||
i2s_ll_enable_intr(s_ddp->periph_dev, I2S_LL_EVENT_TX_EOF | I2S_LL_EVENT_TX_TEOF, false);
|
||||
if (s_ddp->use_apll) {
|
||||
ESP_RETURN_ON_ERROR(esp_clk_tree_enable_src(SOC_MOD_CLK_APLL, false), TAG, "APLL disable failed");
|
||||
s_ddp->use_apll = false;
|
||||
@@ -181,7 +178,7 @@ static void s_dac_dma_periph_reset(void)
|
||||
static void s_dac_dma_periph_start(void)
|
||||
{
|
||||
i2s_ll_enable_dma(s_ddp->periph_dev, true);
|
||||
i2s_ll_tx_enable_intr(s_ddp->periph_dev);
|
||||
i2s_ll_enable_intr(s_ddp->periph_dev, I2S_LL_EVENT_TX_DONE | I2S_LL_EVENT_TX_TEOF, true);
|
||||
i2s_ll_tx_start(s_ddp->periph_dev);
|
||||
i2s_ll_dma_enable_eof_on_fifo_empty(s_ddp->periph_dev, true);
|
||||
i2s_ll_dma_enable_auto_write_back(s_ddp->periph_dev, true);
|
||||
@@ -191,7 +188,7 @@ static void s_dac_dma_periph_stop(void)
|
||||
{
|
||||
i2s_ll_tx_stop(s_ddp->periph_dev);
|
||||
i2s_ll_tx_stop_link(s_ddp->periph_dev);
|
||||
i2s_ll_tx_disable_intr(s_ddp->periph_dev);
|
||||
i2s_ll_enable_intr(s_ddp->periph_dev, I2S_LL_EVENT_TX_DONE | I2S_LL_EVENT_TX_TEOF, false);
|
||||
i2s_ll_enable_dma(s_ddp->periph_dev, false);
|
||||
i2s_ll_dma_enable_eof_on_fifo_empty(s_ddp->periph_dev, false);
|
||||
i2s_ll_dma_enable_auto_write_back(s_ddp->periph_dev, false);
|
||||
@@ -213,28 +210,26 @@ void dac_dma_periph_disable(void)
|
||||
s_dac_dma_periph_stop();
|
||||
}
|
||||
|
||||
uint32_t IRAM_ATTR dac_dma_periph_intr_is_triggered(void)
|
||||
uint32_t IRAM_ATTR dac_dma_periph_intr_get_mask(void)
|
||||
{
|
||||
uint32_t status = i2s_ll_get_intr_status(s_ddp->periph_dev);
|
||||
if (status == 0) {
|
||||
//Avoid spurious interrupt
|
||||
return false;
|
||||
// Avoid spurious interrupt
|
||||
return 0UL;
|
||||
}
|
||||
i2s_ll_clear_intr_status(s_ddp->periph_dev, status);
|
||||
uint32_t ret = 0;
|
||||
ret |= (status & I2S_LL_EVENT_TX_EOF) ? DAC_DMA_EOF_INTR : 0;
|
||||
ret |= (status & I2S_LL_EVENT_TX_DONE) ? DAC_DMA_DONE_INTR : 0;
|
||||
ret |= (status & I2S_LL_EVENT_TX_TEOF) ? DAC_DMA_TEOF_INTR : 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t IRAM_ATTR dac_dma_periph_intr_get_eof_desc(void)
|
||||
{
|
||||
uint32_t finish_desc;
|
||||
i2s_ll_tx_get_eof_des_addr(s_ddp->periph_dev, &finish_desc);
|
||||
return finish_desc;
|
||||
}
|
||||
|
||||
void dac_dma_periph_dma_trans_start(uint32_t desc_addr)
|
||||
void IRAM_ATTR dac_dma_periph_trans_start(uintptr_t desc_addr)
|
||||
{
|
||||
i2s_ll_tx_start_link(s_ddp->periph_dev, desc_addr);
|
||||
}
|
||||
|
||||
void dac_dma_periph_trans_stop(void)
|
||||
{
|
||||
i2s_ll_tx_stop_link(s_ddp->periph_dev);
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ esp_err_t dac_dma_periph_init(uint32_t freq_hz, bool is_alternate, bool is_apll)
|
||||
ESP_GOTO_ON_ERROR(spicommon_dma_chan_alloc(DAC_DMA_PERIPH_SPI_HOST, SPI_DMA_CH_AUTO, 0),
|
||||
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_EOF | SPI_LL_INTR_OUT_TOTAL_EOF);
|
||||
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);
|
||||
return ret;
|
||||
err:
|
||||
@@ -163,7 +163,7 @@ esp_err_t dac_dma_periph_deinit(void)
|
||||
ESP_RETURN_ON_ERROR(spicommon_dma_chan_free(DAC_DMA_PERIPH_SPI_HOST), TAG, "Failed to free dma peripheral channel");
|
||||
}
|
||||
ESP_RETURN_ON_FALSE(spicommon_periph_free(DAC_DMA_PERIPH_SPI_HOST), ESP_FAIL, TAG, "Failed to release DAC DMA peripheral");
|
||||
spi_ll_disable_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_EOF | SPI_LL_INTR_OUT_TOTAL_EOF);
|
||||
spi_ll_disable_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_DONE | SPI_LL_INTR_OUT_TOTAL_EOF);
|
||||
adc_apb_periph_free();
|
||||
if (s_ddp) {
|
||||
if (s_ddp->use_apll) {
|
||||
@@ -200,24 +200,29 @@ void dac_dma_periph_disable(void)
|
||||
dac_ll_digi_trigger_output(false);
|
||||
}
|
||||
|
||||
uint32_t IRAM_ATTR dac_dma_periph_intr_is_triggered(void)
|
||||
uint32_t IRAM_ATTR dac_dma_periph_intr_get_mask(void)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
ret |= spi_ll_get_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_EOF) ? DAC_DMA_EOF_INTR : 0;
|
||||
ret |= spi_ll_get_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_DONE) ? DAC_DMA_DONE_INTR : 0;
|
||||
ret |= spi_ll_get_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_TOTAL_EOF) ? DAC_DMA_TEOF_INTR : 0;
|
||||
spi_ll_clear_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_EOF);
|
||||
spi_ll_clear_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_DONE);
|
||||
spi_ll_clear_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_TOTAL_EOF);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t IRAM_ATTR dac_dma_periph_intr_get_eof_desc(void)
|
||||
{
|
||||
return spi_dma_ll_get_out_eof_desc_addr(s_ddp->periph_dev, s_ddp->dma_chan);
|
||||
}
|
||||
|
||||
void dac_dma_periph_dma_trans_start(uint32_t desc_addr)
|
||||
void IRAM_ATTR dac_dma_periph_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);
|
||||
spi_dma_ll_tx_start(s_ddp->periph_dev, s_ddp->dma_chan, (lldesc_t *)desc_addr);
|
||||
}
|
||||
|
||||
void dac_dma_periph_trans_stop(void)
|
||||
{
|
||||
spi_dma_ll_tx_stop(s_ddp->periph_dev, s_ddp->dma_chan);
|
||||
}
|
||||
|
||||
void dac_dma_periph_trans_append(void)
|
||||
{
|
||||
spi_dma_ll_tx_restart(s_ddp->periph_dev, s_ddp->dma_chan);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -15,16 +15,6 @@ extern "C" {
|
||||
|
||||
#if SOC_DAC_SUPPORTED
|
||||
|
||||
/**
|
||||
* @brief DAC channel mask
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
DAC_CHANNEL_MASK_CH0 = BIT(0), /*!< DAC channel 0 is GPIO25(ESP32) / GPIO17(ESP32S2) */
|
||||
DAC_CHANNEL_MASK_CH1 = BIT(1), /*!< DAC channel 1 is GPIO26(ESP32) / GPIO18(ESP32S2) */
|
||||
DAC_CHANNEL_MASK_ALL = BIT(0) | BIT(1), /*!< Both DAC channel 0 and channel 1 */
|
||||
} dac_channel_mask_t;
|
||||
|
||||
typedef struct dac_continuous_s *dac_continuous_handle_t; /*!< DAC continuous channel handle */
|
||||
|
||||
/**
|
||||
@@ -139,7 +129,7 @@ esp_err_t dac_continuous_enable(dac_continuous_handle_t handle);
|
||||
* @param[in] handle The DAC continuous channel handle that obtained from 'dac_continuous_new_channels'
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG The input parameter is invalid
|
||||
* - ESP_ERR_INVALID_STATE The channels have been enabled already
|
||||
* - ESP_ERR_INVALID_STATE The channels are not enabled, or a write operation is still ongoing
|
||||
* - ESP_OK Disable the continuous output success
|
||||
*/
|
||||
esp_err_t dac_continuous_disable(dac_continuous_handle_t handle);
|
||||
@@ -185,10 +175,24 @@ esp_err_t dac_continuous_write(dac_continuous_handle_t handle, uint8_t *buf, siz
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG The input parameter is invalid
|
||||
* - ESP_ERR_INVALID_STATE The DAC continuous mode has not been enabled yet
|
||||
* - ESP_OK Success to output the acyclic DAC data
|
||||
* - ESP_OK Success to output the cyclic DAC data
|
||||
*/
|
||||
esp_err_t dac_continuous_write_cyclically(dac_continuous_handle_t handle, uint8_t *buf, size_t buf_size, size_t *bytes_loaded);
|
||||
|
||||
/**
|
||||
* @brief Stop the cyclical conversion triggered by 'dac_continuous_write_cyclically'
|
||||
* @note For backward compatibility, calling this function is optional. That is, after a cyclic write (conversion) has started,
|
||||
* users can directly call 'dac_continuous_disable', 'dac_continuous_write_cyclically', 'dac_continuous_start_async_writing',
|
||||
* or 'dac_continuous_write'. These functions will automatically check for and stop any ongoing cyclic conversion. However,
|
||||
* this behavior is NOT recommended.
|
||||
* @param[in] handle The DAC continuous channel handle that obtained from 'dac_continuous_new_channels'
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG The input parameter is invalid
|
||||
* - ESP_ERR_INVALID_STATE The DAC continuous is not in cyclic writing mode
|
||||
* - ESP_OK Success to stop the cyclic conversion
|
||||
*/
|
||||
esp_err_t dac_continuous_stop_cyclically(dac_continuous_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief Set event callbacks for DAC continuous mode
|
||||
*
|
||||
@@ -220,7 +224,7 @@ esp_err_t dac_continuous_register_event_callback(dac_continuous_handle_t handle,
|
||||
esp_err_t dac_continuous_start_async_writing(dac_continuous_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief Stop the sync writing
|
||||
* @brief Stop the async writing
|
||||
*
|
||||
* @param[in] handle The DAC continuous channel handle that obtained from 'dac_continuous_new_channels'
|
||||
* @return
|
||||
@@ -237,7 +241,7 @@ esp_err_t dac_continuous_stop_async_writing(dac_continuous_handle_t handle);
|
||||
*
|
||||
* @param[in] handle The DAC continuous channel handle that obtained from 'dac_continuous_new_channels'
|
||||
* @param[in] dma_buf The DMA buffer address, it can be acquired from 'dac_event_data_t' in the 'on_convert_done' callback
|
||||
* @param[in] dma_buf_len The DMA buffer length, it can be acquired from 'dac_event_data_t' in the 'on_convert_done' callback
|
||||
* @param[in] dma_buf_len The DMA buffer length, it can be acquired from 'dac_event_data_t' in the 'on_convert_done' callback. It should always be equal to the buffer size of descriptors. This parameter is kept for compatibility and ignored by the driver.
|
||||
* @param[in] data The data that need to be written
|
||||
* @param[in] data_len The data length the need to be written
|
||||
* @param[out] bytes_loaded The bytes number that has been loaded/written into the DMA buffer
|
||||
|
||||
@@ -4,3 +4,18 @@ entries:
|
||||
if DAC_CTRL_FUNC_IN_IRAM = y:
|
||||
dac_oneshot: dac_oneshot_output_voltage (noflash)
|
||||
dac_continuous: dac_continuous_write_asynchronously (noflash)
|
||||
dac_continuous: s_dac_load_data_into_desc (noflash)
|
||||
|
||||
[mapping:dac_driver_gdma_link]
|
||||
archive: libesp_driver_dma.a
|
||||
entries:
|
||||
# Reached from the dac_continuous ISR
|
||||
if DAC_ISR_IRAM_SAFE = y:
|
||||
gdma_link: gdma_link_get_length (noflash)
|
||||
gdma_link: gdma_link_get_item_addr (noflash)
|
||||
gdma_link: gdma_link_check_end (noflash)
|
||||
|
||||
# Reached from 'dac_continuous_write_asynchronously' (via 's_dac_load_data_into_desc')
|
||||
if DAC_CTRL_FUNC_IN_IRAM = y:
|
||||
gdma_link: gdma_link_set_length (noflash)
|
||||
gdma_link: gdma_link_set_owner (noflash)
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "unity.h"
|
||||
#include "unity_test_utils.h"
|
||||
#include "driver/dac_oneshot.h"
|
||||
@@ -226,6 +229,50 @@ TEST_CASE("DAC_dma_write_test", "[dac]")
|
||||
TEST_ESP_OK(dac_continuous_del_channels(cont_handle));
|
||||
}
|
||||
|
||||
/* This test targets the synchronous writing "resume after the DMA has fully stopped" path.
|
||||
* With small DMA buffers and a deliberate long delay between writes, the DMA drains all of
|
||||
* its descriptors and stops (raises TEOF) before the next write happens. Every write except
|
||||
* the first one therefore has to re-link a descriptor and resume the transfer through
|
||||
* dac_dma_periph_trans_append(). If that resume path is broken, the descriptors are never
|
||||
* recycled, so dac_continuous_write() will return ESP_ERR_TIMEOUT, and dac_continuous_disable()
|
||||
* (which waits for the ongoing synchronous transfer to stop) will block forever. */
|
||||
TEST_CASE("DAC_dma_sync_write_resume_test", "[dac]")
|
||||
{
|
||||
dac_continuous_handle_t cont_handle;
|
||||
dac_continuous_config_t cont_cfg = {
|
||||
.chan_mask = DAC_CHANNEL_MASK_ALL,
|
||||
.desc_num = 4,
|
||||
.buf_size = 256,
|
||||
.freq_hz = 48000,
|
||||
.offset = 0,
|
||||
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
|
||||
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
|
||||
};
|
||||
|
||||
/* These data will be filled into 5 descriptors */
|
||||
size_t len = 128 * 5;
|
||||
uint8_t buf[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
buf[i] = i % 256;
|
||||
}
|
||||
|
||||
TEST_ESP_OK(dac_continuous_new_channels(&cont_cfg, &cont_handle));
|
||||
TEST_ESP_OK(dac_continuous_enable(cont_handle));
|
||||
|
||||
/* Waiting 100 ms between writes guarantees the DMA has fully drained and stopped,
|
||||
* forcing every subsequent write through the resume path. */
|
||||
for (int i = 0; i < 10; i++) {
|
||||
size_t bytes_loaded = 0;
|
||||
TEST_ESP_OK(dac_continuous_write(cont_handle, buf, len, &bytes_loaded, 1000));
|
||||
TEST_ASSERT_EQUAL(len, bytes_loaded);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
/* disable() internally waits for the ongoing synchronous transfer to stop; it must not hang. */
|
||||
TEST_ESP_OK(dac_continuous_disable(cont_handle));
|
||||
TEST_ESP_OK(dac_continuous_del_channels(cont_handle));
|
||||
}
|
||||
|
||||
/* Test the conversion frequency by counting the pulse of WS signal
|
||||
* The frequency test is currently only supported on ESP32
|
||||
* because there is no such signal to monitor on ESP32-S2 */
|
||||
@@ -355,37 +402,45 @@ TEST_CASE("DAC_cosine_wave_test", "[dac]")
|
||||
TEST_ESP_OK(dac_cosine_del_channel(cos_chan1_handle));
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
dac_continuous_handle_t handle;
|
||||
volatile bool stop;
|
||||
TaskHandle_t notify_task; /* Task to notify before self-deletion */
|
||||
} dac_concurrency_test_ctx_t;
|
||||
|
||||
static void dac_cyclically_write_task(void *arg)
|
||||
{
|
||||
dac_continuous_handle_t dac_handle = (dac_continuous_handle_t)arg;
|
||||
dac_concurrency_test_ctx_t *ctx = arg;
|
||||
size_t len = 1000;
|
||||
uint8_t buf[len];
|
||||
uint8_t max_val = 50;
|
||||
while (1) {
|
||||
while (!ctx->stop) {
|
||||
max_val += 50;
|
||||
for (int i = 0; i < len; i++) {
|
||||
buf[i] = i % max_val;
|
||||
}
|
||||
printf("Write cyclically\n");
|
||||
TEST_ESP_OK(dac_continuous_write_cyclically(dac_handle, buf, len, NULL));
|
||||
TEST_ESP_OK(dac_continuous_write_cyclically(ctx->handle, buf, len, NULL));
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
}
|
||||
xTaskNotifyGive(ctx->notify_task);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void dac_continuously_write_task(void *arg)
|
||||
{
|
||||
dac_continuous_handle_t dac_handle = (dac_continuous_handle_t)arg;
|
||||
dac_concurrency_test_ctx_t *ctx = arg;
|
||||
size_t len = 2048;
|
||||
uint8_t buf[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
buf[i] = i % 256;
|
||||
}
|
||||
while (1) {
|
||||
while (!ctx->stop) {
|
||||
printf("Write continuously\n");
|
||||
TEST_ESP_OK(dac_continuous_write(dac_handle, buf, len, NULL, 100));
|
||||
TEST_ESP_OK(dac_continuous_write(ctx->handle, buf, len, NULL, 100));
|
||||
vTaskDelay(pdMS_TO_TICKS(300));
|
||||
}
|
||||
xTaskNotifyGive(ctx->notify_task);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
@@ -405,15 +460,27 @@ TEST_CASE("DAC_continuous_mode_concurrency_test", "[dac]")
|
||||
TEST_ESP_OK(dac_continuous_new_channels(&cont_cfg, &cont_handle));
|
||||
TEST_ESP_OK(dac_continuous_enable(cont_handle));
|
||||
|
||||
dac_concurrency_test_ctx_t ctx = {
|
||||
.handle = cont_handle,
|
||||
.stop = false,
|
||||
.notify_task = xTaskGetCurrentTaskHandle(),
|
||||
};
|
||||
|
||||
TaskHandle_t cyc_task;
|
||||
TaskHandle_t con_task;
|
||||
xTaskCreate(dac_cyclically_write_task, "dac_cyclically_write_task", 4096, cont_handle, 5, &cyc_task);
|
||||
xTaskCreate(dac_continuously_write_task, "dac_continuously_write_task", 4096, cont_handle, 5, &con_task);
|
||||
xTaskCreate(dac_cyclically_write_task, "dac_cyclically_write_task", 4096, &ctx, 5, &cyc_task);
|
||||
xTaskCreate(dac_continuously_write_task, "dac_continuously_write_task", 4096, &ctx, 5, &con_task);
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||
|
||||
vTaskDelete(cyc_task);
|
||||
vTaskDelete(con_task);
|
||||
ctx.stop = true;
|
||||
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(2000)));
|
||||
TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(2000)));
|
||||
|
||||
/* vTaskDelete(NULL) defers freeing task TCB and stack to the idle task.
|
||||
* Yield here so idle task(s) can reclaim that memory before tearDown() checks for leaks. */
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
TEST_ESP_OK(dac_continuous_disable(cont_handle));
|
||||
TEST_ESP_OK(dac_continuous_del_channels(cont_handle));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -117,6 +117,19 @@ esp_err_t gdma_link_mount_buffers(gdma_link_list_handle_t list, int start_item_i
|
||||
*/
|
||||
uintptr_t gdma_link_get_head_addr(gdma_link_list_handle_t list);
|
||||
|
||||
/**
|
||||
* @brief Get the address of a specific link list item by index
|
||||
* @note The returned address is the cached address used by the DMA hardware (same convention as `gdma_link_get_head_addr`).
|
||||
* It can be passed directly to the DMA start function to resume transmission from a specific descriptor.
|
||||
*
|
||||
* @param[in] list Link list handle, allocated by `gdma_new_link_list`
|
||||
* @param[in] item_index Index of the link list item (wraps around if out of range)
|
||||
* @return
|
||||
* - Address of the specified item
|
||||
* - 0: Invalid handle
|
||||
*/
|
||||
uintptr_t gdma_link_get_item_addr(gdma_link_list_handle_t list, int item_index);
|
||||
|
||||
/**
|
||||
* @brief Concatenate two link lists as follows:
|
||||
*
|
||||
|
||||
@@ -281,6 +281,16 @@ uintptr_t gdma_link_get_head_addr(gdma_link_list_handle_t list)
|
||||
return (uintptr_t)(list->items);
|
||||
}
|
||||
|
||||
uintptr_t gdma_link_get_item_addr(gdma_link_list_handle_t list, int item_index)
|
||||
{
|
||||
if (!list) {
|
||||
return 0;
|
||||
}
|
||||
int num_items = list->num_items;
|
||||
item_index = (item_index % num_items + num_items) % num_items;
|
||||
return (uintptr_t)(list->items + item_index * list->item_size);
|
||||
}
|
||||
|
||||
esp_err_t gdma_link_concat(gdma_link_list_handle_t first_link, int first_link_item_index, gdma_link_list_handle_t second_link, int second_link_item_index)
|
||||
{
|
||||
if (!first_link) {
|
||||
|
||||
@@ -1,19 +1,57 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "esp_assert.h"
|
||||
#include "esp_bit_defs.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#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 channel 0 is GPIO25(ESP32) / GPIO17(ESP32S2) */
|
||||
DAC_CHAN_1 = 1, /*!< DAC channel 1 is GPIO26(ESP32) / GPIO18(ESP32S2) */
|
||||
DAC_CHAN_0 = 0,
|
||||
DAC_CHAN_1 = 1,
|
||||
DAC_CHAN_MAX, /*!< For checking purpose */
|
||||
} dac_channel_t;
|
||||
|
||||
ESP_STATIC_ASSERT(DAC_CHAN_MAX == SOC_DAC_CHAN_NUM, "DAC channel number mismatch");
|
||||
|
||||
#define IS_VALID_DAC_CHANNEL(channel) ((uint32_t)(channel) < (uint32_t)DAC_CHAN_MAX)
|
||||
|
||||
typedef uint32_t dac_channel_mask_t;
|
||||
|
||||
#define DAC_CHANNEL_MASK_CH0 (BIT(0)) /*!< DAC channel 0 mask */
|
||||
#define DAC_CHANNEL_MASK_CH1 (BIT(1)) /*!< DAC channel 1 mask */
|
||||
#define DAC_CHANNEL_MASK_ALL ((1ULL << SOC_DAC_CHAN_NUM) - 1) /*!< Bitwise OR of all valid channel masks */
|
||||
|
||||
/**
|
||||
* @note 0 is valid
|
||||
*/
|
||||
#define IS_VALID_DAC_CHANNEL_MASK(mask) (((mask) & ~DAC_CHANNEL_MASK_ALL) == 0)
|
||||
|
||||
/**
|
||||
* @brief Use a loop to extract every channel (dac_channel_t) from the mask (dac_channel_mask_t).
|
||||
*/
|
||||
#define DAC_CHANNEL_MASK_FOREACH(channel, mask) \
|
||||
for (uint32_t __dac_mask = (mask), __dac_chan = DAC_CHAN_0; __dac_chan < DAC_CHAN_MAX; __dac_chan++) \
|
||||
for (dac_channel_t channel = (dac_channel_t)__dac_chan; __dac_mask & BIT(__dac_chan); __dac_mask &= ~BIT(__dac_chan))
|
||||
|
||||
/**
|
||||
* @brief The attenuation of the amplitude of the cosine wave generator. The max amplitude is VDD3P3_RTC.
|
||||
*/
|
||||
@@ -35,6 +73,8 @@ typedef enum {
|
||||
DAC_COSINE_PHASE_180 = 0x03, /*!< Phase shift +180° */
|
||||
} dac_cosine_phase_t;
|
||||
|
||||
#endif // SOC_DAC_SUPPORTED
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -86,6 +86,7 @@ typedef enum {
|
||||
SPI_LL_INTR_CMD9 = BIT(12), ///< Has received CMD9 command. Only available in slave HD.
|
||||
SPI_LL_INTR_CMDA = BIT(13), ///< Has received CMDA command. Only available in slave HD.
|
||||
SPI_LL_INTR_SEG_DONE = BIT(14),
|
||||
SPI_LL_INTR_OUT_DONE = BIT(15), ///< DMA out_done triggered
|
||||
} spi_ll_intr_t;
|
||||
|
||||
///< Flags for conditions under which the transaction length should be recorded
|
||||
@@ -356,6 +357,7 @@ static inline void spi_ll_cpu_rx_fifo_reset(spi_dev_t *hw)
|
||||
*
|
||||
* @param hw Beginning address of the peripheral registers.
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void spi_ll_dma_tx_fifo_reset(spi_dev_t *hw)
|
||||
{
|
||||
hw->dma_conf.val |= SPI_LL_DMA_FIFO_RST_MASK;
|
||||
@@ -369,6 +371,7 @@ static inline void spi_ll_dma_tx_fifo_reset(spi_dev_t *hw)
|
||||
*
|
||||
* @param hw Beginning address of the peripheral registers.
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void spi_ll_dma_rx_fifo_reset(spi_dev_t *hw)
|
||||
{
|
||||
hw->dma_conf.val |= SPI_LL_DMA_FIFO_RST_MASK;
|
||||
@@ -1126,7 +1129,8 @@ static inline uint32_t spi_ll_slave_get_rcv_bitlen(spi_dev_t *hw)
|
||||
item(SPI_LL_INTR_CMD7, dma_int_ena.cmd7, dma_int_raw.cmd7, dma_int_clr.cmd7=1) \
|
||||
item(SPI_LL_INTR_CMD8, dma_int_ena.cmd8, dma_int_raw.cmd8, dma_int_clr.cmd8=1) \
|
||||
item(SPI_LL_INTR_CMD9, dma_int_ena.cmd9, dma_int_raw.cmd9, dma_int_clr.cmd9=1) \
|
||||
item(SPI_LL_INTR_CMDA, dma_int_ena.cmda, dma_int_raw.cmda, dma_int_clr.cmda=1)
|
||||
item(SPI_LL_INTR_CMDA, dma_int_ena.cmda, dma_int_raw.cmda, dma_int_clr.cmda=1) \
|
||||
item(SPI_LL_INTR_OUT_DONE, dma_int_ena.out_done, dma_int_raw.out_done, dma_int_clr.out_done=1)
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline void spi_ll_enable_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask)
|
||||
|
||||
@@ -46,6 +46,7 @@ extern "C" {
|
||||
#define I2S_LL_BCK_MAX_PRESCALE (64)
|
||||
|
||||
#define I2S_LL_EVENT_RX_EOF BIT(9)
|
||||
#define I2S_LL_EVENT_TX_DONE BIT(11)
|
||||
#define I2S_LL_EVENT_TX_EOF BIT(12)
|
||||
#define I2S_LL_EVENT_RX_DSCR_ERR BIT(13)
|
||||
#define I2S_LL_EVENT_TX_DSCR_ERR BIT(14)
|
||||
@@ -542,6 +543,7 @@ static inline void i2s_ll_rx_reset_dma(i2s_dev_t *hw)
|
||||
*
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void i2s_ll_start_out_link(i2s_dev_t *hw)
|
||||
{
|
||||
hw->out_link.start = 1;
|
||||
@@ -553,6 +555,7 @@ static inline void i2s_ll_start_out_link(i2s_dev_t *hw)
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
* @param val value to set out link address
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void i2s_ll_set_out_link_addr(i2s_dev_t *hw, uint32_t val)
|
||||
{
|
||||
hw->out_link.addr = val;
|
||||
@@ -584,6 +587,7 @@ static inline void i2s_ll_rx_start(i2s_dev_t *hw)
|
||||
* @param hw Peripheral I2S hardware instance address.
|
||||
* @param link_addr DMA descriptor link address.
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void i2s_ll_tx_start_link(i2s_dev_t *hw, uint32_t link_addr)
|
||||
{
|
||||
i2s_ll_set_out_link_addr(hw, link_addr);
|
||||
|
||||
Reference in New Issue
Block a user