Merge branch 'feature/dac_driver_ng' into 'master'

🔨 DAC: driver NG

Closes IDF-4742

See merge request espressif/esp-idf!18097
This commit is contained in:
Kevin (Lao Kaiyao) 2022-10-25 19:24:17 +08:00
commit 9bc2d15008
148 changed files with 11079 additions and 12082 deletions

View File

@ -1,5 +1,13 @@
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
components/driver/test_apps/dac_test_apps/dac:
disable:
- if: SOC_DAC_SUPPORTED != 1
components/driver/test_apps/dac_test_apps/legacy_dac_driver:
disable:
- if: SOC_DAC_SUPPORTED != 1
components/driver/test_apps/i2s_test_apps:
disable:
- if: SOC_I2S_SUPPORTED != 1

View File

@ -99,22 +99,26 @@ if(CONFIG_SOC_SPI_SUPPORT_SLAVE_HD_VER2)
endif()
if(CONFIG_SOC_TOUCH_SENSOR_SUPPORTED)
list(APPEND srcs "touch_sensor_common.c" "${target}/touch_sensor.c")
list(APPEND srcs "touch_sensor_common.c"
"${target}/touch_sensor.c")
endif()
if(CONFIG_SOC_SDIO_SLAVE_SUPPORTED)
list(APPEND srcs "sdio_slave.c")
endif()
if(${target} STREQUAL "esp32")
list(APPEND srcs "dac_common.c"
"deprecated/adc_i2s_deprecated.c"
"esp32/dac.c")
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 "esp32s2")
list(APPEND srcs "dac_common.c"
"esp32s2/dac.c")
if(${target} STREQUAL "esp32")
list(APPEND srcs "deprecated/adc_i2s_deprecated.c")
endif()
if(BOOTLOADER_BUILD)

View File

@ -399,4 +399,50 @@ menu "Driver Configurations"
Note that, this option only controls the I2S driver log, will not affect other drivers.
endmenu # I2S Configuration
menu "DAC Configuration"
depends on SOC_DAC_SUPPORTED
config DAC_CTRL_FUNC_IN_IRAM
bool "Place DAC control functions into IRAM"
default n
help
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.
config DAC_ISR_IRAM_SAFE
bool "DAC ISR IRAM-Safe"
default n
help
Ensure the DAC interrupt is IRAM-Safe by allowing the interrupt handler to be
executable when the cache is disabled (e.g. SPI Flash write).
config DAC_SUPPRESS_DEPRECATE_WARN
bool "Suppress legacy driver deprecated warning"
default n
help
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.
config DAC_ENABLE_DEBUG_LOG
bool "Enable debug log"
default n
help
Wether to enable the debug log message for DAC driver.
Note that, this option only controls the DAC driver log, won't affect other drivers.
config DAC_DMA_AUTO_16BIT_ALIGN
bool "Align the continuous data to 16 bit automatically"
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.
On ESP32, although the DAC resolution is only 8 bits,
the hardware requires 16 bits data in continuous mode.
By enabling this option, the driver will left shift 8 bits for the input data automatically.
Only disable this option when you decide to do this step by yourself.
Note that the driver will allocate a new piece of memory to save the converted data.
endmenu # DAC Configuration
endmenu # Driver configurations

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

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

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

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

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

View File

