dac: optimize the dma stratege

This commit is contained in:
laokaiyao
2022-10-25 17:14:59 +08:00
parent f9f9a09dfb
commit 8ef9fd4623
113 changed files with 9163 additions and 7830 deletions
+10 -8
View File
@@ -103,18 +103,20 @@ if(CONFIG_SOC_TOUCH_SENSOR_SUPPORTED)
"${target}/touch_sensor.c")
endif()
if(CONFIG_SOC_DAC_SUPPORTED)
list(APPEND srcs "dac/dac_driver.c"
"dac/${target}/dac_dma.c"
"deprecated/dac_common_legacy.c"
"deprecated/${target}/dac_legacy.c")
list(APPEND includes "deprecated/${target}")
endif()
if(CONFIG_SOC_SDIO_SLAVE_SUPPORTED)
list(APPEND srcs "sdio_slave.c")
endif()
if(CONFIG_SOC_DAC_SUPPORTED)
list(APPEND srcs "dac/dac_oneshot.c"
"dac/dac_cosine.c"
"dac/dac_conti.c"
"dac/dac_common.c"
"dac/${target}/dac_dma.c"
"deprecated/dac_common_legacy.c"
"deprecated/${target}/dac_legacy.c")
endif()
if(${target} STREQUAL "esp32")
list(APPEND srcs "deprecated/adc_i2s_deprecated.c")
endif()
+3 -3
View File
@@ -405,7 +405,7 @@ menu "Driver Configurations"
bool "Place DAC control functions into IRAM"
default n
help
Place 'dac_channels_set_voltage' function into IRAM,
Place DAC control functions (e.g. 'dac_oneshot_output_voltage') into IRAM,
so that this function can be IRAM-safe and able to be called in the other IRAM interrupt context.
Enabling this option can improve driver performance as well.
@@ -420,7 +420,7 @@ menu "Driver Configurations"
bool "Suppress legacy driver deprecated warning"
default n
help
Wether to suppress the deprecation warnings when using legacy DAC driver (driver/DAC.h).
Wether to suppress the deprecation warnings when using legacy DAC driver (driver/dac.h).
If you want to continue using the legacy driver, and don't want to see related deprecation warnings,
you can enable this option.
@@ -433,7 +433,7 @@ menu "Driver Configurations"
config DAC_DMA_AUTO_16BIT_ALIGN
bool "Align the continuous data to 16 bit automatically"
depends on IDF_TARGET_ESP32
depends on SOC_DAC_DMA_16BIT_ALIGN
default y
help
Whether to left shift the continuous data to align every bytes to 16 bits in the driver.
+98
View File
@@ -0,0 +1,98 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "soc/soc_caps.h"
#include "soc/dac_periph.h"
#include "hal/dac_types.h"
#include "hal/dac_ll.h"
#include "driver/rtc_io.h"
#include "esp_check.h"
#include "dac_priv_common.h"
typedef struct {
bool in_use;
bool is_enabled;
const char *mode;
} dac_channel_info_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,
}
};
/* Global dac spin lock for the whole DAC driver */
portMUX_TYPE dac_spinlock = portMUX_INITIALIZER_UNLOCKED;
static const char *TAG = "dac_common";
esp_err_t dac_priv_register_channel(dac_channel_t chan_id, const char *mode_name)
{
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;
} else {
ret = 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;
} else {
ret = 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");
gpio_num_t gpio_num = (gpio_num_t)dac_periph_signal.dac_channel_io_num[chan_id];
rtc_gpio_init(gpio_num);
rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_DISABLED);
rtc_gpio_pullup_dis(gpio_num);
rtc_gpio_pulldown_dis(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;
}
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");
gpio_num_t gpio_num = (gpio_num_t)dac_periph_signal.dac_channel_io_num[chan_id];
rtc_gpio_deinit(gpio_num);
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;
}
+652
View File
@@ -0,0 +1,652 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdatomic.h>
#include <string.h>
#include <sys/queue.h>
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "sdkconfig.h"
#include "rom/lldesc.h"
#include "soc/soc_caps.h"
#include "driver/dac_conti.h"
#include "dac_priv_common.h"
#include "dac_priv_dma.h"
#include "esp_check.h"
#if CONFIG_DAC_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
// Set the maximum log level for this source file
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#endif
#if CONFIG_PM_ENABLE
#include "esp_pm.h"
#endif
#define DAC_DMA_MAX_BUF_SIZE 4092 // Max DMA buffer size is 4095 but better to align with 4 bytes, so set 4092 here
#if CONFIG_DAC_ISR_IRAM_SAFE || CONFIG_DAC_CTRL_FUNC_IN_IRAM
#define DAC_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
#define DAC_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#endif
#if CONFIG_DAC_ISR_IRAM_SAFE
#define DAC_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_SHARED)
#else
#define DAC_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_SHARED)
#endif
#define DAC_DMA_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA)
#define DAC_STAILQ_REMOVE(head, elm, type, field) do { \
if ((head)->stqh_first == (elm)) { \
STAILQ_REMOVE_HEAD((head), field); \
} else { \
struct type *curelm = (head)->stqh_first; \
while (curelm->field.stqe_next != (elm) && \
curelm->field.stqe_next != NULL) \
curelm = curelm->field.stqe_next; \
if (curelm->field.stqe_next && (curelm->field.stqe_next = \
curelm->field.stqe_next->field.stqe_next) == NULL) \
(head)->stqh_last = &(curelm)->field.stqe_next; \
} \
} while (/*CONSTCOND*/0)
struct dac_conti_s {
uint32_t chan_cnt;
dac_conti_config_t cfg;
atomic_bool is_enabled;
atomic_bool is_cyclic;
atomic_bool is_running;
atomic_bool is_async;
intr_handle_t intr_handle; /* Interrupt handle */
#if CONFIG_PM_ENABLE
esp_pm_lock_handle_t pm_lock;
#endif
SemaphoreHandle_t mutex;
StaticSemaphore_t mutex_struct; /* Static mutex struct */
QueueHandle_t desc_pool; /* The pool of available descriptors
* The descriptors in the pool are not linked in to pending chain */
StaticQueue_t desc_pool_struct; /* Static message queue struct */
void *desc_pool_storage; /* Static message queue storage */
lldesc_t **desc;
uint8_t **bufs;
STAILQ_HEAD(desc_chain_s, lldesc_s) head; /* Head of the descriptor chain
* The descriptors in the chain are pending to be sent or sending now */
dac_event_callbacks_t cbs; /* Interrupt callbacks */
void *user_data;
};
static const char *TAG = "dac_conti";
static bool s_dma_in_use = false;
static portMUX_TYPE desc_spinlock = portMUX_INITIALIZER_UNLOCKED;
#define DESC_ENTER_CRITICAL() portENTER_CRITICAL(&desc_spinlock)
#define DESC_EXIT_CRITICAL() portEXIT_CRITICAL(&desc_spinlock)
#define DESC_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&desc_spinlock)
#define DESC_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&desc_spinlock)
static void s_dac_free_dma_desc(dac_conti_handle_t handle)
{
STAILQ_INIT(&handle->head);
if (handle->desc != NULL) {
if (handle->desc[0]) {
free(handle->desc[0]);
}
free(handle->desc);
handle->desc = NULL;
}
if (handle->bufs != NULL) {
for (int i = 0; i < handle->cfg.desc_num; i++) {
if (handle->bufs[i]) {
free(handle->bufs[i]);
handle->bufs[i] = NULL;
}
}
free(handle->bufs);
handle->bufs = NULL;
}
}
static esp_err_t s_dac_alloc_dma_desc(dac_conti_handle_t handle)
{
esp_err_t ret = ESP_OK;
STAILQ_INIT(&handle->head);
handle->desc = (lldesc_t **) heap_caps_calloc(handle->cfg.desc_num, sizeof(lldesc_t *), DAC_DMA_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(handle->desc, ESP_ERR_NO_MEM, TAG, "failed to allocate dma descriptor array");
handle->bufs = (uint8_t **) heap_caps_calloc(handle->cfg.desc_num, sizeof(uint8_t *), DAC_DMA_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(handle->bufs, ESP_ERR_NO_MEM, TAG, "failed to allocate dma buffer array");
lldesc_t *descs = (lldesc_t *)heap_caps_calloc(handle->cfg.desc_num, sizeof(lldesc_t), DAC_DMA_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(descs, ESP_ERR_NO_MEM, TAG, "failed to allocate dma descriptors");
for (int cnt = 0; cnt < handle->cfg.desc_num; cnt++) {
/* Allocate DMA descriptor */
handle->desc[cnt] = &descs[cnt];
ESP_GOTO_ON_FALSE(handle->desc[cnt], ESP_ERR_NO_MEM, err, TAG, "failed to allocate dma descriptor");
ESP_LOGD(TAG, "desc[%d] %p\n", cnt, handle->desc[cnt]);
/* Allocate DMA buffer */
handle->bufs[cnt] = (uint8_t *) heap_caps_calloc(1, handle->cfg.buf_size, DAC_DMA_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(handle->bufs[cnt], ESP_ERR_NO_MEM, err, TAG, "failed to allocate dma buffer");
/* Assign initial value */
lldesc_config(handle->desc[cnt], LLDESC_SW_OWNED, 1, 0, handle->cfg.buf_size);
handle->desc[cnt]->size = handle->cfg.buf_size;
handle->desc[cnt]->buf = handle->bufs[cnt];
handle->desc[cnt]->offset = 0;
}
return ESP_OK;
err:
/* Free DMA buffer if failed to allocate memory */
s_dac_free_dma_desc(handle);
return ret;
}
static void IRAM_ATTR s_dac_default_intr_handler(void *arg)
{
dac_conti_handle_t handle = (dac_conti_handle_t)arg;
uint32_t dummy;
BaseType_t need_awoke = pdFALSE;
BaseType_t tmp = pdFALSE;
uint32_t intr_mask = dac_dma_periph_intr_is_triggered();
if (intr_mask & DAC_DMA_EOF_INTR) {
lldesc_t *fdesc = (lldesc_t *)dac_dma_periph_intr_get_eof_desc();
if (!atomic_load(&handle->is_cyclic)) {
/* Remove the descriptor in the chain that finished sent */
DESC_ENTER_CRITICAL_ISR();
if (STAILQ_FIRST(&handle->head) != NULL) {
DAC_STAILQ_REMOVE(&handle->head, fdesc, lldesc_s, qe);
}
DESC_EXIT_CRITICAL_ISR();
if (xQueueIsQueueFullFromISR(handle->desc_pool) == pdTRUE) {
xQueueReceiveFromISR(handle->desc_pool, &dummy, &tmp);
need_awoke |= tmp;
}
xQueueSendFromISR(handle->desc_pool, &fdesc, &tmp);
need_awoke |= tmp;
}
if (handle->cbs.on_convert_done) {
dac_event_data_t evt_data = {
.buf = (void *)fdesc->buf,
.buf_size = handle->cfg.buf_size,
.write_bytes = fdesc->length,
};
need_awoke |= handle->cbs.on_convert_done(handle, &evt_data, handle->user_data);
}
}
if (intr_mask & DAC_DMA_TEOF_INTR) {
/* Total end of frame interrupt received, DMA stopped */
atomic_store(&handle->is_running, false);
if (handle->cbs.on_stop) {
need_awoke |= handle->cbs.on_stop(handle, NULL, handle->user_data);
}
}
if (need_awoke == pdTRUE) {
portYIELD_FROM_ISR();
}
}
esp_err_t dac_new_conti_channels(const dac_conti_config_t *conti_cfg, dac_conti_handle_t *ret_handle)
{
#if CONFIG_DAC_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif
/* Parameters validation */
DAC_NULL_POINTER_CHECK(conti_cfg);
DAC_NULL_POINTER_CHECK(ret_handle);
ESP_RETURN_ON_FALSE(conti_cfg->chan_mask <= DAC_CHANNEL_MASK_ALL, ESP_ERR_INVALID_ARG, TAG, "invalid dac channel id");
ESP_RETURN_ON_FALSE(conti_cfg->desc_num > 1, ESP_ERR_INVALID_STATE, TAG, "at least two DMA descriptor needed");
ESP_RETURN_ON_FALSE(!s_dma_in_use, ESP_ERR_INVALID_STATE, TAG, "DMA already in use");
esp_err_t ret = ESP_OK;
/* Register the channels */
for (uint32_t i = 0, mask = conti_cfg->chan_mask; mask; mask >>= 1, i++) {
if (mask & 0x01) {
ESP_GOTO_ON_ERROR(dac_priv_register_channel(i, "dac continuous"),
err4, TAG, "register dac channel %"PRIu32" failed", i);
}
}
/* Allocate continuous mode struct */
dac_conti_handle_t handle = heap_caps_calloc(1, sizeof(struct dac_conti_s), DAC_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(handle, ESP_ERR_NO_MEM, TAG, "no memory for the dac continuous mode structure");
/* Allocate static queue */
handle->desc_pool_storage = (uint8_t *)heap_caps_calloc(conti_cfg->desc_num, sizeof(lldesc_t *), DAC_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(handle->desc_pool_storage, ESP_ERR_NO_MEM, err3, TAG, "no memory for message queue storage");
handle->desc_pool = xQueueCreateStatic(conti_cfg->desc_num, sizeof(lldesc_t *), handle->desc_pool_storage, &handle->desc_pool_struct);
ESP_GOTO_ON_FALSE(handle->desc_pool, ESP_ERR_NO_MEM, err3, TAG, "no memory for message queue");
/* Allocate static mutex */
handle->mutex = xSemaphoreCreateMutexStatic(&handle->mutex_struct);
ESP_GOTO_ON_FALSE(handle->mutex, ESP_ERR_NO_MEM, err3, TAG, "no memory for channels mutex");
/* Create PM lock */
#if CONFIG_PM_ENABLE
esp_pm_lock_type_t pm_lock_type = conti_cfg->clk_src == DAC_DIGI_CLK_SRC_APLL ? ESP_PM_NO_LIGHT_SLEEP : ESP_PM_APB_FREQ_MAX;
ESP_GOTO_ON_ERROR(esp_pm_lock_create(pm_lock_type, 0, "dac_driver", &handle->pm_lock), err3, TAG, "Failed to create DAC pm lock");
#endif
handle->chan_cnt = __builtin_popcount(conti_cfg->chan_mask);
memcpy(&(handle->cfg), conti_cfg, sizeof(dac_conti_config_t));
atomic_init(&handle->is_enabled, false);
atomic_init(&handle->is_cyclic, false);
atomic_init(&handle->is_running, false);
atomic_init(&handle->is_async, false);
/* Allocate DMA buffer */
ESP_GOTO_ON_ERROR(s_dac_alloc_dma_desc(handle), err2, TAG, "Failed to allocate memory for DMA buffers");
/* Initialize DAC DMA peripheral */
ESP_GOTO_ON_ERROR(dac_dma_periph_init(conti_cfg->freq_hz,
conti_cfg->chan_mode == DAC_CHANNEL_MODE_ALTER,
conti_cfg->clk_src == DAC_DIGI_CLK_SRC_APLL),
err2, TAG, "Failed to initialize DAC DMA peripheral");
/* Register DMA interrupt */
ESP_GOTO_ON_ERROR(esp_intr_alloc(dac_dma_periph_get_intr_signal(), DAC_INTR_ALLOC_FLAGS,
s_dac_default_intr_handler, handle, &(handle->intr_handle)),
err1, TAG, "Failed to register DAC DMA interrupt");
/* Connect DAC module to the DMA peripheral */
DAC_RTC_ENTER_CRITICAL();
dac_ll_digi_enable_dma(true);
DAC_RTC_EXIT_CRITICAL();
s_dma_in_use = true;
*ret_handle = handle;
return ret;
err1:
dac_dma_periph_deinit();
err2:
s_dac_free_dma_desc(handle);
err3:
if (handle->desc_pool) {
vQueueDelete(handle->desc_pool);
}
if (handle->desc_pool_storage) {
free(handle->desc_pool_storage);
}
if (handle->mutex) {
vSemaphoreDelete(handle->mutex);
}
free(handle);
err4:
/* Deregister the channels */
for (uint32_t i = 0, mask = conti_cfg->chan_mask; mask; mask >>= 1, i++) {
if (mask & 0x01) {
dac_priv_deregister_channel(i);
}
}
return ret;
}
esp_err_t dac_del_conti_channels(dac_conti_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(!atomic_load(&handle->is_enabled), ESP_ERR_INVALID_STATE, TAG, "dac continuous output not disabled yet");
/* Deregister DMA interrupt */
if (handle->intr_handle) {
ESP_RETURN_ON_ERROR(esp_intr_free(handle->intr_handle), TAG, "Failed to deregister DMA interrupt");
handle->intr_handle = NULL;
}
/* Deinitialize DMA peripheral */
ESP_RETURN_ON_ERROR(dac_dma_periph_deinit(), TAG, "Failed to deinitialize DAC DMA peripheral");
/* Disconnect DAC module from the DMA peripheral */
DAC_RTC_ENTER_CRITICAL();
dac_ll_digi_enable_dma(false);
DAC_RTC_EXIT_CRITICAL();
/* Free allocated resources */
s_dac_free_dma_desc(handle);
if (handle->desc_pool) {
vQueueDelete(handle->desc_pool);
handle->desc_pool = NULL;
}
if (handle->desc_pool_storage) {
free(handle->desc_pool_storage);
handle->desc_pool_storage = NULL;
}
if (handle->mutex) {
vSemaphoreDelete(handle->mutex);
handle->mutex = NULL;
}
#if CONFIG_PM_ENABLE
if (handle->pm_lock) {
esp_pm_lock_delete(handle->pm_lock);
handle->pm_lock = NULL;
}
#endif
/* Deregister the channels */
for (uint32_t i = 0, mask = handle->cfg.chan_mask; mask; mask >>= 1, i++) {
if (mask & 0x01) {
dac_priv_deregister_channel(i);
}
}
free(handle);
s_dma_in_use = false;
return ESP_OK;
}
esp_err_t dac_conti_register_event_callback(dac_conti_handle_t handle, const dac_event_callbacks_t *callbacks, void *user_data)
{
DAC_NULL_POINTER_CHECK(handle);
if (!callbacks) {
memset(&handle->cbs, 0, sizeof(dac_event_callbacks_t));
return ESP_OK;
}
#if CONFIG_DAC_ISR_IRAM_SAFE
if (callbacks->on_convert_done) {
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(callbacks->on_convert_done), ESP_ERR_INVALID_ARG, TAG, "on_convert_done callback not in IRAM");
}
if (callbacks->on_stop) {
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(callbacks->on_stop), ESP_ERR_INVALID_ARG, TAG, "on_stop callback not in IRAM");
}
if (user_data) {
ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM");
}
#endif
memcpy(&handle->cbs, callbacks, sizeof(dac_event_callbacks_t));
handle->user_data = user_data;
return ESP_OK;
}
esp_err_t dac_conti_enable(dac_conti_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(!atomic_load(&handle->is_enabled), ESP_ERR_INVALID_STATE, TAG, "dac continuous has already enabled");
esp_err_t ret = ESP_OK;
/* Reset the descriptor pool */
xQueueReset(handle->desc_pool);
for ( int i = 0; i < handle->cfg.desc_num; i++) {
ESP_GOTO_ON_FALSE(xQueueSend(handle->desc_pool, &handle->desc[i], 0) == pdTRUE,
ESP_ERR_INVALID_STATE, err, TAG, "the descriptor pool is not cleared");
}
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_acquire(handle->pm_lock);
#endif
for (uint32_t i = 0, mask = handle->cfg.chan_mask; mask; mask >>= 1, i++) {
if (mask & 0x01) {
dac_priv_enable_channel(i);
}
}
dac_dma_periph_enable();
esp_intr_enable(handle->intr_handle);
DAC_RTC_ENTER_CRITICAL();
dac_ll_digi_enable_dma(true);
DAC_RTC_EXIT_CRITICAL();
atomic_store(&handle->is_enabled, true);
err:
return ret;
}
esp_err_t dac_conti_disable(dac_conti_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(atomic_load(&handle->is_enabled), ESP_ERR_INVALID_STATE, TAG, "dac continuous has already disabled");
atomic_store(&handle->is_enabled, false);
dac_dma_periph_disable();
esp_intr_disable(handle->intr_handle);
DAC_RTC_ENTER_CRITICAL();
dac_ll_digi_enable_dma(false);
DAC_RTC_EXIT_CRITICAL();
atomic_store(&handle->is_running, false);
for (uint32_t i = 0, mask = handle->cfg.chan_mask; mask; mask >>= 1, i++) {
if (mask & 0x01) {
dac_priv_disable_channel(i);
}
}
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_release(handle->pm_lock);
#endif
return ESP_OK;
}
esp_err_t dac_conti_start_async_writing(dac_conti_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(atomic_load(&handle->is_enabled), ESP_ERR_INVALID_STATE, TAG, "dac continuous has not been enabled");
ESP_RETURN_ON_FALSE(handle->cbs.on_convert_done, ESP_ERR_INVALID_STATE, TAG,
"please register 'on_convert_done' callback before starting asynchronous writing");
atomic_store(&handle->is_async, true);
if (atomic_load(&handle->is_cyclic)) {
/* Break the DMA descriptor chain to stop the DMA first */
for (int i = 0; i < handle->cfg.desc_num; i++) {
handle->desc[i]->empty = 0;
}
}
/* Wait for the previous DMA stop */
while (atomic_load(&handle->is_running)) {}
/* Link all descriptors as a ring */
for (int i = 0; i < handle->cfg.desc_num; i++) {
memset(handle->bufs[i], 0, handle->cfg.buf_size);
handle->desc[i]->empty = (uint32_t)(i < handle->cfg.desc_num - 1 ? handle->desc[i + 1] : handle->desc[0]);
}
dac_dma_periph_dma_trans_start((uint32_t)handle->desc[0]);
atomic_store(&handle->is_running, true);
return ESP_OK;
}
esp_err_t dac_conti_stop_async_writing(dac_conti_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(atomic_load(&handle->is_async), ESP_ERR_INVALID_STATE, TAG, "dac asynchronous writing has not been started");
/* Break the DMA descriptor chain to stop the DMA first */
for (int i = 0; i < handle->cfg.desc_num; i++) {
handle->desc[i]->empty = 0;
}
/* Wait for the previous DMA stop */
while (atomic_load(&handle->is_running)) {}
atomic_store(&handle->is_async, false);
return ESP_OK;
}
/* Buffer expanding coefficient, the input buffer will expand to twice length while enabled AUTO_16_BIT */
#if CONFIG_DAC_DMA_AUTO_16BIT_ALIGN
#define DAC_16BIT_ALIGN_COEFF 2
#else
#define DAC_16BIT_ALIGN_COEFF 1
#endif
static size_t s_dac_load_data_into_buf(dac_conti_handle_t handle, uint8_t *dest, size_t dest_len, const uint8_t *src, size_t src_len)
{
size_t load_bytes = 0;
#if CONFIG_DAC_DMA_AUTO_16BIT_ALIGN
/* Load the data to the high 8 bit in the 16-bit width slot */
load_bytes = (src_len * 2 > dest_len) ? dest_len : src_len * 2;
for (int i = 0; i < load_bytes; i += 2) {
dest[i + 1] = src[i / 2] + handle->cfg.offset;
}
#else
/* Load the data into the DMA buffer */
load_bytes = (src_len > dest_len) ? dest_len : src_len;
for (int i = 0; i < load_bytes; i++) {
dest[i] = src[i] + handle->cfg.offset;
}
#endif
return load_bytes;
}
esp_err_t dac_conti_write_asynchronously(dac_conti_handle_t handle, uint8_t *dma_buf,
size_t dma_buf_len, const uint8_t *data,
size_t data_len, size_t *bytes_loaded)
{
DAC_NULL_POINTER_CHECK_ISR(handle);
DAC_NULL_POINTER_CHECK_ISR(dma_buf);
DAC_NULL_POINTER_CHECK_ISR(data);
ESP_RETURN_ON_FALSE_ISR(atomic_load(&handle->is_async), ESP_ERR_INVALID_STATE, TAG, "The asynchronous writing has not started");
int i;
for (i = 0; i < handle->cfg.desc_num; i++) {
if (dma_buf == handle->bufs[i]) {
break;
}
}
/* Fail to find the DMA buffer address */
ESP_RETURN_ON_FALSE_ISR(i < handle->cfg.desc_num, ESP_ERR_NOT_FOUND, TAG, "Not found the corresponding DMA buffer");
size_t load_bytes = s_dac_load_data_into_buf(handle, dma_buf, dma_buf_len, data, data_len);
lldesc_config(handle->desc[i], LLDESC_HW_OWNED, 1, 0, load_bytes);
if (bytes_loaded) {
*bytes_loaded = load_bytes / DAC_16BIT_ALIGN_COEFF;
}
return ESP_OK;
}
esp_err_t dac_conti_write_cyclically(dac_conti_handle_t handle, uint8_t *buf, size_t buf_size, size_t *bytes_loaded)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(atomic_load(&handle->is_enabled), ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels has not been enabled");
ESP_RETURN_ON_FALSE(!atomic_load(&handle->is_async), ESP_ERR_INVALID_STATE, TAG, "Asynchronous writing is running, can't write cyclically");
ESP_RETURN_ON_FALSE(buf_size <= handle->cfg.buf_size * handle->cfg.desc_num, ESP_ERR_INVALID_ARG, TAG,
"The cyclic buffer size exceeds the total DMA buffer size: %"PRIu32"(desc_num) * %d(buf_size) = %"PRIu32,
handle->cfg.desc_num, handle->cfg.buf_size, handle->cfg.buf_size * handle->cfg.desc_num);
esp_err_t ret = ESP_OK;
xSemaphoreTake(handle->mutex, portMAX_DELAY);
if (atomic_load(&handle->is_cyclic)) {
/* Break the DMA descriptor chain to stop the DMA first */
for (int i = 0; i < handle->cfg.desc_num; i++) {
handle->desc[i]->empty = 0;
}
}
/* Wait for the previous DMA stop */
while (atomic_load(&handle->is_running)) {}
atomic_store(&handle->is_cyclic, true);
size_t src_buf_size = buf_size;
uint32_t split = 1;
int i;
for (i = 0; i < handle->cfg.desc_num && buf_size > 0; i++) {
/* To spread data more averagely, average the last two descriptors */
split = (buf_size * DAC_16BIT_ALIGN_COEFF < handle->cfg.buf_size * 2) ? 3 - split : 1;
size_t load_bytes = s_dac_load_data_into_buf(handle, handle->bufs[i], handle->cfg.buf_size, buf, buf_size / split);
lldesc_config(handle->desc[i], LLDESC_HW_OWNED, 1, 0, load_bytes);
/* Link to the next descriptor */
handle->desc[i]->empty = (uint32_t)(i < handle->cfg.desc_num - 1 ? handle->desc[i + 1] :0);
buf_size -= load_bytes / DAC_16BIT_ALIGN_COEFF;
buf += load_bytes / DAC_16BIT_ALIGN_COEFF;
}
/* Link the tail to the head as a ring */
handle->desc[i-1]->empty = (uint32_t)(handle->desc[0]);
dac_dma_periph_dma_trans_start((uint32_t)handle->desc[0]);
atomic_store(&handle->is_running, true);
if (bytes_loaded) {
*bytes_loaded = src_buf_size - buf_size;
}
xSemaphoreGive(handle->mutex);
return ret;
}
static esp_err_t s_dac_wait_to_load_dma_data(dac_conti_handle_t handle, uint8_t *buf, size_t buf_size, size_t *w_size, TickType_t timeout_tick)
{
lldesc_t *desc;
/* Try to get the descriptor from the pool */
ESP_RETURN_ON_FALSE(xQueueReceive(handle->desc_pool, &desc, timeout_tick) == pdTRUE,
ESP_ERR_TIMEOUT, TAG, "Get available descriptor timeout");
/* To ensure it is not in the pending desc chain */
if (STAILQ_FIRST(&handle->head) != NULL) {
DAC_STAILQ_REMOVE(&handle->head, desc, lldesc_s, qe);
}
static bool split_flag = false;
uint8_t *dma_buf = (uint8_t *)desc->buf;
if (buf_size * DAC_16BIT_ALIGN_COEFF < 2 * handle->cfg.buf_size) {
if (!split_flag) {
buf_size >>= 1;
split_flag = true;
} else {
split_flag = false;
}
}
size_t load_bytes = s_dac_load_data_into_buf(handle, dma_buf, handle->cfg.buf_size, buf, buf_size);
lldesc_config(desc, LLDESC_HW_OWNED, 1, 0, load_bytes);
desc->size = load_bytes;
*w_size = load_bytes / DAC_16BIT_ALIGN_COEFF;
/* Insert the loaded descriptor to the end of the chain, waiting to be sent */
DESC_ENTER_CRITICAL();
STAILQ_INSERT_TAIL(&handle->head, desc, qe);
DESC_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t dac_conti_write(dac_conti_handle_t handle, uint8_t *buf, size_t buf_size, size_t *bytes_loaded, int timeout_ms)
{
DAC_NULL_POINTER_CHECK(handle);
DAC_NULL_POINTER_CHECK(buf);
ESP_RETURN_ON_FALSE(atomic_load(&handle->is_enabled), ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels has not been enabled");
ESP_RETURN_ON_FALSE(!atomic_load(&handle->is_async), ESP_ERR_INVALID_STATE, TAG, "Asynchronous writing is running, can't write synchronously");
esp_err_t ret = ESP_OK;
TickType_t timeout_tick = timeout_ms < 0 ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
ESP_RETURN_ON_FALSE(xSemaphoreTake(handle->mutex, timeout_tick) == pdTRUE, ESP_ERR_TIMEOUT, TAG, "Take semaphore timeout");
size_t w_size = 0;
size_t src_buf_size = buf_size;
/* Reset the desc_pool and chain if called cyclic function last time */
if (atomic_load(&handle->is_cyclic)) {
xQueueReset(handle->desc_pool);
/* Break the chain if DMA still running */
for (int i = 0; i < handle->cfg.desc_num; i++) {
handle->desc[i]->empty = 0;
xQueueSend(handle->desc_pool, &handle->desc[i], 0);
}
STAILQ_INIT(&handle->head);
atomic_store(&handle->is_cyclic, false);
}
/* When there is no descriptor in the chain, DMA has stopped, load data and start the DMA link */
if (STAILQ_FIRST(&handle->head) == NULL) {
/* Wait for the previous DMA stop */
while (atomic_load(&handle->is_running)) {}
for (int i = 0;
i < handle->cfg.desc_num && buf_size > 0;
i++, buf += w_size, buf_size -= w_size) {
ESP_GOTO_ON_ERROR(s_dac_wait_to_load_dma_data(handle, buf, buf_size, &w_size, timeout_tick), err, TAG, "Load data failed");
}
dac_dma_periph_dma_trans_start((uint32_t)(STAILQ_FIRST(&handle->head)));
atomic_store(&handle->is_running, true);
}
/* If the source buffer is not totally loaded, keep loading the rest data */
while (buf_size > 0) {
ESP_GOTO_ON_ERROR(s_dac_wait_to_load_dma_data(handle, buf, buf_size, &w_size, timeout_tick), err, TAG, "Load data failed");
/* If the DMA stopped but there are still some descriptors not sent, start the DMA again */
DESC_ENTER_CRITICAL();
if (STAILQ_FIRST(&handle->head) && !atomic_load(&handle->is_running)) {
dac_dma_periph_dma_trans_start((uint32_t)(STAILQ_FIRST(&handle->head)));
atomic_store(&handle->is_running, true);
}
DESC_EXIT_CRITICAL();
buf += w_size;
buf_size -= w_size;
}
err:
/* The bytes number that has been loaded */
if (bytes_loaded) {
*bytes_loaded = src_buf_size - buf_size;
}
xSemaphoreGive(handle->mutex);
return ret;
}
+154
View File
@@ -0,0 +1,154 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "soc/soc_caps.h"
#include "driver/dac_cosine.h"
#include "hal/clk_tree_ll.h"
#include "dac_priv_common.h"
#include "clk_ctrl_os.h"
#include "esp_check.h"
#if CONFIG_DAC_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
// Set the maximum log level for this source file
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#endif
#if CONFIG_PM_ENABLE
#include "esp_pm.h"
#endif
struct dac_cosine_s {
dac_cosine_config_t cfg; /*!< Cosine mode configurations */
bool is_started; /*!< Flag: is the channel started(not cosine wave generator) */
};
static const char *TAG = "dac_cosine";
/* 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;
esp_err_t dac_new_cosine_channel(const dac_cosine_config_t *cos_cfg, dac_cosine_handle_t *ret_handle)
{
#if CONFIG_DAC_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif
/* 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(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_err_t ret = ESP_OK;
/* Allocate cosine handle */
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));
/* 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);
/* Only enabled for getting the correct rtc clock frequency */
periph_rtc_dig_clk8m_enable();
/* Cosine wave generator uses RTC_FAST clock which is divided from RC_FAST */
// [clk_tree] TODO: replace the following calculation with the RTC_FAST frequency getter
uint32_t rtc_clk_freq = periph_rtc_dig_clk8m_get_freq() / clk_ll_rc_fast_get_divider();
/* Disabled after getting the frequency, will re-enabled again when start outputting cosine wave */
periph_rtc_dig_clk8m_disable();
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;
}
DAC_RTC_ENTER_CRITICAL();
/* Set coefficients for cosine wave generator */
if ((!s_cwg_freq) || cos_cfg->flags.force_set_freq) {
dac_ll_cw_set_freq(cos_cfg->freq_hz, rtc_clk_freq);
s_cwg_freq = cos_cfg->freq_hz;
}
dac_ll_cw_set_atten(cos_cfg->chan_id, cos_cfg->atten);
dac_ll_cw_set_phase(cos_cfg->chan_id, cos_cfg->phase);
dac_ll_cw_set_dc_offset(cos_cfg->chan_id, cos_cfg->offset);
DAC_RTC_EXIT_CRITICAL();
*ret_handle = handle;
return ret;
err1:
free(handle);
return ret;
}
esp_err_t dac_del_cosine_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,
"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;
}
free(handle);
return ESP_OK;
}
esp_err_t dac_cosine_start(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 started");
/* Acquire the RTC clock */
periph_rtc_dig_clk8m_enable();
/* Enabled DAC channel */
ESP_RETURN_ON_ERROR(dac_priv_enable_channel(handle->cfg.chan_id), TAG,
"enable dac channel %d failed", handle->cfg.chan_id);
/* Enabled the cosine wave generator if no channel using it before */
DAC_RTC_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_RTC_EXIT_CRITICAL();
return ESP_OK;
}
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_RTC_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_RTC_EXIT_CRITICAL();
/* Release the RTC clock */
periph_rtc_dig_clk8m_disable();
return ESP_OK;
}
-781
View File
@@ -1,781 +0,0 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include <sys/queue.h>
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "sdkconfig.h"
#include "rom/lldesc.h"
#include "soc/soc_caps.h"
#include "soc/dac_periph.h"
#include "hal/dac_types.h"
#include "hal/dac_ll.h"
#include "driver/rtc_io.h"
#include "driver/dac_driver.h"
#include "dac_dma.h"
#include "esp_memory_utils.h"
#include "clk_ctrl_os.h"
#if CONFIG_DAC_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
// Set the maximum log level for this source file
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#endif
#include "esp_check.h"
#if CONFIG_PM_ENABLE
#include "esp_pm.h"
#endif
#define DAC_DMA_MAX_BUF_SIZE 4092 // Max DMA buffer size is 4095 but better to align with 4 bytes, so set 4092 here
#if CONFIG_DAC_ISR_IRAM_SAFE || CONFIG_DAC_CTRL_FUNC_IN_IRAM
#define DAC_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
#define DAC_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#endif
#if CONFIG_DAC_ISR_IRAM_SAFE
#define DAC_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_SHARED)
#else
#define DAC_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_SHARED)
#endif
#define DAC_DMA_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA)
#define DAC_STAILQ_REMOVE(head, elm, type, field) do { \
if ((head)->stqh_first == (elm)) { \
STAILQ_REMOVE_HEAD((head), field); \
} else { \
struct type *curelm = (head)->stqh_first; \
while (curelm->field.stqe_next != (elm) && \
curelm->field.stqe_next != NULL) \
curelm = curelm->field.stqe_next; \
if (curelm->field.stqe_next && (curelm->field.stqe_next = \
curelm->field.stqe_next->field.stqe_next) == NULL) \
(head)->stqh_last = &(curelm)->field.stqe_next; \
} \
} while (/*CONSTCOND*/0)
typedef enum {
DAC_STATE_INITIAL, /* Initial state, dac has been registered */
DAC_STATE_OUTPUT_READY, /* DAC channels enabled and ready to output constant voltage */
DAC_STATE_DMA_READY, /* DMA mode initialized, but not started */
DAC_STATE_CW_READY, /* Cosine wave mode is initialized, but not started */
DAC_STATE_DMA_ENABLED, /* DAC DMA dma is enabled, can't switch mode in this stage */
DAC_STATE_CW_RUNNING, /* DAC cosine wave generator is running, can't switch mode in this stage */
} dac_state_t;
typedef struct {
QueueHandle_t desc_pool; /* The pool of available descriptors
* The descriptors in the pool are not linked in to pending chain */
#if CONFIG_DAC_ISR_IRAM_SAFE
StaticQueue_t *desc_pool_struct; /* Static message queue struct */
void *desc_pool_storage; /* Static message queue storage */
#endif
dac_conti_config_t cfg;
STAILQ_HEAD(desc_chain_s, lldesc_s) head; /* Head of the descriptor chain
* The descriptors in the chain are pending to be sent or sending now */
lldesc_t **desc;
uint8_t **bufs;
volatile bool is_running; /* Is DMA running or stopped */
volatile bool is_cyclic; /* Is transport data cyclicly */
} dac_dma_t;
typedef struct dac_channel_chain_node_s {
dac_channel_t id;
SLIST_ENTRY(dac_channel_chain_node_s) next;
} dac_channel_info_t;
typedef SLIST_HEAD(dac_channel_chain_s, dac_channel_chain_node_s) dac_channel_chain_t;
struct dac_channels_s {
uint32_t chan_num;
dac_state_t state;
SemaphoreHandle_t mutex;
StaticSemaphore_t *mutex_struct; /*!< Static mutex struct */
#if CONFIG_PM_ENABLE
esp_pm_lock_handle_t pm_lock;
#endif
bool is_enabled;
dac_dma_t dma;
dac_cosine_config_t sw_cfg;
dac_channel_chain_t head;
};
typedef struct {
dac_channel_info_t *chan[SOC_DAC_PERIPH_NUM];
bool dma_in_use;
} dac_platform_t;
static const char *TAG = "DAC";
static dac_platform_t s_dac = {
.chan = {
[0 ... SOC_DAC_PERIPH_NUM - 1] = NULL,
},
.dma_in_use = false,
};
/* Global dac spin lock for the whole DAC driver */
portMUX_TYPE dac_spinlock = portMUX_INITIALIZER_UNLOCKED; // TODO: check rtc_spinlock
#define DAC_NULL_POINTER_CHECK(p) ESP_RETURN_ON_FALSE((p), ESP_ERR_INVALID_ARG, TAG, "input parameter '"#p"' is NULL")
static void dac_free_dma_desc(dac_channels_handle_t handle)
{
STAILQ_EMPTY(&handle->dma.head);
if (handle->dma.desc != NULL) {
for (int i = 0; i < handle->dma.cfg.desc_num; i++) {
if (handle->dma.desc[i]) {
free(handle->dma.desc[i]);
handle->dma.desc[i] = NULL;
}
}
free(handle->dma.desc);
handle->dma.desc = NULL;
}
if (handle->dma.bufs != NULL) {
for (int i = 0; i < handle->dma.cfg.desc_num; i++) {
if (handle->dma.bufs[i]) {
free(handle->dma.bufs[i]);
handle->dma.bufs[i] = NULL;
}
}
free(handle->dma.bufs);
handle->dma.bufs = NULL;
}
}
static esp_err_t dac_alloc_dma_desc(dac_channels_handle_t handle)
{
esp_err_t ret = ESP_OK;
STAILQ_INIT(&handle->dma.head);
handle->dma.desc = (lldesc_t **) heap_caps_calloc(handle->dma.cfg.desc_num, sizeof(lldesc_t *), DAC_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(handle->dma.desc, ESP_ERR_NO_MEM, TAG, "Faild to allocate dma descriptor array");
handle->dma.bufs = (uint8_t **) heap_caps_calloc(handle->dma.cfg.desc_num, sizeof(uint8_t *), DAC_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(handle->dma.bufs, ESP_ERR_NO_MEM, TAG, "Faild to allocate dma buffer array");
for (int cnt = 0; cnt < handle->dma.cfg.desc_num; cnt++) {
/* Allocate DMA descpriptor */
handle->dma.desc[cnt] = (lldesc_t *) heap_caps_calloc(1, sizeof(lldesc_t), DAC_DMA_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(handle->dma.desc[cnt], ESP_ERR_NO_MEM, err, TAG, "failed to allocate dma descriptor");
/* Allocate DMA buffer */
handle->dma.bufs[cnt] = (uint8_t *) heap_caps_calloc(1, handle->dma.cfg.buf_size, DAC_DMA_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(handle->dma.desc[cnt], ESP_ERR_NO_MEM, err, TAG, "failed to allocate dma buffer");
/* Assign initial value */
lldesc_config(handle->dma.desc[cnt], LLDESC_HW_OWNED, 1, 0, handle->dma.cfg.buf_size);
handle->dma.desc[cnt]->size = handle->dma.cfg.buf_size;
handle->dma.desc[cnt]->buf = handle->dma.bufs[cnt];
handle->dma.desc[cnt]->offset = 0;
xQueueSend(handle->dma.desc_pool, &handle->dma.desc[cnt], portMAX_DELAY);
}
return ESP_OK;
err:
/* Free DMA buffer if failed to allocate memory */
dac_free_dma_desc(handle);
return ret;
}
static void IRAM_ATTR dac_default_intr_handler(void *arg)
{
dac_channels_handle_t handle = (dac_channels_handle_t)arg;
uint32_t dummy;
BaseType_t need_awoke = pdFALSE;
BaseType_t tmp;
uint32_t intr_mask = dac_dma_periph_intr_is_triggered();
if ((intr_mask & DAC_DMA_EOF_INTR) && (!handle->dma.is_cyclic)) {
lldesc_t *fdesc = (lldesc_t *)dac_dma_periph_intr_get_eof_desc();
/* Remove the descriptor in the chain that finished sent */
portENTER_CRITICAL(&dac_spinlock);
if (STAILQ_FIRST(&handle->dma.head) != NULL) {
DAC_STAILQ_REMOVE(&handle->dma.head, fdesc, lldesc_s, qe);
}
portEXIT_CRITICAL(&dac_spinlock);
if (xQueueIsQueueFullFromISR(handle->dma.desc_pool) == pdTRUE) {
xQueueReceiveFromISR(handle->dma.desc_pool, &dummy, &tmp);
need_awoke |= tmp;
}
xQueueSendFromISR(handle->dma.desc_pool, &fdesc, &tmp);
need_awoke |= tmp;
}
if (intr_mask & DAC_DMA_TEOF_INTR) {
/* Total end of frame interrupt received, DMA stopped */
handle->dma.is_running = false;
}
if (need_awoke == pdTRUE) {
portYIELD_FROM_ISR();
}
}
/*--------------------------------------------------------------------------
DAC common APIs
---------------------------------------------------------------------------*/
esp_err_t dac_new_channels(const dac_channels_config_t *dac_cfg, dac_channels_handle_t *handle)
{
#if CONFIG_DAC_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif
esp_err_t ret = ESP_OK;
DAC_NULL_POINTER_CHECK(dac_cfg);
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE((uint32_t)dac_cfg->chan_sel > 0, ESP_ERR_INVALID_ARG, TAG, "invalid DAC channel");
ESP_RETURN_ON_FALSE((32 - __builtin_clz((uint32_t)dac_cfg->chan_sel)) <= SOC_DAC_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid DAC channel");
dac_channels_handle_t channels = (dac_channels_handle_t)heap_caps_calloc(1, sizeof(struct dac_channels_s), DAC_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(channels, ESP_ERR_NO_MEM, TAG, "No memory for DAC channels' structure");
channels->mutex_struct = (StaticSemaphore_t *)heap_caps_calloc(1, sizeof(StaticSemaphore_t), DAC_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(channels->mutex_struct, ESP_ERR_NO_MEM, err, TAG, "No memory for channels mutex struct");
channels->mutex = xSemaphoreCreateMutexStatic(channels->mutex_struct);
ESP_GOTO_ON_FALSE(channels->mutex, ESP_ERR_NO_MEM, err, TAG, "No memory for channels mutex");
channels->chan_num = 0;
channels->is_enabled = false;
channels->state = DAC_STATE_INITIAL; // Set static output as default
/* Register selected channels and link into a chain*/
SLIST_INIT(&channels->head);
for (uint32_t msk = (uint32_t)dac_cfg->chan_sel, i = 0; msk != 0; msk >>= 1, i++) {
if (msk & 0x01) {
/* Allocate memory for the channel when it is enabled */
ESP_GOTO_ON_FALSE(!s_dac.chan[i], ESP_ERR_INVALID_STATE, err, TAG, "DAC channel %d has been registered already", i + 1);
dac_channel_info_t *node = (dac_channel_info_t *)calloc(1, sizeof(dac_channel_info_t));
ESP_GOTO_ON_FALSE(node, ESP_ERR_NO_MEM, err, TAG, "No memory for DAC channel object");
node->id = i;
s_dac.chan[i] = node;
channels->chan_num++;
/* Add the channel node into a chain */
SLIST_INSERT_HEAD(&channels->head, node, next);
}
}
*handle = channels;
return ret;
err:
/* Free the resource when error occurs */
dac_del_channels(channels);
channels = NULL;
return ret;
}
esp_err_t dac_del_channels(dac_channels_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_INITIAL, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels is not deinitialized");
ESP_RETURN_ON_FALSE(!handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels is not disabled");
if (handle->mutex) {
vSemaphoreDelete(handle->mutex);
}
if (handle->mutex_struct) {
free(handle->mutex_struct);
}
while (SLIST_FIRST(&handle->head)) {
dac_channel_info_t *node = SLIST_FIRST(&handle->head);
SLIST_REMOVE_HEAD(&handle->head, next);
s_dac.chan[node->id] = NULL;
free(node);
}
free(handle);
return ESP_OK;
}
esp_err_t dac_channels_enable(dac_channels_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(!handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels has been enabled already");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
dac_channel_info_t *p;
SLIST_FOREACH(p, &handle->head, next) {
gpio_num_t gpio_num = (gpio_num_t)dac_periph_signal.dac_channel_io_num[p->id];
rtc_gpio_init(gpio_num);
rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_DISABLED);
rtc_gpio_pullup_dis(gpio_num);
rtc_gpio_pulldown_dis(gpio_num);
portENTER_CRITICAL(&dac_spinlock);
dac_ll_power_on(p->id);
dac_ll_rtc_sync_by_adc(false);
portEXIT_CRITICAL(&dac_spinlock);
}
handle->is_enabled = true;
/* If the channels has not been intialized to other mode, set it `DAC_STATE_OUTPUT_READY` as default */
if (handle->state == DAC_STATE_INITIAL) {
handle->state = DAC_STATE_OUTPUT_READY;
}
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
esp_err_t dac_channels_disable(dac_channels_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels has not been enabled yet");
ESP_RETURN_ON_FALSE(handle->state < DAC_STATE_DMA_ENABLED, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels is still running");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
dac_channel_info_t *p;
SLIST_FOREACH(p, &handle->head, next) {
gpio_num_t gpio_num = (gpio_num_t)dac_periph_signal.dac_channel_io_num[p->id];
rtc_gpio_deinit(gpio_num);
portENTER_CRITICAL(&dac_spinlock);
dac_ll_power_down(p->id);
portEXIT_CRITICAL(&dac_spinlock);
}
handle->is_enabled = false;
/* If the channels has not been intialized to other mode, set it `DAC_STATE_INITIAL` as default */
if (handle->state == DAC_STATE_OUTPUT_READY) {
handle->state = DAC_STATE_INITIAL;
}
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
/*--------------------------------------------------------------------------
DAC direct voltage outputting APIs
---------------------------------------------------------------------------*/
#if CONFIG_DAC_ISR_IRAM_SAFE
IRAM_ATTR
#endif
esp_err_t dac_channels_set_voltage(dac_channels_handle_t handle, uint8_t value)
{
ESP_RETURN_ON_FALSE_ISR(handle, ESP_ERR_INVALID_ARG, TAG, "NULL pointer");
ESP_RETURN_ON_FALSE_ISR(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "Not enabled");
ESP_RETURN_ON_FALSE_ISR(handle->state == DAC_STATE_OUTPUT_READY, ESP_ERR_INVALID_STATE, TAG, "Incorrect mode");
/* Set the voltage for each channel in the channels */
dac_channel_info_t *p;
SLIST_FOREACH(p, &handle->head, next) {
portENTER_CRITICAL(&dac_spinlock);
dac_ll_update_output_value(p->id, value);
portEXIT_CRITICAL(&dac_spinlock);
}
return ESP_OK;
}
/*--------------------------------------------------------------------------
DAC continuous mode APIs
---------------------------------------------------------------------------*/
esp_err_t dac_channels_init_continuous_mode(dac_channels_handle_t handle, const dac_conti_config_t *conti_cfg)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->state < DAC_STATE_DMA_READY, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels has been initialized already");
ESP_RETURN_ON_FALSE(conti_cfg->desc_num > 1, ESP_ERR_INVALID_STATE, TAG, "At least two DMA descriptor needed");
ESP_RETURN_ON_FALSE(!s_dac.dma_in_use, ESP_ERR_INVALID_STATE, TAG, "Only one set of channels can use DMA output");
esp_err_t ret = ESP_OK;
xSemaphoreTake(handle->mutex, portMAX_DELAY);
#if CONFIG_DAC_ISR_IRAM_SAFE
handle->dma.desc_pool_storage = (uint8_t *)heap_caps_calloc(conti_cfg->desc_num, sizeof(lldesc_t *), DAC_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(handle->dma.desc_pool_storage, ESP_ERR_NO_MEM, err3, TAG, "No memory for message queue storage");
handle->dma.desc_pool_struct = (StaticQueue_t *)heap_caps_calloc(1, sizeof(StaticQueue_t), DAC_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(handle->dma.desc_pool_struct , ESP_ERR_NO_MEM, err3, TAG, "No memory for message queue struct");
handle->dma.desc_pool = xQueueCreateStatic(conti_cfg->desc_num, sizeof(lldesc_t *), handle->dma.desc_pool_storage, handle->dma.desc_pool_struct);
#else
handle->dma.desc_pool = xQueueCreate(conti_cfg->desc_num, sizeof(lldesc_t *));
#endif
ESP_GOTO_ON_FALSE(handle->dma.desc_pool, ESP_ERR_NO_MEM, err3, TAG, "No memory for message queue");
#if CONFIG_PM_ENABLE
esp_pm_lock_type_t pm_lock_type = conti_cfg->clk_src == DAC_DIGI_CLK_SRC_APLL ? ESP_PM_NO_LIGHT_SLEEP : ESP_PM_APB_FREQ_MAX;
ESP_GOTO_ON_ERROR(esp_pm_lock_create(pm_lock_type, 0, "dac_driver", &handle->pm_lock), err3, TAG, "Failed to create DAC pm lock");
#endif
handle->dma.is_running = false;
/* Allocate DMA buffer */
memcpy(&(handle->dma.cfg), conti_cfg, sizeof(dac_conti_config_t));
ESP_GOTO_ON_ERROR(dac_alloc_dma_desc(handle), err2, TAG, "Failed to allocate memory for DMA buffers");
/* Initialize DAC DMA peripheral */
ESP_GOTO_ON_ERROR(dac_dma_periph_init(handle->chan_num ,conti_cfg->freq_hz, conti_cfg->chan_mode == DAC_CHANNEL_MODE_ALTER, conti_cfg->clk_src == DAC_DIGI_CLK_SRC_APLL),
err2, TAG, "Failed to initialize DAC DMA peripheral");
/* Register DMA interrupt */
ESP_GOTO_ON_ERROR(dac_dma_periph_register_intr(dac_default_intr_handler, handle), err1, TAG, "Failed to register DMA interrupt");
/* Connect DAC module to the DMA peripheral */
portENTER_CRITICAL(&dac_spinlock);
dac_ll_digi_enable_dma(true);
portEXIT_CRITICAL(&dac_spinlock);
handle->state = DAC_STATE_DMA_READY;
s_dac.dma_in_use = true;
xSemaphoreGive(handle->mutex);
return ret;
err1:
dac_dma_periph_deinit();
err2:
dac_free_dma_desc(handle);
err3:
if (handle->dma.desc_pool) {
vQueueDelete(handle->dma.desc_pool);
}
#if CONFIG_DAC_ISR_IRAM_SAFE
if (handle->dma.desc_pool_struct) {
free(handle->dma.desc_pool_struct);
}
if (handle->dma.desc_pool_storage) {
free(handle->dma.desc_pool_storage);
}
#endif
xSemaphoreGive(handle->mutex);
return ret;
}
esp_err_t dac_channels_deinit_continuous_mode(dac_channels_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_DMA_READY, ESP_ERR_INVALID_STATE, TAG,
"This set of DAC channels is still running or has been configured to other mode");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
#if CONFIG_DAC_ISR_IRAM_SAFE
free(handle->dma.desc_pool_storage);
handle->dma.desc_pool_storage = NULL;
free(handle->dma.desc_pool_struct);
handle->dma.desc_pool_struct = NULL;
#endif
#if CONFIG_PM_ENABLE
if (handle->pm_lock) {
esp_pm_lock_delete(handle->pm_lock);
handle->pm_lock = NULL;
}
#endif
vQueueDelete(handle->dma.desc_pool);
/* Free DMA buffer */
dac_free_dma_desc(handle);
/* Deregister DMA interrupt */
ESP_RETURN_ON_ERROR(dac_dma_periph_deregister_intr(), TAG, "Failed to deregister DMA interrupt");
/* Deinitialize DMA peripheral */
ESP_RETURN_ON_ERROR(dac_dma_periph_deinit(), TAG, "Failed to deinitialize DAC DMA peripheral");
/* Disconnect DAC module to the DMA peripheral */
portENTER_CRITICAL(&dac_spinlock);
dac_ll_digi_enable_dma(false);
portEXIT_CRITICAL(&dac_spinlock);
if (handle->is_enabled) {
handle->state = DAC_STATE_OUTPUT_READY;
} else {
handle->state = DAC_STATE_INITIAL;
}
s_dac.dma_in_use = false;
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
esp_err_t dac_channels_enable_continuous_mode(dac_channels_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels has not been enabled");
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_DMA_READY, ESP_ERR_INVALID_STATE, TAG,
"This set of DAC channels has started already or not working at DMA mode");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_acquire(handle->pm_lock);
#endif
dac_dma_periph_enable();
portENTER_CRITICAL(&dac_spinlock);
dac_ll_digi_enable_dma(true);
portEXIT_CRITICAL(&dac_spinlock);
handle->state = DAC_STATE_DMA_ENABLED;
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
esp_err_t dac_channels_disable_continuous_mode(dac_channels_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels has not been enabled");
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_DMA_ENABLED, ESP_ERR_INVALID_STATE, TAG,
"This set of DAC channels has stopped already or not working at DMA mode");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
dac_dma_periph_disable();
portENTER_CRITICAL(&dac_spinlock);
dac_ll_digi_enable_dma(false);
portEXIT_CRITICAL(&dac_spinlock);
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_release(handle->pm_lock);
#endif
handle->state = DAC_STATE_DMA_READY;
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
static esp_err_t s_dac_load_dma_data(dac_channels_handle_t handle, uint8_t *buf, size_t buf_size, size_t *w_size, uint32_t timeout_ms)
{
lldesc_t *desc = NULL;
/* Try to get the descriptor from the pool */
ESP_RETURN_ON_FALSE(xQueueReceive(handle->dma.desc_pool, &desc, pdMS_TO_TICKS(timeout_ms)) == pdTRUE,
ESP_ERR_TIMEOUT, TAG, "Get available descriptor timeout");
if (STAILQ_FIRST(&handle->dma.head) != NULL) {
DAC_STAILQ_REMOVE(&handle->dma.head, desc, lldesc_s, qe);
}
/* Get the DMA buffer address (use 'memcpy' to avoid the 'volatile' warning) */
uint8_t *dma_buf;
memcpy(&dma_buf, &desc->buf, sizeof(desc->buf));
#if SOC_DAC_DMA_16BIT_ALIGN && CONFIG_DAC_DMA_AUTO_16BIT_ALIGN
/* Load the data to the high 8 bit in the 16-bit width slot */
size_t _w_size = (buf_size > handle->dma.cfg.buf_size / 2) ? handle->dma.cfg.buf_size / 2 : buf_size;
for (int i = 0; i < _w_size; i++) {
dma_buf[2 * i + 1] = buf[i];
}
lldesc_config(desc, LLDESC_HW_OWNED, 1, 0, _w_size * 2);
desc->size = _w_size * 2;
#else
/* Load the data into the DMA buffer */
size_t _w_size = (buf_size > handle->dma.cfg.buf_size) ? handle->dma.cfg.buf_size : buf_size;
memcpy(dma_buf, buf, _w_size);
lldesc_config(desc, LLDESC_HW_OWNED, 1, 0, _w_size);
desc->size = _w_size;
#endif
*w_size = _w_size;
/* Insert the loaded descriptor to the end of the chain, waiting to be sent */
portENTER_CRITICAL(&dac_spinlock);
STAILQ_INSERT_TAIL(&handle->dma.head, desc, qe);
portEXIT_CRITICAL(&dac_spinlock);
return ESP_OK;
}
esp_err_t dac_channels_write_continuously(dac_channels_handle_t handle, uint8_t *buf, size_t buf_size, size_t *bytes_loaded, uint32_t timeout_ms)
{
DAC_NULL_POINTER_CHECK(handle);
DAC_NULL_POINTER_CHECK(buf);
ESP_RETURN_ON_FALSE(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels has not been enabled");
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_DMA_ENABLED, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels is not started");
#if CONFIG_DAC_ISR_IRAM_SAFE
ESP_RETURN_ON_FALSE(esp_ptr_internal(buf), ESP_ERR_INVALID_ARG, TAG, "the buffer is not in internal RAM");
#endif
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(xSemaphoreTake(handle->mutex, pdMS_TO_TICKS(timeout_ms) == pdTRUE), ESP_ERR_TIMEOUT, TAG, "Take semaphore timeout");
size_t w_size = 0;
size_t src_buf_size = buf_size;
/* When there is no descriptor in the chain, DMA has stopped, load data and start the DMA link */
if (STAILQ_FIRST(&handle->dma.head) == NULL) {
/* Break the chain if DMA still running */
for (int i = 0; handle->dma.is_running && i < handle->dma.cfg.desc_num; i++) {
handle->dma.desc[i]->empty = 0;
}
for (int i = 0;
i < handle->dma.cfg.desc_num && buf_size > 0;
i++, buf += w_size, buf_size -= w_size) {
ESP_GOTO_ON_ERROR(s_dac_load_dma_data(handle, buf, buf_size, &w_size, timeout_ms), err, TAG, "Load data failed");
}
/* Wait for the previous DMA stop */
while (handle->dma.is_running) {}
handle->dma.is_cyclic = false;
dac_dma_periph_dma_trans_start((uint32_t)(STAILQ_FIRST(&handle->dma.head)));
handle->dma.is_running = true;
}
/* If the source buffer is not totally loaded, keep loading the rest data */
while (buf_size > 0) {
/* If the DMA stopped but there are still some decriptors not sent, start the DMA again */
if ((!handle->dma.is_running) && STAILQ_FIRST(&handle->dma.head)) {
dac_dma_periph_dma_trans_start((uint32_t)(STAILQ_FIRST(&handle->dma.head)));
}
ESP_GOTO_ON_ERROR(s_dac_load_dma_data(handle, buf, buf_size, &w_size, timeout_ms), err, TAG, "Load data failed");
buf += w_size;
buf_size -= w_size;
}
err:
/* The bytes number that has been loaded */
if (bytes_loaded) {
*bytes_loaded = src_buf_size - buf_size;
}
xSemaphoreGive(handle->mutex);
return ret;
}
esp_err_t dac_channels_write_cyclically(dac_channels_handle_t handle, uint8_t *buf, size_t buf_size, size_t *bytes_loaded, uint32_t timeout_ms)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels has not been enabled");
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_DMA_ENABLED, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels is not started");
#if CONFIG_DAC_ISR_IRAM_SAFE
ESP_RETURN_ON_FALSE(esp_ptr_internal(buf), ESP_ERR_INVALID_ARG, TAG, "the buffer is not in internal RAM");
#endif
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(xSemaphoreTake(handle->mutex, pdMS_TO_TICKS(timeout_ms) == pdTRUE), ESP_ERR_TIMEOUT, TAG, "Take semaphore timeout");
/* Break the DMA descriptor chain to stop the DMA first */
for (int i = 0; i < handle->dma.cfg.desc_num; i++) {
handle->dma.desc[i]->empty = 0;
}
size_t src_buf_size = buf_size;
/* If the source buffer size is smaller than the DMA buffer size, load the source buffer into two descriptors */
if (buf_size < handle->dma.cfg.buf_size) {
/* Load data */
size_t half_size = buf_size / 2;
memcpy(handle->dma.bufs[0], buf, half_size);
memcpy(handle->dma.bufs[1], buf, buf_size - half_size);
lldesc_config(handle->dma.desc[0], LLDESC_HW_OWNED, 1, 0, half_size);
lldesc_config(handle->dma.desc[1], LLDESC_HW_OWNED, 1, 0, buf_size - half_size);
buf_size = 0;
/* Link as a circle */
handle->dma.desc[0]->empty = (uint32_t)handle->dma.desc[1];
handle->dma.desc[1]->empty = (uint32_t)handle->dma.desc[0];
} else {
int i;
for (i = 0; i < handle->dma.cfg.desc_num && buf_size > 0; i++) {
size_t w_size = buf_size > handle->dma.cfg.buf_size ? handle->dma.cfg.buf_size : buf_size;
memcpy(handle->dma.bufs[i], buf, w_size);
lldesc_config(handle->dma.desc[i], LLDESC_HW_OWNED, 1, 0, w_size);
handle->dma.desc[i]->empty = (uint32_t)(i < handle->dma.cfg.desc_num - 1 ? handle->dma.desc[i+1] :0);
buf_size -= w_size;
buf += w_size;
}
handle->dma.desc[i-1]->empty = (uint32_t)(handle->dma.desc[0]);
}
/* Wait for the previous DMA stop */
while (handle->dma.is_running) {}
handle->dma.is_cyclic = true;
dac_dma_periph_dma_trans_start((uint32_t)handle->dma.desc[0]);
handle->dma.is_running = true;
if (bytes_loaded) {
*bytes_loaded = src_buf_size - buf_size;
}
if (buf_size > 0) {
ESP_LOGW(TAG, "The cyclic buffer size exceeds the total DMA buffer size: %d(desc_num) * %d(buf_size) = %d",
handle->dma.cfg.desc_num, handle->dma.cfg.buf_size, handle->dma.cfg.buf_size * handle->dma.cfg.desc_num);
}
xSemaphoreGive(handle->mutex);
return ret;
}
/*--------------------------------------------------------------------------
DAC cosine wave outputting APIs
---------------------------------------------------------------------------*/
esp_err_t dac_channels_init_cosine_mode(dac_channels_handle_t handle, const dac_cosine_config_t *cw_cfg)
{
DAC_NULL_POINTER_CHECK(handle);
DAC_NULL_POINTER_CHECK(cw_cfg);
ESP_RETURN_ON_FALSE((handle->state == DAC_STATE_INITIAL) | (handle->state == DAC_STATE_OUTPUT_READY),
ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels has been initialized already");
ESP_RETURN_ON_FALSE(cw_cfg->freq_hz >= 130, ESP_ERR_NOT_SUPPORTED, TAG, "The cosine wave generator doesn't support frequency below 130 Hz");
esp_err_t ret = ESP_OK;
xSemaphoreTake(handle->mutex, portMAX_DELAY);
#if CONFIG_PM_ENABLE
ESP_GOTO_ON_ERROR(esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "dac_driver", &handle->pm_lock), err, TAG, "Failed to create DAC pm lock");
#endif
portENTER_CRITICAL(&dac_spinlock);
dac_channel_info_t *p;
SLIST_FOREACH(p, &handle->head, next) {
/* Connect DAC module to cosine wave generator */
dac_ll_cw_set_channel(p->id, true);
/* Set coefficients for cosine wave generator */
dac_ll_cw_set_freq(cw_cfg->freq_hz, periph_rtc_dig_clk8m_get_freq());
dac_ll_cw_set_scale(p->id, cw_cfg->scale);
dac_ll_cw_set_phase(p->id, cw_cfg->phase == DAC_COSINE_PHASE_180 ? DAC_LL_CW_PHASE_180 : DAC_LL_CW_PHASE_0);
dac_ll_cw_set_dc_offset(p->id, cw_cfg->offset);
}
portEXIT_CRITICAL(&dac_spinlock);
handle->state = DAC_STATE_CW_READY;
#if CONFIG_PM_ENABLE
err:
#endif
xSemaphoreGive(handle->mutex);
return ret;
}
esp_err_t dac_channels_deinit_cosine_mode(dac_channels_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_CW_READY, ESP_ERR_INVALID_STATE, TAG,
"This set of DAC channels is still running or not working at cosine wave mode");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
#if CONFIG_PM_ENABLE
if (handle->pm_lock) {
esp_pm_lock_delete(handle->pm_lock);
handle->pm_lock = NULL;
}
#endif
portENTER_CRITICAL(&dac_spinlock);
dac_channel_info_t *p;
SLIST_FOREACH(p, &handle->head, next) {
/* Disonnect DAC module to cosine wave generator */
dac_ll_cw_set_channel(p->id, false);
}
portEXIT_CRITICAL(&dac_spinlock);
if (handle->is_enabled) {
handle->state = DAC_STATE_OUTPUT_READY;
} else {
handle->state = DAC_STATE_INITIAL;
}
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
esp_err_t dac_channels_start_cosine_output(dac_channels_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels has not been enabled");
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_CW_READY, ESP_ERR_INVALID_STATE, TAG,
"This set of DAC channels has started already or not working at cosine wave mode");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_acquire(handle->pm_lock);
#endif
portENTER_CRITICAL(&dac_spinlock);
dac_ll_cw_generator_enable();
portEXIT_CRITICAL(&dac_spinlock);
handle->state = DAC_STATE_CW_RUNNING;
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
esp_err_t dac_channels_stop_cosine_output(dac_channels_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels has not been enabled");
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_CW_RUNNING, ESP_ERR_INVALID_STATE, TAG,
"This set of DAC channels has stopped already or not working at cosine wave mode");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
portENTER_CRITICAL(&dac_spinlock);
dac_ll_cw_generator_disable();
portEXIT_CRITICAL(&dac_spinlock);
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_release(handle->pm_lock);
#endif
handle->state = DAC_STATE_CW_READY;
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
+84
View File
@@ -0,0 +1,84 @@
/*
* SPDX-FileCopyrightText: 2022 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"
#include "esp_check.h"
#if CONFIG_DAC_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
// Set the maximum log level for this source file
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#endif
#if CONFIG_PM_ENABLE
#include "esp_pm.h"
#endif
struct dac_oneshot_s {
dac_oneshot_config_t cfg; /*!< Oneshot mode configurations */
};
static const char *TAG = "dac_oneshot";
esp_err_t dac_new_oneshot_channel(const dac_oneshot_config_t *oneshot_cfg, dac_oneshot_handle_t *ret_handle)
{
#if CONFIG_DAC_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif
/* 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_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));
/* 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_enable_channel(oneshot_cfg->chan_id), 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);
err2:
free(handle);
return ret;
}
esp_err_t dac_del_oneshot_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);
/* Free resources */
free(handle);
return ESP_OK;
}
esp_err_t dac_oneshot_output_voltage(dac_oneshot_handle_t handle, uint8_t digi_value)
{
if (!handle) {
return ESP_ERR_INVALID_ARG;
}
/* Set the voltage by the digital value */
DAC_RTC_ENTER_CRITICAL_SAFE();
dac_ll_update_output_value(handle->cfg.chan_id, digi_value);
DAC_RTC_EXIT_CRITICAL_SAFE();
return ESP_OK;
}
+76
View File
@@ -0,0 +1,76 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "freertos/FreeRTOS.h"
#include "hal/dac_types.h"
#include "hal/dac_ll.h"
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
extern portMUX_TYPE rtc_spinlock; /*!< Extern global rtc spinlock */
#define DAC_RTC_ENTER_CRITICAL() portENTER_CRITICAL(&rtc_spinlock)
#define DAC_RTC_EXIT_CRITICAL() portEXIT_CRITICAL(&rtc_spinlock)
#define DAC_RTC_ENTER_CRITICAL_SAFE() portENTER_CRITICAL_SAFE(&rtc_spinlock)
#define DAC_RTC_EXIT_CRITICAL_SAFE() portEXIT_CRITICAL_SAFE(&rtc_spinlock)
#define DAC_NULL_POINTER_CHECK(p) ESP_RETURN_ON_FALSE((p), ESP_ERR_INVALID_ARG, TAG, "input parameter '"#p"' is NULL")
#define DAC_NULL_POINTER_CHECK_ISR(p) ESP_RETURN_ON_FALSE_ISR((p), ESP_ERR_INVALID_ARG, TAG, "input parameter '"#p"' is NULL")
/**
* @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);
/**
* @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_ARG The channel id is incorrect
* - ESP_OK Deregister the channel success
*/
esp_err_t dac_priv_deregister_channel(dac_channel_t chan_id);
/**
* @brief Enable the DAC channel and turn on its power
*
* @param chan_id DAC channel id
* @return
* - ESP_ERR_INVALID_STATE The channel has not been registered
* - ESP_ERR_INVALID_ARG The channel id is incorrect
* - ESP_OK Deregister the channel success
*/
esp_err_t dac_priv_enable_channel(dac_channel_t chan_id);
/**
* @brief Disable the DAC channel and turn off its power
*
* @param chan_id DAC channel id
* @return
* - ESP_ERR_INVALID_STATE The channel has not been registered
* - ESP_ERR_INVALID_ARG The channel id is incorrect
* - ESP_OK Deregister the channel success
*/
esp_err_t dac_priv_disable_channel(dac_channel_t chan_id);
#ifdef __cplusplus
}
#endif
@@ -26,39 +26,28 @@ extern "C" {
* @return
* - ESP_ERR_NOT_FOUND The DMA peripheral has been occupied
* - ESP_ERR_NO_MEM No memory for the DMA peripheral struct
* - ESP_ERR_INVALID_ARG The frequency is outof range
* - ESP_ERR_INVALID_ARG The frequency is out of range
* - ESP_OK Initialize DAC DMA peripheral success
*/
esp_err_t dac_dma_periph_init(uint8_t chan_num, uint32_t freq_hz, bool is_alternate, bool is_apll);
esp_err_t dac_dma_periph_init(uint32_t freq_hz, bool is_alternate, bool is_apll);
/**
* @brief Deinitialize DAC DMA peripheral
*
* @return
* - ESP_ERR_INVALID_STATE The DAC DMA has deinitialized already
* - ESP_ERR_INVALID_STATE The DAC DMA has been de-initialized already
* or the interrupt has not been de-registered
* - ESP_OK Deinitialize DAC DMA peripheral success
*/
esp_err_t dac_dma_periph_deinit(void);
/**
* @brief Register the DMA interrupt
*
* @param[in] intr_handler_func DMA interrupt handler function
* @param[in] user_ctx User contex that pass to the interrupt handler
* @return
* - ESP_ERR_INVALID_STATE DAC DMA peripheral is not initialized
* - ESP_OK Register DMA interrupt success
*/
esp_err_t dac_dma_periph_register_intr(intr_handler_t intr_handler_func, void *user_ctx);
/**
* @brief Deregister the DMA interrupt
* @brief Get the DMA interrupt signal id
*
* @return
* - ESP_ERR_INVALID_STATE DAC DMA peripheral is not initialized
* - ESP_OK Deregister DMA interrupt success
* - int DMA interrupt signal id
*/
esp_err_t dac_dma_periph_deregister_intr(void);
int dac_dma_periph_get_intr_signal(void);
/**
* @brief Enable the DMA and interrupt of the DAC DMA peripheral
@@ -76,8 +65,7 @@ void dac_dma_periph_disable(void);
* @brief Whether the TX_EOF interrupt is triggered
*
* @return
* - true TX_EOF interrupt is triggered
* - false TX_EOF interrupt is not triggered
* - uint32_t Mask of the triggered interrupt: DAC_DMA_EOF_INTR, DAC_DMA_EOF_INTR
*/
uint32_t dac_dma_periph_intr_is_triggered(void);
+63 -67
View File
@@ -4,15 +4,23 @@
* SPDX-License-Identifier: Apache-2.0
*/
/**
* This file is a target specific for DAC DMA peripheral
* Target: ESP32
* DAC DMA peripheral (data source): I2S0 (i.e. use I2S DMA to transmit data)
* DAC DMA interrupt source: I2S0
* DAC digital controller clock source: I2S ws signal (root clock: D2PLL or APLL)
*/
#include "freertos/FreeRTOS.h"
#include "sdkconfig.h"
#include "hal/adc_ll.h"
#include "hal/i2s_ll.h"
#include "hal/i2s_types.h"
#include "soc/i2s_periph.h"
#include "../dac_dma.h"
#include "../dac_priv_dma.h"
#include "esp_private/i2s_platform.h"
#include "esp_private/esp_clk.h"
#include "clk_ctrl_os.h"
#if CONFIG_DAC_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
@@ -35,9 +43,7 @@ static dac_dma_periph_i2s_t *s_ddp = NULL; // Static DAC DMA peripheral structur
static const char *TAG = "DAC_DMA";
extern portMUX_TYPE dac_spinlock; /* Global DAC spinlock */
static uint32_t dac_set_apll(uint32_t mclk)
static uint32_t s_dac_set_apll_freq(uint32_t mclk)
{
/* Calculate the expected APLL */
int div = (int)((SOC_APLL_MIN_HZ / mclk) + 1);
@@ -54,42 +60,50 @@ static uint32_t dac_set_apll(uint32_t mclk)
return 0;
}
if (ret == ESP_ERR_INVALID_STATE) {
ESP_LOGW(TAG, "APLL is occupied already, it is working at %d Hz", real_freq);
ESP_LOGW(TAG, "APLL is occupied already, it is working at %"PRIu32" Hz", real_freq);
}
ESP_LOGD(TAG, "APLL expected frequency is %d Hz, real frequency is %d Hz", expt_freq, real_freq);
ESP_LOGD(TAG, "APLL expected frequency is %"PRIu32" Hz, real frequency is %"PRIu32" Hz", expt_freq, real_freq);
return real_freq;
}
static esp_err_t dac_dma_periph_set_clock(uint32_t freq_hz, bool is_apll)
/**
* @brief Calculate and set DAC data frequency
* @note DAC frequency is decided by I2S WS frequency, the clock source of I2S is D2PLL or APLL on ESP32
* freq_hz = ws = bclk / I2S_LL_AD_BCK_FACTOR
* @param freq_hz DAC byte transmit frequency
* @return
* - ESP_OK config success
* - ESP_ERR_INVALID_ARG invalid frequency
*/
static esp_err_t s_dac_dma_periph_set_clock(uint32_t freq_hz, bool is_apll)
{
/* Calculate clock coefficients */
uint32_t bclk = freq_hz * I2S_LL_AD_BCK_FACTOR;
uint32_t bclk_div = DAC_DMA_PERIPH_I2S_BIT_WIDTH;
uint32_t mclk = bclk * bclk_div;
uint32_t sclk; // use 160M PLL clock as default, minimun support freq: 19.6 KHz maximun support freq: 2.5 MHz
uint32_t sclk; // use 160M PLL clock as default, minimum support freq: 19.6 KHz maximum support freq: 2.5 MHz
if (is_apll) {
sclk = dac_set_apll(mclk);
sclk = s_dac_set_apll_freq(mclk);
ESP_RETURN_ON_FALSE(sclk, ESP_ERR_INVALID_ARG, TAG, "set APLL coefficients failed");
} else {
sclk = I2S_LL_BASE_CLK;
// [clk_tree] TODO: replace the following clock by clk_tree API
sclk = esp_clk_apb_freq() * 2; // D2PLL
}
uint32_t mclk_div = sclk / mclk;
/* Check if the configuration is correct */
ESP_RETURN_ON_FALSE(sclk / (float)mclk > 1.99, ESP_ERR_INVALID_ARG, TAG, "Frequency is too large, the mclk division is below minimum value 2");
ESP_RETURN_ON_FALSE(mclk_div < 256, ESP_ERR_INVALID_ARG, TAG, "Frequency is too small, the mclk division exceed the maximum value 255");
ESP_LOGD(TAG, "[sclk] %d [mclk] %d [mclk_div] %d [bclk] %d [bclk_div] %d", sclk, mclk, mclk_div, bclk, bclk_div);
ESP_LOGD(TAG, "[sclk] %"PRIu32" [mclk] %"PRIu32" [mclk_div] %"PRIu32" [bclk] %"PRIu32" [bclk_div] %"PRIu32, sclk, mclk, mclk_div, bclk, bclk_div);
portENTER_CRITICAL(&dac_spinlock);
i2s_ll_tx_clk_set_src(s_ddp->periph_dev, is_apll ? I2S_CLK_SRC_APLL : I2S_CLK_SRC_DEFAULT);
i2s_ll_tx_set_mclk(s_ddp->periph_dev, sclk, mclk, mclk_div);
i2s_ll_tx_set_bck_div_num(s_ddp->periph_dev, bclk_div);
portEXIT_CRITICAL(&dac_spinlock);
return ESP_OK;
}
esp_err_t dac_dma_periph_init(uint8_t chan_num, uint32_t freq_hz, bool is_alternate, bool is_apll)
esp_err_t dac_dma_periph_init(uint32_t freq_hz, bool is_alternate, bool is_apll)
{
#if CONFIG_DAC_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
@@ -98,7 +112,7 @@ esp_err_t dac_dma_periph_init(uint8_t chan_num, uint32_t freq_hz, bool is_altern
/* Acquire DMA peripheral */
ESP_RETURN_ON_ERROR(i2s_platform_acquire_occupation(DAC_DMA_PERIPH_I2S_NUM, "dac_dma"), TAG, "Failed to acquire DAC DMA peripheral");
/* Allocate DAC DMA peripheral object */
s_ddp = (dac_dma_periph_i2s_t *)calloc(1, sizeof(dac_dma_periph_i2s_t));
s_ddp = (dac_dma_periph_i2s_t *)heap_caps_calloc(1, sizeof(dac_dma_periph_i2s_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
ESP_GOTO_ON_FALSE(s_ddp, ESP_ERR_NO_MEM, err, TAG, "No memory for DAC DMA object");
s_ddp->periph_dev = (void *)I2S_LL_GET_HW(DAC_DMA_PERIPH_I2S_NUM);
@@ -106,22 +120,23 @@ esp_err_t dac_dma_periph_init(uint8_t chan_num, uint32_t freq_hz, bool is_altern
periph_rtc_apll_acquire();
s_ddp->use_apll = true;
}
ESP_GOTO_ON_ERROR(dac_dma_periph_set_clock(freq_hz, is_apll), err, TAG, "Failed to set clock of DMA peripheral");
ESP_GOTO_ON_ERROR(s_dac_dma_periph_set_clock(freq_hz, is_apll), err, TAG, "Failed to set clock of DMA peripheral");
portENTER_CRITICAL(&dac_spinlock);
i2s_ll_enable_builtin_dac(s_ddp->periph_dev, true);
i2s_ll_tx_reset(s_ddp->periph_dev);
i2s_ll_tx_set_slave_mod(s_ddp->periph_dev, false);
i2s_ll_tx_set_sample_bit(s_ddp->periph_dev, DAC_DMA_PERIPH_I2S_BIT_WIDTH, DAC_DMA_PERIPH_I2S_BIT_WIDTH);
i2s_ll_tx_enable_mono_mode(s_ddp->periph_dev, !is_alternate);
i2s_ll_tx_select_std_slot(s_ddp->periph_dev, 0x03, !is_alternate);
i2s_ll_tx_select_std_slot(s_ddp->periph_dev, I2S_STD_SLOT_BOTH, !is_alternate);
i2s_ll_tx_enable_msb_shift(s_ddp->periph_dev, false);
i2s_ll_tx_set_ws_width(s_ddp->periph_dev, DAC_DMA_PERIPH_I2S_BIT_WIDTH);
i2s_ll_tx_enable_msb_right(s_ddp->periph_dev, false);
i2s_ll_tx_enable_right_first(s_ddp->periph_dev, true);
/* Should always enable fifo */
i2s_ll_tx_force_enable_fifo_mod(s_ddp->periph_dev, true);
portEXIT_CRITICAL(&dac_spinlock);
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:
@@ -131,11 +146,10 @@ err:
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(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) {
if (s_ddp->intr_handle) {
dac_dma_periph_deregister_intr();
}
if (s_ddp->use_apll) {
periph_rtc_apll_release();
s_ddp->use_apll = false;
@@ -147,67 +161,51 @@ esp_err_t dac_dma_periph_deinit(void)
return ESP_OK;
}
esp_err_t dac_dma_periph_register_intr(intr_handler_t intr_handler_func, void *user_ctx)
int dac_dma_periph_get_intr_signal(void)
{
ESP_RETURN_ON_FALSE(s_ddp, ESP_ERR_INVALID_STATE, TAG, "DAC DMA peripheral has not initialized yet");
/* Regigster interrupt */
ESP_RETURN_ON_ERROR(esp_intr_alloc(i2s_periph_signal[DAC_DMA_PERIPH_I2S_NUM].irq, ESP_INTR_FLAG_LEVEL1,
intr_handler_func, user_ctx, &(s_ddp->intr_handle)),
TAG, "Failed to register DAC DMA interrupt");
portENTER_CRITICAL(&dac_spinlock);
i2s_ll_enable_intr(s_ddp->periph_dev, I2S_LL_EVENT_TX_EOF | I2S_LL_EVENT_TX_TEOF, true);
portEXIT_CRITICAL(&dac_spinlock);
return ESP_OK;
return i2s_periph_signal[DAC_DMA_PERIPH_I2S_NUM].irq;
}
esp_err_t dac_dma_periph_deregister_intr(void)
static void s_dac_dma_periph_reset(void)
{
ESP_RETURN_ON_FALSE(s_ddp, ESP_ERR_INVALID_STATE, TAG, "DAC DMA peripheral has not initialized yet");
if (s_ddp->intr_handle) {
portENTER_CRITICAL(&dac_spinlock);
i2s_ll_enable_intr(s_ddp->periph_dev, I2S_LL_EVENT_TX_EOF | I2S_LL_EVENT_TX_TEOF, false);
portEXIT_CRITICAL(&dac_spinlock);
esp_intr_free(s_ddp->intr_handle);
s_ddp->intr_handle = NULL;
}
return ESP_OK;
}
void dac_dma_periph_enable(void)
{
portENTER_CRITICAL(&dac_spinlock);
/* Reset */
i2s_ll_tx_reset(s_ddp->periph_dev);
i2s_ll_tx_reset_dma(s_ddp->periph_dev);
i2s_ll_tx_reset_fifo(s_ddp->periph_dev);
/* Start */
}
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_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);
portEXIT_CRITICAL(&dac_spinlock);
/* Enable interrupt */
esp_intr_enable(s_ddp->intr_handle);
}
void dac_dma_periph_disable(void)
static void s_dac_dma_periph_stop(void)
{
portENTER_CRITICAL(&dac_spinlock);
/* Reset */
i2s_ll_tx_reset(s_ddp->periph_dev);
i2s_ll_tx_reset_dma(s_ddp->periph_dev);
i2s_ll_tx_reset_fifo(s_ddp->periph_dev);
/* Stop */
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_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);
portEXIT_CRITICAL(&dac_spinlock);
/* Disable interrupt */
esp_intr_disable(s_ddp->intr_handle);
}
void dac_dma_periph_enable(void)
{
/* Reset */
s_dac_dma_periph_reset();
/* Start */
s_dac_dma_periph_start();
}
void dac_dma_periph_disable(void)
{
/* Reset */
s_dac_dma_periph_reset();
/* Stop */
s_dac_dma_periph_stop();
}
uint32_t IRAM_ATTR dac_dma_periph_intr_is_triggered(void)
@@ -231,9 +229,7 @@ uint32_t IRAM_ATTR dac_dma_periph_intr_get_eof_desc(void)
return finish_desc;
}
void inline dac_dma_periph_dma_trans_start(uint32_t desc_addr)
void dac_dma_periph_dma_trans_start(uint32_t desc_addr)
{
portENTER_CRITICAL(&dac_spinlock);
i2s_ll_tx_start_link(s_ddp->periph_dev, desc_addr);
portEXIT_CRITICAL(&dac_spinlock);
}
+36 -61
View File
@@ -4,6 +4,15 @@
* SPDX-License-Identifier: Apache-2.0
*/
/**
* This file is a target specific for DAC DMA peripheral
* Target: ESP32-S2
* DAC DMA peripheral (data source): SPI3 (i.e. use SPI DMA to transmit data)
* DAC DMA interrupt source: SPI3
* DAC digital controller clock source: DIG_SARADC_CLK (root clock: APB or APLL)
*/
#include "sdkconfig.h"
#include "esp_private/spi_common_internal.h"
#include "esp_private/periph_ctrl.h"
#include "hal/spi_ll.h"
@@ -12,7 +21,7 @@
#include "soc/lldesc.h"
#include "soc/soc.h"
#include "soc/soc_caps.h"
#include "../dac_dma.h"
#include "../dac_priv_dma.h"
#include "clk_ctrl_os.h"
#if CONFIG_DAC_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
@@ -35,9 +44,7 @@ static dac_dma_periph_spi_t *s_ddp = NULL; // Static DAC DMA peripheral structur
static const char *TAG = "DAC_DMA";
extern portMUX_TYPE dac_spinlock; /* Global DAC spinlock */
static uint32_t dac_set_apll_freq(uint32_t expt_freq)
static uint32_t s_dac_set_apll_freq(uint32_t expt_freq)
{
/* Set APLL coefficients to the given frequency */
uint32_t real_freq = 0;
@@ -46,30 +53,30 @@ static uint32_t dac_set_apll_freq(uint32_t expt_freq)
return 0;
}
if (ret == ESP_ERR_INVALID_STATE) {
ESP_LOGW(TAG, "APLL is occupied already, it is working at %d Hz", real_freq);
ESP_LOGW(TAG, "APLL is occupied already, it is working at %"PRIu32" Hz", real_freq);
}
ESP_LOGD(TAG, "APLL expected frequency is %d Hz, real frequency is %d Hz", expt_freq, real_freq);
ESP_LOGD(TAG, "APLL expected frequency is %"PRIu32" Hz, real frequency is %"PRIu32" Hz", expt_freq, real_freq);
return real_freq;
}
/**
* @brief Calculate and set DAC data frequency
* @note DAC clcok shares clock devider with ADC, the clock source is APB or APLL on ESP32-S2
* @note DAC clock shares clock divider with ADC, the clock source is APB or APLL on ESP32-S2
* freq_hz = (source_clk / (clk_div + (b / a) + 1)) / interval
* interval range: 1~4095, to avoid decimal as possible, all calculations will base on interval = 4000
* interval range: 1~4095
* @param freq_hz DAC byte transmit frequency
* @return
* - ESP_OK config success
* - ESP_ERR_INVALID_ARG invalid frequency
*/
static esp_err_t dac_dma_periph_set_clock(uint32_t freq_hz, bool is_apll){
static esp_err_t s_dac_dma_periph_set_clock(uint32_t freq_hz, bool is_apll){
/* Step 1: Determine the digital clock source frequency */
uint32_t digi_ctrl_freq; // Digital controller clock
if (is_apll) {
/* Theoretical frequency range (due to the limitation of DAC, the maximum frequency may not reach):
* SOC_APLL_MAX_HZ: 119.24 Hz ~ 67.5 MHz
* SOC_APLL_MIN_HZ: 5.06 Hz ~ 2.65 MHz */
digi_ctrl_freq = dac_set_apll_freq(freq_hz < 120 ? SOC_APLL_MIN_HZ :SOC_APLL_MAX_HZ);
digi_ctrl_freq = s_dac_set_apll_freq(freq_hz < 120 ? SOC_APLL_MIN_HZ :SOC_APLL_MAX_HZ);
ESP_RETURN_ON_FALSE(digi_ctrl_freq, ESP_ERR_INVALID_ARG, TAG, "set APLL coefficients failed");
} else {
digi_ctrl_freq = APB_CLK_FREQ;
@@ -83,7 +90,7 @@ static esp_err_t dac_dma_periph_set_clock(uint32_t freq_hz, bool is_apll){
if (total_div < 256) { // For the case that smaller than the maximum ADC controller division
/* Fix the interval to 1, the division is fully realized by the ADC controller clock divider */
interval = 1;
} else if (total_div < 8192) { // for the case that smaller than the maximum inverval
} else if (total_div < 8192) { // for the case that smaller than the maximum interval
/* Set the interval to 'total_div / 2', fix the integer part of ADC controller clock division to 2 */
interval = total_div / 2;
} else {
@@ -119,16 +126,14 @@ static esp_err_t dac_dma_periph_set_clock(uint32_t freq_hz, bool is_apll){
finish:
/* Step 4: Set the clock coefficients */
portENTER_CRITICAL(&dac_spinlock);
dac_ll_digi_clk_inv(true);
dac_ll_digi_set_trigger_interval(interval); // secondary clock division
adc_ll_digi_controller_clk_div(clk_div - 1, b, a);
adc_ll_digi_clk_sel(is_apll);
portEXIT_CRITICAL(&dac_spinlock);
return ESP_OK;
}
esp_err_t dac_dma_periph_init(uint8_t chan_num, uint32_t freq_hz, bool is_alternate, bool is_apll)
esp_err_t dac_dma_periph_init(uint32_t freq_hz, bool is_alternate, bool is_apll)
{
#if CONFIG_DAC_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
@@ -138,7 +143,7 @@ esp_err_t dac_dma_periph_init(uint8_t chan_num, uint32_t freq_hz, bool is_altern
ESP_RETURN_ON_FALSE(spicommon_periph_claim(DAC_DMA_PERIPH_SPI_HOST, "dac_dma"), ESP_ERR_NOT_FOUND, TAG, "Failed to acquire DAC DMA peripheral");
periph_module_enable(PERIPH_SARADC_MODULE);
/* Allocate DAC DMA peripheral object */
s_ddp = (dac_dma_periph_spi_t *)calloc(1, sizeof(dac_dma_periph_spi_t));
s_ddp = (dac_dma_periph_spi_t *)heap_caps_calloc(1, sizeof(dac_dma_periph_spi_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
ESP_GOTO_ON_FALSE(s_ddp, ESP_ERR_NO_MEM, err, TAG, "No memory for DAC DMA object");
s_ddp->periph_dev = (void *)SPI_LL_GET_HW(DAC_DMA_PERIPH_SPI_HOST);
@@ -148,11 +153,12 @@ esp_err_t dac_dma_periph_init(uint8_t chan_num, uint32_t freq_hz, bool is_altern
}
/* When transmit alternately, twice frequency is needed to guarantee the convert frequency in one channel */
uint32_t trans_freq_hz = freq_hz * (is_alternate ? 2 : 1);
ESP_GOTO_ON_ERROR(dac_dma_periph_set_clock(trans_freq_hz, is_apll), err, TAG, "Failed to set clock of DMA peripheral");
ESP_GOTO_ON_ERROR(s_dac_dma_periph_set_clock(trans_freq_hz, is_apll), err, TAG, "Failed to set clock of DMA peripheral");
ESP_GOTO_ON_ERROR(spicommon_dma_chan_alloc(DAC_DMA_PERIPH_SPI_HOST, SPI_DMA_CH_AUTO, &s_ddp->dma_chan, &s_ddp->dma_chan),
err, TAG, "Failed to allocate dma peripheral channel");
portENTER_CRITICAL(&dac_spinlock);
spi_ll_enable_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_EOF | SPI_LL_INTR_OUT_TOTAL_EOF);
dac_ll_digi_set_convert_mode(is_alternate);
portEXIT_CRITICAL(&dac_spinlock);
return ret;
err:
dac_dma_periph_deinit();
@@ -161,12 +167,14 @@ err:
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");
if (s_ddp->dma_chan) {
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);
periph_module_disable(PERIPH_SARADC_MODULE);
if (s_ddp) {
if (s_ddp->intr_handle) {
dac_dma_periph_deregister_intr();
}
if (s_ddp->use_apll) {
periph_rtc_apll_release();
s_ddp->use_apll = false;
@@ -177,59 +185,28 @@ esp_err_t dac_dma_periph_deinit(void)
return ESP_OK;
}
esp_err_t dac_dma_periph_register_intr(intr_handler_t intr_handler_func, void *user_ctx)
int dac_dma_periph_get_intr_signal(void)
{
ESP_RETURN_ON_FALSE(s_ddp, ESP_ERR_INVALID_STATE, TAG, "DAC DMA peripheral has not initialized yet");
ESP_RETURN_ON_ERROR(spicommon_dma_chan_alloc(DAC_DMA_PERIPH_SPI_HOST, SPI_DMA_CH_AUTO, &s_ddp->dma_chan, &s_ddp->dma_chan),
TAG, "Failed to allocate dma peripheral channel");
esp_err_t ret = ESP_OK;
/* Regigster interrupt */
ESP_GOTO_ON_ERROR(esp_intr_alloc(spicommon_irqdma_source_for_host(DAC_DMA_PERIPH_SPI_HOST),
0, intr_handler_func, user_ctx, &(s_ddp->intr_handle)), err, TAG, "Failed to register DAC DMA interrupt");
portENTER_CRITICAL(&dac_spinlock);
spi_ll_enable_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_EOF | SPI_LL_INTR_OUT_TOTAL_EOF);
portEXIT_CRITICAL(&dac_spinlock);
return ret;
err:
spicommon_dma_chan_free(DAC_DMA_PERIPH_SPI_HOST);
return ret;
return spicommon_irqdma_source_for_host(DAC_DMA_PERIPH_SPI_HOST);
}
esp_err_t dac_dma_periph_deregister_intr(void)
static void s_dac_dma_periph_reset(void)
{
ESP_RETURN_ON_FALSE(s_ddp, ESP_ERR_INVALID_STATE, TAG, "DAC DMA peripheral has not initialized yet");
ESP_RETURN_ON_ERROR(spicommon_dma_chan_free(DAC_DMA_PERIPH_SPI_HOST), TAG, "Failed to free dma peripheral channel");
if (s_ddp->intr_handle) {
portENTER_CRITICAL(&dac_spinlock);
spi_ll_disable_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_EOF | SPI_LL_INTR_OUT_TOTAL_EOF);
portEXIT_CRITICAL(&dac_spinlock);
esp_intr_free(s_ddp->intr_handle);
s_ddp->intr_handle = NULL;
}
return ESP_OK;
spi_dma_ll_tx_reset(s_ddp->periph_dev, s_ddp->dma_chan);
spi_ll_dma_tx_fifo_reset(s_ddp->periph_dev);
}
void dac_dma_periph_enable(void)
{
portENTER_CRITICAL(&dac_spinlock);
spi_dma_ll_tx_reset(s_ddp->periph_dev, s_ddp->dma_chan);
spi_ll_dma_tx_fifo_reset(s_ddp->periph_dev);
s_dac_dma_periph_reset();
dac_ll_digi_trigger_output(true);
portEXIT_CRITICAL(&dac_spinlock);
/* Enable interrupt */
esp_intr_enable(s_ddp->intr_handle);
}
void dac_dma_periph_disable(void)
{
portENTER_CRITICAL(&dac_spinlock);
spi_dma_ll_tx_reset(s_ddp->periph_dev, s_ddp->dma_chan);
spi_ll_dma_tx_fifo_reset(s_ddp->periph_dev);
s_dac_dma_periph_reset();
spi_dma_ll_tx_stop(s_ddp->periph_dev, s_ddp->dma_chan);
dac_ll_digi_trigger_output(false);
portEXIT_CRITICAL(&dac_spinlock);
/* Disable interrupt */
esp_intr_disable(s_ddp->intr_handle);
}
uint32_t IRAM_ATTR dac_dma_periph_intr_is_triggered(void)
@@ -249,9 +226,7 @@ uint32_t IRAM_ATTR dac_dma_periph_intr_get_eof_desc(void)
void dac_dma_periph_dma_trans_start(uint32_t desc_addr)
{
portENTER_CRITICAL(&dac_spinlock);
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);
portEXIT_CRITICAL(&dac_spinlock);
}
@@ -24,7 +24,7 @@ static __attribute__((unused)) const char *TAG = "DAC";
---------------------------------------------------------------*/
esp_err_t dac_pad_get_io_num(dac_channel_t channel, gpio_num_t *gpio_num)
{
ESP_RETURN_ON_FALSE(channel < SOC_DAC_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
ESP_RETURN_ON_FALSE(channel < SOC_DAC_CHAN_NUM, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
*gpio_num = (gpio_num_t)dac_periph_signal.dac_channel_io_num[channel];
@@ -33,7 +33,7 @@ esp_err_t dac_pad_get_io_num(dac_channel_t channel, gpio_num_t *gpio_num)
static esp_err_t dac_rtc_pad_init(dac_channel_t channel)
{
ESP_RETURN_ON_FALSE(channel < SOC_DAC_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
ESP_RETURN_ON_FALSE(channel < SOC_DAC_CHAN_NUM, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
gpio_num_t gpio_num = 0;
dac_pad_get_io_num(channel, &gpio_num);
@@ -47,7 +47,7 @@ static esp_err_t dac_rtc_pad_init(dac_channel_t channel)
esp_err_t dac_output_enable(dac_channel_t channel)
{
ESP_RETURN_ON_FALSE(channel < SOC_DAC_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
ESP_RETURN_ON_FALSE(channel < SOC_DAC_CHAN_NUM, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
dac_rtc_pad_init(channel);
portENTER_CRITICAL(&rtc_spinlock);
@@ -60,7 +60,7 @@ esp_err_t dac_output_enable(dac_channel_t channel)
esp_err_t dac_output_disable(dac_channel_t channel)
{
ESP_RETURN_ON_FALSE(channel < SOC_DAC_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
ESP_RETURN_ON_FALSE(channel < SOC_DAC_CHAN_NUM, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
portENTER_CRITICAL(&rtc_spinlock);
dac_ll_power_down(channel);
@@ -71,7 +71,7 @@ esp_err_t dac_output_disable(dac_channel_t channel)
esp_err_t dac_output_voltage(dac_channel_t channel, uint8_t dac_value)
{
ESP_RETURN_ON_FALSE(channel < SOC_DAC_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
ESP_RETURN_ON_FALSE(channel < SOC_DAC_CHAN_NUM, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
portENTER_CRITICAL(&rtc_spinlock);
dac_ll_update_output_value(channel, dac_value);
@@ -82,7 +82,7 @@ esp_err_t dac_output_voltage(dac_channel_t channel, uint8_t dac_value)
esp_err_t dac_out_voltage(dac_channel_t channel, uint8_t dac_value)
{
ESP_RETURN_ON_FALSE(channel < SOC_DAC_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
ESP_RETURN_ON_FALSE(channel < SOC_DAC_CHAN_NUM, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
portENTER_CRITICAL(&rtc_spinlock);
dac_ll_update_output_value(channel, dac_value);
@@ -94,6 +94,7 @@ esp_err_t dac_out_voltage(dac_channel_t channel, uint8_t dac_value)
esp_err_t dac_cw_generator_enable(void)
{
portENTER_CRITICAL(&rtc_spinlock);
periph_rtc_dig_clk8m_enable();
dac_ll_cw_generator_enable();
portEXIT_CRITICAL(&rtc_spinlock);
@@ -104,6 +105,7 @@ esp_err_t dac_cw_generator_disable(void)
{
portENTER_CRITICAL(&rtc_spinlock);
dac_ll_cw_generator_disable();
periph_rtc_dig_clk8m_disable();
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
@@ -112,13 +114,16 @@ esp_err_t dac_cw_generator_disable(void)
esp_err_t dac_cw_generator_config(dac_cw_config_t *cw)
{
ESP_RETURN_ON_FALSE(cw, ESP_ERR_INVALID_ARG, TAG, "invalid clock configuration");
portENTER_CRITICAL(&rtc_spinlock);
dac_ll_cw_set_freq(cw->freq, periph_rtc_dig_clk8m_get_freq());
dac_ll_cw_set_scale(cw->en_ch, cw->scale);
dac_ll_cw_set_phase(cw->en_ch, cw->phase);
/* Enable the rtc8m clock temporary to get the correct frequecy */
periph_rtc_dig_clk8m_enable();
uint32_t rtc_freq = periph_rtc_dig_clk8m_get_freq();
periph_rtc_dig_clk8m_disable();
dac_ll_cw_set_freq(cw->freq, rtc_freq);
dac_ll_cw_set_atten(cw->en_ch, (dac_cosine_atten_t)cw->scale);
dac_ll_cw_set_phase(cw->en_ch, (dac_cosine_phase_t)cw->phase);
dac_ll_cw_set_dc_offset(cw->en_ch, cw->offset);
dac_ll_cw_set_channel(cw->en_ch, true);
dac_ll_cw_enable_channel(cw->en_ch, true);
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
@@ -131,11 +136,11 @@ __attribute__((constructor))
static void check_dac_legacy_driver_conflict(void)
{
// This function was declared as weak here. The new DAC driver has one implementation.
// So if the new DAC driver is not linked in, then `dac_new_channels()` should be NULL at runtime.
extern __attribute__((weak)) esp_err_t dac_new_channels(const void *dac_cfg, void **handle);
if ((void *)dac_new_channels != NULL) {
// So if the new DAC driver is not linked in, then `dac_priv_register_channel()` should be NULL at runtime.
extern __attribute__((weak)) esp_err_t dac_priv_register_channel(dac_channel_t chan_id, const char *mode_name);
if ((void *)dac_priv_register_channel != NULL) {
ESP_EARLY_LOGE(TAG, "CONFLICT! The new DAC driver is not allowed to be used together with the legacy driver");
abort();
}
ESP_EARLY_LOGW(TAG, "legacy driver is deprecated, please migrate to `driver/dac_driver.h` instead");
ESP_EARLY_LOGW(TAG, "legacy driver is deprecated, please migrate to `driver/dac_oneshot.h`, `driver/dac_cosine.h` or `driver/dac_conti.h` instead");
}
+3 -3
View File
@@ -13,7 +13,7 @@
#include "driver/dac_types_legacy.h"
#if !CONFIG_DAC_SUPPRESS_DEPRECATE_WARN
#warning "The legacy DAC driver is deprecated, please use driver/dac_driver.h instead"
#warning "The legacy DAC driver is deprecated, please use `driver/dac_oneshot.h`, `driver/dac_cosine.h` or `driver/dac_conti.h` instead"
#endif
#ifdef __cplusplus
@@ -109,7 +109,7 @@ esp_err_t dac_i2s_enable(void);
* - ESP_OK success
*/
esp_err_t dac_i2s_disable(void);
#endif
#endif // CONFIG_IDF_TARGET_ESP32
#if CONFIG_IDF_TARGET_ESP32S2
/**
@@ -164,7 +164,7 @@ esp_err_t dac_digi_fifo_reset(void);
* - ESP_OK success
*/
esp_err_t dac_digi_reset(void);
#endif
#endif // CONFIG_IDF_TARGET_ESP32S2
#ifdef __cplusplus
}
@@ -0,0 +1,263 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "driver/dac_types.h"
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
#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_conti_s *dac_conti_handle_t; /*!< DAC continuous channel handle */
/**
* @brief DAC continuous channels' configurations
*
*/
typedef struct {
dac_channel_mask_t chan_mask; /*!< DAC channels' mask for selecting which channels are used */
uint32_t desc_num; /*!< The number of DMA descriptor, at least 2 descriptors are required
* The number of descriptors is directly proportional to the max data buffer size while converting in cyclic output
* but only need to ensure it is greater than '1' in acyclic output
* Typically, suggest to set the number bigger than 5, in case the DMA stopped while sending a short buffer
*/
size_t buf_size; /*!< The DMA buffer size, should be within 32~4092 bytes. Each DMA buffer will be attached to a DMA descriptor,
* i.e. the number of DMA buffer will be equal to the DMA descriptor number
* The DMA buffer size is not allowed to be greater than 4092 bytes
* The total DMA buffer size equal to `desc_num * buf_size`
* Typically, suggest to set the size to the multiple of 4
*/
uint32_t freq_hz; /*!< The frequency of DAC conversion in continuous mode, unit: Hz
* The supported range is related to the target and the clock source.
* For the clock `DAC_DIGI_CLK_SRC_DEFAULT`: the range is 19.6 KHz to several MHz on ESP32
* and 77 Hz to several MHz on ESP32-S2.
* For the clock `DAC_DIGI_CLK_SRC_APLL`: the range is 648 Hz to several MHz on ESP32
* and 6 Hz to several MHz on ESP32-S2.
* Typically not suggest to set the frequency higher than 2 MHz, otherwise the severe distortion will appear
*/
int8_t offset; /*!< The offset of the DAC digital data. Range -128~127 */
dac_conti_digi_clk_src_t clk_src; /*!< The clock source of digital controller, which can affect the range of supported frequency
* Currently `DAC_DIGI_CLK_SRC_DEFAULT` and `DAC_DIGI_CLK_SRC_APLL` are available
*/
dac_conti_channel_mode_t chan_mode; /*!< The channel mode of continuous mode, only take effect when multiple channels enabled, depends converting the buffer alternately or simultaneously */
} dac_conti_config_t;
/**
* @brief Event structure used in DAC event queue
*/
typedef struct {
void *buf; /*!< The pointer of DMA buffer that just finished sending */
size_t buf_size; /*!< The writable buffer size of the DMA buffer, equal to 'dac_conti_config_t::buf_size' */
size_t write_bytes; /*!< The number of bytes that be written successfully */
} dac_event_data_t;
/**
* @brief DAC event callback
* @param[in] handle DAC channel handle, created from `dac_new_conti_channels()`
* @param[in] event DAC event data
* @param[in] user_data User registered context, passed from `dac_conti_register_event_callback()`
*
* @return Whether a high priority task has been waken up by this callback function
*/
typedef bool (*dac_isr_callback_t)(dac_conti_handle_t handle, const dac_event_data_t *event, void *user_data);
/**
* @brief Group of DAC callbacks
* @note The callbacks are all running under ISR environment
* @note When CONFIG_DAC_ISR_IRAM_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM.
* The variables used in the function should be in the SRAM as well.
*/
typedef struct {
dac_isr_callback_t on_convert_done; /**< Callback of data conversion done event
* An event data buffer previously loaded to the driver has been output and converted.
* The event data includes DMA buffer address and size that just finished converting.
*/
dac_isr_callback_t on_stop; /**< Callback of finished sending all the data.
* All loaded event data buffers are converted. Driver is pending for new data buffers to be loaded.
* The event data will be NULL in this callback.
*/
} dac_event_callbacks_t;
/**
* @brief Allocate new DAC channels in continuous mode
* @note The DAC channels can't be registered to continuous mode separately
*
* @param[in] conti_cfg Continuous mode configuration
* @param[out] ret_handle The returned continuous mode handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC channel has been registered already
* - ESP_ERR_NOT_FOUND Not found the available dma peripheral, might be occupied
* - ESP_ERR_NO_MEM No memory for the DAC continuous mode resources
* - ESP_OK Allocate the new DAC continuous mode success
*/
esp_err_t dac_new_conti_channels(const dac_conti_config_t *conti_cfg, dac_conti_handle_t *ret_handle);
/**
* @brief Delete the DAC continuous handle
*
* @param[in] handle The DAC continuous channel handle that obtained from 'dac_new_conti_channels'
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The channels have already been deregistered or not disabled
* - ESP_OK Delete the continuous channels success
*/
esp_err_t dac_del_conti_channels(dac_conti_handle_t handle);
/**
* @brief Enabled the DAC continuous mode
* @note Must enable the channels before
*
* @param[in] handle The DAC continuous channel handle that obtained from 'dac_new_conti_channels'
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The channels have been enabled already
* - ESP_OK Enable the continuous output success
*/
esp_err_t dac_conti_enable(dac_conti_handle_t handle);
/**
* @brief Disable the DAC continuous mode
*
* @param[in] handle The DAC continuous channel handle that obtained from 'dac_new_conti_channels'
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The channels have been enabled already
* - ESP_OK Disable the continuous output success
*/
esp_err_t dac_conti_disable(dac_conti_handle_t handle);
/**
* @brief Write DAC continuous data cyclically
* @note The data in buffer will be converted cyclically using DMA once this function is called,
* This function will return once the data loaded into DMA buffers.
* @note The buffer size of cyclically output is limited by the descriptor number and
* dma buffer size while initializing the continuous mode.
* Concretely, in order to load all the data into descriptors,
* the cyclic buffer size is not supposed to be greater than `desc_num * buf_size`
* @note Specially, on ESP32, the data bit width of DAC continuous data is fixed to 16 bits while only the high 8 bits are available,
* The driver will help to expand the inputted buffer automatically by default,
* you can also align the data to 16 bits manually by clearing `CONFIG_DAC_DMA_AUTO_16BIT_ALIGN` in menuconfig.
*
* @param[in] handle The DAC continuous channel handle that obtained from 'dac_new_conti_channels'
* @param[in] buf The digital data buffer to convert
* @param[in] buf_size The buffer size of digital data buffer
* @param[out] bytes_loaded The bytes that has been loaded into DMA buffer, can be NULL if don't need it
* @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_err_t dac_conti_write_cyclically(dac_conti_handle_t handle, uint8_t *buf, size_t buf_size, size_t *bytes_loaded);
/**
* @brief Write DAC data continuously
* @note The data in buffer will only be converted one time,
* This function will be blocked until all data loaded or timeout
* then the DAC output will keep outputting the voltage of the last data in the buffer
* @note Specially, on ESP32, the data bit width of DAC continuous data is fixed to 16 bits while only the high 8 bits are available,
* The driver will help to expand the inputted buffer automatically by default,
* you can also align the data to 16 bits manually by clearing `CONFIG_DAC_DMA_AUTO_16BIT_ALIGN` in menuconfig.
*
* @param[in] handle The DAC continuous channel handle that obtained from 'dac_new_conti_channels'
* @param[in] buf The digital data buffer to convert
* @param[in] buf_size The buffer size of digital data buffer
* @param[out] bytes_loaded The bytes that has been loaded into DMA buffer, can be NULL if don't need it
* @param[in] timeout_ms The timeout time in millisecond, set a minus value means will block forever
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC continuous mode has not been enabled yet
* - ESP_ERR_TIMEOUT Waiting for semaphore or message queue timeout
* - ESP_OK Success to output the acyclic DAC data
*/
esp_err_t dac_conti_write(dac_conti_handle_t handle, uint8_t *buf, size_t buf_size, size_t *bytes_loaded, int timeout_ms);
/**
* @brief Set event callbacks for DAC continuous mode
*
* @note User can deregister a previously registered callback by calling this function and setting the callback member in the `callbacks` structure to NULL.
* @note When CONFIG_DAC_ISR_IRAM_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM.
* The variables used in this function, including the `user_data`, should be in the internal RAM as well.
*
* @param[in] handle The DAC continuous channel handle that obtained from 'dac_new_conti_channels'
* @param[in] callbacks Group of callback functions, input NULL to clear the former callbacks
* @param[in] user_data User data, which will be passed to callback functions directly
* @return
* - ESP_OK Set event callbacks successfully
* - ESP_ERR_INVALID_ARG Set event callbacks failed because of invalid argument
*/
esp_err_t dac_conti_register_event_callback(dac_conti_handle_t handle, const dac_event_callbacks_t *callbacks, void *user_data);
/**
* @brief Start the async writing
* @note When the asynchronous writing start, the DAC will keep outputting '0' until the data are loaded into the DMA buffer.
* To loaded the data into DMA buffer, 'on_convert_done' callback is required,
* which can be registered by 'dac_conti_register_event_callback' before enabling
*
* @param[in] handle The DAC continuous channel handle that obtained from 'dac_new_conti_channels'
* @return
* - ESP_OK Start asynchronous writing successfully
* - ESP_ERR_INVALID_ARG The handle is NULL
* - ESP_ERR_INVALID_STATE The channel is not enabled or the 'on_convert_done' callback is not registered
*/
esp_err_t dac_conti_start_async_writing(dac_conti_handle_t handle);
/**
* @brief Stop the sync writing
*
* @param[in] handle The DAC continuous channel handle that obtained from 'dac_new_conti_channels'
* @return
* - ESP_OK Stop asynchronous writing successfully
* - ESP_ERR_INVALID_ARG The handle is NULL
* - ESP_ERR_INVALID_STATE Asynchronous writing has not started
*/
esp_err_t dac_conti_stop_async_writing(dac_conti_handle_t handle);
/**
* @brief Write DAC data asynchronously
* @note This function can be called when the asynchronous writing started, and it can be called in the callback directly
* but recommend to writing data in a task, referring to :example:`peripherals/dac/dac_continuous/dac_audio`
*
* @param[in] handle The DAC continuous channel handle that obtained from 'dac_new_conti_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] 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
* @return
* - ESP_OK Write the data into DMA buffer successfully
* - ESP_ERR_INVALID_ARG NULL pointer
* - ESP_ERR_INVALID_STATE The channels haven't start the asynchronous writing
* - ESP_ERR_NOT_FOUND The param 'dam_buf' not match any existed DMA buffer
*/
esp_err_t dac_conti_write_asynchronously(dac_conti_handle_t handle,
uint8_t *dma_buf,
size_t dma_buf_len,
const uint8_t *data,
size_t data_len,
size_t *bytes_loaded);
#endif // SOC_DAC_SUPPORTED
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,97 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "driver/dac_types.h"
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
#if SOC_DAC_SUPPORTED
typedef struct dac_cosine_s *dac_cosine_handle_t; /*!< DAC cosine wave channel handle */
/**
* @brief DAC cosine channel configurations
*
*/
typedef struct {
dac_channel_t chan_id; /*!< The cosine wave channel id */
uint32_t freq_hz; /*!< The frequency of cosine wave, unit: Hz.
* The cosine wave generator is driven by RTC_FAST clock which is divide from RC_FAST,
* With the default RTC clock, the minimum frequency of cosine wave is about 130 Hz,
* Although it can support up to several MHz frequency theoretically,
* the waveform will distort at high frequency due to the hardware limitation.
* Typically not suggest to set the frequency higher than 200 KHz
*/
dac_cosine_clk_src_t clk_src; /*!< The clock source of the cosine wave generator, currently only support `DAC_COSINE_CLK_SRC_DEFAULT` */
dac_cosine_atten_t atten; /*!< The attenuation of cosine wave amplitude */
dac_cosine_phase_t phase; /*!< The phase of cosine wave, can only support DAC_COSINE_PHASE_0 or DAC_COSINE_PHASE_180, default as 0 while setting an unsupported phase */
int8_t offset; /*!< The DC offset of cosine wave */
struct {
bool force_set_freq: 1; /*!< Force to set the cosine wave frequency */
} flags; /*!< Flags of cosine mode */
} dac_cosine_config_t;
/**
* @brief Allocate a new DAC cosine wave channel
* @note Since there is only one cosine wave generator,
* only the first channel can set the frequency of the cosine wave.
* Normally, the latter one is not allowed to set a different frequency,
* but the it can be forced to set by setting the bit `force_set_freq` in the configuration,
* notice that another channel will be affected as well when the frequency is updated.
*
* @param[in] cos_cfg The configuration of cosine wave channel
* @param[out] ret_handle The returned cosine wave channel handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC channel has been registered already
* - ESP_ERR_NO_MEM No memory for the DAC cosine wave channel resources
* - ESP_OK Allocate the new DAC cosine wave channel success
*/
esp_err_t dac_new_cosine_channel(const dac_cosine_config_t *cos_cfg, dac_cosine_handle_t *ret_handle);
/**
* @brief Delete the DAC cosine wave channel
*
* @param[in] handle The DAC cosine wave channel handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The channel has already been deregistered
* - ESP_OK Delete the cosine wave channel success
*/
esp_err_t dac_del_cosine_channel(dac_cosine_handle_t handle);
/**
* @brief Start outputting the cosine wave on the channel
*
* @param[in] handle The DAC cosine wave channel handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The channel has been started already
* - ESP_OK Start the cosine wave success
*/
esp_err_t dac_cosine_start(dac_cosine_handle_t handle);
/**
* @brief Stop outputting the cosine wave on the channel
*
* @param[in] handle The DAC cosine wave channel handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The channel has been stopped already
* - ESP_OK Stop the cosine wave success
*/
esp_err_t dac_cosine_stop(dac_cosine_handle_t handle);
#endif // SOC_DAC_SUPPORTED
#ifdef __cplusplus
}
#endif
@@ -1,298 +0,0 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "driver/dac_types.h"
#include "driver/gpio.h"
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
#if SOC_DAC_SUPPORTED
/**
* @brief DAC channel configuration
*
*/
typedef struct {
dac_channel_mask_t chan_sel; /*!< Using DAC channel mask to select the channels */
} dac_channels_config_t;
/**
* @brief DAC continuous mode configration
*
*/
typedef struct {
uint32_t freq_hz; /*!< The frequency of DAC conversion in continuous mode, unit: Hz
* The supported range is related to the target and the clock source.
* For the clock `DAC_DIGI_CLK_SRC_DEFAULT`: the range is 19.6 KHz to several MHz on ESP32
* and 77 Hz to several MHz on ESP32-S2.
* For the clock `DAC_DIGI_CLK_SRC_APLL`: the range is 648 Hz to several MHz on ESP32
* and 6 Hz to several MHz on ESP32-S2.
* Typically not suggest to set the frequency higher than 2 MHz, otherwise the severe distortion will appear
*/
dac_conti_clk_src_t clk_src; /*!< The clock source of digital controller, which can affect the range of supported frequency
* Currently `DAC_DIGI_CLK_SRC_DEFAULT` and `DAC_DIGI_CLK_SRC_APLL` are available
*/
uint32_t desc_num; /*!< The number of DMA descriptor, at least 2 descriptors are required
* The number of descriptors is directly proportional to the max data buffer size while converting in cyclic output
* but only need to ensure it is greater than '1' in acyclic output
* Typically, suggest to set the number bigger than 5, in case the DMA stopped while sending a short buffer
*/
uint32_t buf_size; /*!< The DMA buffer size, should be within 4092 bytes. Each DMA buffer will be attached to a DMA descriptor,
* i.e. the number of DMA buffer will be equal to the DMA descriptor number
* The DMA buffer size is not allowed to be greater than 4092 bytes
* The total DMA buffer size equal to `desc_num * buf_size`
* Typically, suggest to set the size to the multiple of 4
*/
dac_conti_channel_mode_t chan_mode; /*!< The channel mode of continuous mode, only take effect when multiple channels enabled, depends converting the buffer alternately or simultaneously */
} dac_conti_config_t;
/**
* @brief DAC cosine wave gnerator configuration
*
*/
typedef struct {
uint32_t freq_hz; /*!< The frequency of cosine wave, unit: Hz.
* The cosine wave generator is driven by RTC clock which is about SOC_CLK_RC_FAST_FREQ_APPROX Hz by default,
* With the default RTC clock, the minimun frequency of cosine wave is about 130 Hz,
* Although it can support up to serveral MHz frequency theoretically,
* the waveform will distort at high frequency due to the hardware limitation.
* Typically not suggest to set the frequency higher than 200 KHz
*/
dac_cosine_clk_src_t clk_src; /*!< The clock source of the cosine wave generator, currently only support `DAC_COSINE_CLK_SRC_DEFAULT` which comes from RTC FAST clock */
dac_cosine_scale_t scale; /*!< The scale of cosine wave amplitude */
dac_cosine_phase_t phase; /*!< The phase of cosine wave */
int8_t offset; /*!< The DC offset of cosine wave */
} dac_cosine_config_t;
typedef struct dac_channels_s *dac_channels_handle_t; /*!< DAC channels' handle of DAC peripheral, one or multiple DAC channels can be controlled by this handle */
/*--------------------------------------------------------------------------
DAC common APIs
---------------------------------------------------------------------------*/
/**
* @brief Allocate a new DAC channels' handle
* @note The driver supports to manage one single channel by enabling only one channel in the channel mask,
* or multiple channels together as a whole by enabling multiple channels in the channel mask.
* Moreover, the channels can also be managed separately if they are allocated separately.
*
* @param[in] dac_cfg DAC basic configuration
* @param[out] handle DAC channels handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_NO_MEM No memory for a new DAC handle
* - ESP_ERR_INVALID_STATE The specified DAC channel is occupied already
* - ESP_OK Success to allocate DAC channels
*/
esp_err_t dac_new_channels(const dac_channels_config_t *dac_cfg, dac_channels_handle_t *handle);
/**
* @brief Delete and free the DAC channels
*
* @param[in] handle DAC channels handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The channels are not disabled
* - ESP_OK Success to delete the channels
*/
esp_err_t dac_del_channels(dac_channels_handle_t handle);
/**
* @brief Enabled the DAC channels in the channels
* @note GPIOs of DAC channles will be enabled in this step
*
* @param[in] handle DAC channels handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The channels has enabled already or the channels are running
* - ESP_OK Success to enable the channels
*/
esp_err_t dac_channels_enable(dac_channels_handle_t handle);
/**
* @brief Disable the DAC channels in the channels
*
* @param[in] handle DAC channels handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The channels has disabled already or the channels are running
* - ESP_OK Success to enable the channels
*/
esp_err_t dac_channels_disable(dac_channels_handle_t handle);
/*--------------------------------------------------------------------------
DAC direct voltage outputting APIs
---------------------------------------------------------------------------*/
/**
* @brief DAC channels output a voltage directly
* @note This function is available when DAC chennels is enbled
* @note Please enable 'DAC ISR IRAM-Safe' in memuconfig when it is called in an IRAM safe ISR
*
* @param[in] handle DAC channels handle
* @param[in] value The digital value of the voltage
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The channels are not enabled
* - ESP_OK Success to enable the channels
*/
esp_err_t dac_channels_set_voltage(dac_channels_handle_t handle, uint8_t value);
/*--------------------------------------------------------------------------
DAC continuous outputting APIs
---------------------------------------------------------------------------*/
/**
* @brief Initialize the DAC channels to continuous mode
* @note DAC can convert digital data continuously in continuous mode
*
* @param[in] handle DAC channels handle
* @param[in] conti_cfg DAC continuous mode configuration
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC channels has been initialized already
* - ESP_ERR_NO_MEM No memory for DAC continuous mode
* - ESP_OK Success to initializing the DAC channels to continuous mode
*/
esp_err_t dac_channels_init_continuous_mode(dac_channels_handle_t handle, const dac_conti_config_t *conti_cfg);
/**
* @brief Deinitialize the continuous mode of the DAC channels
* @note It can only be deinitialized when the continuous output is disabled
*
* @param[in] handle DAC channels handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC continuous mode is not disabled yet
* - ESP_OK Success to deinitialize the DAC continuous mode
*/
esp_err_t dac_channels_deinit_continuous_mode(dac_channels_handle_t handle);
/**
* @brief Enable the DAC continuous output
*
* @param[in] handle DAC channels handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC continuous mode has been enabled already
* - ESP_OK Success to start the continuous output
*/
esp_err_t dac_channels_enable_continuous_mode(dac_channels_handle_t handle);
/**
* @brief Disable the DAC continuous output
*
* @param[in] handle DAC channels handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC continuous mode is disabled already
* - ESP_OK Success to stop the continuous output
*/
esp_err_t dac_channels_disable_continuous_mode(dac_channels_handle_t handle);
/**
* @brief Write DAC continuous data continuously
* @note The data in buffer will only be converted one time,
* This function will be blocked until all data loaded or timeout
* then the DAC output will keep outputting the voltage of the last data in the buffer
* @note On ESP32, the data bit width of DAC continuous data is fixed to 16 bits while only the high 8 bits are available,
* you can align the DAC data to 16 bits manually or set `CONFIG_DAC_DMA_AUTO_16BIT_ALIGN` to get the correct wave.
* But the data bit width is already 8 bits on ESP32-S2, each byte stands for an vailable voltage,
* no need to do any alignment.
*
* @param[in] handle DAC channels handle
* @param[in] buf The digital data buffer to convert
* @param[in] buf_size The buffer size of digital data buffer
* @param[out] bytes_loaded The bytes that has been loaded into DMA buffer, can be NULL if don't need it
* @param[in] timeout_ms The timeout time in mili-second
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC continuous mode has not been enabled yet
* - ESP_ERR_TIMEOUT Waiting for semaphore or message queue timeout
* - ESP_OK Success to output the acyclic DAC data
*/
esp_err_t dac_channels_write_continuously(dac_channels_handle_t handle, uint8_t *buf, size_t buf_size, size_t *bytes_loaded, uint32_t timeout_ms);
/**
* @brief Write DAC continuous data cyclically
* @note The data in buffer will be converted cyclically once this function is called,
* This function won't be blocked, it will return once the data loaded into DMA buffers
* @note The buffer size of cyclically output is limited by the descriptor number while initializing the continuous mode,
* Concretely, in order to load all the data into descriptors,
* the cyclic buffer size is not supposed to be greater than `desc_num * 4092`
* @note On ESP32, the data bit width of DAC continuous data is fixed to 16 bits while only the high 8 bits are available,
* you can align the DAC data to 16 bits manually or set `CONFIG_DAC_DMA_AUTO_16BIT_ALIGN` to get the correct wave.
* But the data bit width is already 8 bits on ESP32-S2, each byte stands for an vailable voltage,
* no need to do any alignment.
*
* @param[in] handle DAC channels handle
* @param[in] buf The digital data buffer to convert
* @param[in] buf_size The buffer size of digital data buffer
* @param[out] bytes_loaded The bytes that has been loaded into DMA buffer, can be NULL if don't need it
* @param[in] timeout_ms The timeout time in mili-second
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC continuous mode has not been enabled yet
* - ESP_ERR_TIMEOUT Waiting for semaphore or message queue timeout
* - ESP_OK Success to output the acyclic DAC data
*/
esp_err_t dac_channels_write_cyclically(dac_channels_handle_t handle, uint8_t *buf, size_t buf_size, size_t *bytes_loaded, uint32_t timeout_ms);
/*--------------------------------------------------------------------------
DAC cosine wave outputting APIs
---------------------------------------------------------------------------*/
/**
* @brief Initialize the DAC channels to cosine wave mode
*
* @param[in] handle DAC channels handle
* @param[in] cw_cfg DAC cosine wave generater configuration
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC channels has been initialized already
* - ESP_OK Success to initialize the DAC channels into cosine wave mode
*/
esp_err_t dac_channels_init_cosine_mode(dac_channels_handle_t handle, const dac_cosine_config_t *cw_cfg);
/**
* @brief Deinitialize the DAC channels to cosine wave mode
*
* @param[in] handle DAC channels handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC cosine wave generator is not stopped yet
* - ESP_OK Success to deinitialize the DAC cosine mode
*/
esp_err_t dac_channels_deinit_cosine_mode(dac_channels_handle_t handle);
/**
* @brief Start the DAC cosine wave generator output
*
* @param[in] handle DAC channels handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC channels has not been enabled yet or started already
* - ESP_OK Success to start cosine wave generator
*/
esp_err_t dac_channels_start_cosine_output(dac_channels_handle_t handle);
/**
* @brief Stop the DAC cosine wave generator output
*
* @param[in] handle DAC channels handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC channels has not been enabled yet or stoppped already
* - ESP_OK Success to stop cosine wave generator
*/
esp_err_t dac_channels_stop_cosine_output(dac_channels_handle_t handle);
#endif // SOC_DAC_SUPPORTED
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,70 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "driver/dac_types.h"
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
#if SOC_DAC_SUPPORTED
typedef struct dac_oneshot_s *dac_oneshot_handle_t; /*!< DAC oneshot channel handle */
/**
* @brief DAC oneshot channel configuration
*
*/
typedef struct {
dac_channel_t chan_id; /*!< DAC channel id */
} dac_oneshot_config_t;
/**
* @brief Allocate a new DAC oneshot channel
* @note The channel will be enabled as well when the channel allocated
*
* @param[in] oneshot_cfg The configuration for the oneshot channel
* @param[out] ret_handle The returned oneshot channel handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC channel has been registered already
* - ESP_ERR_NO_MEM No memory for the DAC oneshot channel resources
* - ESP_OK Allocate the new DAC oneshot channel success
*/
esp_err_t dac_new_oneshot_channel(const dac_oneshot_config_t *oneshot_cfg, dac_oneshot_handle_t *ret_handle);
/**
* @brief Delete the DAC oneshot channel
* @note The channel will be disabled as well when the channel deleted
*
* @param[in] handle The DAC oneshot channel handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The channel has already been de-registered
* - ESP_OK Delete the oneshot channel success
*/
esp_err_t dac_del_oneshot_channel(dac_oneshot_handle_t handle);
/**
* @brief Output the voltage
* @note Generally it'll take 7~11 us on ESP32 and 10~21 us on ESP32-S2
*
* @param[in] handle The DAC oneshot channel handle
* @param[in] digi_value The digital value that need to be converted
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_OK Convert the digital value success
*/
esp_err_t dac_oneshot_output_voltage(dac_oneshot_handle_t handle, uint8_t digi_value);
#endif // SOC_DAC_SUPPORTED
#ifdef __cplusplus
}
#endif
+2 -32
View File
@@ -12,6 +12,7 @@
#include "soc/soc_caps.h"
#include "soc/clk_tree_defs.h"
#include "hal/adc_types.h"
#include "hal/dac_types.h"
#include "esp_bit_defs.h"
#include "sdkconfig.h"
@@ -20,17 +21,6 @@ extern "C" {
#endif
#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_BOTH = BIT(0) | BIT(1), /*!< Both DAC channel 0 and channel 1 */
} dac_channel_mask_t;
/**
* @brief DAC channel work mode in dma mode
* @note Only take effect when multiple channels enabled.
@@ -51,7 +41,7 @@ typedef enum {
* @brief DAC DMA (digitial controller) clock source
*
*/
typedef soc_periph_dac_digi_clk_src_t dac_conti_clk_src_t;
typedef soc_periph_dac_digi_clk_src_t dac_conti_digi_clk_src_t;
/**
* @brief DAC cosine wave generator clock source
@@ -59,26 +49,6 @@ typedef soc_periph_dac_digi_clk_src_t dac_conti_clk_src_t;
*/
typedef soc_periph_dac_cosine_clk_src_t dac_cosine_clk_src_t;
/**
* @brief The attenuation of the amplitude of the cosine wave generator. The max amplitude is VDD3P3_RTC.
*/
typedef enum {
DAC_COSINE_NO_ATTEN = 0x0, /*!< No attenuation to the DAC cosine wave amplitude. Default. */
DAC_COSINE_ATTEN_2 = 0x1, /*!< 1/2 amplitude of the DAC cosine wave */
DAC_COSINE_ATTEN_4 = 0x2, /*!< 1/4 amplitude of the DAC cosine wave */
DAC_COSINE_ATTEN_8 = 0x3, /*!< 1/8 amplitude of the DAC cosine wave */
} dac_cosine_scale_t;
/**
* @brief Set the phase of the cosine wave generator output.
* @note Only 0 or 180 are supported,
* it will be set to 0 as default if configured to an unsupported phase.
*/
typedef enum {
DAC_COSINE_PHASE_0 = 0, /*!< Phase shift +0° */
DAC_COSINE_PHASE_180 = 180, /*!< Phase shift +180° */
} dac_cosine_phase_t;
#endif // SOC_DAC_SUPPORTED
#ifdef __cplusplus
+2 -1
View File
@@ -18,6 +18,7 @@ entries:
if SDM_CTRL_FUNC_IN_IRAM = y:
sdm: sdm_channel_set_duty (noflash)
if DAC_CTRL_FUNC_IN_IRAM = y:
dac_driver: dac_channels_set_voltage (noflash)
dac_oneshot: dac_oneshot_output_voltage (noflash)
dac_conti: dac_conti_write_asynchronously (noflash)
if MCPWM_CTRL_FUNC_IN_IRAM = y:
mcpwm_cmpr: mcpwm_comparator_set_compare_value (noflash)
@@ -4,11 +4,15 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <inttypes.h>
#include <stdio.h>
#include "unity.h"
#include "unity_test_utils.h"
#include "driver/dac_driver.h"
#include "driver/adc.h"
#include "driver/dac_oneshot.h"
#include "driver/dac_cosine.h"
#include "driver/dac_conti.h"
#include "driver/gpio.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_err.h"
#if CONFIG_IDF_TARGET_ESP32
#include "esp_private/i2s_platform.h"
@@ -22,109 +26,129 @@
#endif
#if CONFIG_IDF_TARGET_ESP32
#define ADC_TEST_CHANNEL_NUM ADC2_CHANNEL_8 // GPIO25, same as DAC channel 0
#define ADC_TEST_WIDTH ADC_WIDTH_BIT_12
#define ADC_TEST_CHANNEL_NUM ADC_CHANNEL_8 // GPIO25, same as DAC channel 0
#define ADC_TEST_WIDTH ADC_BITWIDTH_12
#elif CONFIG_IDF_TARGET_ESP32S2
#define ADC_TEST_CHANNEL_NUM ADC2_CHANNEL_6 // GPIO17, same as DAC channel 0
#define ADC_TEST_WIDTH ADC_WIDTH_BIT_13
#define ADC_TEST_CHANNEL_NUM ADC_CHANNEL_6 // GPIO17, same as DAC channel 0
#define ADC_TEST_WIDTH ADC_BITWIDTH_13
#endif
#define ADC_TEST_ATTEN ADC_ATTEN_DB_11
TEST_CASE("DAC_API_basic_logic_test", "[dac]")
{
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
dac_conti_config_t dma_cfg = {
.freq_hz = 20000,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
.desc_num = 10,
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
};
dac_cosine_config_t cos_cfg = {
.freq_hz = 1000,
/* Constant API test */
dac_oneshot_handle_t oneshot_chan0_handle;
TEST_ESP_OK(dac_new_oneshot_channel(&(dac_oneshot_config_t){.chan_id = DAC_CHAN_0}, &oneshot_chan0_handle));
TEST_ESP_OK(dac_oneshot_output_voltage(oneshot_chan0_handle, 128));
TEST_ESP_OK(dac_del_oneshot_channel(oneshot_chan0_handle));
dac_oneshot_handle_t oneshot_chan1_handle;
TEST_ESP_OK(dac_new_oneshot_channel(&(dac_oneshot_config_t){.chan_id = DAC_CHAN_1}, &oneshot_chan1_handle));
TEST_ESP_OK(dac_oneshot_output_voltage(oneshot_chan1_handle, 100));
TEST_ESP_OK(dac_del_oneshot_channel(oneshot_chan1_handle));
/* Cosine wave API test */
dac_cosine_handle_t cos_chan0_handle;
dac_cosine_handle_t cos_chan1_handle;
dac_cosine_config_t cos0_cfg = {
.chan_id = DAC_CHAN_0,
.freq_hz = 1000, // It will be covered by 8000 in the latter configuration
.clk_src = DAC_COSINE_CLK_SRC_DEFAULT,
.offset = 0,
.phase = DAC_COSINE_PHASE_0,
.scale = DAC_COSINE_NO_ATTEN,
.atten = DAC_COSINE_ATTEN_DEFAULT,
.flags.force_set_freq = false,
};
/* Constant API test */
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_set_voltage(handle, 100));
TEST_ESP_OK(dac_channels_disable(handle));
dac_cosine_config_t cos1_cfg = {
.chan_id = DAC_CHAN_1,
.freq_hz = 8000,
.clk_src = DAC_COSINE_CLK_SRC_DEFAULT,
.offset = 0,
.phase = DAC_COSINE_PHASE_180,
.atten = DAC_COSINE_ATTEN_DB_6,
.flags.force_set_freq = false,
};
TEST_ESP_OK(dac_new_cosine_channel(&cos0_cfg, &cos_chan0_handle));
/* Try to update the frequency without force set */
TEST_ASSERT(dac_new_cosine_channel(&cos1_cfg, &cos_chan1_handle) == ESP_ERR_INVALID_STATE);
/* Force update the frequnecy */
cos1_cfg.flags.force_set_freq = true;
TEST_ESP_OK(dac_new_cosine_channel(&cos1_cfg, &cos_chan1_handle));
TEST_ASSERT(dac_cosine_stop(cos_chan0_handle) == ESP_ERR_INVALID_STATE);
TEST_ESP_OK(dac_cosine_start(cos_chan0_handle));
TEST_ESP_OK(dac_cosine_start(cos_chan1_handle));
TEST_ASSERT(dac_del_cosine_channel(cos_chan0_handle) == ESP_ERR_INVALID_STATE);
TEST_ESP_OK(dac_cosine_stop(cos_chan0_handle));
TEST_ESP_OK(dac_cosine_stop(cos_chan1_handle));
TEST_ESP_OK(dac_del_cosine_channel(cos_chan0_handle));
TEST_ESP_OK(dac_del_cosine_channel(cos_chan1_handle));
/* DMA API test */
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ASSERT(dac_channels_enable_continuous_mode(handle) == ESP_ERR_INVALID_STATE);
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
TEST_ASSERT(dac_channels_disable(handle) == ESP_ERR_INVALID_STATE);
TEST_ASSERT(dac_channels_deinit_continuous_mode(handle) == ESP_ERR_INVALID_STATE);
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
/* Cosine wave API test */
TEST_ESP_OK(dac_channels_init_cosine_mode(handle, &cos_cfg));
TEST_ASSERT(dac_del_channels(handle) == ESP_ERR_INVALID_STATE);
TEST_ESP_OK(dac_channels_start_cosine_output(handle));
TEST_ASSERT(dac_channels_disable(handle) == ESP_ERR_INVALID_STATE);
TEST_ESP_OK(dac_channels_stop_cosine_output(handle));
TEST_ESP_OK(dac_channels_deinit_cosine_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
dac_conti_handle_t conti_handle;
dac_conti_config_t conti_cfg = {
.chan_mask = DAC_CHANNEL_MASK_ALL,
.desc_num = 8,
.buf_size = 2048,
.freq_hz = 48000,
.offset = 0,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
};
/* DMA peripheral availability test */
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
#if CONFIG_IDF_TARGET_ESP32
TEST_ESP_OK(i2s_platform_acquire_occupation(0, "dac_test"));
#elif CONFIG_IDF_TARGET_ESP32S2
TEST_ASSERT(spicommon_periph_claim(SPI3_HOST, "dac_test"));
#endif
TEST_ASSERT(dac_channels_init_continuous_mode(handle, &dma_cfg) == ESP_ERR_NOT_FOUND);
TEST_ASSERT(dac_new_conti_channels(&conti_cfg, &conti_handle) == ESP_ERR_NOT_FOUND);
#if CONFIG_IDF_TARGET_ESP32
TEST_ESP_OK(i2s_platform_release_occupation(0));
#elif CONFIG_IDF_TARGET_ESP32S2
TEST_ASSERT(spicommon_periph_free(SPI3_HOST));
#endif
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
TEST_ESP_OK(dac_new_conti_channels(&conti_cfg, &conti_handle));
TEST_ASSERT(dac_conti_disable(conti_handle) == ESP_ERR_INVALID_STATE);
TEST_ESP_OK(dac_conti_enable(conti_handle));
TEST_ASSERT(dac_del_conti_channels(conti_handle) == ESP_ERR_INVALID_STATE);
TEST_ESP_OK(dac_conti_disable(conti_handle));
TEST_ESP_OK(dac_del_conti_channels(conti_handle));
}
TEST_CASE("DAC_memory_leak_test", "[dac]")
{
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
dac_conti_config_t dma_cfg = {
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
dac_conti_handle_t conti_handle;
dac_conti_config_t conti_cfg = {
.chan_mask = DAC_CHANNEL_MASK_ALL,
.desc_num = 8,
.buf_size = 2048,
.freq_hz = 48000,
.offset = 0,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
.desc_num = 10,
.freq_hz = 20000,
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
};
/* Some resources will be lazy installed, ignore the first around */
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
size_t len = 1024;
uint8_t buf[len];
for (int i = 0; i < len; i++) {
buf[i] = i % 256;
}
int initial_size = esp_get_free_heap_size();
printf("Initial free heap size: %d\n", initial_size);
TEST_ESP_OK(dac_new_conti_channels(&conti_cfg, &conti_handle));
TEST_ESP_OK(dac_conti_enable(conti_handle));
TEST_ESP_OK(dac_conti_write_cyclically(conti_handle, buf, len, NULL));
TEST_ESP_OK(dac_conti_disable(conti_handle));
TEST_ESP_OK(dac_del_conti_channels(conti_handle));
uint32_t initial_size = esp_get_free_heap_size();
printf("Initial free heap size: %"PRIu32"\n", initial_size);
for (int i = 0; i < 20; i++) {
printf("# %d: ---------------------------------\n", i + 1);
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
printf("current heap size: %d\n", esp_get_free_heap_size());
TEST_ESP_OK(dac_new_conti_channels(&conti_cfg, &conti_handle));
TEST_ESP_OK(dac_conti_enable(conti_handle));
TEST_ESP_OK(dac_conti_write_cyclically(conti_handle, buf, len, NULL));
TEST_ESP_OK(dac_conti_disable(conti_handle));
TEST_ESP_OK(dac_del_conti_channels(conti_handle));
printf("current heap size: %"PRIu32"\n", esp_get_free_heap_size());
TEST_ASSERT(initial_size == esp_get_free_heap_size());
}
vTaskDelay(100 / portTICK_PERIOD_MS);
@@ -133,60 +157,70 @@ TEST_CASE("DAC_memory_leak_test", "[dac]")
TEST_CASE("DAC_set_voltage_test", "[dac]")
{
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
dac_oneshot_handle_t oneshot_chan0_handle;
dac_oneshot_config_t onshot_cfg = {
.chan_id = DAC_CHAN_0,
};
TEST_ESP_OK(dac_new_oneshot_channel(&onshot_cfg, &oneshot_chan0_handle));
/* Prepare ADC2 */
TEST_ESP_OK(adc2_config_channel_atten(ADC_TEST_CHANNEL_NUM, ADC_TEST_ATTEN));
adc_oneshot_unit_handle_t adc2_handle;
adc_oneshot_unit_init_cfg_t unit_cfg = {
.unit_id = ADC_UNIT_2,
.ulp_mode = false,
};
TEST_ESP_OK(adc_oneshot_new_unit(&unit_cfg, &adc2_handle));
adc_oneshot_chan_cfg_t chan_cfg = {
.atten = ADC_TEST_ATTEN,
.bitwidth = ADC_BITWIDTH_DEFAULT,
};
TEST_ESP_OK(adc_oneshot_config_channel(adc2_handle, ADC_TEST_CHANNEL_NUM, &chan_cfg));
int curr_adc = 0;
int last_adc = 0;
for (uint8_t i = 0; i <= 200; i += 20) {
TEST_ESP_OK(dac_channels_set_voltage(handle, i));
TEST_ESP_OK(dac_oneshot_output_voltage(oneshot_chan0_handle, i));
vTaskDelay(pdMS_TO_TICKS(20));
TEST_ESP_OK(adc2_get_raw(ADC_TEST_CHANNEL_NUM, ADC_TEST_WIDTH, &curr_adc));
TEST_ESP_OK(adc_oneshot_read(adc2_handle, ADC_TEST_CHANNEL_NUM, &curr_adc));
printf("DAC: %d - ADC: %d\n", i, curr_adc);
if (last_adc != 0) {
TEST_ASSERT_GREATER_THAN(last_adc, curr_adc);
}
last_adc = curr_adc;
}
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
TEST_ESP_OK(dac_del_oneshot_channel(oneshot_chan0_handle));
TEST_ESP_OK(adc_oneshot_del_unit(adc2_handle));
}
TEST_CASE("DAC_dma_write_test", "[dac]")
{
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
dac_conti_config_t dma_cfg = {
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
dac_conti_handle_t conti_handle;
dac_conti_config_t conti_cfg = {
.chan_mask = DAC_CHANNEL_MASK_ALL,
.desc_num = 8,
.buf_size = 1024,
.freq_hz = 48000,
.offset = 0,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
.desc_num = 10,
.freq_hz = 20000,
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
};
uint8_t *data = (uint8_t *)calloc(1, 2000);
TEST_ASSERT(data);
for (int i = 0; i < 2000; i++) {
data[i] = i % 256;
size_t len = 520; // To test if the driver can work correctly with uncommon length
uint8_t buf[len];
for (int i = 0; i < len; i++) {
buf[i] = i % 104;
}
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_write_continuously(handle, data, 2000, NULL, 1000));
vTaskDelay(pdMS_TO_TICKS(200));
TEST_ESP_OK(dac_channels_write_cyclically(handle, data, 2000, NULL, 1000));
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
free(data);
TEST_ESP_OK(dac_new_conti_channels(&conti_cfg, &conti_handle));
TEST_ESP_OK(dac_conti_enable(conti_handle));
for (int i = 0; i < 4; i++) {
TEST_ESP_OK(dac_conti_write_cyclically(conti_handle, buf, len, NULL));
vTaskDelay(pdMS_TO_TICKS(200));
for (int j = 0; j < 10; j++) {
TEST_ESP_OK(dac_conti_write(conti_handle, buf, len, NULL, 1000));
}
}
TEST_ESP_OK(dac_conti_disable(conti_handle));
TEST_ESP_OK(dac_del_conti_channels(conti_handle));
}
/* Test the conversion frequency by counting the pulse of WS signal
@@ -221,26 +255,25 @@ TEST_CASE("DAC_dma_conver_frequency_test", "[dac]")
esp_rom_gpio_connect_out_signal(GPIO_NUM_4, i2s_periph_signal[0].m_tx_ws_sig, 0, 0);
esp_rom_gpio_connect_in_signal(GPIO_NUM_4, pcnt_periph_signals.groups[0].units[0].channels[0].pulse_sig, 0);
uint8_t *data = (uint8_t *)calloc(1, 2000);
TEST_ASSERT(data);
size_t len = 800;
uint8_t data[len];
for (int i = 0; i < len; i++) {
data[i] = i % 256;
}
/* Register DAC DMA using PLL */
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
dac_conti_config_t dma_cfg = {
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
.desc_num = 10,
dac_conti_handle_t conti_handle;
dac_conti_config_t conti_cfg = {
.chan_mask = DAC_CHANNEL_MASK_ALL,
.desc_num = 8,
.buf_size = 2048,
.freq_hz = 20000,
.offset = 0,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
};
/* Initialize DAC to test default PLL clock */
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
/* Start transmitting data on line */
TEST_ESP_OK(dac_channels_write_cyclically(handle, data, 2000, NULL, 1000));
TEST_ESP_OK(dac_new_conti_channels(&conti_cfg, &conti_handle));
TEST_ESP_OK(dac_conti_enable(conti_handle));
TEST_ESP_OK(dac_conti_write_cyclically(conti_handle, data, len, NULL));
int expt_pulse = 2000;
int real_pulse;
@@ -250,23 +283,19 @@ TEST_CASE("DAC_dma_conver_frequency_test", "[dac]")
vTaskDelay(pdMS_TO_TICKS(100));
TEST_ESP_OK(pcnt_unit_stop(pcnt_unit));
TEST_ESP_OK(pcnt_unit_get_count(pcnt_unit, &real_pulse));
/* Delete DAC handle */
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
/* Delete DAC continuous handle */
TEST_ESP_OK(dac_conti_disable(conti_handle));
TEST_ESP_OK(dac_del_conti_channels(conti_handle));
printf("[PLL | 20000 Hz] %d pulses, expected %d, err %d\n", real_pulse, expt_pulse, real_pulse - expt_pulse);
TEST_ASSERT_INT_WITHIN(expt_pulse * 0.01, expt_pulse, real_pulse);
dma_cfg.clk_src = DAC_DIGI_CLK_SRC_APLL;
conti_cfg.clk_src = DAC_DIGI_CLK_SRC_APLL;
/* Initialize DAC to test APLL clock */
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
TEST_ESP_OK(dac_new_conti_channels(&conti_cfg, &conti_handle));
TEST_ESP_OK(dac_conti_enable(conti_handle));
/* Start transmitting data on line */
TEST_ESP_OK(dac_channels_write_cyclically(handle, data, 2000, NULL, 1000));
TEST_ESP_OK(dac_conti_write_cyclically(conti_handle, data, len, NULL));
/* Count pulse by PCNT */
TEST_ESP_OK(pcnt_unit_clear_count(pcnt_unit));
@@ -275,14 +304,11 @@ TEST_CASE("DAC_dma_conver_frequency_test", "[dac]")
TEST_ESP_OK(pcnt_unit_stop(pcnt_unit));
TEST_ESP_OK(pcnt_unit_get_count(pcnt_unit, &real_pulse));
/* Delete DAC handle */
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
TEST_ESP_OK(dac_conti_disable(conti_handle));
TEST_ESP_OK(dac_del_conti_channels(conti_handle));
printf("[APLL | 20000 Hz] %d pulses, expected %d, err %d\n", real_pulse, expt_pulse, real_pulse - expt_pulse);
TEST_ASSERT_INT_WITHIN(expt_pulse * 0.01, expt_pulse, real_pulse);
free(data);
/* Free PCNT */
TEST_ESP_OK(pcnt_del_channel(pcnt_chan));
TEST_ESP_OK(pcnt_unit_stop(pcnt_unit));
@@ -293,76 +319,100 @@ TEST_CASE("DAC_dma_conver_frequency_test", "[dac]")
TEST_CASE("DAC_cosine_wave_test", "[dac]")
{
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
dac_cosine_config_t cos_cfg = {
.freq_hz = 1000,
dac_cosine_handle_t cos_chan0_handle;
dac_cosine_handle_t cos_chan1_handle;
dac_cosine_config_t cos0_cfg = {
.chan_id = DAC_CHAN_0,
.freq_hz = 1000, // It will be covered by 8000 in the latter configuration
.clk_src = DAC_COSINE_CLK_SRC_DEFAULT,
.offset = 0,
.phase = DAC_COSINE_PHASE_0,
.scale = DAC_COSINE_NO_ATTEN,
.atten = DAC_COSINE_ATTEN_DEFAULT,
.flags.force_set_freq = false,
};
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_init_cosine_mode(handle, &cos_cfg));
TEST_ESP_OK(dac_channels_start_cosine_output(handle));
dac_cosine_config_t cos1_cfg = {
.chan_id = DAC_CHAN_1,
.freq_hz = 1000,
.clk_src = DAC_COSINE_CLK_SRC_DEFAULT,
.offset = 0,
.phase = DAC_COSINE_PHASE_180,
.atten = DAC_COSINE_ATTEN_DB_6,
.flags.force_set_freq = false,
};
TEST_ESP_OK(dac_new_cosine_channel(&cos0_cfg, &cos_chan0_handle));
TEST_ESP_OK(dac_new_cosine_channel(&cos1_cfg, &cos_chan1_handle));
TEST_ESP_OK(dac_cosine_start(cos_chan0_handle));
TEST_ESP_OK(dac_cosine_start(cos_chan1_handle));
vTaskDelay(pdMS_TO_TICKS(200));
// TODO: find some more meaningful way to test cosine wave
vTaskDelay(pdMS_TO_TICKS(500));
TEST_ESP_OK(dac_channels_stop_cosine_output(handle));
TEST_ESP_OK(dac_channels_deinit_cosine_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
TEST_ESP_OK(dac_cosine_stop(cos_chan0_handle));
TEST_ESP_OK(dac_cosine_stop(cos_chan1_handle));
TEST_ESP_OK(dac_del_cosine_channel(cos_chan0_handle));
TEST_ESP_OK(dac_del_cosine_channel(cos_chan1_handle));
}
static volatile bool task_run_flag;
static void dac_acyclicly_write_task(void *arg)
static void dac_cyclically_write_task(void *arg)
{
dac_channels_handle_t dac_handle = (dac_channels_handle_t)arg;
uint8_t buf[1000];
for (int i = 0; i < 1000; i++) {
buf[i] = i % 256;
}
while (task_run_flag) {
if (dac_channels_write_continuously(dac_handle, buf, 100, NULL, 1000) == ESP_OK) {
printf("DAC write data success\n");
dac_conti_handle_t dac_handle = (dac_conti_handle_t)arg;
size_t len = 1000;
uint8_t buf[len];
uint8_t max_val = 50;
while (1) {
max_val += 50;
for (int i = 0; i < len; i++) {
buf[i] = i % max_val;
}
vTaskDelay(20);
printf("Write cyclically\n");
TEST_ESP_OK(dac_conti_write_cyclically(dac_handle, buf, len, NULL));
vTaskDelay(pdMS_TO_TICKS(200));
}
vTaskDelete(NULL);
}
TEST_CASE("DAC_DMA_thread_safe", "[dac]")
static void dac_continuously_write_task(void *arg)
{
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_CH0};
dac_conti_config_t dma_cfg = {
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
.desc_num = 10,
.freq_hz = 20000,
};
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
task_run_flag = true;
xTaskCreate(dac_acyclicly_write_task, "dac_acyclicly_write_task", 4096, handle, 5, NULL);
for (int i = 0; i < 5; i++) {
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
printf("DAC stopped\n");
vTaskDelay(pdMS_TO_TICKS(100));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
printf("DAC started\n");
dac_conti_handle_t dac_handle = (dac_conti_handle_t)arg;
size_t len = 2048;
uint8_t buf[len];
for (int i = 0; i < len; i++) {
buf[i] = i % 256;
}
while (1) {
printf("Write continuously\n");
TEST_ESP_OK(dac_conti_write(dac_handle, buf, len, NULL, 100));
vTaskDelay(pdMS_TO_TICKS(300));
}
task_run_flag = false;
vTaskDelay(pdMS_TO_TICKS(100));
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
vTaskDelete(NULL);
}
TEST_CASE("DAC_continuous_mode_concurrency_test", "[dac]")
{
dac_conti_handle_t conti_handle;
dac_conti_config_t conti_cfg = {
.chan_mask = DAC_CHANNEL_MASK_ALL,
.desc_num = 8,
.buf_size = 1024,
.freq_hz = 48000,
.offset = 0,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
};
TEST_ESP_OK(dac_new_conti_channels(&conti_cfg, &conti_handle));
TEST_ESP_OK(dac_conti_enable(conti_handle));
TaskHandle_t cyc_task;
TaskHandle_t con_task;
xTaskCreate(dac_cyclically_write_task, "dac_cyclically_write_task", 4096, conti_handle, 5, &cyc_task);
xTaskCreate(dac_continuously_write_task, "dac_continuously_write_task", 4096, conti_handle, 5, &con_task);
vTaskDelay(pdMS_TO_TICKS(5000));
vTaskDelete(cyc_task);
vTaskDelete(con_task);
TEST_ESP_OK(dac_conti_disable(conti_handle));
TEST_ESP_OK(dac_del_conti_channels(conti_handle));
}
@@ -7,40 +7,91 @@
#include <stdio.h>
#include "unity.h"
#include "unity_test_utils.h"
#include "driver/dac_driver.h"
#include "esp_private/spi_flash_os.h"
#include "unity_test_utils_cache.h"
#include "driver/dac_oneshot.h"
#include "driver/dac_conti.h"
#include "esp_heap_caps.h"
#include "esp_err.h"
#include "esp_log.h"
#define BUF_SIZE 2000
static void IRAM_ATTR test_dac_direct_set_safety(dac_channels_handle_t handle)
typedef struct {
int cnt;
bool result;
} test_dac_intr_data_t;
static void IRAM_ATTR test_dac_direct_set_safety(void *usr_ctx)
{
spi_flash_guard_get()->start();
dac_channels_set_voltage(handle, 128);
spi_flash_guard_get()->end();
dac_oneshot_handle_t handle = (dac_oneshot_handle_t)usr_ctx;
dac_oneshot_output_voltage(handle, 128);
}
static void IRAM_ATTR test_dac_dma_iram_safety(dac_channels_handle_t handle, uint8_t *data, uint32_t len)
static void IRAM_ATTR test_dac_dma_iram_safety(void *usr_ctx)
{
spi_flash_guard_get()->start();
// Change the data of DMA
for (int i = 0; i < len; i++) {
data[i] = i % 100;
uint8_t *data = (uint8_t *)usr_ctx;
for (int i = 0; i < BUF_SIZE; i++) {
data[i] = i % 128 + 1;
}
spi_flash_guard_get()->end();
}
static void IRAM_ATTR test_dac_dma_intr_iram_safety(void *usr_ctx)
{
test_dac_intr_data_t *data = (test_dac_intr_data_t *)usr_ctx;
data->cnt = 0;
esp_rom_delay_us(100 * 1000);
data->result = data->cnt > 0;
}
static bool IRAM_ATTR test_dac_on_convert_done_cb(dac_conti_handle_t handle, const dac_event_data_t *event, void *user_data)
{
test_dac_intr_data_t *data = (test_dac_intr_data_t *)user_data;
data->cnt++;
return false;
}
TEST_CASE("DAC_IRAM_safe_test", "[dac]")
{
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
dac_conti_config_t dma_cfg = {
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
.desc_num = 10,
dac_oneshot_handle_t oneshot_handle;
TEST_ESP_OK(dac_new_oneshot_channel(&(dac_oneshot_config_t){.chan_id = DAC_CHAN_0}, &oneshot_handle));
/* Test direct voltage setting safety */
unity_utils_run_cache_disable_stub(test_dac_direct_set_safety, oneshot_handle);
TEST_ESP_OK(dac_del_oneshot_channel(oneshot_handle));
dac_conti_handle_t conti_handle;
dac_conti_config_t conti_cfg = {
.chan_mask = DAC_CHANNEL_MASK_ALL,
.desc_num = 8,
.buf_size = 2048,
.freq_hz = 40000,
.offset = 0,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT, // If the frequency is out of range, try 'DAC_DIGI_CLK_SRC_APLL'
/* Assume the data in buffer is 'A B C D E F'
* DAC_CHANNEL_MODE_SIMUL:
* - channel 0: A B C D E F
* - channel 1: A B C D E F
* DAC_CHANNEL_MODE_ALTER:
* - channel 0: A C E
* - channel 1: B D F
*/
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
};
/* Allocate continuous channel */
TEST_ESP_OK(dac_new_conti_channels(&conti_cfg, &conti_handle));
/* Register a callback to check if the interrupt can still triggered when cache is disabled */
dac_event_callbacks_t cbs = {
.on_convert_done = test_dac_on_convert_done_cb,
.on_stop = NULL,
};
test_dac_intr_data_t intr_data = {
.cnt = 0,
.result = false,
};
TEST_ESP_OK(dac_conti_register_event_callback(conti_handle, &cbs, &intr_data));
/* Enable the channels in the group */
TEST_ESP_OK(dac_conti_enable(conti_handle));
/* Real data in internal memory */
uint8_t *data = (uint8_t *)heap_caps_calloc(1, BUF_SIZE, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
@@ -48,23 +99,12 @@ TEST_CASE("DAC_IRAM_safe_test", "[dac]")
for (int i = 0; i < BUF_SIZE; i++) {
data[i] = i % 256;
}
/* Get ready for dma transmition */
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
/* Test direct voltage setting safety */
test_dac_direct_set_safety(handle);
dac_conti_write_cyclically(conti_handle, data, BUF_SIZE, NULL);
unity_utils_run_cache_disable_stub(test_dac_dma_iram_safety, data);
unity_utils_run_cache_disable_stub(test_dac_dma_intr_iram_safety, &intr_data);
TEST_ASSERT(intr_data.result);
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
/* Simulate cache off */
dac_channels_write_cyclically(handle, data, BUF_SIZE, NULL, 1000);
test_dac_dma_iram_safety(handle, data, BUF_SIZE);
/* Deregister DAC DMA channel group */
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
TEST_ESP_OK(dac_conti_disable(conti_handle));
TEST_ESP_OK(dac_del_conti_channels(conti_handle));
free(data);
}
@@ -1,5 +1,4 @@
CONFIG_COMPILER_DUMP_RTL_FILES=y
CONFIG_DAC_ISR_IRAM_SAFE=y
CONFIG_DAC_CTRL_FUNC_IN_IRAM=y
# silent the error check, as the error string are stored in rodata, causing RTL check failure
CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y
CONFIG_COMPILER_OPTIMIZATION_NONE=y
@@ -1,4 +1,4 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT=n
# Disable this config, otherwise DAC will be disabled when ADC initialized
CONFIG_ADC_DISABLE_DAC=n
CONFIG_ADC_DISABLE_DAC_OUTPUT=n
@@ -6,6 +6,7 @@
/*
Tests for the dac device driver
*/
#include <inttypes.h>
#include "esp_system.h"
#include "unity.h"
@@ -158,26 +159,26 @@ TEST_CASE("esp32s2_adc2-dac_with_adc2_calibration", "[dac_legacy]")
printf("Test 0dB atten...\n");
adc2_config_channel_atten((adc2_channel_t)ADC_TEST_CHANNEL_NUM, ADC_ATTEN_DB_0);
esp_adc_cal_characterize(ADC_UNIT_2, ADC_ATTEN_DB_0, ADC_WIDTH_BIT_13, 0, &chars);
printf("a %d, b %d\n", chars.coeff_a, chars.coeff_b);
printf("a %"PRIu32", b %"PRIu32"\n", chars.coeff_a, chars.coeff_b);
subtest_adc_dac(750, &chars);
printf("Test 2.5dB atten...\n");
adc2_config_channel_atten((adc2_channel_t)ADC_TEST_CHANNEL_NUM, ADC_ATTEN_DB_2_5);
esp_adc_cal_characterize(ADC_UNIT_2, ADC_ATTEN_DB_2_5, ADC_WIDTH_BIT_13, 0, &chars);
printf("a %d, b %d\n", chars.coeff_a, chars.coeff_b);
printf("a %"PRIu32", b %"PRIu32"\n", chars.coeff_a, chars.coeff_b);
subtest_adc_dac(1100, &chars);
printf("Test 6dB atten...\n");
adc2_config_channel_atten((adc2_channel_t)ADC_TEST_CHANNEL_NUM, ADC_ATTEN_DB_6);
esp_adc_cal_characterize(ADC_UNIT_2, ADC_ATTEN_DB_6, ADC_WIDTH_BIT_13, 0, &chars);
printf("a %d, b %d\n", chars.coeff_a, chars.coeff_b);
printf("a %"PRIu32", b %"PRIu32"\n", chars.coeff_a, chars.coeff_b);
subtest_adc_dac(800, &chars);
subtest_adc_dac(1250, &chars);
printf("Test 11dB atten...\n");
adc2_config_channel_atten((adc2_channel_t)ADC_TEST_CHANNEL_NUM, ADC_ATTEN_DB_11);
esp_adc_cal_characterize(ADC_UNIT_2, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_13, 0, &chars);
printf("a %d, b %d\n", chars.coeff_a, chars.coeff_b);
printf("a %"PRIu32", b %"PRIu32"\n", chars.coeff_a, chars.coeff_b);
subtest_adc_dac(1500, &chars);
subtest_adc_dac(2500, &chars);
}
+2 -2
View File
@@ -158,7 +158,7 @@ static void s_disable_dac(adc_oneshot_hal_ctx_t *hal, adc_channel_t channel)
#if CONFIG_IDF_TARGET_ESP32
if (hal->unit == ADC_UNIT_2) {
if (channel == ADC_CHANNEL_8) {
dac_ll_power_down(DAC_CHAN_0); // the same as DAC channel 1
dac_ll_power_down(DAC_CHAN_0); // the same as DAC channel 0
}
if (channel == ADC_CHANNEL_9) {
dac_ll_power_down(DAC_CHAN_1);
@@ -167,7 +167,7 @@ static void s_disable_dac(adc_oneshot_hal_ctx_t *hal, adc_channel_t channel)
#elif CONFIG_IDF_TARGET_ESP32S2
if (hal->unit == ADC_UNIT_2) {
if (channel == ADC_CHANNEL_6) {
dac_ll_power_down(DAC_CHAN_0); // the same as DAC channel 1
dac_ll_power_down(DAC_CHAN_0); // the same as DAC channel 0
}
if (channel == ADC_CHANNEL_7) {
dac_ll_power_down(DAC_CHAN_1);
+13 -8
View File
@@ -56,6 +56,7 @@ static inline void dac_ll_power_down(dac_channel_t channel)
* @param value Output value. Value range: 0 ~ 255.
* The corresponding range of voltage is 0v ~ VDD3P3_RTC.
*/
__attribute__((always_inline))
static inline void dac_ll_update_output_value(dac_channel_t channel, uint8_t value)
{
if (channel == DAC_CHAN_0) {
@@ -104,7 +105,7 @@ static inline void dac_ll_cw_generator_disable(void)
* @param channel DAC channel num.
* @param enable
*/
static inline void dac_ll_cw_set_channel(dac_channel_t channel, bool enable)
static inline void dac_ll_cw_enable_channel(dac_channel_t channel, bool enable)
{
if (channel == DAC_CHAN_0) {
SENS.sar_dac_ctrl2.dac_cw_en1 = enable;
@@ -117,12 +118,12 @@ static inline void dac_ll_cw_set_channel(dac_channel_t channel, bool 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: 130(130Hz)
* @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)(((float)freq / (float)rtc8m_freq) * 65536);
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);
}
@@ -130,14 +131,18 @@ static inline void dac_ll_cw_set_freq(uint32_t freq, uint32_t rtc8m_freq)
* Set the amplitude of the cosine wave generator output.
*
* @param channel DAC channel num.
* @param scale The multiple of the amplitude. The max amplitude is VDD3P3_RTC.
* @param atten The attenuation of the amplitude. The max amplitude is VDD3P3_RTC.
* 0: attenuation = 1, amplitude = VDD3P3_RTC / attenuation,
* 1: attenuation = 2, amplitude = VDD3P3_RTC / attenuation,
* 2: attenuation = 4, amplitude = VDD3P3_RTC / attenuation,
* 3: attenuation = 8, amplitude = VDD3P3_RTC / attenuation
*/
static inline void dac_ll_cw_set_scale(dac_channel_t channel, uint32_t scale)
static inline void dac_ll_cw_set_atten(dac_channel_t channel, dac_cosine_atten_t atten)
{
if (channel == DAC_CHAN_0) {
SENS.sar_dac_ctrl2.dac_scale1 = scale;
SENS.sar_dac_ctrl2.dac_scale1 = atten;
} else if (channel == DAC_CHAN_1) {
SENS.sar_dac_ctrl2.dac_scale2 = scale;
SENS.sar_dac_ctrl2.dac_scale2 = atten;
}
}
@@ -147,7 +152,7 @@ static inline void dac_ll_cw_set_scale(dac_channel_t channel, uint32_t scale)
* @param channel DAC channel num.
* @param phase Phase value. 0: 0x02 180: 0x03.
*/
static inline void dac_ll_cw_set_phase(dac_channel_t channel, uint32_t phase)
static inline void dac_ll_cw_set_phase(dac_channel_t channel, dac_cosine_phase_t phase)
{
if (channel == DAC_CHAN_0) {
SENS.sar_dac_ctrl2.dac_inv1 = phase;
+13 -8
View File
@@ -68,6 +68,7 @@ static inline void dac_ll_power_down(dac_channel_t channel)
* @param value Output value. Value range: 0 ~ 255.
* The corresponding range of voltage is 0v ~ VDD3P3_RTC.
*/
__attribute__((always_inline))
static inline void dac_ll_update_output_value(dac_channel_t channel, uint8_t value)
{
if (channel == DAC_CHAN_0) {
@@ -125,7 +126,7 @@ static inline void dac_ll_cw_generator_disable(void)
* @param channel DAC channel num.
* @param enable
*/
static inline void dac_ll_cw_set_channel(dac_channel_t channel, bool enable)
static inline void dac_ll_cw_enable_channel(dac_channel_t channel, bool enable)
{
if (channel == DAC_CHAN_0) {
SENS.sar_dac_ctrl2.dac_cw_en1 = enable;
@@ -138,12 +139,12 @@ static inline void dac_ll_cw_set_channel(dac_channel_t channel, bool 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: >= 130(130Hz)
* @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)(((float)freq / (float)rtc8m_freq) * 65536);
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);
}
@@ -151,14 +152,18 @@ static inline void dac_ll_cw_set_freq(uint32_t freq, uint32_t rtc8m_freq)
* Set the amplitude of the cosine wave generator output.
*
* @param channel DAC channel num.
* @param scale The multiple of the amplitude. The max amplitude is VDD3P3_RTC.
* @param atten The attenuation of the amplitude. The max amplitude is VDD3P3_RTC.
* 0: attenuation = 1, amplitude = VDD3P3_RTC / attenuation,
* 1: attenuation = 2, amplitude = VDD3P3_RTC / attenuation,
* 2: attenuation = 4, amplitude = VDD3P3_RTC / attenuation,
* 3: attenuation = 8, amplitude = VDD3P3_RTC / attenuation
*/
static inline void dac_ll_cw_set_scale(dac_channel_t channel, uint32_t scale)
static inline void dac_ll_cw_set_atten(dac_channel_t channel, dac_cosine_atten_t atten)
{
if (channel == DAC_CHAN_0) {
SENS.sar_dac_ctrl2.dac_scale1 = scale;
SENS.sar_dac_ctrl2.dac_scale1 = atten;
} else if (channel == DAC_CHAN_1) {
SENS.sar_dac_ctrl2.dac_scale2 = scale;
SENS.sar_dac_ctrl2.dac_scale2 = atten;
}
}
@@ -168,7 +173,7 @@ static inline void dac_ll_cw_set_scale(dac_channel_t channel, uint32_t scale)
* @param channel DAC channel num.
* @param phase Phase value. 0: 0x02 180: 0x03.
*/
static inline void dac_ll_cw_set_phase(dac_channel_t channel, uint32_t phase)
static inline void dac_ll_cw_set_phase(dac_channel_t channel, dac_cosine_phase_t phase)
{
if (channel == DAC_CHAN_0) {
SENS.sar_dac_ctrl2.dac_inv1 = phase;
@@ -1260,6 +1260,7 @@ static inline void spi_dma_ll_enable_out_auto_wrback(spi_dma_dev_t *dma_out, uin
* @param channel DMA channel, for chip version compatibility, not used.
* @return The address
*/
__attribute__((always_inline))
static inline uint32_t spi_dma_ll_get_out_eof_desc_addr(spi_dma_dev_t *dma_out, uint32_t channel)
{
return dma_out->dma_out_eof_des_addr;
+21
View File
@@ -11,6 +11,27 @@ typedef enum {
DAC_CHANNEL_2 __attribute__((deprecated("please use 'DAC_CHAN_1' instead"))) = 1, /*!< Alias of 'DAC_CHAN_1', now the channel index start from '0' */
} dac_channel_t;
/**
* @brief The attenuation of the amplitude of the cosine wave generator. The max amplitude is VDD3P3_RTC.
*/
typedef enum {
DAC_COSINE_ATTEN_DEFAULT = 0x0, /*!< No attenuation to the DAC cosine wave amplitude. Default. */
DAC_COSINE_ATTEN_DB_0 = 0x0, /*!< Original amplitude of the DAC cosine wave, equals to DAC_COSINE_ATTEN_DEFAULT */
DAC_COSINE_ATTEN_DB_6 = 0x1, /*!< 1/2 amplitude of the DAC cosine wave */
DAC_COSINE_ATTEN_DB_12 = 0x2, /*!< 1/4 amplitude of the DAC cosine wave */
DAC_COSINE_ATTEN_DB_18 = 0x3, /*!< 1/8 amplitude of the DAC cosine wave */
} dac_cosine_atten_t;
/**
* @brief Set the phase of the cosine wave generator output.
* @note Only 0 or 180 are supported,
* it will be set to 0 as default if configured to an unsupported phase.
*/
typedef enum {
DAC_COSINE_PHASE_0 = 0x02, /*!< Phase shift +0° */
DAC_COSINE_PHASE_180 = 0x03, /*!< Phase shift +180° */
} dac_cosine_phase_t;
#ifdef __cplusplus
}
#endif
+2 -2
View File
@@ -10,6 +10,6 @@
Bunch of constants for DAC peripheral: GPIO number
*/
const dac_signal_conn_t dac_periph_signal = {
.dac_channel_io_num[0] = DAC_CHAN_0_GPIO_NUM,
.dac_channel_io_num[1] = DAC_CHAN_1_GPIO_NUM,
.dac_channel_io_num[0] = 25,
.dac_channel_io_num[1] = 26,
};
@@ -243,7 +243,7 @@ config SOC_CPU_WATCHPOINT_SIZE
int
default 64
config SOC_DAC_PERIPH_NUM
config SOC_DAC_CHAN_NUM
int
default 2
@@ -289,23 +289,23 @@ typedef enum {
*
*/
typedef enum {
DAC_CLK_SRC_PLLD2 = SOC_MOD_CLK_PLL_D2,
DAC_DIGI_CLK_SRC_APLL = SOC_MOD_CLK_APLL,
DAC_DIGI_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_D2,
DAC_DIGI_CLK_SRC_PLLD2 = SOC_MOD_CLK_PLL_D2, /*!< Select PLL_D2 as the source clock */
DAC_DIGI_CLK_SRC_APLL = SOC_MOD_CLK_APLL, /*!< Select APLL as the source clock */
DAC_DIGI_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_D2, /*!< Select PLL_D2 as the default source clock */
} soc_periph_dac_digi_clk_src_t;
/**
* @brief Array initializer for all supported clock sources of DAC cosine wave generator
*/
#define SOC_DAC_COSINE_CLKS {DAC_COSINE_CLK_SRC_RTC}
#define SOC_DAC_COSINE_CLKS {SOC_MOD_CLK_RTC_FAST}
/**
* @brief DAC cosine wave generator clock source
*
*/
typedef enum {
DAC_COSINE_CLK_SRC_RTC = SOC_MOD_CLK_RTC_FAST,
DAC_COSINE_CLK_SRC_DEFAULT = SOC_MOD_CLK_RTC_FAST,
DAC_COSINE_CLK_SRC_RTC_FAST = SOC_MOD_CLK_RTC_FAST, /*!< Select RTC FAST as the source clock */
DAC_COSINE_CLK_SRC_DEFAULT = SOC_MOD_CLK_RTC_FAST, /*!< Select RTC FAST as the default source clock */
} soc_periph_dac_cosine_clk_src_t;
#ifdef __cplusplus
@@ -4,13 +4,13 @@
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _SOC_DAC_CHANNEL_H
#define _SOC_DAC_CHANNEL_H
#pragma once
#define DAC_GPIO25_CHANNEL DAC_CHAN_0
#define DAC_CHAN_0_GPIO_NUM 25
#define DAC_CHAN0_GPIO_NUM 25
#define DAC_CHANNEL_1_GPIO_NUM DAC_CHAN0_GPIO_NUM //`DAC_CHANNEL_1_GPIO_NUM` is defined for DAC legacy driver, indicating the first DAC channel.
#define DAC_GPIO26_CHANNEL DAC_CHAN_1
#define DAC_CHAN_1_GPIO_NUM 26
#endif
#define DAC_CHAN1_GPIO_NUM 26
#define DAC_CHANNEL_2_GPIO_NUM DAC_CHAN1_GPIO_NUM //`DAC_CHANNEL_2_GPIO_NUM` is defined for DAC legacy driver, indicating the second DAC channel.
+1 -1
View File
@@ -148,7 +148,7 @@
#define SOC_CPU_WATCHPOINT_SIZE 64 // bytes
/*-------------------------- DAC CAPS ----------------------------------------*/
#define SOC_DAC_PERIPH_NUM 2
#define SOC_DAC_CHAN_NUM 2
#define SOC_DAC_RESOLUTION 8 // DAC resolution ratio 8 bit
#define SOC_DAC_DMA_16BIT_ALIGN 1 // The DMA data should left shift 8 bit to be aligned with 16 bit
+2 -2
View File
@@ -10,6 +10,6 @@
Bunch of constants for DAC peripheral: GPIO number
*/
const dac_signal_conn_t dac_periph_signal = {
.dac_channel_io_num[0] = DAC_CHAN_0_GPIO_NUM,
.dac_channel_io_num[1] = DAC_CHAN_1_GPIO_NUM,
.dac_channel_io_num[0] = 17,
.dac_channel_io_num[1] = 18,
};
@@ -271,7 +271,7 @@ config SOC_CPU_WATCHPOINT_SIZE
int
default 64
config SOC_DAC_PERIPH_NUM
config SOC_DAC_CHAN_NUM
int
default 2
@@ -277,23 +277,23 @@ typedef enum {
*
*/
typedef enum {
DAC_DIGI_CLK_SRC_APB = SOC_MOD_CLK_APB,
DAC_DIGI_CLK_SRC_APLL = SOC_MOD_CLK_APLL,
DAC_DIGI_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB,
DAC_DIGI_CLK_SRC_APB = SOC_MOD_CLK_APB, /*!< Select APB as the source clock */
DAC_DIGI_CLK_SRC_APLL = SOC_MOD_CLK_APLL, /*!< Select APLL as the source clock */
DAC_DIGI_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB as the default source clock */
} soc_periph_dac_digi_clk_src_t;
/**
* @brief Array initializer for all supported clock sources of DAC cosine wave generator
*/
#define SOC_DAC_COSINE_CLKS {DAC_COSINE_CLK_SRC_RTC}
#define SOC_DAC_COSINE_CLKS {SOC_MOD_CLK_RTC_FAST}
/**
* @brief DAC cosine wave generator clock source
*
*/
typedef enum {
DAC_COSINE_CLK_SRC_RTC = SOC_MOD_CLK_RTC_FAST,
DAC_COSINE_CLK_SRC_DEFAULT = SOC_MOD_CLK_RTC_FAST,
DAC_COSINE_CLK_SRC_RTC_FAST = SOC_MOD_CLK_RTC_FAST, /*!< Select RTC FAST as the source clock */
DAC_COSINE_CLK_SRC_DEFAULT = SOC_MOD_CLK_RTC_FAST, /*!< Select RTC FAST as the default source clock */
} soc_periph_dac_cosine_clk_src_t;
#ifdef __cplusplus
@@ -4,13 +4,12 @@
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _SOC_DAC_CHANNEL_H
#define _SOC_DAC_CHANNEL_H
#pragma once
#define DAC_GPIO17_CHANNEL DAC_CHAN_0
#define DAC_CHAN_0_GPIO_NUM 17
#define DAC_CHAN0_GPIO_NUM 17
#define DAC_CHANNEL_1_GPIO_NUM DAC_CHAN0_GPIO_NUM //`DAC_CHANNEL_1_GPIO_NUM` is defined for DAC legacy driver, indicating the first DAC channel.
#define DAC_GPIO18_CHANNEL DAC_CHAN_1
#define DAC_CHAN_1_GPIO_NUM 18
#endif
#define DAC_CHAN1_GPIO_NUM 18
#define DAC_CHANNEL_2_GPIO_NUM DAC_CHAN1_GPIO_NUM //`DAC_CHANNEL_2_GPIO_NUM` is defined for DAC legacy driver, indicating the second DAC channel.
@@ -130,7 +130,7 @@
#define SOC_CPU_WATCHPOINT_SIZE 64 // bytes
/*-------------------------- DAC CAPS ----------------------------------------*/
#define SOC_DAC_PERIPH_NUM 2
#define SOC_DAC_CHAN_NUM 2
#define SOC_DAC_RESOLUTION 8 // DAC resolution ratio 8 bit
/*-------------------------- GPIO CAPS ---------------------------------------*/
+6 -15
View File
@@ -1,16 +1,8 @@
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
@@ -19,7 +11,6 @@
#include "soc/rtc_io_reg.h"
#include "soc/rtc_io_struct.h"
#include "soc/rtc.h"
#include "soc/dac_channel.h"
#include "soc/soc_caps.h"
#ifdef __cplusplus
@@ -28,7 +19,7 @@ extern "C"
#endif
typedef struct {
const uint8_t dac_channel_io_num[SOC_DAC_PERIPH_NUM];
const uint8_t dac_channel_io_num[SOC_DAC_CHAN_NUM];
} dac_signal_conn_t;
extern const dac_signal_conn_t dac_periph_signal;