@ -0,0 +1,90 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_err.h"
#include "esp_intr_alloc.h"
#ifdef __cplusplus
extern "C" {
#endif
#define DAC_DMA_EOF_INTR 0x01
#define DAC_DMA_TEOF_INTR 0x02
/**
* @brief Initialize DAC DMA peripheral
*
* @param[in] freq_hz DAC data frequency per channel
* @param[in] is_alternate Transmit data alternate between two channels or simultaneously
* @param[in] is_apll Whether use APLL as DAC digital controller clock source
* @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 out of range
* - ESP_OK Initialize DAC DMA peripheral success
*/
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 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 Get the DMA interrupt signal id
*
* @return
* - int DMA interrupt signal id
*/
int dac_dma_periph_get_intr_signal(void);
/**
* @brief Enable the DMA and interrupt of the DAC DMA peripheral
*
*/
void dac_dma_periph_enable(void);
/**
* @brief Disable the DMA and interrupt of the DAC DMA peripheral
*
*/
void dac_dma_periph_disable(void);
/**
* @brief Whether the TX_EOF interrupt is triggered
*
* @return
* - uint32_t Mask of the triggered interrupt: DAC_DMA_EOF_INTR, DAC_DMA_EOF_INTR
*/
uint32_t dac_dma_periph_intr_is_triggered(void);
/**
* @brief Get the descriptor that just finished sending data
*
* @return
* - uint32_t The address of the EOF descriptor
*/
uint32_t dac_dma_periph_intr_get_eof_desc(void);
/**
* @brief Start a DMA transaction
* @note DMA transaction will stop when reaches the tail of the descriptor link
*
* @param[in] desc_addr Descriptor address
*/
void dac_dma_periph_dma_trans_start(uint32_t desc_addr);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,235 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* 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_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
// Set the maximum log level for this source file
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#endif
#include "esp_check.h"
#include "esp_attr.h"
#define DAC_DMA_PERIPH_I2S_NUM 0
#define DAC_DMA_PERIPH_I2S_BIT_WIDTH 16 // Fixed bit width, only the high 8 bits take effect
typedef struct {
void *periph_dev; /* DMA peripheral device address */
intr_handle_t intr_handle; /* Interrupt handle */
bool use_apll; /* Whether use APLL as clock source */
} dac_dma_periph_i2s_t;
static dac_dma_periph_i2s_t *s_ddp = NULL; // Static DAC DMA peripheral structure pointer
static const char *TAG = "DAC_DMA";
static uint32_t s_dac_set_apll_freq(uint32_t mclk)
{
/* Calculate the expected APLL */
int div = (int)((SOC_APLL_MIN_HZ / mclk) + 1);
/* apll_freq = mclk * div
* when div = 1, hardware will still divide 2
* when div = 0, hardware will divide 255
* So the div here should be at least 2 */
div = div < 2 ? 2 : div;
uint32_t expt_freq = mclk * div;
/* Set APLL coefficients to the given frequency */
uint32_t real_freq = 0;
esp_err_t ret = periph_rtc_apll_freq_set(expt_freq, &real_freq);
if (ret == ESP_ERR_INVALID_ARG) {
return 0;
}
if (ret == ESP_ERR_INVALID_STATE) {
ESP_LOGW(TAG, "APLL is occupied already, it is working at %"PRIu32" Hz", 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 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, minimum support freq: 19.6 KHz maximum support freq: 2.5 MHz
if (is_apll) {
sclk = s_dac_set_apll_freq(mclk);
ESP_RETURN_ON_FALSE(sclk, ESP_ERR_INVALID_ARG, TAG, "set APLL coefficients failed");
} else {
// [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] %"PRIu32" [mclk] %"PRIu32" [mclk_div] %"PRIu32" [bclk] %"PRIu32" [bclk_div] %"PRIu32, sclk, mclk, mclk_div, bclk, bclk_div);
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);
return ESP_OK;
}
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);
#endif
esp_err_t ret = ESP_OK;
/* 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 *)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);
if (is_apll) {
periph_rtc_apll_acquire();
s_ddp->use_apll = true;
}
ESP_GOTO_ON_ERROR(s_dac_dma_periph_set_clock(freq_hz, is_apll), err, TAG, "Failed to set clock of DMA peripheral");
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, 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);
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:
dac_dma_periph_deinit();
return ret;
}
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->use_apll) {
periph_rtc_apll_release();
s_ddp->use_apll = false;
}
free(s_ddp);
s_ddp = NULL;
}
return ESP_OK;
}
int dac_dma_periph_get_intr_signal(void)
{
return i2s_periph_signal[DAC_DMA_PERIPH_I2S_NUM].irq;
}
static void s_dac_dma_periph_reset(void)
{
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);
}
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);
}
static void s_dac_dma_periph_stop(void)
{
i2s_ll_tx_stop(s_ddp->periph_dev);
i2s_ll_tx_stop_link(s_ddp->periph_dev);
i2s_ll_tx_disable_intr(s_ddp->periph_dev);
i2s_ll_enable_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);
}
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)
{
uint32_t status = i2s_ll_get_intr_status(s_ddp->periph_dev);
if (status == 0) {
//Avoid spurious interrupt
return false;
}
i2s_ll_clear_intr_status(s_ddp->periph_dev, status);
uint32_t ret = 0;
ret |= (status & I2S_LL_EVENT_TX_EOF) ? DAC_DMA_EOF_INTR : 0;
ret |= (status & I2S_LL_EVENT_TX_TEOF) ? DAC_DMA_TEOF_INTR : 0;
return ret;
}
uint32_t IRAM_ATTR dac_dma_periph_intr_get_eof_desc(void)
{
uint32_t finish_desc;
i2s_ll_tx_get_eof_des_addr(s_ddp->periph_dev, &finish_desc);
return finish_desc;
}
void dac_dma_periph_dma_trans_start(uint32_t desc_addr)
{
i2s_ll_tx_start_link(s_ddp->periph_dev, desc_addr);
}

View File

@ -0,0 +1,232 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* 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"
#include "hal/dac_ll.h"
#include "hal/adc_ll.h"
#include "soc/lldesc.h"
#include "soc/soc.h"
#include "soc/soc_caps.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
// Set the maximum log level for this source file
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#endif
#include "esp_check.h"
#include "esp_attr.h"
#define DAC_DMA_PERIPH_SPI_HOST SPI3_HOST
typedef struct {
void *periph_dev; /* DMA peripheral device address */
uint32_t dma_chan;
intr_handle_t intr_handle; /* Interrupt handle */
bool use_apll; /* Whether use APLL as digital controller clock source */
} dac_dma_periph_spi_t;
static dac_dma_periph_spi_t *s_ddp = NULL; // Static DAC DMA peripheral structure pointer
static const char *TAG = "DAC_DMA";
static uint32_t s_dac_set_apll_freq(uint32_t expt_freq)
{
/* Set APLL coefficients to the given frequency */
uint32_t real_freq = 0;
esp_err_t ret = periph_rtc_apll_freq_set(expt_freq, &real_freq);
if (ret == ESP_ERR_INVALID_ARG) {
return 0;
}
if (ret == ESP_ERR_INVALID_STATE) {
ESP_LOGW(TAG, "APLL is occupied already, it is working at %"PRIu32" Hz", 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 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
* @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){
/* 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 = 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;
}
/* Step 2: Determine the interval */
uint32_t total_div = digi_ctrl_freq / freq_hz;
uint32_t interval;
/* For the case that smaller than the minimum ADC controller division, the required frequency is too big */
ESP_RETURN_ON_FALSE(total_div >= 2, ESP_ERR_INVALID_ARG, TAG, "the DAC frequency is too big");
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 interval
/* Set the interval to 'total_div / 2', fix the integer part of ADC controller clock division to 2 */
interval = total_div / 2;
} else {
/* Fix the interval to 4095, */
interval = 4095;
}
ESP_RETURN_ON_FALSE(interval * 256 > total_div, ESP_ERR_INVALID_ARG, TAG, "the DAC frequency is too small");
/* Step 3: Calculate the coefficients of ADC digital controller divider*/
uint32_t fsclk = interval * freq_hz; /* The clock frequency that produced by ADC controller divider */
uint32_t clk_div = digi_ctrl_freq / fsclk;
uint32_t mod = digi_ctrl_freq % fsclk;
uint32_t a = 0;
uint32_t b = 1;
if (mod == 0) {
goto finish;
}
uint32_t min_diff = mod + 1;
for (uint32_t tmp_b = 1; tmp_b < 64; tmp_b++) {
uint32_t tmp_a = (uint32_t)(((mod * b) / (float)fsclk) + 0.5);
uint32_t diff = (uint32_t)abs((int)(mod * tmp_b) - (int)(fsclk * tmp_a));
if (diff == 0) {
a = tmp_a;
b = tmp_b;
goto finish;
}
if (diff < min_diff) {
min_diff = diff;
a = tmp_a;
b = tmp_b;
}
}
finish:
/* Step 4: Set the clock coefficients */
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);
return ESP_OK;
}
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);
#endif
esp_err_t ret = ESP_OK;
/* Acquire DMA peripheral */
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 *)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);
if (is_apll) {
periph_rtc_apll_acquire();
s_ddp->use_apll = true;
}
/* 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(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");
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);
return ret;
err:
dac_dma_periph_deinit();
return ret;
}
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->use_apll) {
periph_rtc_apll_release();
s_ddp->use_apll = false;
}
free(s_ddp);
s_ddp = NULL;
}
return ESP_OK;
}
int dac_dma_periph_get_intr_signal(void)
{
return spicommon_irqdma_source_for_host(DAC_DMA_PERIPH_SPI_HOST);
}
static void s_dac_dma_periph_reset(void)
{
spi_dma_ll_tx_reset(s_ddp->periph_dev, s_ddp->dma_chan);
spi_ll_dma_tx_fifo_reset(s_ddp->periph_dev);
}
void dac_dma_periph_enable(void)
{
s_dac_dma_periph_reset();
dac_ll_digi_trigger_output(true);
}
void dac_dma_periph_disable(void)
{
s_dac_dma_periph_reset();
spi_dma_ll_tx_stop(s_ddp->periph_dev, s_ddp->dma_chan);
dac_ll_digi_trigger_output(false);
}
uint32_t IRAM_ATTR dac_dma_periph_intr_is_triggered(void)
{
uint32_t ret = 0;
ret |= spi_ll_get_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_EOF) ? DAC_DMA_EOF_INTR : 0;
ret |= spi_ll_get_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_TOTAL_EOF) ? DAC_DMA_TEOF_INTR : 0;
spi_ll_clear_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_EOF);
spi_ll_clear_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_TOTAL_EOF);
return ret;
}
uint32_t IRAM_ATTR dac_dma_periph_intr_get_eof_desc(void)
{
return spi_dma_ll_get_out_eof_desc_addr(s_ddp->periph_dev, s_ddp->dma_chan);
}
void dac_dma_periph_dma_trans_start(uint32_t desc_addr)
{
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);
}

View File

@ -1,123 +0,0 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_log.h"
#include "esp_err.h"
#include "esp_check.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/timers.h"
#include "driver/rtc_io.h"
#include "driver/dac.h"
#include "soc/dac_periph.h"
#include "hal/dac_hal.h"
extern portMUX_TYPE rtc_spinlock; //TODO: Will be placed in the appropriate position after the rtc module is finished.
static __attribute__((unused)) const char *TAG = "DAC";
/*---------------------------------------------------------------
DAC
---------------------------------------------------------------*/
esp_err_t dac_pad_get_io_num(dac_channel_t channel, gpio_num_t *gpio_num)
{
ESP_RETURN_ON_FALSE(channel < DAC_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
*gpio_num = (gpio_num_t)dac_periph_signal.dac_channel_io_num[channel];
return ESP_OK;
}
static esp_err_t dac_rtc_pad_init(dac_channel_t channel)
{
ESP_RETURN_ON_FALSE(channel < DAC_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
gpio_num_t gpio_num = 0;
dac_pad_get_io_num(channel, &gpio_num);
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);
return ESP_OK;
}
esp_err_t dac_output_enable(dac_channel_t channel)
{
ESP_RETURN_ON_FALSE(channel < DAC_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
dac_rtc_pad_init(channel);
portENTER_CRITICAL(&rtc_spinlock);
dac_hal_power_on(channel);
dac_hal_rtc_sync_by_adc(false);
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
}
esp_err_t dac_output_disable(dac_channel_t channel)
{
ESP_RETURN_ON_FALSE(channel < DAC_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
portENTER_CRITICAL(&rtc_spinlock);
dac_hal_power_down(channel);
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
}
esp_err_t dac_output_voltage(dac_channel_t channel, uint8_t dac_value)
{
ESP_RETURN_ON_FALSE(channel < DAC_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
portENTER_CRITICAL(&rtc_spinlock);
dac_hal_update_output_value(channel, dac_value);
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
}
esp_err_t dac_out_voltage(dac_channel_t channel, uint8_t dac_value)
{
ESP_RETURN_ON_FALSE(channel < DAC_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
portENTER_CRITICAL(&rtc_spinlock);
dac_hal_update_output_value(channel, dac_value);
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
}
esp_err_t dac_cw_generator_enable(void)
{
portENTER_CRITICAL(&rtc_spinlock);
dac_hal_cw_generator_enable();
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
}
esp_err_t dac_cw_generator_disable(void)
{
portENTER_CRITICAL(&rtc_spinlock);
dac_hal_cw_generator_disable();
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
}
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_hal_cw_generator_config(cw);
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
}

View File

@ -29,8 +29,8 @@
#include "driver/adc_types_legacy.h"
#if SOC_DAC_SUPPORTED
#include "driver/dac.h"
#include "hal/dac_hal.h"
#include "hal/dac_types.h"
#include "hal/dac_ll.h"
#endif
#if CONFIG_IDF_TARGET_ESP32S3
@ -151,7 +151,7 @@ static void adc_rtc_chan_init(adc_unit_t adc_unit)
/* Workaround: Disable the synchronization operation function of ADC1 and DAC.
If enabled(default), ADC RTC controller sampling will cause the DAC channel output voltage. */
#if SOC_DAC_SUPPORTED
dac_hal_rtc_sync_by_adc(false);
dac_ll_rtc_sync_by_adc(false);
#endif
adc_oneshot_ll_output_invert(ADC_UNIT_1, ADC_HAL_DATA_INVERT_DEFAULT(ADC_UNIT_1));
adc_ll_set_sar_clk_div(ADC_UNIT_1, ADC_HAL_SAR_CLK_DIV_DEFAULT(ADC_UNIT_1));
@ -471,16 +471,16 @@ static inline void adc2_dac_disable( adc2_channel_t channel)
{
#if SOC_DAC_SUPPORTED
#ifdef CONFIG_IDF_TARGET_ESP32
if ( channel == ADC2_CHANNEL_8 ) { // the same as DAC channel 1
dac_output_disable(DAC_CHANNEL_1);
if ( channel == ADC2_CHANNEL_8 ) { // the same as DAC channel 0
dac_ll_power_down(DAC_CHAN_0);
} else if ( channel == ADC2_CHANNEL_9 ) {
dac_output_disable(DAC_CHANNEL_2);
dac_ll_power_down(DAC_CHAN_1);
}
#else
if ( channel == ADC2_CHANNEL_6 ) { // the same as DAC channel 1
dac_output_disable(DAC_CHANNEL_1);
if ( channel == ADC2_CHANNEL_6 ) { // the same as DAC channel 0
dac_ll_power_down(DAC_CHAN_0);
} else if ( channel == ADC2_CHANNEL_7 ) {
dac_output_disable(DAC_CHANNEL_2);
dac_ll_power_down(DAC_CHAN_1);
}
#endif
#endif // SOC_DAC_SUPPORTED

View File

@ -0,0 +1,146 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_check.h"
#include "freertos/FreeRTOS.h"
#include "driver/rtc_io.h"
#include "driver/dac_types_legacy.h"
#include "soc/dac_periph.h"
#include "hal/gpio_types.h"
#include "hal/dac_ll.h"
#include "clk_ctrl_os.h"
extern portMUX_TYPE rtc_spinlock; //TODO: Will be placed in the appropriate position after the rtc module is finished.
static __attribute__((unused)) const char *TAG = "DAC";
/*---------------------------------------------------------------
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_CHAN_NUM, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
*gpio_num = (gpio_num_t)dac_periph_signal.dac_channel_io_num[channel];
return ESP_OK;
}
static esp_err_t dac_rtc_pad_init(dac_channel_t channel)
{
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);
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);
return ESP_OK;
}
esp_err_t dac_output_enable(dac_channel_t channel)
{
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);
dac_ll_power_on(channel);
dac_ll_rtc_sync_by_adc(false);
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
}
esp_err_t dac_output_disable(dac_channel_t channel)
{
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);
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
}
esp_err_t dac_output_voltage(dac_channel_t channel, uint8_t dac_value)
{
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);
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
}
esp_err_t dac_out_voltage(dac_channel_t channel, uint8_t dac_value)
{
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);
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
}
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);
return ESP_OK;
}
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;
}
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);
/* 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_enable_channel(cw->en_ch, true);
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
}
/**
* @brief This function will be called during start up, to check that this legacy DAC driver is not running along with the new driver
*/
__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_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_oneshot.h`, `driver/dac_cosine.h` or `driver/dac_conti.h` instead");
}

View File

@ -0,0 +1,171 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "sdkconfig.h"
#include "driver/gpio.h"
#include "driver/dac_types_legacy.h"
#if !CONFIG_DAC_SUPPRESS_DEPRECATE_WARN
#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
extern "C" {
#endif
/**
* @brief Get the GPIO number of a specific DAC channel.
*
* @param channel Channel to get the gpio number
* @param gpio_num output buffer to hold the gpio number
* @return
* - ESP_OK if success
*/
esp_err_t dac_pad_get_io_num(dac_channel_t channel, gpio_num_t *gpio_num);
/**
* @brief Set DAC output voltage.
* DAC output is 8-bit. Maximum (255) corresponds to VDD3P3_RTC.
*
* @note Need to configure DAC pad before calling this function.
* DAC channel 0 is attached to GPIO25, DAC channel 1 is attached to GPIO26
* @param channel DAC channel
* @param dac_value DAC output value
*
* @return
* - ESP_OK success
*/
esp_err_t dac_output_voltage(dac_channel_t channel, uint8_t dac_value);
/**
* @brief DAC pad output enable
*
* @param channel DAC channel
* @note DAC channel 0 is attached to GPIO25, DAC channel 1 is attached to GPIO26
* I2S left channel will be mapped to DAC channel 1
* I2S right channel will be mapped to DAC channel 0
*/
esp_err_t dac_output_enable(dac_channel_t channel);
/**
* @brief DAC pad output disable
*
* @param channel DAC channel
* @note DAC channel 0 is attached to GPIO25, DAC channel 1 is attached to GPIO26
* @return
* - ESP_OK success
*/
esp_err_t dac_output_disable(dac_channel_t channel);
/**
* @brief Enable cosine wave generator output.
*
* @return
* - ESP_OK success
*/
esp_err_t dac_cw_generator_enable(void);
/**
* @brief Disable cosine wave generator output.
*
* @return
* - ESP_OK success
*/
esp_err_t dac_cw_generator_disable(void);
/**
* @brief Config the cosine wave generator function in DAC module.
*
* @param cw Configuration.
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG The parameter is NULL.
*/
esp_err_t dac_cw_generator_config(dac_cw_config_t *cw);
/*---------------------------------------------------------------
Digital controller setting
---------------------------------------------------------------*/
#if CONFIG_IDF_TARGET_ESP32
/**
* @brief Enable DAC output data from I2S
*
* @return
* - ESP_OK success
*/
esp_err_t dac_i2s_enable(void);
/**
* @brief Disable DAC output data from I2S
*
* @return
* - ESP_OK success
*/
esp_err_t dac_i2s_disable(void);
#endif // CONFIG_IDF_TARGET_ESP32
#if CONFIG_IDF_TARGET_ESP32S2
/**
* @brief DAC digital controller initialization.
* @return
* - ESP_OK success
*/
esp_err_t dac_digi_init(void);
/**
* @brief DAC digital controller deinitialization.
* @return
* - ESP_OK success
*/
esp_err_t dac_digi_deinit(void);
/**
* @brief Setting the DAC digital controller.
*
* @param cfg Pointer to digital controller paramter. See ``dac_digi_config_t``.
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t dac_digi_controller_config(const dac_digi_config_t *cfg);
/**
* @brief DAC digital controller start output voltage.
* @return
* - ESP_OK success
*/
esp_err_t dac_digi_start(void);
/**
* @brief DAC digital controller stop output voltage.
* @return
* - ESP_OK success
*/
esp_err_t dac_digi_stop(void);
/**
* @brief Reset DAC digital controller FIFO.
* @return
* - ESP_OK success
*/
esp_err_t dac_digi_fifo_reset(void);
/**
* @brief Reset DAC digital controller.
* @return
* - ESP_OK success
*/
esp_err_t dac_digi_reset(void);
#endif // CONFIG_IDF_TARGET_ESP32S2
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,79 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stddef.h>
#include "sdkconfig.h"
#include "hal/dac_types.h"
#if CONFIG_IDF_TARGET_ESP32S2
#include "hal/adc_types.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief The multiple of the amplitude of the cosine wave generator. The max amplitude is VDD3P3_RTC.
*/
typedef enum {
DAC_CW_SCALE_1 = 0x0, /*!< 1/1. Default. */
DAC_CW_SCALE_2 = 0x1, /*!< 1/2. */
DAC_CW_SCALE_4 = 0x2, /*!< 1/4. */
DAC_CW_SCALE_8 = 0x3, /*!< 1/8. */
} dac_cw_scale_t;
/**
* @brief Set the phase of the cosine wave generator output.
*/
typedef enum {
DAC_CW_PHASE_0 = 0x2, /*!< Phase shift +0° */
DAC_CW_PHASE_180 = 0x3, /*!< Phase shift +180° */
} dac_cw_phase_t;
#if CONFIG_IDF_TARGET_ESP32S2
/**
* @brief DAC digital controller (DMA mode) work mode.
*/
typedef enum {
DAC_CONV_NORMAL, /*!< The data in the DMA buffer is simultaneously output to the enable channel of the DAC. */
DAC_CONV_ALTER, /*!< The data in the DMA buffer is alternately output to the enable channel of the DAC. */
DAC_CONV_MAX
} dac_digi_convert_mode_t;
/**
* @brief DAC digital controller (DMA mode) configuration parameters.
*/
typedef struct {
dac_digi_convert_mode_t mode; /*!<DAC digital controller (DMA mode) work mode. See ``dac_digi_convert_mode_t``. */
uint32_t interval; /*!<The number of interval clock cycles for the DAC digital controller to output voltage.
The unit is the divided clock. Range: 1 ~ 4095.
Expression: `dac_output_freq` = `controller_clk` / interval. Refer to ``adc_digi_clk_t``.
Note: The sampling rate of each channel is also related to the conversion mode (See ``dac_digi_convert_mode_t``) and pattern table settings. */
adc_digi_clk_t dig_clk; /*!<DAC digital controller clock divider settings. Refer to ``adc_digi_clk_t``.
Note: The clocks of the DAC digital controller use the ADC digital controller clock divider. */
} dac_digi_config_t;
#endif
/**
* @brief Config the cosine wave generator function in DAC module.
*/
typedef struct {
dac_channel_t en_ch; /*!< Enable the cosine wave generator of DAC channel. */
dac_cw_scale_t scale; /*!< Set the amplitude of the cosine wave generator output. */
dac_cw_phase_t phase; /*!< Set the phase of the cosine wave generator output. */
uint32_t freq; /*!< Set frequency of cosine wave generator output. Range: 130(130Hz) ~ 55000(100KHz). */
int8_t offset; /*!< Set the voltage value of the DC component of the cosine wave generator output.
Note: Unreasonable settings can cause waveform to be oversaturated. Range: -128 ~ 127. */
} dac_cw_config_t;
#ifdef __cplusplus
}
#endif

View File

@ -135,8 +135,8 @@ typedef enum {
*/
typedef enum {
I2S_DAC_CHANNEL_DISABLE = 0, /*!< Disable I2S built-in DAC signals*/
I2S_DAC_CHANNEL_RIGHT_EN = 1, /*!< Enable I2S built-in DAC right channel, maps to DAC channel 1 on GPIO25*/
I2S_DAC_CHANNEL_LEFT_EN = 2, /*!< Enable I2S built-in DAC left channel, maps to DAC channel 2 on GPIO26*/
I2S_DAC_CHANNEL_RIGHT_EN = 1, /*!< Enable I2S built-in DAC right channel, maps to DAC channel 0 on GPIO25*/
I2S_DAC_CHANNEL_LEFT_EN = 2, /*!< Enable I2S built-in DAC left channel, maps to DAC channel 1 on GPIO26*/
I2S_DAC_CHANNEL_BOTH_EN = 0x3, /*!< Enable both of the I2S built-in DAC channels.*/
I2S_DAC_CHANNEL_MAX = 0x4, /*!< I2S built-in DAC mode max index*/
} i2s_dac_mode_t;

View File

@ -5,16 +5,9 @@
*/
#include <string.h>
#include "esp_log.h"
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/timers.h"
#include "driver/rtc_io.h"
#include "driver/dac.h"
#include "soc/dac_periph.h"
#include "hal/dac_hal.h"
#include "hal/dac_ll.h"
#include "esp_err.h"
extern portMUX_TYPE rtc_spinlock; //TODO: Will be placed in the appropriate position after the rtc module is finished.
#define DAC_ENTER_CRITICAL() portENTER_CRITICAL(&rtc_spinlock)
@ -27,7 +20,7 @@ extern portMUX_TYPE rtc_spinlock; //TODO: Will be placed in the appropriate posi
esp_err_t dac_i2s_enable(void)
{
DAC_ENTER_CRITICAL();
dac_hal_digi_enable_dma(true);
dac_ll_digi_enable_dma(true);
DAC_EXIT_CRITICAL();
return ESP_OK;
@ -36,7 +29,7 @@ esp_err_t dac_i2s_enable(void)
esp_err_t dac_i2s_disable(void)
{
DAC_ENTER_CRITICAL();
dac_hal_digi_enable_dma(false);
dac_ll_digi_enable_dma(false);
DAC_EXIT_CRITICAL();
return ESP_OK;

View File

@ -6,17 +6,13 @@
#include <string.h>
#include "esp_log.h"
#include "esp_err.h"
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "driver/dac_types_legacy.h"
#include "hal/adc_ll.h"
#include "hal/dac_ll.h"
#include "esp_check.h"
#include "esp_pm.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/timers.h"
#include "driver/rtc_io.h"
#include "driver/dac.h"
#include "soc/dac_periph.h"
#include "hal/dac_hal.h"
static __attribute__((unused)) const char *TAG = "DAC";
@ -35,7 +31,7 @@ static esp_pm_lock_handle_t s_dac_digi_lock = NULL;
esp_err_t dac_digi_init(void)
{
DAC_ENTER_CRITICAL();
dac_hal_digi_init();
dac_ll_digi_clk_inv(true);
DAC_EXIT_CRITICAL();
return ESP_OK;
@ -50,7 +46,10 @@ esp_err_t dac_digi_deinit(void)
}
#endif
DAC_ENTER_CRITICAL();
dac_hal_digi_deinit();
dac_ll_digi_trigger_output(false);
dac_ll_digi_enable_dma(false);
dac_ll_digi_fifo_reset();
dac_ll_digi_reset();
DAC_EXIT_CRITICAL();
return ESP_OK;
@ -58,7 +57,7 @@ esp_err_t dac_digi_deinit(void)
esp_err_t dac_digi_controller_config(const dac_digi_config_t *cfg)
{
ESP_RETURN_ON_FALSE(cfg->mode < DAC_CONV_MAX, ESP_ERR_INVALID_ARG, TAG, "DAC mode error");
ESP_RETURN_ON_FALSE(cfg->mode <= DAC_CONV_ALTER, ESP_ERR_INVALID_ARG, TAG, "DAC mode error");
ESP_RETURN_ON_FALSE(cfg->interval > 0 && cfg->interval < 4096, ESP_ERR_INVALID_ARG, TAG, "DAC interval error");
ESP_RETURN_ON_FALSE(cfg->dig_clk.div_num < 256, ESP_ERR_INVALID_ARG, TAG, "DAC clk div_num error");
ESP_RETURN_ON_FALSE(cfg->dig_clk.div_b > 0 && cfg->dig_clk.div_b < 64, ESP_ERR_INVALID_ARG, TAG, "DAC clk div_b error");
@ -80,7 +79,10 @@ esp_err_t dac_digi_controller_config(const dac_digi_config_t *cfg)
#endif //CONFIG_PM_ENABLE
DAC_ENTER_CRITICAL();
dac_hal_digi_controller_config(cfg);
dac_ll_digi_set_convert_mode(cfg->mode == DAC_CONV_ALTER);
dac_ll_digi_set_trigger_interval(cfg->interval);
adc_ll_digi_controller_clk_div(cfg->dig_clk.div_num, cfg->dig_clk.div_b, cfg->dig_clk.div_a);
adc_ll_digi_clk_sel(cfg->dig_clk.use_apll);
DAC_EXIT_CRITICAL();
return ESP_OK;
@ -93,7 +95,8 @@ esp_err_t dac_digi_start(void)
esp_pm_lock_acquire(s_dac_digi_lock);
#endif
DAC_ENTER_CRITICAL();
dac_hal_digi_start();
dac_ll_digi_enable_dma(true);
dac_ll_digi_trigger_output(true);
DAC_EXIT_CRITICAL();
return ESP_OK;
@ -107,7 +110,8 @@ esp_err_t dac_digi_stop(void)
}
#endif
DAC_ENTER_CRITICAL();
dac_hal_digi_stop();
dac_ll_digi_trigger_output(false);
dac_ll_digi_enable_dma(false);
DAC_EXIT_CRITICAL();
return ESP_OK;
@ -116,7 +120,7 @@ esp_err_t dac_digi_stop(void)
esp_err_t dac_digi_fifo_reset(void)
{
DAC_ENTER_CRITICAL();
dac_hal_digi_fifo_reset();
dac_ll_digi_fifo_reset();
DAC_EXIT_CRITICAL();
return ESP_OK;
@ -125,7 +129,7 @@ esp_err_t dac_digi_fifo_reset(void)
esp_err_t dac_digi_reset(void)
{
DAC_ENTER_CRITICAL();
dac_hal_digi_reset();
dac_ll_digi_reset();
DAC_EXIT_CRITICAL();
return ESP_OK;

View File

@ -26,7 +26,8 @@
#include "driver/i2s_types_legacy.h"
#include "hal/i2s_hal.h"
#if SOC_I2S_SUPPORTS_DAC
#include "driver/dac.h"
#include "hal/dac_ll.h"
#include "hal/dac_types.h"
#include "esp_private/adc_share_hw_ctrl.h"
#include "adc1_private.h"
#include "driver/adc_i2s_legacy.h"
@ -836,20 +837,22 @@ esp_err_t i2s_set_dac_mode(i2s_dac_mode_t dac_mode)
{
ESP_RETURN_ON_FALSE((dac_mode < I2S_DAC_CHANNEL_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s dac mode error");
if (dac_mode == I2S_DAC_CHANNEL_DISABLE) {
dac_output_disable(DAC_CHANNEL_1);
dac_output_disable(DAC_CHANNEL_2);
dac_i2s_disable();
dac_ll_power_down(DAC_CHAN_0);
dac_ll_power_down(DAC_CHAN_1);
dac_ll_digi_enable_dma(false);
} else {
dac_i2s_enable();
dac_ll_digi_enable_dma(true);
}
if (dac_mode & I2S_DAC_CHANNEL_RIGHT_EN) {
//DAC1, right channel
dac_output_enable(DAC_CHANNEL_1);
dac_ll_power_on(DAC_CHAN_0);
dac_ll_rtc_sync_by_adc(false);
}
if (dac_mode & I2S_DAC_CHANNEL_LEFT_EN) {
//DAC2, left channel
dac_output_enable(DAC_CHANNEL_2);
dac_ll_power_on(DAC_CHAN_1);
dac_ll_rtc_sync_by_adc(false);
}
return ESP_OK;
}

View File

@ -1,36 +0,0 @@
/*
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "driver/dac_common.h"
#ifdef __cplusplus
extern "C" {
#endif
/*---------------------------------------------------------------
Digital controller setting
---------------------------------------------------------------*/
/**
* @brief Enable DAC output data from I2S
*
* @return
* - ESP_OK success
*/
esp_err_t dac_i2s_enable(void);
/**
* @brief Disable DAC output data from I2S
*
* @return
* - ESP_OK success
*/
esp_err_t dac_i2s_disable(void);
#ifdef __cplusplus
}
#endif

View File

@ -1,73 +0,0 @@
/*
* SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "driver/dac_common.h"
#ifdef __cplusplus
extern "C" {
#endif
/*---------------------------------------------------------------
Digital controller setting
---------------------------------------------------------------*/
/**
* @brief DAC digital controller initialization.
* @return
* - ESP_OK success
*/
esp_err_t dac_digi_init(void);
/**
* @brief DAC digital controller deinitialization.
* @return
* - ESP_OK success
*/
esp_err_t dac_digi_deinit(void);
/**
* @brief Setting the DAC digital controller.
*
* @param cfg Pointer to digital controller paramter. See ``dac_digi_config_t``.
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t dac_digi_controller_config(const dac_digi_config_t *cfg);
/**
* @brief DAC digital controller start output voltage.
* @return
* - ESP_OK success
*/
esp_err_t dac_digi_start(void);
/**
* @brief DAC digital controller stop output voltage.
* @return
* - ESP_OK success
*/
esp_err_t dac_digi_stop(void);
/**
* @brief Reset DAC digital controller FIFO.
* @return
* - ESP_OK success
*/
esp_err_t dac_digi_fifo_reset(void);
/**
* @brief Reset DAC digital controller.
* @return
* - ESP_OK success
*/
esp_err_t dac_digi_reset(void);
#ifdef __cplusplus
}
#endif

View File

@ -1,90 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "esp_err.h"
#include "driver/gpio.h"
#include "hal/dac_types.h"
/**
* @brief Get the GPIO number of a specific DAC channel.
*
* @param channel Channel to get the gpio number
* @param gpio_num output buffer to hold the gpio number
* @return
* - ESP_OK if success
*/
esp_err_t dac_pad_get_io_num(dac_channel_t channel, gpio_num_t *gpio_num);
/**
* @brief Set DAC output voltage.
* DAC output is 8-bit. Maximum (255) corresponds to VDD3P3_RTC.
*
* @note Need to configure DAC pad before calling this function.
* DAC channel 1 is attached to GPIO25, DAC channel 2 is attached to GPIO26
* @param channel DAC channel
* @param dac_value DAC output value
*
* @return
* - ESP_OK success
*/
esp_err_t dac_output_voltage(dac_channel_t channel, uint8_t dac_value);
/**
* @brief DAC pad output enable
*
* @param channel DAC channel
* @note DAC channel 1 is attached to GPIO25, DAC channel 2 is attached to GPIO26
* I2S left channel will be mapped to DAC channel 2
* I2S right channel will be mapped to DAC channel 1
*/
esp_err_t dac_output_enable(dac_channel_t channel);
/**
* @brief DAC pad output disable
*
* @param channel DAC channel
* @note DAC channel 1 is attached to GPIO25, DAC channel 2 is attached to GPIO26
* @return
* - ESP_OK success
*/
esp_err_t dac_output_disable(dac_channel_t channel);
/**
* @brief Enable cosine wave generator output.
*
* @return
* - ESP_OK success
*/
esp_err_t dac_cw_generator_enable(void);
/**
* @brief Disable cosine wave generator output.
*
* @return
* - ESP_OK success
*/
esp_err_t dac_cw_generator_disable(void);
/**
* @brief Config the cosine wave generator function in DAC module.
*
* @param cw Configuration.
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG The parameter is NULL.
*/
esp_err_t dac_cw_generator_config(dac_cw_config_t *cw);
#ifdef __cplusplus
}
#endif

View File

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

View File

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

View File

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

View File

@ -0,0 +1,56 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stddef.h>
#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"
#ifdef __cplusplus
extern "C" {
#endif
#if SOC_DAC_SUPPORTED
/**
* @brief DAC channel work mode in dma mode
* @note Only take effect when multiple channels enabled.
* @note 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
*/
typedef enum {
DAC_CHANNEL_MODE_SIMUL, /*!< The data in the DMA buffer is simultaneously output to the enable channel of the DAC. */
DAC_CHANNEL_MODE_ALTER, /*!< The data in the DMA buffer is alternately output to the enable channel of the DAC. */
} dac_conti_channel_mode_t;
/**
* @brief DAC DMA (digitial controller) clock source
*
*/
typedef soc_periph_dac_digi_clk_src_t dac_conti_digi_clk_src_t;
/**
* @brief DAC cosine wave generator clock source
*
*/
typedef soc_periph_dac_cosine_clk_src_t dac_cosine_clk_src_t;
#endif // SOC_DAC_SUPPORTED
#ifdef __cplusplus
}
#endif

View File

@ -17,5 +17,8 @@ entries:
gpio: gpio_intr_disable (noflash)
if SDM_CTRL_FUNC_IN_IRAM = y:
sdm: sdm_channel_set_duty (noflash)
if DAC_CTRL_FUNC_IN_IRAM = y:
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)

View File

@ -1,4 +1,4 @@
idf_component_register(SRC_DIRS . param_test dac_dma_test
idf_component_register(SRC_DIRS . param_test
PRIV_INCLUDE_DIRS include param_test/include
PRIV_REQUIRES cmock test_utils driver nvs_flash
esp_timer esp_adc esp_event esp_wifi)

File diff suppressed because it is too large Load Diff

View File

@ -1,357 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
Tests for the dac device driver on ESP32-S2 only
*/
#include "sdkconfig.h"
#if CONFIG_IDF_TARGET_ESP32S2
#include "esp_system.h"
#include "esp_intr_alloc.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/rtc_io.h"
#include "driver/gpio.h"
#include "unity.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "test_utils.h"
#include "soc/soc.h"
#include "soc/spi_reg.h"
#include "soc/adc_periph.h"
#include "soc/dac_periph.h"
#include "soc/spi_periph.h"
#include "test/test_common_adc.h"
#include "driver/dac.h"
#include "soc/system_reg.h"
#include "esp32s2/rom/lldesc.h"
#include "test/test_adc_dac_dma.h"
static const char *TAG = "test_adc";
#define PLATFORM_SELECT (1) //0: pxp; 1: chip
#if (PLATFORM_SELECT == 0) //PXP platform
#include "soc/syscon_reg.h"
#define SET_BREAK_POINT(flag) REG_WRITE(SYSCON_DATE_REG, flag)
//PXP clk is slower.
#define SYS_DELAY_TIME_MOM (1/40)
#define RTC_SLOW_CLK_FLAG 1 // Slow clock is 32KHz.
static void test_pxp_deinit_io(void)
{
for (int i = 0; i < 22; i++) {
rtc_gpio_init(i);
}
}
#else
//PXP clk is slower.
#define SET_BREAK_POINT(flag)
#define SYS_DELAY_TIME_MOM (1)
#define RTC_SLOW_CLK_FLAG 0 // Slow clock is 32KHz.
#endif
#define SAR_SIMPLE_NUM 512 // Set out number of enabled unit.
typedef struct dma_msg {
uint32_t int_msk;
uint8_t *data;
uint32_t data_len;
} dac_dma_event_t;
static QueueHandle_t que_dac = NULL;
static uint8_t link_buf[2][SAR_SIMPLE_NUM*2] = {0};
static lldesc_t dma1 = {0};
static lldesc_t dma2 = {0};
/*******************************************/
/** DAC-DMA INIT CODE */
/*******************************************/
/**
* DMA liner initialization and start.
* @param is_loop
* - true: The two dma linked lists are connected end to end, with no end mark (eof).
* - false: The two dma linked lists are connected end to end, with end mark (eof).
* @param int_mask DMA interrupt types.
*/
uint32_t dac_dma_linker_init(bool is_alter, bool is_loop)
{
/* The DAC output is a sawtooth wave. */
if (is_alter) {
for(int i=0; i<SAR_SIMPLE_NUM*2; i++) {
if(i%2){
link_buf[0][i] = i%256;
}else{
link_buf[0][i] = 256-i%256;
}
if(i%2){
link_buf[1][i] = i%256;
}else{
link_buf[1][i] = 256-i%256;
}
}
} else {
for(int i=0; i<SAR_SIMPLE_NUM; i++) {
link_buf[0][i] = i%256;
link_buf[1][i] = i%256;
}
}
dma1 = (lldesc_t) {
.size = (is_alter) ? SAR_SIMPLE_NUM*2 : SAR_SIMPLE_NUM,
.length = (is_alter) ? SAR_SIMPLE_NUM*2 : SAR_SIMPLE_NUM,
.eof = 0,
.owner = 1,
.buf = &link_buf[0][0],
.qe.stqe_next = &dma2,
};
dma2 = (lldesc_t) {
.size = (is_alter) ? SAR_SIMPLE_NUM*2 : SAR_SIMPLE_NUM,
.length = (is_alter) ? SAR_SIMPLE_NUM*2 : SAR_SIMPLE_NUM,
.owner = 1,
.buf = &link_buf[1][0],
};
if (is_loop) {
dma2.eof = 0;
dma2.qe.stqe_next = &dma1;
} else {
dma2.eof = 1;
dma2.qe.stqe_next = NULL;
}
return (uint32_t)&dma1;
}
/** ADC-DMA ISR handler. */
static IRAM_ATTR void dac_dma_isr(void * arg)
{
uint32_t int_st = REG_READ(SPI_DMA_INT_ST_REG(3));
int task_awoken = pdFALSE;
dac_dma_event_t adc_evt;
adc_evt.int_msk = int_st;
REG_WRITE(SPI_DMA_INT_CLR_REG(3), int_st);
xQueueSendFromISR(que_dac, &adc_evt, &task_awoken);
ESP_EARLY_LOGV(TAG, "int msk%x, raw%x", int_st, REG_READ(SPI_DMA_INT_RAW_REG(3)));
if (task_awoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}
/**
* Testcase: Check the interrupt types of DAC-DMA.
*/
void test_dac_dig_dma_intr_check(dac_digi_convert_mode_t mode)
{
ESP_LOGI(TAG, " >> %s - dac mode %d<< ", __func__, mode);
dac_dma_event_t evt;
dac_digi_init();
const dac_digi_config_t cfg = {
.mode = mode,
.interval = 100,
.dig_clk.use_apll = false, // APB clk
.dig_clk.div_num = 79,
.dig_clk.div_b = 1,
.dig_clk.div_a = 0,
};
dac_digi_controller_config(&cfg);
dac_output_enable(DAC_CHANNEL_1);
dac_output_enable(DAC_CHANNEL_2);
/* DAC-DMA linker init */
if (que_dac == NULL) {
que_dac = xQueueCreate(5, sizeof(dac_dma_event_t));
} else {
xQueueReset(que_dac);
}
uint32_t int_mask = SPI_OUT_DONE_INT_ENA | SPI_OUT_EOF_INT_ENA | SPI_OUT_TOTAL_EOF_INT_ENA;
uint32_t dma_addr = dac_dma_linker_init(mode, false);
adc_dac_dma_isr_register(dac_dma_isr, NULL, int_mask);
adc_dac_dma_linker_start(DMA_ONLY_DAC_OUTLINK, (void *)dma_addr, int_mask);
/* ADC-DMA start output */
dac_digi_start();
/* Check interrupt type */
while (int_mask) {
TEST_ASSERT_EQUAL( xQueueReceive(que_dac, &evt, 2000 / portTICK_PERIOD_MS), pdTRUE );
ESP_LOGI(TAG, "DAC-DMA intr type 0x%x", evt.int_msk);
if (evt.int_msk & int_mask) {
int_mask &= (~evt.int_msk);
}
}
ESP_LOGI(TAG, "DAC-DMA intr test over");
adc_dac_dma_linker_deinit();
adc_dac_dma_isr_deregister(dac_dma_isr, NULL);
TEST_ESP_OK( dac_digi_deinit() );
}
TEST_CASE("DAC-DMA interrupt test", "[dac]")
{
test_dac_dig_dma_intr_check(DAC_CONV_NORMAL);
test_dac_dig_dma_intr_check(DAC_CONV_ALTER);
}
/*******************************************/
/** SPI DMA INIT CODE */
/*******************************************/
#include "sys/queue.h"
static bool adc_dac_dma_isr_flag = false;
/*---------------------------------------------------------------
INTERRUPT HANDLER
---------------------------------------------------------------*/
typedef struct adc_dac_dma_isr_handler_ {
uint32_t mask;
intr_handler_t handler;
void* handler_arg;
SLIST_ENTRY(adc_dac_dma_isr_handler_) next;
} adc_dac_dma_isr_handler_t;
static SLIST_HEAD(adc_dac_dma_isr_handler_list_, adc_dac_dma_isr_handler_) s_adc_dac_dma_isr_handler_list =
SLIST_HEAD_INITIALIZER(s_adc_dac_dma_isr_handler_list);
portMUX_TYPE s_isr_handler_list_lock = portMUX_INITIALIZER_UNLOCKED;
static intr_handle_t s_adc_dac_dma_isr_handle;
static IRAM_ATTR void adc_dac_dma_isr_default(void* arg)
{
uint32_t status = REG_READ(SPI_DMA_INT_ST_REG(3));
adc_dac_dma_isr_handler_t* it;
portENTER_CRITICAL_ISR(&s_isr_handler_list_lock);
SLIST_FOREACH(it, &s_adc_dac_dma_isr_handler_list, next) {
if (it->mask & status) {
portEXIT_CRITICAL_ISR(&s_isr_handler_list_lock);
(*it->handler)(it->handler_arg);
portENTER_CRITICAL_ISR(&s_isr_handler_list_lock);
}
}
portEXIT_CRITICAL_ISR(&s_isr_handler_list_lock);
REG_WRITE(SPI_DMA_INT_CLR_REG(3), status);
}
static esp_err_t adc_dac_dma_isr_ensure_installed(void)
{
esp_err_t err = ESP_OK;
portENTER_CRITICAL(&s_isr_handler_list_lock);
if (s_adc_dac_dma_isr_handle) {
goto out;
}
REG_WRITE(SPI_DMA_INT_ENA_REG(3), 0);
REG_WRITE(SPI_DMA_INT_CLR_REG(3), UINT32_MAX);
err = esp_intr_alloc(ETS_SPI3_DMA_INTR_SOURCE, 0, &adc_dac_dma_isr_default, NULL, &s_adc_dac_dma_isr_handle);
if (err != ESP_OK) {
goto out;
}
out:
portEXIT_CRITICAL(&s_isr_handler_list_lock);
return err;
}
esp_err_t adc_dac_dma_isr_register(intr_handler_t handler, void* handler_arg, uint32_t intr_mask)
{
esp_err_t err = adc_dac_dma_isr_ensure_installed();
if (err != ESP_OK) {
return err;
}
adc_dac_dma_isr_handler_t* item = malloc(sizeof(*item));
if (item == NULL) {
return ESP_ERR_NO_MEM;
}
item->handler = handler;
item->handler_arg = handler_arg;
item->mask = intr_mask;
portENTER_CRITICAL(&s_isr_handler_list_lock);
SLIST_INSERT_HEAD(&s_adc_dac_dma_isr_handler_list, item, next);
portEXIT_CRITICAL(&s_isr_handler_list_lock);
return ESP_OK;
}
esp_err_t adc_dac_dma_isr_deregister(intr_handler_t handler, void* handler_arg)
{
adc_dac_dma_isr_handler_t* it;
adc_dac_dma_isr_handler_t* prev = NULL;
bool found = false;
portENTER_CRITICAL(&s_isr_handler_list_lock);
SLIST_FOREACH(it, &s_adc_dac_dma_isr_handler_list, next) {
if (it->handler == handler && it->handler_arg == handler_arg) {
if (it == SLIST_FIRST(&s_adc_dac_dma_isr_handler_list)) {
SLIST_REMOVE_HEAD(&s_adc_dac_dma_isr_handler_list, next);
} else {
SLIST_REMOVE_AFTER(prev, next);
}
found = true;
free(it);
break;
}
prev = it;
}
portEXIT_CRITICAL(&s_isr_handler_list_lock);
return found ? ESP_OK : ESP_ERR_INVALID_STATE;
}
void adc_dac_dma_linker_start(spi_dma_link_type_t type, void *dma_addr, uint32_t int_msk)
{
REG_SET_BIT(DPORT_PERIP_CLK_EN_REG, DPORT_APB_SARADC_CLK_EN_M);
REG_SET_BIT(DPORT_PERIP_CLK_EN_REG, DPORT_SPI3_DMA_CLK_EN_M);
REG_SET_BIT(DPORT_PERIP_CLK_EN_REG, DPORT_SPI3_CLK_EN);
REG_CLR_BIT(DPORT_PERIP_RST_EN_REG, DPORT_SPI3_DMA_RST_M);
REG_CLR_BIT(DPORT_PERIP_RST_EN_REG, DPORT_SPI3_RST_M);
REG_WRITE(SPI_DMA_INT_CLR_REG(3), 0xFFFFFFFF);
REG_WRITE(SPI_DMA_INT_ENA_REG(3), int_msk | REG_READ(SPI_DMA_INT_ENA_REG(3)));
if (type & DMA_ONLY_ADC_INLINK) {
REG_SET_BIT(SPI_DMA_IN_LINK_REG(3), SPI_INLINK_STOP);
REG_CLR_BIT(SPI_DMA_IN_LINK_REG(3), SPI_INLINK_START);
SET_PERI_REG_BITS(SPI_DMA_IN_LINK_REG(3), SPI_INLINK_ADDR, (uint32_t)dma_addr, 0);
REG_SET_BIT(SPI_DMA_CONF_REG(3), SPI_IN_RST);
REG_CLR_BIT(SPI_DMA_CONF_REG(3), SPI_IN_RST);
REG_CLR_BIT(SPI_DMA_IN_LINK_REG(3), SPI_INLINK_STOP);
REG_SET_BIT(SPI_DMA_IN_LINK_REG(3), SPI_INLINK_START);
}
if (type & DMA_ONLY_DAC_OUTLINK) {
REG_SET_BIT(SPI_DMA_OUT_LINK_REG(3), SPI_OUTLINK_STOP);
REG_CLR_BIT(SPI_DMA_OUT_LINK_REG(3), SPI_OUTLINK_START);
SET_PERI_REG_BITS(SPI_DMA_OUT_LINK_REG(3), SPI_OUTLINK_ADDR, (uint32_t)dma_addr, 0);
REG_SET_BIT(SPI_DMA_CONF_REG(3), SPI_OUT_RST);
REG_CLR_BIT(SPI_DMA_CONF_REG(3), SPI_OUT_RST);
REG_CLR_BIT(SPI_DMA_OUT_LINK_REG(3), SPI_OUTLINK_STOP);
REG_SET_BIT(SPI_DMA_OUT_LINK_REG(3), SPI_OUTLINK_START);
}
}
void adc_dac_dma_linker_stop(spi_dma_link_type_t type)
{
if (type & DMA_ONLY_ADC_INLINK) {
REG_SET_BIT(SPI_DMA_IN_LINK_REG(3), SPI_INLINK_STOP);
REG_CLR_BIT(SPI_DMA_IN_LINK_REG(3), SPI_INLINK_START);
}
if (type & DMA_ONLY_DAC_OUTLINK) {
REG_SET_BIT(SPI_DMA_OUT_LINK_REG(3), SPI_OUTLINK_STOP);
REG_CLR_BIT(SPI_DMA_OUT_LINK_REG(3), SPI_OUTLINK_START);
}
}
void adc_dac_dma_linker_deinit(void)
{
adc_dac_dma_linker_stop(DMA_BOTH_ADC_DAC);
REG_WRITE(SPI_DMA_INT_CLR_REG(3), 0xFFFFFFFF);
REG_WRITE(SPI_DMA_INT_ENA_REG(3), 0);
adc_dac_dma_isr_flag = false;
}
/*******************************************/
/** SPI DMA INIT CODE END */
/*******************************************/
#endif // CONFIG_IDF_TARGET_ESP32S2

View File

@ -0,0 +1,18 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(dac_test)
if(CONFIG_COMPILER_DUMP_RTL_FILES)
add_custom_target(check_test_app_sections ALL
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
--rtl-dir ${CMAKE_BINARY_DIR}/esp-idf/driver/
--elf-file ${CMAKE_BINARY_DIR}/dac_test.elf
find-refs
--from-sections=.iram0.text
--to-sections=.flash.text,.flash.rodata
--exit-code
DEPENDS ${elf}
)
endif()

View File

@ -0,0 +1,2 @@
| Supported Targets | ESP32 | ESP32-S2 |
| ----------------- | ----- | -------- |

View File

@ -0,0 +1,11 @@
set(srcs "test_app_main.c"
"test_dac.c")
if(CONFIG_DAC_ISR_IRAM_SAFE)
list(APPEND srcs "test_dac_iram.c")
endif()
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
# the component can be registered as WHOLE_ARCHIVE
idf_component_register(SRCS ${srcs}
WHOLE_ARCHIVE)

View File

@ -0,0 +1,54 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "unity.h"
#include "unity_test_runner.h"
#include "esp_heap_caps.h"
// Some resources are lazy allocated in gpio/dedicated_gpio/delta_sigma driver, the threshold is left for that case
#define TEST_MEMORY_LEAK_THRESHOLD (-400)
static size_t before_free_8bit;
static size_t before_free_32bit;
static void check_leak(size_t before_free, size_t after_free, const char *type)
{
ssize_t delta = after_free - before_free;
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
}
void setUp(void)
{
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
}
void tearDown(void)
{
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
check_leak(before_free_8bit, after_free_8bit, "8BIT");
check_leak(before_free_32bit, after_free_32bit, "32BIT");
}
void app_main(void)
{
// ____ _ ____ _____ _
// | _ \ / \ / ___| |_ _|__ ___| |_
// | | | |/ _ \| | | |/ _ \/ __| __|
// | |_| / ___ \ |___ | | __/\__ \ |_
// |____/_/ \_\____| |_|\___||___/\__|
printf(" ____ _ ____ _____ _ \n");
printf(" | _ \\ / \\ / ___| |_ _|__ ___| |_ \n");
printf(" | | | |/ _ \\| | | |/ _ \\/ __| __|\n");
printf(" | |_| / ___ \\ |___ | | __/\\__ \\ |_ \n");
printf(" |____/_/ \\_\\____| |_|\\___||___/\\__|\n");
unity_run_menu();
}

View File

@ -0,0 +1,418 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <inttypes.h>
#include <stdio.h>
#include "unity.h"
#include "unity_test_utils.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"
// Following headers are used to test the conversion frequency
#include "soc/i2s_periph.h"
#include "hal/gpio_hal.h"
#include "driver/pulse_cnt.h"
#include "soc/pcnt_periph.h"
#elif CONFIG_IDF_TARGET_ESP32S2
#include "esp_private/spi_common_internal.h"
#endif
#if CONFIG_IDF_TARGET_ESP32
#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 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]")
{
/* 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,
.atten = DAC_COSINE_ATTEN_DEFAULT,
.flags.force_set_freq = false,
};
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 */
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 */
#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_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_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_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,
};
size_t len = 1024;
uint8_t buf[len];
for (int i = 0; i < len; i++) {
buf[i] = i % 256;
}
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_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);
TEST_ASSERT(initial_size == esp_get_free_heap_size());
}
TEST_CASE("DAC_set_voltage_test", "[dac]")
{
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 */
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_oneshot_output_voltage(oneshot_chan0_handle, i));
vTaskDelay(pdMS_TO_TICKS(20));
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_del_oneshot_channel(oneshot_chan0_handle));
TEST_ESP_OK(adc_oneshot_del_unit(adc2_handle));
}
TEST_CASE("DAC_dma_write_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,
};
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_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
* The frequency test is currently only supported on ESP32
* because there is no such signal to monitor on ESP32-S2 */
#if CONFIG_IDF_TARGET_ESP32
TEST_CASE("DAC_dma_conver_frequency_test", "[dac]")
{
/* Prepare configuration for the PCNT unit */
pcnt_unit_handle_t pcnt_unit = NULL;
pcnt_channel_handle_t pcnt_chan = NULL;
pcnt_unit_config_t unit_config = {
.high_limit = (int16_t)0x7fff,
.low_limit = (int16_t)0x8000,
};
pcnt_chan_config_t chan_config = {
.edge_gpio_num = GPIO_NUM_4,
.level_gpio_num = -1,
};
TEST_ESP_OK(pcnt_new_unit(&unit_config, &pcnt_unit));
TEST_ESP_OK(pcnt_unit_set_glitch_filter(pcnt_unit, NULL));
TEST_ESP_OK(pcnt_new_channel(pcnt_unit, &chan_config, &pcnt_chan));
TEST_ESP_OK(pcnt_channel_set_edge_action(pcnt_chan, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD));
TEST_ESP_OK(pcnt_channel_set_level_action(pcnt_chan, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_KEEP));
TEST_ESP_OK(pcnt_unit_enable(pcnt_unit));
// Connect the clock signal to pcnt input signal
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[GPIO_NUM_4], PIN_FUNC_GPIO);
gpio_set_direction(GPIO_NUM_4, GPIO_MODE_INPUT_OUTPUT);
// The DAC conversion frequency is equal to I2S bclk.
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);
size_t len = 800;
uint8_t data[len];
for (int i = 0; i < len; i++) {
data[i] = i % 256;
}
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,
};
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;
/* Count pulse by PCNT */
TEST_ESP_OK(pcnt_unit_clear_count(pcnt_unit));
TEST_ESP_OK(pcnt_unit_start(pcnt_unit));
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 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);
conti_cfg.clk_src = DAC_DIGI_CLK_SRC_APLL;
/* Initialize DAC to test APLL clock */
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_conti_write_cyclically(conti_handle, data, len, NULL));
/* Count pulse by PCNT */
TEST_ESP_OK(pcnt_unit_clear_count(pcnt_unit));
TEST_ESP_OK(pcnt_unit_start(pcnt_unit));
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_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 PCNT */
TEST_ESP_OK(pcnt_del_channel(pcnt_chan));
TEST_ESP_OK(pcnt_unit_stop(pcnt_unit));
TEST_ESP_OK(pcnt_unit_disable(pcnt_unit));
TEST_ESP_OK(pcnt_del_unit(pcnt_unit));
}
#endif
TEST_CASE("DAC_cosine_wave_test", "[dac]")
{
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,
.atten = DAC_COSINE_ATTEN_DEFAULT,
.flags.force_set_freq = false,
};
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));
// TODO: find some more meaningful way to test cosine wave
vTaskDelay(pdMS_TO_TICKS(500));
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 void dac_cyclically_write_task(void *arg)
{
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;
}
printf("Write cyclically\n");
TEST_ESP_OK(dac_conti_write_cyclically(dac_handle, buf, len, NULL));
vTaskDelay(pdMS_TO_TICKS(200));
}
vTaskDelete(NULL);
}
static void dac_continuously_write_task(void *arg)
{
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));
}
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));
}

View File

@ -0,0 +1,110 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include "unity.h"
#include "unity_test_utils.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
typedef struct {
int cnt;
bool result;
} test_dac_intr_data_t;
static void IRAM_ATTR test_dac_direct_set_safety(void *usr_ctx)
{
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(void *usr_ctx)
{
uint8_t *data = (uint8_t *)usr_ctx;
for (int i = 0; i < BUF_SIZE; i++) {
data[i] = i % 128 + 1;
}
}
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_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);
TEST_ASSERT(data);
for (int i = 0; i < BUF_SIZE; i++) {
data[i] = i % 256;
}
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_conti_disable(conti_handle));
TEST_ESP_OK(dac_del_conti_channels(conti_handle));
free(data);
}

View File

@ -0,0 +1,22 @@
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32
@pytest.mark.esp32s2
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'iram_safe',
'release',
],
indirect=True,
)
def test_dac(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('*')
dut.expect_unity_test_output()

View File

@ -0,0 +1,4 @@
CONFIG_COMPILER_DUMP_RTL_FILES=y
CONFIG_DAC_ISR_IRAM_SAFE=y
CONFIG_DAC_CTRL_FUNC_IN_IRAM=y
CONFIG_COMPILER_OPTIMIZATION_NONE=y

View File

@ -0,0 +1,5 @@
CONFIG_PM_ENABLE=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y

View File

@ -0,0 +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_OUTPUT=n

View File

@ -0,0 +1,18 @@
# This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(dac_legacy_test)
if(CONFIG_COMPILER_DUMP_RTL_FILES)
add_custom_target(check_test_app_sections ALL
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
--rtl-dir ${CMAKE_BINARY_DIR}/esp-idf/driver/
--elf-file ${CMAKE_BINARY_DIR}/dac_legacy_test.elf
find-refs
--from-sections=.iram0.text
--to-sections=.flash.text,.flash.rodata
--exit-code
DEPENDS ${elf}
)
endif()

View File

@ -0,0 +1,2 @@
| Supported Targets | ESP32 | ESP32-S2 |
| ----------------- | ----- | -------- |

View File

@ -0,0 +1,7 @@
set(srcs "test_app_main.c"
"test_legacy_dac.c")
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
# the component can be registered as WHOLE_ARCHIVE
idf_component_register(SRCS ${srcs}
WHOLE_ARCHIVE)

View File

@ -0,0 +1,53 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "unity.h"
#include "unity_test_runner.h"
#include "esp_heap_caps.h"
// Some resources are lazy allocated in dac driver, the threshold is left for that case
#define TEST_MEMORY_LEAK_THRESHOLD (-300)
static size_t before_free_8bit;
static size_t before_free_32bit;
static void check_leak(size_t before_free, size_t after_free, const char *type)
{
ssize_t delta = after_free - before_free;
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
}
void setUp(void)
{
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
}
void tearDown(void)
{
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
check_leak(before_free_8bit, after_free_8bit, "8BIT");
check_leak(before_free_32bit, after_free_32bit, "32BIT");
}
void app_main(void)
{
// ____ _ ____ _____ _
// | _ \ / \ / ___| |_ _|__ ___| |_
// | | | |/ _ \| | | |/ _ \/ __| __|
// | |_| / ___ \ |___ | | __/\__ \ |_
// |____/_/ \_\____| |_|\___||___/\__|
printf(" ____ _ ____ _____ _ \n");
printf(" | _ \\ / \\ / ___| |_ _|__ ___| |_ \n");
printf(" | | | |/ _ \\| | | |/ _ \\/ __| __|\n");
printf(" | |_| / ___ \\ |___ | | __/\\__ \\ |_ \n");
printf(" |____/_/ \\_\\____| |_|\\___||___/\\__| (legacy)\n");
unity_run_menu();
}

View File

@ -6,20 +6,20 @@
/*
Tests for the dac device driver
*/
#include <inttypes.h>
#include "esp_system.h"
#include "unity.h"
#include "unity_test_utils.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "test_utils.h"
#include "soc/soc_caps.h"
#define CONFIG_ADC_SUPPRESS_DEPRECATE_WARN 1
#include "driver/adc.h"
#if SOC_DAC_SUPPORTED
#include "driver/dac.h"
#include "esp_adc_cal.h"
@ -34,10 +34,10 @@ static const char *TAG = "test_dac";
#if CONFIG_IDF_TARGET_ESP32
#define ADC_TEST_CHANNEL_NUM ADC2_CHANNEL_8 // GPIO25
#define DAC_TEST_CHANNEL_NUM DAC_CHANNEL_1 // GPIO25
#define DAC_TEST_CHANNEL_NUM DAC_CHAN_0 // GPIO25
#elif CONFIG_IDF_TARGET_ESP32S2
#define ADC_TEST_CHANNEL_NUM ADC2_CHANNEL_6 // GPIO17
#define DAC_TEST_CHANNEL_NUM DAC_CHANNEL_1 // GPIO17
#define DAC_TEST_CHANNEL_NUM DAC_CHAN_0 // GPIO17
#endif
#define DAC_OUT_MAX (200)
@ -46,7 +46,7 @@ static const char *TAG = "test_dac";
#define DAC_TEST_TIMES (100)
TEST_CASE("DAC output (RTC) check by adc", "[dac]")
TEST_CASE("DAC_output(RTC)_check_by_adc", "[dac_legacy]")
{
gpio_num_t adc_gpio_num, dac_gpio_num;
@ -72,7 +72,7 @@ TEST_CASE("DAC output (RTC) check by adc", "[dac]")
output_data += DAC_OUT_STEP;
vTaskDelay(2 * portTICK_PERIOD_MS);
TEST_ESP_OK( adc2_get_raw( ADC_TEST_CHANNEL_NUM, ADC_TEST_WIDTH, &read_raw) );
ESP_LOGI(TAG, "DAC%d - ADC%d", output_data, read_raw);
ESP_LOGI(TAG, "DAC: %d - ADC: %d", output_data, read_raw);
if (read_old != 0) {
TEST_ASSERT_GREATER_THAN(read_old, read_raw);
}
@ -81,7 +81,7 @@ TEST_CASE("DAC output (RTC) check by adc", "[dac]")
TEST_ESP_OK( dac_output_disable( DAC_TEST_CHANNEL_NUM ) );
}
TEST_CASE("DAC cw generator output (RTC) check by adc", "[dac]")
TEST_CASE("DAC_cw_generator_output(RTC)_check_by_adc", "[dac_legacy]")
{
gpio_num_t adc_gpio_num, dac_gpio_num;
@ -118,10 +118,6 @@ TEST_CASE("DAC cw generator output (RTC) check by adc", "[dac]")
vTaskDelay(10 * portTICK_PERIOD_MS);
TEST_ESP_OK( adc2_get_raw( ADC_TEST_CHANNEL_NUM, ADC_TEST_WIDTH, &read_raw[0]) );
ESP_LOGI(TAG, "ADC: %d", read_raw[0]);
/* Should open after dac cali. */
// if (read_raw[0] == read_raw[1]) {
// TEST_ASSERT_NOT_EQUAL(read_raw[1], read_raw[2]);
// }
read_raw[2] = read_raw[1];
read_raw[1] = read_raw[0];
}
@ -146,7 +142,7 @@ static bool subtest_adc_dac(int mV_ref, esp_adc_cal_characteristics_t * chars)
return true;
}
TEST_CASE("esp32s2 adc2-dac with adc2 calibration", "[adc-dac]")
TEST_CASE("esp32s2_adc2-dac_with_adc2_calibration", "[dac_legacy]")
{
gpio_num_t adc_gpio_num, dac_gpio_num;
if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) != ESP_OK) {
@ -163,29 +159,27 @@ TEST_CASE("esp32s2 adc2-dac with adc2 calibration", "[adc-dac]")
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);
}
#endif
#endif // SOC_DAC_SUPPORTED

View File

@ -0,0 +1,21 @@
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32
@pytest.mark.esp32s2
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'release',
],
indirect=True,
)
def test_legacy_dac(dut: Dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests')
dut.write('*')
dut.expect_unity_test_output()

View File

@ -0,0 +1,5 @@
CONFIG_PM_ENABLE=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y

View File

@ -0,0 +1,4 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT=n
CONFIG_ADC_DISABLE_DAC=n
CONFIG_DAC_SUPPRESS_DEPRECATE_WARN=y

View File

@ -14,6 +14,7 @@
#include "driver/adc.h"
#include "driver/rtc_io.h"
#include "driver/gpio.h"
#include "soc/adc_periph.h"
#include "unity.h"
#include "esp_system.h"
#include "esp_event.h"
@ -21,8 +22,6 @@
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_rom_sys.h"
#include "driver/dac.h"
#include "soc/adc_periph.h"
/*
* ADC DMA testcase

View File

@ -1,4 +1,5 @@
CONFIG_I2S_SUPPRESS_DEPRECATE_WARN=y
CONFIG_ADC_SUPPRESS_DEPRECATE_WARN=y
CONFIG_DAC_SUPPRESS_DEPRECATE_WARN=y
CONFIG_I2S_ENABLE_DEBUG_LOG=y
CONFIG_ESP_TASK_WDT=n

View File

@ -103,7 +103,6 @@ if(NOT BOOTLOADER_BUILD)
if(${target} STREQUAL "esp32")
list(APPEND srcs
"dac_hal.c"
"sdio_slave_hal.c"
"touch_sensor_hal.c"
"aes_hal.c"
@ -114,7 +113,6 @@ if(NOT BOOTLOADER_BUILD)
if(${target} STREQUAL "esp32s2")
list(APPEND srcs
"dac_hal.c"
"spi_flash_hal_gpspi.c"
"spi_slave_hd_hal.c"
"touch_sensor_hal.c"
@ -125,7 +123,6 @@ if(NOT BOOTLOADER_BUILD)
"esp32s2/brownout_hal.c"
"esp32s2/cp_dma_hal.c"
"esp32s2/touch_sensor_hal.c"
"esp32s2/dac_hal.c"
"usb_dwc_hal.c")
endif()

View File

@ -158,19 +158,19 @@ 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_CHANNEL_1); // 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_CHANNEL_2);
dac_ll_power_down(DAC_CHAN_1);
}
}
#elif CONFIG_IDF_TARGET_ESP32S2
if (hal->unit == ADC_UNIT_2) {
if (channel == ADC_CHANNEL_6) {
dac_ll_power_down(DAC_CHANNEL_1); // 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_CHANNEL_2);
dac_ll_power_down(DAC_CHAN_1);
}
}
#else

View File

@ -1,24 +0,0 @@
// 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.
#include "hal/dac_hal.h"
void dac_hal_cw_generator_config(dac_cw_config_t *cw)
{
dac_ll_cw_set_freq(cw->freq);
dac_ll_cw_set_scale(cw->en_ch, cw->scale);
dac_ll_cw_set_phase(cw->en_ch, cw->phase);
dac_ll_cw_set_dc_offset(cw->en_ch, cw->offset);
dac_ll_cw_set_channel(cw->en_ch, true);
}

View File

@ -23,6 +23,9 @@
extern "C" {
#endif
#define DAC_LL_CW_PHASE_0 0x02
#define DAC_LL_CW_PHASE_180 0x03
/**
* Power on dac module and start output voltage.
*
@ -53,12 +56,13 @@ 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_CHANNEL_1) {
if (channel == DAC_CHAN_0) {
SENS.sar_dac_ctrl2.dac_cw_en1 = 0;
HAL_FORCE_MODIFY_U32_REG_FIELD(RTCIO.pad_dac[channel], dac, value);
} else if (channel == DAC_CHANNEL_2) {
} else if (channel == DAC_CHAN_1) {
SENS.sar_dac_ctrl2.dac_cw_en2 = 0;
HAL_FORCE_MODIFY_U32_REG_FIELD(RTCIO.pad_dac[channel], dac, value);
}
@ -101,11 +105,11 @@ 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_CHANNEL_1) {
if (channel == DAC_CHAN_0) {
SENS.sar_dac_ctrl2.dac_cw_en1 = enable;
} else if (channel == DAC_CHANNEL_2) {
} else if (channel == DAC_CHAN_1) {
SENS.sar_dac_ctrl2.dac_cw_en2 = enable;
}
}
@ -114,11 +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) ~ 55000(100KHz).
* @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)
static inline void dac_ll_cw_set_freq(uint32_t freq, uint32_t rtc8m_freq)
{
uint32_t sw_freq = freq * 0xFFFF / SOC_CLK_RC_FAST_FREQ_APPROX;
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);
}
@ -126,14 +131,18 @@ static inline void dac_ll_cw_set_freq(uint32_t 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, dac_cw_scale_t scale)
static inline void dac_ll_cw_set_atten(dac_channel_t channel, dac_cosine_atten_t atten)
{
if (channel == DAC_CHANNEL_1) {
SENS.sar_dac_ctrl2.dac_scale1 = scale;
} else if (channel == DAC_CHANNEL_2) {
SENS.sar_dac_ctrl2.dac_scale2 = scale;
if (channel == DAC_CHAN_0) {
SENS.sar_dac_ctrl2.dac_scale1 = atten;
} else if (channel == DAC_CHAN_1) {
SENS.sar_dac_ctrl2.dac_scale2 = atten;
}
}
@ -141,13 +150,13 @@ static inline void dac_ll_cw_set_scale(dac_channel_t channel, dac_cw_scale_t sca
* Set the phase of the cosine wave generator output.
*
* @param channel DAC channel num.
* @param scale Phase value.
* @param phase Phase value. 0: 0x02 180: 0x03.
*/
static inline void dac_ll_cw_set_phase(dac_channel_t channel, dac_cw_phase_t phase)
static inline void dac_ll_cw_set_phase(dac_channel_t channel, dac_cosine_phase_t phase)
{
if (channel == DAC_CHANNEL_1) {
if (channel == DAC_CHAN_0) {
SENS.sar_dac_ctrl2.dac_inv1 = phase;
} else if (channel == DAC_CHANNEL_2) {
} else if (channel == DAC_CHAN_1) {
SENS.sar_dac_ctrl2.dac_inv2 = phase;
}
}
@ -162,14 +171,14 @@ static inline void dac_ll_cw_set_phase(dac_channel_t channel, dac_cw_phase_t pha
*/
static inline void dac_ll_cw_set_dc_offset(dac_channel_t channel, int8_t offset)
{
if (channel == DAC_CHANNEL_1) {
if (SENS.sar_dac_ctrl2.dac_inv1 == DAC_CW_PHASE_180) {
offset = 0 - offset;
if (channel == DAC_CHAN_0) {
if (SENS.sar_dac_ctrl2.dac_inv1 == DAC_LL_CW_PHASE_180) {
offset = -offset;
}
HAL_FORCE_MODIFY_U32_REG_FIELD(SENS.sar_dac_ctrl2, dac_dc1, offset);
} else if (channel == DAC_CHANNEL_2) {
if (SENS.sar_dac_ctrl2.dac_inv2 == DAC_CW_PHASE_180) {
offset = 0 - offset;
} else if (channel == DAC_CHAN_1) {
if (SENS.sar_dac_ctrl2.dac_inv2 == DAC_LL_CW_PHASE_180) {
offset = -offset;
}
HAL_FORCE_MODIFY_U32_REG_FIELD(SENS.sar_dac_ctrl2, dac_dc2, offset);
}

View File

@ -39,6 +39,7 @@ extern "C" {
#define I2S_LL_EVENT_TX_EOF BIT(12)
#define I2S_LL_EVENT_RX_DSCR_ERR BIT(13)
#define I2S_LL_EVENT_TX_DSCR_ERR BIT(14)
#define I2S_LL_EVENT_TX_TEOF BIT(16)
#define I2S_INTR_MAX (UINT32_MAX)
#define I2S_LL_TX_EVENT_MASK I2S_LL_EVENT_TX_EOF

View File

@ -1,48 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// The HAL layer for ADC (esp32s2 specific part)
#include "hal/dac_hal.h"
#include "hal/adc_ll.h"
#include "hal/dac_types.h"
/*---------------------------------------------------------------
Digital controller setting
---------------------------------------------------------------*/
void dac_hal_digi_init(void)
{
dac_ll_digi_clk_inv(true);
}
void dac_hal_digi_deinit(void)
{
dac_ll_digi_trigger_output(false);
dac_ll_digi_enable_dma(false);
dac_ll_digi_fifo_reset();
dac_ll_digi_reset();
}
void dac_hal_digi_controller_config(const dac_digi_config_t *cfg)
{
dac_ll_digi_set_convert_mode(cfg->mode);
dac_ll_digi_set_trigger_interval(cfg->interval);
adc_ll_digi_controller_clk_div(cfg->dig_clk.div_num, cfg->dig_clk.div_b, cfg->dig_clk.div_a);
adc_ll_digi_clk_sel(cfg->dig_clk.use_apll);
}
void dac_hal_digi_start(void)
{
dac_ll_digi_enable_dma(true);
dac_ll_digi_trigger_output(true);
}
void dac_hal_digi_stop(void)
{
dac_ll_digi_trigger_output(false);
dac_ll_digi_enable_dma(false);
}

View File

@ -1,76 +0,0 @@
// 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.
/*******************************************************************************
* NOTICE
* The hal is not public api, don't use in application code.
* See readme.md in hal/include/hal/readme.md
******************************************************************************/
// The HAL layer for DAC (esp32s2 specific part)
#pragma once
#include "hal/dac_ll.h"
#include "hal/dac_types.h"
#include_next "hal/dac_hal.h"
#ifdef __cplusplus
extern "C" {
#endif
/*---------------------------------------------------------------
Digital controller setting
---------------------------------------------------------------*/
/**
* Digital controller initialization.
*/
void dac_hal_digi_init(void);
/**
* Digital controller deinitialization.
*/
void dac_hal_digi_deinit(void);
/**
* Setting the DAC digital controller.
*
* @param cfg Pointer to digital controller paramter.
*/
void dac_hal_digi_controller_config(const dac_digi_config_t *cfg);
/**
* DAC digital controller start output voltage.
*/
void dac_hal_digi_start(void);
/**
* DAC digital controller stop output voltage.
*/
void dac_hal_digi_stop(void);
/**
* Reset DAC digital controller FIFO.
*/
#define dac_hal_digi_fifo_reset() dac_ll_digi_fifo_reset()
/**
* Reset DAC digital controller.
*/
#define dac_hal_digi_reset() dac_ll_digi_reset()
#ifdef __cplusplus
}
#endif

View File

@ -25,6 +25,9 @@
extern "C" {
#endif
#define DAC_LL_CW_PHASE_0 0x02
#define DAC_LL_CW_PHASE_180 0x03
/*---------------------------------------------------------------
DAC common setting
---------------------------------------------------------------*/
@ -65,12 +68,13 @@ 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_CHANNEL_1) {
if (channel == DAC_CHAN_0) {
SENS.sar_dac_ctrl2.dac_cw_en1 = 0;
HAL_FORCE_MODIFY_U32_REG_FIELD(RTCIO.pad_dac[channel], dac, value);
} else if (channel == DAC_CHANNEL_2) {
} else if (channel == DAC_CHAN_1) {
SENS.sar_dac_ctrl2.dac_cw_en2 = 0;
HAL_FORCE_MODIFY_U32_REG_FIELD(RTCIO.pad_dac[channel], dac, value);
}
@ -122,11 +126,11 @@ 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_CHANNEL_1) {
if (channel == DAC_CHAN_0) {
SENS.sar_dac_ctrl2.dac_cw_en1 = enable;
} else if (channel == DAC_CHANNEL_2) {
} else if (channel == DAC_CHAN_1) {
SENS.sar_dac_ctrl2.dac_cw_en2 = enable;
}
}
@ -135,11 +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) ~ 55000(100KHz).
* @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)
static inline void dac_ll_cw_set_freq(uint32_t freq, uint32_t rtc8m_freq)
{
uint32_t sw_freq = freq * 0xFFFF / SOC_CLK_RC_FAST_FREQ_APPROX;
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);
}
@ -147,14 +152,18 @@ static inline void dac_ll_cw_set_freq(uint32_t 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, dac_cw_scale_t scale)
static inline void dac_ll_cw_set_atten(dac_channel_t channel, dac_cosine_atten_t atten)
{
if (channel == DAC_CHANNEL_1) {
SENS.sar_dac_ctrl2.dac_scale1 = scale;
} else if (channel == DAC_CHANNEL_2) {
SENS.sar_dac_ctrl2.dac_scale2 = scale;
if (channel == DAC_CHAN_0) {
SENS.sar_dac_ctrl2.dac_scale1 = atten;
} else if (channel == DAC_CHAN_1) {
SENS.sar_dac_ctrl2.dac_scale2 = atten;
}
}
@ -162,13 +171,13 @@ static inline void dac_ll_cw_set_scale(dac_channel_t channel, dac_cw_scale_t sca
* Set the phase of the cosine wave generator output.
*
* @param channel DAC channel num.
* @param scale Phase value.
* @param phase Phase value. 0: 0x02 180: 0x03.
*/
static inline void dac_ll_cw_set_phase(dac_channel_t channel, dac_cw_phase_t phase)
static inline void dac_ll_cw_set_phase(dac_channel_t channel, dac_cosine_phase_t phase)
{
if (channel == DAC_CHANNEL_1) {
if (channel == DAC_CHAN_0) {
SENS.sar_dac_ctrl2.dac_inv1 = phase;
} else if (channel == DAC_CHANNEL_2) {
} else if (channel == DAC_CHAN_1) {
SENS.sar_dac_ctrl2.dac_inv2 = phase;
}
}
@ -183,14 +192,14 @@ static inline void dac_ll_cw_set_phase(dac_channel_t channel, dac_cw_phase_t pha
*/
static inline void dac_ll_cw_set_dc_offset(dac_channel_t channel, int8_t offset)
{
if (channel == DAC_CHANNEL_1) {
if (SENS.sar_dac_ctrl2.dac_inv1 == DAC_CW_PHASE_180) {
offset = 0 - offset;
if (channel == DAC_CHAN_0) {
if (SENS.sar_dac_ctrl2.dac_inv1 == DAC_LL_CW_PHASE_180) {
offset = -offset;
}
HAL_FORCE_MODIFY_U32_REG_FIELD(SENS.sar_dac_ctrl2, dac_dc1, offset);
} else if (channel == DAC_CHANNEL_2) {
if (SENS.sar_dac_ctrl2.dac_inv2 == DAC_CW_PHASE_180) {
offset = 0 - offset;
} else if (channel == DAC_CHAN_1) {
if (SENS.sar_dac_ctrl2.dac_inv2 == DAC_LL_CW_PHASE_180) {
offset = -offset;
}
HAL_FORCE_MODIFY_U32_REG_FIELD(SENS.sar_dac_ctrl2, dac_dc2, offset);
}
@ -251,13 +260,9 @@ static inline void dac_ll_digi_trigger_output(bool enable)
*
* @param mode Conversion mode select. See ``dac_digi_convert_mode_t``.
*/
static inline void dac_ll_digi_set_convert_mode(dac_digi_convert_mode_t mode)
static inline void dac_ll_digi_set_convert_mode(bool is_alternate)
{
if (mode == DAC_CONV_NORMAL) {
APB_SARADC.apb_dac_ctrl.apb_dac_alter_mode = 0;
} else {
APB_SARADC.apb_dac_ctrl.apb_dac_alter_mode = 1;
}
APB_SARADC.apb_dac_ctrl.apb_dac_alter_mode = is_alternate;
}
/**

View File

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

View File

@ -1,78 +0,0 @@
// 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.
/*******************************************************************************
* NOTICE
* The hal is not public api, don't use in application code.
* See readme.md in hal/include/hal/readme.md
******************************************************************************/
#pragma once
#include "hal/dac_ll.h"
/**
* Power on dac module and start output voltage.
*
* @note Before powering up, make sure the DAC PAD is set to RTC PAD and floating status.
* @param channel DAC channel num.
*/
#define dac_hal_power_on(channel) dac_ll_power_on(channel)
/**
* Power done dac module and stop output voltage.
*
* @param channel DAC channel num.
*/
#define dac_hal_power_down(channel) dac_ll_power_down(channel)
/**
* Enable/disable the synchronization operation function of ADC1 and DAC.
*
* @note If enabled(default), ADC RTC controller sampling will cause the DAC channel output voltage.
*
* @param enable Enable or disable adc and dac synchronization function.
*/
#define dac_hal_rtc_sync_by_adc(enable) dac_ll_rtc_sync_by_adc(enable)
/**
* Output voltage with value (8 bit).
*
* @param channel DAC channel num.
* @param value Output value. Value range: 0 ~ 255.
* The corresponding range of voltage is 0v ~ VDD3P3_RTC.
*/
#define dac_hal_update_output_value(channel, value) dac_ll_update_output_value(channel, value)
/**
* Enable cosine wave generator output.
*/
#define dac_hal_cw_generator_enable() dac_ll_cw_generator_enable()
/**
* Disable cosine wave generator output.
*/
#define dac_hal_cw_generator_disable() dac_ll_cw_generator_disable()
/**
* Config the cosine wave generator function in DAC module.
*
* @param cw Configuration.
*/
void dac_hal_cw_generator_config(dac_cw_config_t *cw);
/**
* Enable/disable DAC output data from DMA.
*/
#define dac_hal_digi_enable_dma(enable) dac_ll_digi_enable_dma(enable)

View File

@ -1,67 +1,37 @@
#pragma once
#include "soc/soc_caps.h"
#include "hal/adc_types.h"
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
DAC_CHANNEL_1 = 0, /*!< DAC channel 1 is GPIO25(ESP32) / GPIO17(ESP32S2) */
DAC_CHANNEL_2 = 1, /*!< DAC channel 2 is GPIO26(ESP32) / GPIO18(ESP32S2) */
DAC_CHANNEL_MAX,
DAC_CHAN_0 = 0, /*!< DAC channel 0 is GPIO25(ESP32) / GPIO17(ESP32S2) */
DAC_CHAN_1 = 1, /*!< DAC channel 1 is GPIO26(ESP32) / GPIO18(ESP32S2) */
DAC_CHANNEL_1 __attribute__((deprecated("please use 'DAC_CHAN_0' instead"))) = 0, /*!< Alias of 'DAC_CHAN_0', now the channel index start from '0' */
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 multiple of the amplitude of the cosine wave generator. The max amplitude is VDD3P3_RTC.
* @brief The attenuation of the amplitude of the cosine wave generator. The max amplitude is VDD3P3_RTC.
*/
typedef enum {
DAC_CW_SCALE_1 = 0x0, /*!< 1/1. Default. */
DAC_CW_SCALE_2 = 0x1, /*!< 1/2. */
DAC_CW_SCALE_4 = 0x2, /*!< 1/4. */
DAC_CW_SCALE_8 = 0x3, /*!< 1/8. */
} dac_cw_scale_t;
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_CW_PHASE_0 = 0x2, /*!< Phase shift +0° */
DAC_CW_PHASE_180 = 0x3, /*!< Phase shift +180° */
} dac_cw_phase_t;
DAC_COSINE_PHASE_0 = 0x02, /*!< Phase shift +0° */
DAC_COSINE_PHASE_180 = 0x03, /*!< Phase shift +180° */
} dac_cosine_phase_t;
/**
* @brief Config the cosine wave generator function in DAC module.
*/
typedef struct {
dac_channel_t en_ch; /*!< Enable the cosine wave generator of DAC channel. */
dac_cw_scale_t scale; /*!< Set the amplitude of the cosine wave generator output. */
dac_cw_phase_t phase; /*!< Set the phase of the cosine wave generator output. */
uint32_t freq; /*!< Set frequency of cosine wave generator output. Range: 130(130Hz) ~ 55000(100KHz). */
int8_t offset; /*!< Set the voltage value of the DC component of the cosine wave generator output.
Note: Unreasonable settings can cause waveform to be oversaturated. Range: -128 ~ 127. */
} dac_cw_config_t;
#if CONFIG_IDF_TARGET_ESP32S2
/**
* @brief DAC digital controller (DMA mode) work mode.
*/
typedef enum {
DAC_CONV_NORMAL, /*!< The data in the DMA buffer is simultaneously output to the enable channel of the DAC. */
DAC_CONV_ALTER, /*!< The data in the DMA buffer is alternately output to the enable channel of the DAC. */
DAC_CONV_MAX
} dac_digi_convert_mode_t;
/**
* @brief DAC digital controller (DMA mode) configuration parameters.
*/
typedef struct {
dac_digi_convert_mode_t mode; /*!<DAC digital controller (DMA mode) work mode. See ``dac_digi_convert_mode_t``. */
uint32_t interval; /*!<The number of interval clock cycles for the DAC digital controller to output voltage.
The unit is the divided clock. Range: 1 ~ 4095.
Expression: `dac_output_freq` = `controller_clk` / interval. Refer to ``adc_digi_clk_t``.
Note: The sampling rate of each channel is also related to the conversion mode (See ``dac_digi_convert_mode_t``) and pattern table settings. */
adc_digi_clk_t dig_clk; /*!<DAC digital controller clock divider settings. Refer to ``adc_digi_clk_t``.
Note: The clocks of the DAC digital controller use the ADC digital controller clock divider. */
} dac_digi_config_t;
#endif //CONFIG_IDF_TARGET_ESP32S2
#ifdef __cplusplus
}
#endif

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
*/
#include "soc/dac_periph.h"
@ -18,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_CHANNEL_1_GPIO_NUM,
.dac_channel_io_num[1] = DAC_CHANNEL_2_GPIO_NUM,
.dac_channel_io_num[0] = 25,
.dac_channel_io_num[1] = 26,
};

View File

@ -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
@ -251,6 +251,10 @@ config SOC_DAC_RESOLUTION
int
default 8
config SOC_DAC_DMA_16BIT_ALIGN
bool
default y
config SOC_GPIO_PORT
int
default 1

View File

@ -277,6 +277,37 @@ typedef enum {
SDM_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB as the default clock choice */
} soc_periph_sdm_clk_src_t;
////////////////////////////////////////////////////DAC/////////////////////////////////////////////////////////////////
/**
* @brief Array initializer for all supported clock sources of DAC digital controller
*/
#define SOC_DAC_DIGI_CLKS {SOC_MOD_CLK_PLL_D2, SOC_MOD_CLK_APLL}
/**
* @brief DAC digital controller clock source
*
*/
typedef enum {
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 {SOC_MOD_CLK_RTC_FAST}
/**
* @brief DAC cosine wave generator clock source
*
*/
typedef enum {
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
}
#endif

View File

@ -1,24 +1,16 @@
// Copyright 2010-2016 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
/*
* SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// 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.
#pragma once
#ifndef _SOC_DAC_CHANNEL_H
#define _SOC_DAC_CHANNEL_H
#define DAC_GPIO25_CHANNEL DAC_CHANNEL_1
#define DAC_CHANNEL_1_GPIO_NUM 25
#define DAC_GPIO25_CHANNEL DAC_CHAN_0
#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_CHANNEL_2
#define DAC_CHANNEL_2_GPIO_NUM 26
#endif
#define DAC_GPIO26_CHANNEL DAC_CHAN_1
#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.

View File

@ -148,8 +148,9 @@
#define SOC_CPU_WATCHPOINT_SIZE 64 // bytes
/*-------------------------- DAC CAPS ----------------------------------------*/
#define SOC_DAC_PERIPH_NUM 2
#define SOC_DAC_RESOLUTION 8 // DAC resolution ratio 8 bit
#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
/*-------------------------- GPIO CAPS ---------------------------------------*/
// ESP32 has 1 GPIO peripheral

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
*/
#include "soc/dac_periph.h"
@ -18,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_CHANNEL_1_GPIO_NUM,
.dac_channel_io_num[1] = DAC_CHANNEL_2_GPIO_NUM,
.dac_channel_io_num[0] = 17,
.dac_channel_io_num[1] = 18,
};

View File

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

View File

@ -265,6 +265,37 @@ typedef enum {
SDM_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB as the default clock choice */
} soc_periph_sdm_clk_src_t;
////////////////////////////////////////////////////DAC/////////////////////////////////////////////////////////////////
/**
* @brief Array initializer for all supported clock sources of DAC digital controller
*/
#define SOC_DAC_DIGI_CLKS {SOC_MOD_CLK_APB, SOC_MOD_CLK_APLL}
/**
* @brief DAC digital controller clock source
*
*/
typedef enum {
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 {SOC_MOD_CLK_RTC_FAST}
/**
* @brief DAC cosine wave generator clock source
*
*/
typedef enum {
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
}
#endif

View File

@ -1,24 +1,15 @@
// Copyright 2010-2016 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
/*
* SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// 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.
#pragma once
#ifndef _SOC_DAC_CHANNEL_H
#define _SOC_DAC_CHANNEL_H
#define DAC_GPIO17_CHANNEL DAC_CHAN_0
#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_GPIO17_CHANNEL DAC_CHANNEL_1
#define DAC_CHANNEL_1_GPIO_NUM 17
#define DAC_GPIO18_CHANNEL DAC_CHANNEL_2
#define DAC_CHANNEL_2_GPIO_NUM 18
#endif
#define DAC_GPIO18_CHANNEL DAC_CHAN_1
#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.

View File

@ -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 ---------------------------------------*/

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;

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

View File

@ -61,7 +61,10 @@ INPUT = \
$(PROJECT_PATH)/components/bt/host/nimble/esp-hci/include/esp_nimble_hci.h \
$(PROJECT_PATH)/components/bt/include/esp32/include/esp_bt.h \
$(PROJECT_PATH)/components/console/esp_console.h \
$(PROJECT_PATH)/components/driver/include/driver/dac_common.h \
$(PROJECT_PATH)/components/driver/include/driver/dac_conti.h \
$(PROJECT_PATH)/components/driver/include/driver/dac_cosine.h \
$(PROJECT_PATH)/components/driver/include/driver/dac_oneshot.h \
$(PROJECT_PATH)/components/driver/include/driver/dac_types.h \
$(PROJECT_PATH)/components/driver/include/driver/dedic_gpio.h \
$(PROJECT_PATH)/components/driver/include/driver/gpio.h \
$(PROJECT_PATH)/components/driver/include/driver/gptimer.h \

View File

@ -1,5 +1,4 @@
INPUT += \
$(PROJECT_PATH)/components/driver/$(IDF_TARGET)/include/driver/dac.h \
$(PROJECT_PATH)/components/driver/$(IDF_TARGET)/include/driver/touch_sensor.h \
$(PROJECT_PATH)/components/esp_psram/include/esp32/himem.h \
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/dac_channel.h \

View File

@ -1,5 +1,4 @@
INPUT += \
$(PROJECT_PATH)/components/driver/$(IDF_TARGET)/include/driver/dac.h \
$(PROJECT_PATH)/components/driver/$(IDF_TARGET)/include/driver/touch_sensor.h \
$(PROJECT_PATH)/components/esp_hw_support/include/soc/esp32s2/esp_ds.h \
$(PROJECT_PATH)/components/esp_hw_support/include/soc/esp32s2/esp_hmac.h \

View File

@ -7,52 +7,135 @@ Digital To Analog Converter (DAC)
Overview
--------
{IDF_TARGET_NAME} has two 8-bit DAC (digital to analog converter) channels, connected to {IDF_TARGET_DAC_CH_1} (Channel 1) and {IDF_TARGET_DAC_CH_2} (Channel 2).
{IDF_TARGET_NAME} has two 8-bit DAC (digital to analog converter) channels, connected to {IDF_TARGET_DAC_CH_1} (Channel 1) and {IDF_TARGET_DAC_CH_2} (Channel 2). Which means each channel of DAC can convert digital value 0~255 to the analog voltage 0~Vref, the output voltage can be calculate by::
out_voltage = Vref * digi_val / 255
The DAC peripheral supports outputting analog signal in following ways:
1. Outputting a voltage directly. The DAC channel will keep outputting a specified voltage.
2. Outputting continuous analog signal by DMA. The DAC will convert the data in a buffer at the specified frequency.
3. Outputting a cosine wave by the cosine wave generator. The DAC channel can output a cosine wave with specified frequency and amplitude.
For other analog output options, see the :doc:`Sigma-delta Modulation module <sdm>` and the :doc:`LED Control module <ledc>`. Both modules produce high-frequency PWM/PDM output, which can be hardware low-pass filtered in order to generate a lower frequency analog output.
DAC File Structure
------------------
.. figure:: ../../../_static/diagrams/dac/dac_file_structure.png
:align: center
:alt: DAC file structure
**Public headers that need to be included in the DAC application**
The DAC driver allows these channels to be set to arbitrary voltages.
- ``dac.h``: The top header file of legacy DAC driver, only included in the apps which use legacy driver API
- ``dac_oneshot.h``: The top header file of new DAC driver, should be included in the apps which use the new driver API with oneshot mode.
- ``dac_cosine.h``: The top header file of new DAC driver, should be included in the apps which use the new driver API with cosine mode.
- ``dac_conti.h``: The top header file of new DAC driver, should be included in the apps which use the new driver API with continuous mode.
.. note::
The legacy driver can't coexist with the new driver. Including ``dac.h`` to use the legacy driver or ``dac_oneshot.h``, ``dac_cosine.h`` and ``dac_conti.h`` to use the new driver. The legacy driver might be removed in future.
Functional Overview
-------------------
Resources Management
^^^^^^^^^^^^^^^^^^^^
The DAC on {IDF_TARGET_NAME} has two channels, due to the software resources are separate, they could be managed by the :cpp:type:`dac_oneshot_handle_t`, :cpp:type:`dac_cosine_handle_t`:cpp:type:`dac_conti_handle_t` according to the usage. Of cause, registering different modes on a same DAC channel is not allowed.
Direct Voltage Output (One-shot/Direct Mode)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The DAC channels in the group can convert a 8-bit digital value into the analog every time calling :cpp:func:`dac_oneshot_output_voltage` (it can be called in ISR), and then the analog voltage will be kept on the DAC channel until next conversion start. To start to convert the voltage, the DAC channels need to be registered by :cpp:func:`dac_new_oneshot_channel` first, and the channel will be enabled after it is registered.
Continuous Wave Output (Continuous/DMA Mode)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
DAC channels can convert digital data continuously via the DMA. There are three ways to writing DAC data:
1. Normal writing (synchronous): It can transmit the data one time and keep blocking until all data has been loaded into the DMA buffer, and the voltage will be kept according to the last conversion value while no more data inputted, usually it is used to transport a long signal like an audio. To convert data continuously, the continuous channel handle need to be allocated by calling :cpp:func:`dac_new_conti_channels` and the DMA conversion should be enabled by :cpp:func:`dac_conti_enable`, then data can be written by :cpp:func:`dac_conti_write` synchronously. Referring to :example:`peripherals/dac/dac_continuous/dac_audio` for example.
2. Cyclical writing: It can convert a piece of data cyclically without blocking, no more operation needed after the data are loaded into the DMA buffer,but note that the inputted buffer size is limited by the descriptor number and the DMA buffer size, usually it is used to transport some short signal that need to be repeated, for example, a sine wave. To achieve cyclical writing, :cpp:func:`dac_conti_write_cyclically` can be called after the DAC continuous mode is enabled. For the cyclical writing example, please refer to :example:`peripherals/dac/dac_continuous/signal_generator`
3. Asynchronous writing: It can transmit the data asynchronously based on the event callback. Thus :cpp:member:`dac_event_callbacks_t::on_convert_done` must be registered to use asynchronous mode, and then users can get the :cpp:type:`dac_event_data_t` in the callback which contains the DMA buffer address and length, allowing user to load the data into it directly. As mentioned, to use the asynchronous writing, :cpp:func:`dac_conti_register_event_callback` need to be called to register the :cpp:member:`dac_event_callbacks_t::on_convert_done` before enabling, and then calling :cpp:func:`dac_conti_start_async_writing` to start the asynchronous writing, note that once the asynchronous writing started, the callback function will be triggered continuously, :cpp:func:`dac_conti_write_asynchronously` can help to load the data either in a separate task or the callback directly. For the asynchronous example, please refer to :example:`peripherals/dac/dac_continuous/dac_audio` as well.
.. only:: esp32
The DAC channels can also be driven with DMA-style written sample data by the digital controller, via the :doc:`I2S driver <i2s>` when using the "built-in DAC mode".
On ESP32, DAC digital controller can be connected internally to the I2S0 and use its DMA for continuous conversion. Although the DAC only needs 8-bit data for conversion, it has to be left shifted 8 bits (i.e. the high 8 bits in 16-bit slot) to satisfy the I2S communication format. But the driver can help to expand automatically, if you want to expand manually, please disable :ref:`CONFIG_DAC_DMA_AUTO_16BIT_ALIGN` in the menuconfig.
The clock of DAC digital controller comes from I2S0 as well, so there are two kinds of clock source can be selected:
- :cpp:enumerator:`dac_conti_digi_clk_src_t::DAC_DIGI_CLK_SRC_PLL_D2` can support frequency between 19.6 KHz to several MHz. It is the default clock which can also be selected by :cpp:enumerator:`dac_conti_digi_clk_src_t::DAC_DIGI_CLK_SRC_DEFAULT`.
- :cpp:enumerator:`dac_conti_digi_clk_src_t::DAC_DIGI_CLK_SRC_APLL` can support frequency between 648 Hz to several MHz, however, it might be occupied by other peripherals, then it may not provide the required frequency. But it doesn't mean APLL is not available in this case, it can still work as long as it can be divided to the target DAC DMA frequency correctly.
.. only:: esp32s2
The DAC channels can also be driven with DMA-style written sample data by the digital controller, however the driver does not supported this yet.
On ESP32-S2, DAC digital controller can be connected internally to the SPI3 and use its DMA for continuous conversion.
The clock source of DAC digital controller are:
- :cpp:enumerator:`dac_conti_digi_clk_src_t::DAC_DIGI_CLK_SRC_APB` can support frequency between 77 Hz to several MHz. It is the default clock which can also be selected by :cpp:enumerator:`dac_conti_digi_clk_src_t::DAC_DIGI_CLK_SRC_DEFAULT`.
- :cpp:enumerator:`dac_conti_digi_clk_src_t::DAC_DIGI_CLK_SRC_APLL` can support frequency between 6 Hz to several MHz, however, it might be occupied by other peripherals, then it may not provide the required frequency. But it doesn't mean APLL is not available in this case, it can still work as long as it can be divided to the target DAC DMA frequency correctly.
For other analog output options, see the :doc:`Sigma-delta Modulation module <sdm>` and the :doc:`LED Control module <ledc>`. Both these modules produce high frequency PDM/PWM output, which can be hardware low-pass filtered in order to generate a lower frequency analog output.
Cosine Wave Output (Cosine Mode)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The DAC peripheral has one cosine wave generator, it can generate cosine wave on the channels, users can specify the frequency, amplitude and phase of the cosine wave. To output the cosine wave, please acquire the DAC to cosine mode handle by :cpp:func:`dac_new_cosine_channel` first, and then start the cosine wave generator by :cpp:func:`dac_cosine_start`.
Currently, the source clock of the cosine wave generator only comes from ``RTC_FAST`` which can be chosen by :cpp:enumerator:`dac_cosine_clk_src_t::DAC_COSINE_CLK_SRC_RTC_FAST`, it is also the default clock source which is same as :cpp:enumerator:`dac_cosine_clk_src_t::DAC_COSINE_CLK_SRC_RTC_DEFAULT`.
Power Management
^^^^^^^^^^^^^^^^
When the power management is enabled (i.e. :ref:`CONFIG_PM_ENABLE` is on), the system will adjust or stop the source clock of DAC before going into light sleep, thus potentially influence to the DAC signals may lead the data conversion goes wrong.
When using DAC driver in continuous mode, it can prevent the system from changing or stopping the source clock in DMA or cosine wave mode by acquiring a power management lock. When the source clock is generated from APB, the lock type will be set to :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_APB_FREQ_MAX` and when the source clock is APLL (only in DMA mode), it will be set to :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_NO_LIGHT_SLEEP`. Whenever the DAC is converting (i.e. DMA or cosine wave generator is working), the driver will guarantee that the power management lock is acquired after calling :cpp:func:`dac_conti_enable`. Likewise, the driver will release the lock when :cpp:func:`dac_conti_disable` is called.
IRAM Safe
^^^^^^^^^
By default, the DAC DMA interrupt will be deferred when the Cache is disabled for reasons like writing/erasing Flash. Thus the DMA EOF interrupt will not get executed in time, which is not expected in a real-time application.
There's a Kconfig option :ref:`CONFIG_DAC_ISR_IRAM_SAFE` that will:
1. Enable the interrupt being serviced even when cache is disabled
2. Place driver object into DRAM (in case it's linked to PSRAM by accident)
This will allow the interrupt to run while the cache is disabled but will come at the cost of increased IRAM consumption.
Thread Safety
^^^^^^^^^^^^^
All the public DAC APIs are guaranteed to be thread safe by the driver, which means, users can call them from different RTOS tasks without protection by extra locks. Notice that DAC driver uses mutex lock to ensure the thread safety, thus the APIs except :cpp:func:`dac_oneshot_output_voltage` are not allowed to be used in ISR.
Kconfig Options
^^^^^^^^^^^^^^^
- :ref:`CONFIG_DAC_ISR_IRAM_SAFE` controls whether the default ISR handler can work when cache is disabled, see `IRAM Safe <#iram-safe>`__ for more information.
- :ref:`CONFIG_DAC_SUPPRESS_DEPRECATE_WARN` controls whether to suppress the compiling warning message while using the legacy DAC driver.
- :ref:`CONFIG_DAC_ENABLE_DEBUG_LOG` is used to enabled the debug log output. Enable this option will increase the firmware binary size.
.. only:: esp32
- :ref:`CONFIG_DAC_DMA_AUTO_16BIT_ALIGN` will auto expand the 8-bit data to 16-bit data in the driver to satisfy the I2S DMA format.
Application Example
-------------------
Setting DAC channel 1 ({IDF_TARGET_DAC_CH_1}) voltage to approx 0.78 of VDD_A voltage (VDD * 200 / 255). For VDD_A 3.3V, this is 2.59V.
.. code:: c
#include <driver/dac.h>
...
dac_output_enable(DAC_CHANNEL_1);
dac_output_voltage(DAC_CHANNEL_1, 200);
The basic examples for the ``One-shot Mode``, ``Continuous Mode`` and ``Cosine Mode`` can be found in:
- :example:`peripherals/dac/dac_oneshot`
- :example:`peripherals/dac/dac_continuous`
- :example:`peripherals/dac/dac_cosine_wave`
API Reference
-------------
.. include-build-file:: inc/dac.inc
.. include-build-file:: inc/dac_common.inc
GPIO Lookup Macros
^^^^^^^^^^^^^^^^^^
Some useful macros can be used to specified the GPIO number of a DAC channel, or vice versa.
e.g.
1. ``DAC_CHANNEL_1_GPIO_NUM`` is the GPIO number of channel 1 ({IDF_TARGET_DAC_CH_1});
2. ``DAC_{IDF_TARGET_DAC_CH_2}_CHANNEL`` is the channel number of GPIO 26 (channel 2).
.. include-build-file:: inc/dac_channel.inc
.. include-build-file:: inc/dac_types.inc
.. include-build-file:: inc/dac_oneshot.inc
.. include-build-file:: inc/dac_cosine.inc
.. include-build-file:: inc/dac_conti.inc
.. include-build-file:: inc/components/driver/include/driver/dac_types.inc
.. include-build-file:: inc/components/hal/include/hal/dac_types.inc

View File

@ -3,3 +3,47 @@ Peripherals
:link_to_translation:`zh_CN:[中文]`
.. only:: SOC_DAC_SUPPORTED
DAC
---
DAC driver has been redesigned (see :doc:`DAC API Reference <../../../api-reference/peripherals/dac>`), which aims to unify and extend the usage of DAC peripheral. Although it's recommended to use the new driver APIs, the legacy driver is still available in the previous include path ``driver/dac.h``. However, by default, including ``driver/dac.h`` will bring a build warning like `The legacy DAC driver is deprecated, please use 'driver/dac_oneshot.h', 'driver/dac_cosine.h' or 'driver/dac_conti.h' instead`. The warning can be suppressed by the Kconfig option :ref:`CONFIG_DAC_SUPPRESS_DEPRECATE_WARN`.
The major breaking changes in concept and usage are listed as follows:
Breaking Changes in Concepts
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ``dac_channel_t`` which used to identify the hardware channel are removed from user space, the channel index now starts from ``0``, please use `DAC_CHAN_0` and `DAC_CHAN_1` instead. And in the new driver, DAC channels can be seleted by :cpp:type:`dac_channel_mask_t`. And these channels can be allocated in a same channel group which is represented by :cpp:type:`dac_channels_handle_t`.
- ``dac_cw_scale_t`` is replaced by :cpp:type:`dac_cosine_atten_t`, to decupling the legacy driver and the new driver.
- ``dac_cw_phase_t`` is replaced by :cpp:type:`dac_cosine_phase_t`, the enum value is now the phase angle directly.
- ``dac_cw_config_t`` is replaced by :cpp:type:`dac_cosine_config_t`, but the ``en_ch`` field is removed because it should be specify while allocate the channel group.
.. only:: esp32s2
- ``dac_digi_convert_mode_t`` is removed. The driver now can transmit DMA data in different ways by calling :cpp:func:`dac_channels_write_continuously` or :cpp:func:`dac_channels_write_cyclically`.
- ``dac_digi_config_t`` is replaced by :cpp:type:`dac_conti_config_t`.
Breaking Changes in Usage
~~~~~~~~~~~~~~~~~~~~~~~~~
- ``dac_pad_get_io_num`` is removed.
- ``dac_output_voltage`` is replaced by :cpp:func:`dac_oneshot_output_voltage`.
- ``dac_output_enable`` is removed, for oneshot mode, it will be enabled after the channel allocated.
- ``dac_output_disable`` is removed, for oneshot mode, it will be disabled before the channel deleted.
- ``dac_cw_generator_enable`` is replaced by :cpp:func:`dac_cosine_start`.
- ``dac_cw_generator_disable`` is replaced by :cpp:func:`dac_cosine_stop`.
- ``dac_cw_generator_config`` is replaced by :cpp:func:`dac_new_cosine_channel`.
.. only:: esp32
- ``dac_i2s_enable`` is replaced by :cpp:func:`dac_conti_enable`, but it needs to allocate the continuous DAC channel first by :cpp:func:`dac_new_conti_channels`.
- ``dac_i2s_disable`` is replaced by :cpp:func:`dac_conti_disable`.
.. only:: esp32s2
- ``dac_digi_init`` and ``dac_digi_controller_config`` is merged into :cpp:func:`dac_new_conti_channels`.
- ``dac_digi_deinit`` is replaced by :cpp:func:`dac_del_conti_channels`.
- ``dac_digi_start``, ``dac_digi_fifo_reset`` and ``dac_digi_reset`` are merged into :cpp:func:`dac_conti_enable`.
- ``dac_digi_stop`` is replaced by :cpp:func:`dac_conti_disable`.

View File

@ -9,7 +9,9 @@ menu "A2DP Example Configuration"
config EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
bool "Internal DAC"
help
Select this to use Internal DAC sink output
Select this to use Internal DAC sink output,
note that DAC_DMA_AUTO_16BIT_ALIGN should be turned off
because the audio data are already 16-bit width
config EXAMPLE_A2DP_SINK_OUTPUT_EXTERNAL_I2S
bool "External I2S Codec"

View File

@ -21,8 +21,7 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
// DAC DMA mode is only supported by the legacy I2S driver, it will be replaced once DAC has its own DMA dirver
#include "driver/i2s.h"
#include "driver/dac_conti.h"
#else
#include "driver/i2s_std.h"
#endif
@ -89,6 +88,8 @@ static uint8_t s_volume = 0; /* local volume value */
static bool s_volume_notify; /* notify volume change or not */
#ifndef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
i2s_chan_handle_t tx_chan = NULL;
#else
dac_conti_handle_t tx_chan;
#endif
/********************************
@ -169,23 +170,19 @@ static void bt_av_notify_evt_handler(uint8_t event_id, esp_avrc_rn_param_t *even
void bt_i2s_driver_install(void)
{
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
/* I2S configuration parameters */
i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN,
.sample_rate = 44100,
.bits_per_sample = 16,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, /* 2-channels */
.communication_format = I2S_COMM_FORMAT_STAND_MSB,
.dma_buf_count = 6,
.dma_buf_len = 60,
.intr_alloc_flags = 0, /* default interrupt priority */
.tx_desc_auto_clear = true /* auto clear tx descriptor on underflow */
dac_conti_config_t conti_cfg = {
.chan_mask = DAC_CHANNEL_MASK_ALL,
.desc_num = 8,
.buf_size = 2048,
.freq_hz = 44100,
.offset = 127,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT, // Using APLL as clock source to get a wider frequency range
.chan_mode = DAC_CHANNEL_MODE_ALTER,
};
/* enable I2S */
ESP_ERROR_CHECK(i2s_driver_install(0, &i2s_config, 0, NULL));
ESP_ERROR_CHECK(i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN));
ESP_ERROR_CHECK(i2s_set_pin(0, NULL));
/* Allocate continuous channels */
ESP_ERROR_CHECK(dac_new_conti_channels(&conti_cfg, &tx_chan));
/* Enable the continuous channels */
ESP_ERROR_CHECK(dac_conti_enable(tx_chan));
#else
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
chan_cfg.auto_clear = true;
@ -215,7 +212,8 @@ void bt_i2s_driver_install(void)
void bt_i2s_driver_uninstall(void)
{
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
i2s_driver_uninstall(0);
ESP_ERROR_CHECK(dac_conti_disable(tx_chan));
ESP_ERROR_CHECK(dac_del_conti_channels(tx_chan));
#else
ESP_ERROR_CHECK(i2s_channel_disable(tx_chan));
ESP_ERROR_CHECK(i2s_del_channel(tx_chan));
@ -316,7 +314,21 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
ch_count = 1;
}
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
i2s_set_clk(0, sample_rate, 16, ch_count);
dac_conti_disable(tx_chan);
dac_del_conti_channels(tx_chan);
dac_conti_config_t conti_cfg = {
.chan_mask = DAC_CHANNEL_MASK_ALL,
.desc_num = 8,
.buf_size = 2048,
.freq_hz = sample_rate,
.offset = 127,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT, // Using APLL as clock source to get a wider frequency range
.chan_mode = (ch_count == 1) ? DAC_CHANNEL_MODE_SIMUL : DAC_CHANNEL_MODE_ALTER,
};
/* Allocate continuous channels */
dac_new_conti_channels(&conti_cfg, &tx_chan);
/* Enable the continuous channels */
dac_conti_enable(tx_chan);
#else
i2s_channel_disable(tx_chan);
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(sample_rate);

View File

@ -16,8 +16,7 @@
#include "esp_log.h"
#include "bt_app_core.h"
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
// DAC DMA mode is only supported by the legacy I2S driver, it will be replaced once DAC has its own DMA dirver
#include "driver/i2s.h"
#include "driver/dac_conti.h"
#else
#include "driver/i2s_std.h"
#endif
@ -62,6 +61,8 @@ static uint16_t ringbuffer_mode = RINGBUFFER_MODE_PROCESSING;
********************************/
#ifndef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
extern i2s_chan_handle_t tx_chan;
#else
extern dac_conti_handle_t tx_chan;
#endif
/*******************************
@ -139,7 +140,7 @@ static void bt_i2s_task_handler(void *arg)
}
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
i2s_write(0, data, item_size, &bytes_written, portMAX_DELAY);
dac_conti_write(tx_chan, data, item_size, &bytes_written, -1);
#else
i2s_channel_write(tx_chan, data, item_size, &bytes_written, portMAX_DELAY);
#endif

View File

@ -7,4 +7,4 @@ CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=y
CONFIG_BT_CLASSIC_ENABLED=y
CONFIG_BT_A2DP_ENABLE=y
CONFIG_I2S_SUPPRESS_DEPRECATE_WARN=y
CONFIG_DAC_DMA_AUTO_16BIT_ALIGN=n

View File

@ -9,7 +9,9 @@ menu "A2DP Example Configuration"
config EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
bool "Internal DAC"
help
Select this to use Internal DAC sink output
Select this to use Internal DAC sink output,
note that DAC_DMA_AUTO_16BIT_ALIGN should be turned off
because the audio data are already 16-bit width
config EXAMPLE_A2DP_SINK_OUTPUT_EXTERNAL_I2S
bool "External I2S Codec"

View File

@ -22,8 +22,7 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
// DAC DMA mode is only supported by the legacy I2S driver, it will be replaced once DAC has its own DMA dirver
#include "driver/i2s.h"
#include "driver/dac_conti.h"
#else
#include "driver/i2s_std.h"
#endif
@ -55,6 +54,8 @@ static uint8_t s_volume = 0;
static bool s_volume_notify;
#ifndef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
extern i2s_chan_handle_t tx_chan;
#else
extern dac_conti_handle_t tx_chan;
#endif
/* callback for A2DP sink */
@ -171,7 +172,21 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
sample_rate = 48000;
}
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
i2s_set_clk(0, sample_rate, 16, 2);
dac_conti_disable(tx_chan);
dac_del_conti_channels(tx_chan);
dac_conti_config_t conti_cfg = {
.chan_mask = DAC_CHANNEL_MASK_ALL,
.desc_num = 8,
.buf_size = 2048,
.freq_hz = sample_rate,
.offset = 127,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT, // Using APLL as clock source to get a wider frequency range
.chan_mode = DAC_CHANNEL_MODE_ALTER,
};
/* Allocate continuous channels */
dac_new_conti_channels(&conti_cfg, &tx_chan);
/* Enable the continuous channels */
dac_conti_enable(tx_chan);
#else
i2s_channel_disable(tx_chan);
i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(sample_rate);

View File

@ -16,8 +16,7 @@
#include "bt_app_core.h"
#include "freertos/ringbuf.h"
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
// DAC DMA mode is only supported by the legacy I2S driver, it will be replaced once DAC has its own DMA dirver
#include "driver/i2s.h"
#include "driver/dac_conti.h"
#else
#include "driver/i2s_std.h"
#endif
@ -32,6 +31,8 @@ static TaskHandle_t s_bt_i2s_task_handle = NULL;
static RingbufHandle_t s_ringbuf_i2s = NULL;
#ifndef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
extern i2s_chan_handle_t tx_chan;
#else
extern dac_conti_handle_t tx_chan;
#endif
bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, int param_len, bt_app_copy_cb_t p_copy_cback)
@ -132,7 +133,7 @@ static void bt_i2s_task_handler(void *arg)
data = (uint8_t *)xRingbufferReceive(s_ringbuf_i2s, &item_size, (TickType_t)portMAX_DELAY);
if (item_size != 0){
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
i2s_write(0, data, item_size, &bytes_written, portMAX_DELAY);
dac_conti_write(tx_chan, data, item_size, &bytes_written, -1);
#else
i2s_channel_write(tx_chan, data, item_size, &bytes_written, portMAX_DELAY);
#endif

View File

@ -36,8 +36,7 @@
#include "esp_a2dp_api.h"
#include "esp_avrc_api.h"
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
// DAC DMA mode is only supported by the legacy I2S driver, it will be replaced once DAC has its own DMA dirver
#include "driver/i2s.h"
#include "driver/dac_conti.h"
#else
#include "driver/i2s_std.h"
#endif
@ -77,6 +76,8 @@ static prepare_type_env_t b_prepare_write_env;
#ifndef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
i2s_chan_handle_t tx_chan;
#else
dac_conti_handle_t tx_chan;
#endif
//Declare the static function
@ -691,23 +692,19 @@ void app_main(void)
ESP_ERROR_CHECK(err);
#ifdef CONFIG_EXAMPLE_A2DP_SINK_OUTPUT_INTERNAL_DAC
/* I2S configuration parameters */
i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN,
.sample_rate = 44100,
.bits_per_sample = 16,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, /* 2-channels */
.communication_format = I2S_COMM_FORMAT_STAND_MSB,
.dma_buf_count = 6,
.dma_buf_len = 60,
.intr_alloc_flags = 0, /* default interrupt priority */
.tx_desc_auto_clear = true /* auto clear tx descriptor on underflow */
dac_conti_config_t conti_cfg = {
.chan_mask = DAC_CHANNEL_MASK_ALL,
.desc_num = 8,
.buf_size = 2048,
.freq_hz = 44100,
.offset = 127,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT, // Using APLL as clock source to get a wider frequency range
.chan_mode = DAC_CHANNEL_MODE_ALTER,
};
/* enable I2S */
ESP_ERROR_CHECK(i2s_driver_install(0, &i2s_config, 0, NULL));
ESP_ERROR_CHECK(i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN));
ESP_ERROR_CHECK(i2s_set_pin(0, NULL));
/* Allocate continuous channels */
ESP_ERROR_CHECK(dac_new_conti_channels(&conti_cfg, &tx_chan));
/* Enable the continuous channels */
ESP_ERROR_CHECK(dac_conti_enable(tx_chan));
#else
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
chan_cfg.auto_clear = true;

View File

@ -29,4 +29,4 @@ CONFIG_BT_ACL_CONNECTIONS=4
CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST=n
CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY=n
CONFIG_BT_SMP_ENABLE=y
CONFIG_I2S_SUPPRESS_DEPRECATE_WARN=y
CONFIG_DAC_DMA_AUTO_16BIT_ALIGN=n

View File

@ -6,6 +6,10 @@ examples/peripherals/adc/continuous_read:
temporary: true
reason: adc dma mode isn't supported on these targets
examples/peripherals/dac:
disable:
- if: SOC_DAC_SUPPORTED != 1
examples/peripherals/gpio/generic_gpio:
disable_test:
- if: IDF_TARGET != "esp32"

View File

@ -3,4 +3,4 @@
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(wave_gen)
project(dac_audio)

View File

@ -0,0 +1,53 @@
| Supported Targets | ESP32 | ESP32-S2 |
| ----------------- | ----- | -------- |
# DAC Constant Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
This example shows how to play a piece of audio by DAC driver.
## How to use the Example
### Hardware Required
* A development board with ESP32 or ESP32-S2 SoC
- Note that some ESP32-S2 DevKits have LED on it which is connected to GPIO18 (same pin as DAC channel2), so the output voltage of DAC channel 1 can't go down due the this LED.
* An Audio Power Amplifier like `NS4150`
* A speaker or earphone to play the audio
### Configure the Project
This example uses the audio that stored in a buffer, which is put in `audio_example_file.h`. You can also create your own audio buffer by the python script `generate_audio_file.py`.
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
You can see the following logs on the monitor:
```
I (277) dac audio: DAC audio example start
I (277) dac audio: --------------------------------------
I (287) dac audio: DAC initialized success, DAC DMA is ready
I (297) dac audio: Audio size 79512 bytes, played at frequency 16000 Hz
I (5137) dac audio: Audio size 79512 bytes, played at frequency 16000 Hz
I (9967) dac audio: Audio size 79512 bytes, played at frequency 16000 Hz
...
```
And meanwhile, you can hear the audio played every 1 second from the speaker or earphone.

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "dac_audio_example_main.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,21 @@
menu "Example Configuration"
choice EXAMPLE_DAC_WRITE_MODE
prompt "Select DAC writing mode"
default EXAMPLE_DAC_WRITE_SYNC
help
DAC writes data synchronously or asynchronously
config EXAMPLE_DAC_WRITE_SYNC
bool "Synchronous transmitting"
config EXAMPLE_DAC_WRITE_ASYNC
bool "Asynchronous transmitting"
endchoice
config EXAMPLE_AUDIO_SAMPLE_RATE
int "The audio sample rate (Unit: Hz)"
default 48000
help
The audio sample rate
endmenu

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,117 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
#include <inttypes.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/dac_conti.h"
#include "esp_check.h"
#include "audio_example_file.h"
static const char *TAG = "dac_audio";
#if CONFIG_EXAMPLE_DAC_WRITE_ASYNC
static bool IRAM_ATTR dac_on_convert_done_callback(dac_conti_handle_t handle, const dac_event_data_t *event, void *user_data)
{
QueueHandle_t que = (QueueHandle_t)user_data;
BaseType_t need_awoke;
/* When the queue is full, drop the oldest item */
if (xQueueIsQueueFullFromISR(que)) {
dac_event_data_t dummy;
xQueueReceiveFromISR(que, &dummy, &need_awoke);
}
/* Send the event from callback */
xQueueSendFromISR(que, event, &need_awoke);
return need_awoke;
}
static void dac_write_data_asynchronously(dac_conti_handle_t handle, QueueHandle_t que, uint8_t *data, size_t data_size)
{
ESP_LOGI(TAG, "Audio size %d bytes, played at frequency %d Hz asynchronously", data_size, CONFIG_EXAMPLE_AUDIO_SAMPLE_RATE);
uint32_t cnt = 1;
while (1) {
printf("Play count: %"PRIu32"\n", cnt++);
dac_event_data_t evt_data;
size_t byte_written = 0;
/* Receive the event from callback and load the data into the DMA buffer until the whole audio loaded */
while (byte_written < data_size) {
xQueueReceive(que, &evt_data, portMAX_DELAY);
size_t loaded_bytes = 0;
ESP_ERROR_CHECK(dac_conti_write_asynchronously(handle, evt_data.buf, evt_data.buf_size,
data + byte_written, data_size - byte_written, &loaded_bytes));
byte_written += loaded_bytes;
}
/* Clear the legacy data in DMA, clear times equal to the 'dac_conti_config_t::desc_num' */
for (int i = 0; i < 4; i++) {
xQueueReceive(que, &evt_data, portMAX_DELAY);
memset(evt_data.buf, 0, evt_data.buf_size);
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
#else
static void dac_write_data_synchronously(dac_conti_handle_t handle, uint8_t *data, size_t data_size)
{
ESP_LOGI(TAG, "Audio size %d bytes, played at frequency %d Hz synchronously", data_size, CONFIG_EXAMPLE_AUDIO_SAMPLE_RATE);
uint32_t cnt = 1;
while (1) {
printf("Play count: %"PRIu32"\n", cnt++);
ESP_ERROR_CHECK(dac_conti_write(handle, data, data_size, NULL, -1));
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
#endif
void app_main(void)
{
ESP_LOGI(TAG, "DAC audio example start");
ESP_LOGI(TAG, "--------------------------------------");
dac_conti_handle_t dac_handle;
dac_conti_config_t conti_cfg = {
.chan_mask = DAC_CHANNEL_MASK_ALL,
.desc_num = 4,
.buf_size = 2048,
.freq_hz = CONFIG_EXAMPLE_AUDIO_SAMPLE_RATE,
.offset = 0,
.clk_src = DAC_DIGI_CLK_SRC_APLL, // Using APLL as clock source to get a wider frequency range
/* 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 channels */
ESP_ERROR_CHECK(dac_new_conti_channels(&conti_cfg, &dac_handle));
#if CONFIG_EXAMPLE_DAC_WRITE_ASYNC
/* Create a queue to transport the interrupt event data */
QueueHandle_t que = xQueueCreate(10, sizeof(dac_event_data_t));
assert(que);
dac_event_callbacks_t cbs = {
.on_convert_done = dac_on_convert_done_callback,
.on_stop = NULL,
};
/* Must register the callback if using asynchronous writing */
ESP_ERROR_CHECK(dac_conti_register_event_callback(dac_handle, &cbs, que));
#endif
/* Enable the continuous channels */
ESP_ERROR_CHECK(dac_conti_enable(dac_handle));
ESP_LOGI(TAG, "DAC initialized success, DAC DMA is ready");
size_t audio_size = sizeof(audio_table);
#if CONFIG_EXAMPLE_DAC_WRITE_ASYNC
ESP_ERROR_CHECK(dac_conti_start_async_writing(dac_handle));
dac_write_data_asynchronously(dac_handle, que, (uint8_t *)audio_table, audio_size);
#else
dac_write_data_synchronously(dac_handle, (uint8_t *)audio_table, audio_size);
#endif
}

View File

@ -0,0 +1,32 @@
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
def dac_example_expect(dut: Dut, mode: str) -> None:
dut.expect('dac_audio: DAC audio example start', timeout=10)
dut.expect('dac_audio: --------------------------------------', timeout=5)
dut.expect('dac_audio: DAC initialized success, DAC DMA is ready', timeout=5)
dut.expect('dac_audio: Audio size 95824 bytes, played at frequency 48000 Hz ' + mode, timeout=5)
dut.expect('Play count: 2', timeout=10)
@pytest.mark.esp32
@pytest.mark.esp32s2
@pytest.mark.generic
@pytest.mark.parametrize(
'config',
[
'sync',
'async',
],
indirect=True,
)
def test_dac_audio_example(dut: Dut) -> None:
sdkconfig = dut.app.sdkconfig
if sdkconfig['EXAMPLE_DAC_WRITE_SYNC']:
dac_example_expect(dut, 'synchronously')
else:
dac_example_expect(dut, 'asynchronously')

View File

@ -0,0 +1,2 @@
CONFIG_EXAMPLE_DAC_WRITE_SYNC=n
CONFIG_EXAMPLE_DAC_WRITE_ASYNC=y

View File

@ -0,0 +1,2 @@
CONFIG_EXAMPLE_DAC_WRITE_SYNC=y
CONFIG_EXAMPLE_DAC_WRITE_ASYNC=n

Some files were not shown because too many files have changed in this diff Show More