dac: update unit-test docs and examples for driver-NG

This commit is contained in:
laokaiyao 2022-05-24 17:26:36 +08:00
parent 351a18415c
commit f9f9a09dfb
129 changed files with 4163 additions and 8227 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

@ -104,8 +104,10 @@ if(CONFIG_SOC_TOUCH_SENSOR_SUPPORTED)
endif()
if(CONFIG_SOC_DAC_SUPPORTED)
list(APPEND srcs "dac.c"
"deprecated/dac_common_legacy.c")
list(APPEND srcs "dac/dac_driver.c"
"dac/${target}/dac_dma.c"
"deprecated/dac_common_legacy.c"
"deprecated/${target}/dac_legacy.c")
list(APPEND includes "deprecated/${target}")
endif()
@ -114,12 +116,7 @@ if(CONFIG_SOC_SDIO_SLAVE_SUPPORTED)
endif()
if(${target} STREQUAL "esp32")
list(APPEND srcs "deprecated/adc_i2s_deprecated.c"
"deprecated/esp32/dac_legacy.c")
endif()
if(${target} STREQUAL "esp32s2")
list(APPEND srcs "deprecated/esp32s2/dac_legacy.c")
list(APPEND srcs "deprecated/adc_i2s_deprecated.c")
endif()
if(BOOTLOADER_BUILD)

View File

@ -401,6 +401,14 @@ menu "Driver Configurations"
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_channels_set_voltage' function 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
@ -422,6 +430,19 @@ menu "Driver Configurations"
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 IDF_TARGET_ESP32
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

@ -1,643 +0,0 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "sdkconfig.h"
#include "soc/soc_caps.h"
#include "soc/lldesc.h"
#include "soc/dac_periph.h"
#include "hal/dac_types.h"
#include "hal/dac_ll.h"
#include "driver/rtc_io.h"
#include "driver/dac_new.h"
#include "esp_private/dac_dma.h"
#include "esp_check.h"
#define DAC_DMA_MAX_BUF_SIZE 4092
#if CONFIG_DAC_ISR_IRAM_SAFE
#define DAC_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_SHARED)
#define DAC_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
#define DAC_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_SHARED)
#define DAC_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#endif
#define DAC_DMA_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA)
typedef enum {
DAC_STATE_INITIAL, /* Initial state, dac has been registered */
DAC_STATE_OUTPUT_READY, /* DAC channels enabled and ready to output constant voltage */
DAC_STATE_DMA_READY, /* DMA mode initialized, but not started */
DAC_STATE_CW_READY, /* Cosine wave mode is initialized, but not started */
DAC_STATE_DMA_RUNNING, /* DAC DMA dma is running, can't switch mode in this stage */
DAC_STATE_CW_RUNNING, /* DAC cosine wave generator is running, can't switch mode in this stage */
} dac_state_t;
typedef struct {
QueueHandle_t msg_que;
#if CONFIG_DAC_ISR_IRAM_SAFE
StaticQueue_t *msg_que_struct; /*!< Static message queue struct */
void *msg_que_storage; /*!< Static message queue storage */
#endif
dac_dma_config_t cfg;
lldesc_t **desc;
} dac_dma_t;
typedef struct dac_channel_chain_s {
dac_channel_t id;
struct dac_channel_chain_s *next;
} dac_channel_chain_t;
struct dac_channel_group_s {
uint32_t chan_num;
dac_state_t state;
SemaphoreHandle_t mutex;
#if CONFIG_DAC_ISR_IRAM_SAFE
StaticSemaphore_t *mutex_struct; /*!< Static mutex struct */
#endif
#if CONFIG_PM_ENABLE
esp_pm_lock_handle_t pm_lock;
#endif
bool is_enabled;
dac_dma_t dma;
dac_cosine_config_t sw_cfg;
dac_channel_chain_t *head;
};
typedef struct {
dac_channel_chain_t *chan[SOC_DAC_PERIPH_NUM];
} dac_platform_t;
static const char *TAG = "DAC";
static dac_platform_t s_dac = {
.chan = {
[0 ... SOC_DAC_PERIPH_NUM - 1] = NULL
},
};
/* Global dac spin lock for the whole DAC driver */
portMUX_TYPE dac_spinlock = portMUX_INITIALIZER_UNLOCKED; // TODO: check rtc_spinlock
#define DAC_NULL_POINTER_CHECK(p) ESP_RETURN_ON_FALSE((p), ESP_ERR_INVALID_ARG, TAG, "input parameter '"#p"' is NULL")
static void dac_free_dma_desc(dac_channel_group_handle_t handle)
{
if (handle->dma.desc == NULL) {
return;
}
for (int i = 0; i < handle->dma.cfg.desc_num; i++) {
if (handle->dma.desc[i]) {
free(handle->dma.desc[i]);
handle->dma.desc[i] = NULL;
}
}
free(handle->dma.desc);
handle->dma.desc = NULL;
}
static esp_err_t dac_alloc_dma_desc(dac_channel_group_handle_t handle)
{
esp_err_t ret = ESP_OK;
handle->dma.desc = (lldesc_t **) heap_caps_calloc(handle->dma.cfg.desc_num, sizeof(lldesc_t *), DAC_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(handle->dma.desc, ESP_ERR_NO_MEM, TAG, "Faild to allocate dma descriptor buffer");
for (int cnt = 0; cnt < handle->dma.cfg.desc_num; cnt++) {
/* Allocate DMA descpriptor */
handle->dma.desc[cnt] = (lldesc_t *) heap_caps_calloc(1, sizeof(lldesc_t), DAC_DMA_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(handle->dma.desc[cnt], ESP_ERR_NO_MEM, err, TAG, "failed to allocate dma descriptor");
}
return ESP_OK;
err:
/* Free DMA buffer if failed to allocate memory */
dac_free_dma_desc(handle);
return ret;
}
static void IRAM_ATTR dac_default_intr_handler(void *arg)
{
dac_channel_group_handle_t handle = (dac_channel_group_handle_t)arg;
uint32_t dummy;
BaseType_t need_awoke = pdFALSE;
BaseType_t tmp;
if (dac_dma_periph_intr_is_triggered()) {
lldesc_t *fdesc = (lldesc_t *)dac_dma_periph_intr_get_eof_desc();
if (xQueueIsQueueFullFromISR(handle->dma.msg_que) == pdTRUE) {
xQueueReceiveFromISR(handle->dma.msg_que, &dummy, &tmp);
need_awoke |= tmp;
}
xQueueSendFromISR(handle->dma.msg_que, fdesc, &tmp);
need_awoke |= tmp;
}
if (need_awoke == pdTRUE) {
portYIELD_FROM_ISR();
}
}
static void dac_free_channel_chain(dac_channel_chain_t *head)
{
if (head->next) {
dac_free_channel_chain(head->next);
}
s_dac.chan[head->id - 1] = NULL;
free(head);
}
/*--------------------------------------------------------------------------
DAC common APIs
---------------------------------------------------------------------------*/
esp_err_t dac_new_channel_group(const dac_group_config_t *dac_cfg, dac_channel_group_handle_t *handle)
{
esp_err_t ret = ESP_OK;
DAC_NULL_POINTER_CHECK(dac_cfg);
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE((uint32_t)dac_cfg->chan_sel > 0, ESP_ERR_INVALID_ARG, TAG, "invalid DAC channel");
ESP_RETURN_ON_FALSE((32 - __builtin_clz((uint32_t)dac_cfg->chan_sel)) <= SOC_DAC_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid DAC channel");
dac_channel_group_handle_t group = (dac_channel_group_handle_t)calloc(1, sizeof(struct dac_channel_group_s));
ESP_RETURN_ON_FALSE(group, ESP_ERR_NO_MEM, TAG, "No memory for DAC channel group");
group->chan_num = 0;
group->is_enabled = false;
group->state = DAC_STATE_INITIAL; // Set static output as default
#if CONFIG_DAC_ISR_IRAM_SAFE
group->mutex_struct = (StaticSemaphore_t *)heap_caps_calloc(1, sizeof(StaticSemaphore_t), DAC_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(group->mutex_struct, ESP_ERR_NO_MEM, err, TAG, "No memory for group mutex struct");
group->mutex = xSemaphoreCreateMutexStatic(group->mutex_struct);
#else
group->mutex = xSemaphoreCreateMutex();
#endif
ESP_GOTO_ON_FALSE(group->mutex, ESP_ERR_NO_MEM, err, TAG, "No memory for group mutex");
#if CONFIG_PM_ENABLE
ESP_GOTO_ON_ERROR(esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "dac_driver", &group->pm_lock), err, TAG, "Failed to create DAC pm lock");
#endif
/* Register selected channels and link into a chain*/
dac_channel_chain_t *temp = NULL;
for (uint32_t msk = (uint32_t)dac_cfg->chan_sel, i = 0; msk != 0; msk >>= 1, i++) {
if (msk & 0x01) {
/* Allocate memory for the channel when it is enabled */
ESP_GOTO_ON_FALSE(!s_dac.chan[i], ESP_ERR_INVALID_STATE, err, TAG, "DAC channel %d has been registered already", i + 1);
dac_channel_chain_t *node = (dac_channel_chain_t *)calloc(1, sizeof(dac_channel_chain_t));
ESP_GOTO_ON_FALSE(node, ESP_ERR_NO_MEM, err, TAG, "No memory for DAC channel object");
node->id = i;
s_dac.chan[i] = node;
group->chan_num++;
/* Link the channel into a chain */
if (!temp) {
temp = node;
group->head = node;
} else {
temp->next = node;
temp = temp->next;
}
}
}
*handle = group;
return ret;
err:
/* Free the resource when error occurs */
dac_del_channel_group(group);
group = NULL;
return ret;
}
esp_err_t dac_del_channel_group(dac_channel_group_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_INITIAL, ESP_ERR_INVALID_STATE, TAG, "This DAC group is not deinitialized");
ESP_RETURN_ON_FALSE(!handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This DAC group is not disabled");
if (handle->mutex) {
vSemaphoreDelete(handle->mutex);
}
#if CONFIG_DAC_ISR_IRAM_SAFE
if (handle->mutex_struct) {
free(handle_mutex_struct);
}
#endif
#if CONFIG_PM_ENABLE
if (handle->pm_lock) {
esp_pm_lock_delete(handle->pm_lock);
handle->pm_lock = NULL;
}
#endif
if (handle->head) {
dac_free_channel_chain(handle->head);
}
free(handle);
return ESP_OK;
}
esp_err_t dac_channel_group_enable(dac_channel_group_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(!handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This DAC group has been enabled already");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
for (dac_channel_chain_t *p = handle->head; p != NULL; p = p->next) {
gpio_num_t gpio_num = (gpio_num_t)dac_periph_signal.dac_channel_io_num[p->id];
rtc_gpio_init(gpio_num);
rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_DISABLED);
rtc_gpio_pullup_dis(gpio_num);
rtc_gpio_pulldown_dis(gpio_num);
portENTER_CRITICAL(&dac_spinlock);
dac_ll_power_on(p->id);
dac_ll_rtc_sync_by_adc(false);
portEXIT_CRITICAL(&dac_spinlock);
}
handle->is_enabled = true;
/* If the group has not been intialized to other mode, set it `DAC_STATE_OUTPUT_READY` as default */
if (handle->state == DAC_STATE_INITIAL) {
handle->state = DAC_STATE_OUTPUT_READY;
}
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
esp_err_t dac_channel_group_disable(dac_channel_group_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This DAC group has not been enabled yet");
ESP_RETURN_ON_FALSE(handle->state < DAC_STATE_DMA_RUNNING, ESP_ERR_INVALID_STATE, TAG, "This DAC group is still running");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
for (dac_channel_chain_t *p = handle->head; p != NULL; p = p->next) {
gpio_num_t gpio_num = (gpio_num_t)dac_periph_signal.dac_channel_io_num[p->id];
rtc_gpio_deinit(gpio_num);
portENTER_CRITICAL(&dac_spinlock);
dac_ll_power_down(p->id);
portEXIT_CRITICAL(&dac_spinlock);
}
handle->is_enabled = false;
/* If the group has not been intialized to other mode, set it `DAC_STATE_INITIAL` as default */
if (handle->state == DAC_STATE_OUTPUT_READY) {
handle->state = DAC_STATE_INITIAL;
}
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
/*--------------------------------------------------------------------------
DAC constant voltage outputting APIs
---------------------------------------------------------------------------*/
esp_err_t dac_channel_group_output_constant_voltage(dac_channel_group_handle_t handle, uint8_t value)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This DAC group has not been enabled");
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_OUTPUT_READY, ESP_ERR_INVALID_STATE, TAG, "This DAC group has been configured to other mode");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
/* Set the constant voltage for each channel in the group */
for (dac_channel_chain_t *p = handle->head; p != NULL; p = p->next) {
portENTER_CRITICAL(&dac_spinlock);
dac_ll_update_output_value(p->id, value);
portEXIT_CRITICAL(&dac_spinlock);
}
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
/*--------------------------------------------------------------------------
DAC DMA outputting APIs
---------------------------------------------------------------------------*/
esp_err_t dac_channel_group_init_dma_output(dac_channel_group_handle_t handle, const dac_dma_config_t *dma_cfg)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->state < DAC_STATE_DMA_READY, ESP_ERR_INVALID_STATE, TAG, "This DAC group has been initialized already");
esp_err_t ret = ESP_OK;
xSemaphoreTake(handle->mutex, portMAX_DELAY);
#if CONFIG_DAC_ISR_IRAM_SAFE
handle->dma.msg_que_storage = (uint8_t *)heap_caps_calloc(desc_num - 1, sizeof(lldesc_t *), I2S_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(handle->dma.msg_que_storage, ESP_ERR_NO_MEM, err, TAG, "No memory for message queue storage");
handle->dma.msg_que_struct = (StaticQueue_t *)heap_caps_calloc(1, sizeof(StaticQueue_t), I2S_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(handle->dma.msg_que_storage, ESP_ERR_NO_MEM, err, TAG, "No memory for message queue struct");
handle->dma.msg_queue = xQueueCreateStatic(desc_num - 1, sizeof(lldesc_t *), handle->dma.msg_que_storage, handle->dma.msg_que_struct);
#else
handle->dma.msg_que = xQueueCreate(dma_cfg->desc_num - 1, sizeof(lldesc_t *));
#endif
ESP_GOTO_ON_FALSE(handle->dma.msg_que, ESP_ERR_NO_MEM, err3, TAG, "No memory for message queue");
/* Allocate DMA buffer */
memcpy(&(handle->dma.cfg), dma_cfg, sizeof(dac_dma_config_t));
ESP_GOTO_ON_ERROR(dac_alloc_dma_desc(handle), err2, TAG, "Failed to allocate memory for DMA buffers");
/* Initialize DAC DMA peripheral */
ESP_GOTO_ON_ERROR(dac_dma_periph_init(handle->chan_num, dma_cfg->freq_hz, dma_cfg->chan_mode == DAC_CHANNEL_ALTERNATE), err2, TAG, "Failed to initialize DAC DMA peripheral");
/* Register DMA interrupt */
ESP_GOTO_ON_ERROR(dac_dma_periph_register_intr(dac_default_intr_handler, handle), err1, TAG, "Failed to register DMA interrupt");
/* Connect DAC module to the DMA peripheral */
portENTER_CRITICAL(&dac_spinlock);
dac_ll_digi_enable_dma(true);
portEXIT_CRITICAL(&dac_spinlock);
handle->state = DAC_STATE_DMA_READY;
xSemaphoreGive(handle->mutex);
return ret;
err1:
dac_dma_periph_deinit();
err2:
dac_free_dma_desc(handle);
err3:
if (handle->dma.msg_que) {
vQueueDelete(handle->dma.msg_que);
}
#if CONFIG_DAC_ISR_IRAM_SAFE
if (handle->dma.msq_que_struct) {
free(handle->dma.msq_que_struct);
}
if (handle->dma.msq_que_storage) {
free(handle->dma.msq_que_storage);
}
#endif
xSemaphoreGive(handle->mutex);
return ret;
}
esp_err_t dac_channel_group_deinit_dma_output(dac_channel_group_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_DMA_READY, ESP_ERR_INVALID_STATE, TAG,
"This DAC group is still running or has been configured to other mode");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
/* Free DMA buffer */
dac_free_dma_desc(handle);
/* Deregister DMA interrupt */
ESP_RETURN_ON_ERROR(dac_dma_periph_deregister_intr(), TAG, "Failed to deregister DMA interrupt");
/* Deinitialize DMA peripheral */
ESP_RETURN_ON_ERROR(dac_dma_periph_deinit(), TAG, "Failed to deinitialize DAC DMA peripheral");
/* Disconnect DAC module to the DMA peripheral */
portENTER_CRITICAL(&dac_spinlock);
dac_ll_digi_enable_dma(false);
portEXIT_CRITICAL(&dac_spinlock);
if (handle->is_enabled) {
handle->state = DAC_STATE_OUTPUT_READY;
} else {
handle->state = DAC_STATE_INITIAL;
}
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
esp_err_t dac_channel_group_start_dma_output(dac_channel_group_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This DAC group has not been enabled");
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_DMA_READY, ESP_ERR_INVALID_STATE, TAG,
"This DAC group has started already or not working at DMA mode");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_acquire(handle->pm_lock);
#endif
dac_dma_periph_enable();
portENTER_CRITICAL(&dac_spinlock);
dac_ll_digi_enable_dma(true);
portEXIT_CRITICAL(&dac_spinlock);
handle->state = DAC_STATE_DMA_RUNNING;
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
esp_err_t dac_channel_group_stop_dma_output(dac_channel_group_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This DAC group has not been enabled");
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_DMA_RUNNING, ESP_ERR_INVALID_STATE, TAG,
"This DAC group has stopped already or not working at DMA mode");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
dac_dma_periph_disable();
portENTER_CRITICAL(&dac_spinlock);
dac_ll_digi_enable_dma(false);
portEXIT_CRITICAL(&dac_spinlock);
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_release(handle->pm_lock);
#endif
handle->state = DAC_STATE_DMA_READY;
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
static uint32_t dac_load_dma_data(lldesc_t *desc, uint8_t *data, uint32_t size)
{
uint32_t byte_to_load = size > DAC_DMA_MAX_BUF_SIZE ? DAC_DMA_MAX_BUF_SIZE : size;
desc->owner = 1;
desc->eof = 1;
desc->sosf = 0;
desc->length = byte_to_load;
desc->size = byte_to_load;
desc->buf = data;
desc->offset = 0;
return byte_to_load;
}
// TODO: wait until all data sent or all data loaded? If all data loaded, need to monitor end of frame
esp_err_t dac_channel_group_write_acyclicly(dac_channel_group_handle_t handle, uint8_t *buf, size_t buf_size, uint32_t timeout_ms)
{
DAC_NULL_POINTER_CHECK(handle);
DAC_NULL_POINTER_CHECK(buf);
ESP_RETURN_ON_FALSE(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This DAC group has not been enabled");
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_DMA_RUNNING, ESP_ERR_INVALID_STATE, TAG, "This DAC group is not started");
#if CONFIG_DAC_ISR_IRAM_SAFE
ESP_RETURN_ON_ERROR(esp_ptr_internal(buf), ESP_ERR_INVALID_ARG, err, TAG, "the buffer is not in internal RAM");
#endif
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(xSemaphoreTake(handle->mutex, pdMS_TO_TICKS(timeout_ms) == pdTRUE), ESP_ERR_TIMEOUT, TAG, "Take semaphore timeout");
/* Reset the queue to drop the legacy descriptors */
xQueueReset(handle->dma.msg_que);
/* Break the legacy descriptor chain for the new data */
for (int i=0; i < handle->dma.cfg.desc_num; i++) {
handle->dma.desc[i]->empty = 0;
}
/* Pre-load data to DMA */
size_t index = 0;
uint32_t pending_desc_cnt = 0;
for (int i = 0; i < handle->dma.cfg.desc_num && index < buf_size; i++, pending_desc_cnt++) {
index += dac_load_dma_data(handle->dma.desc[i], &buf[index], buf_size - index);
/* Link to the previous descriptor */
if (i > 0) {
handle->dma.desc[i-1]->empty = (uint32_t)handle->dma.desc[i];
}
}
/* Link the start and end desc as a ring if the buffer not loaded conmpletely */
handle->dma.desc[pending_desc_cnt-1]->empty = index < buf_size ? (uint32_t)handle->dma.desc[0] : 0;
dac_dma_periph_dma_trans_start((uint32_t)handle->dma.desc[0]);
/* Wait until all data be sent */
for (lldesc_t *finish_desc = NULL; pending_desc_cnt > 0; pending_desc_cnt--) {
ESP_GOTO_ON_FALSE(xQueueReceive(handle->dma.msg_que, &finish_desc, pdMS_TO_TICKS(timeout_ms)) == pdTRUE,
ESP_ERR_TIMEOUT, err, TAG, "Receive message queue timeout");
/* Load those unsent data */
if (index < buf_size) {
index += dac_load_dma_data(finish_desc, &buf[index], buf_size - index);
pending_desc_cnt++;
/* If all date loaded, break the ring desc */
if (index >= buf_size) {
finish_desc->empty = 0;
}
}
}
err:
xSemaphoreGive(handle->mutex);
return ret;
}
esp_err_t dac_channel_group_write_cyclicly(dac_channel_group_handle_t handle, uint8_t *buf, size_t buf_size, uint32_t timeout_ms)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This DAC group has not been enabled");
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_DMA_RUNNING, ESP_ERR_INVALID_STATE, TAG, "This DAC group is not started");
ESP_RETURN_ON_FALSE(buf_size < (DAC_DMA_MAX_BUF_SIZE * handle->dma.cfg.desc_num),
ESP_ERR_INVALID_ARG, TAG, "The cyclic buffer size exceeds the total DMA buffer size: desc_num * %d = %d",
DAC_DMA_MAX_BUF_SIZE, DAC_DMA_MAX_BUF_SIZE * handle->dma.cfg.desc_num);
#if CONFIG_DAC_ISR_IRAM_SAFE
ESP_RETURN_ON_ERROR(esp_ptr_internal(buf), ESP_ERR_INVALID_ARG, err, TAG, "the buffer is not in internal RAM");
#endif
ESP_RETURN_ON_FALSE(xSemaphoreTake(handle->mutex, pdMS_TO_TICKS(timeout_ms) == pdTRUE), ESP_ERR_TIMEOUT, TAG, "Take semaphore timeout");
/* If the buffer size is small, split it into two descriptors */
if (buf_size > DAC_DMA_MAX_BUF_SIZE) {
size_t index = 0;
int i = 0;
for (i = 0; (i < handle->dma.cfg.desc_num) && (index < buf_size); i++) {
index += dac_load_dma_data(handle->dma.desc[i], &buf[index], buf_size - index);
/* Link to the previous descriptor */
if (i > 0) {
handle->dma.desc[i-1]->empty = (uint32_t)handle->dma.desc[i];
}
}
/* Link as a loop */
handle->dma.desc[i-1]->empty = (uint32_t)handle->dma.desc[0];
}
/* If the buffer size is small, split it into two descriptors */
else {
uint32_t half = buf_size / 2;
dac_load_dma_data(handle->dma.desc[0], buf, half);
dac_load_dma_data(handle->dma.desc[1], &buf[half], buf_size - half);
handle->dma.desc[0]->empty = (uint32_t)handle->dma.desc[1];
handle->dma.desc[1]->empty = (uint32_t)handle->dma.desc[0];
}
dac_dma_periph_dma_trans_start((uint32_t)handle->dma.desc[0]);
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
/*--------------------------------------------------------------------------
DAC cosine wave outputting APIs
---------------------------------------------------------------------------*/
esp_err_t dac_channel_group_init_cosine_output(dac_channel_group_handle_t handle, const dac_cosine_config_t *cw_cfg)
{
DAC_NULL_POINTER_CHECK(handle);
DAC_NULL_POINTER_CHECK(cw_cfg);
ESP_RETURN_ON_FALSE((handle->state == DAC_STATE_INITIAL) | (handle->state == DAC_STATE_OUTPUT_READY),
ESP_ERR_INVALID_STATE, TAG, "This DAC group has been initialized already");
ESP_RETURN_ON_FALSE(cw_cfg->freq_hz >= 130, ESP_ERR_NOT_SUPPORTED, TAG, "The cosine wave generator doesn't support frequency below 130 Hz");
ESP_RETURN_ON_FALSE(cw_cfg->freq_hz <= 55000, ESP_ERR_NOT_SUPPORTED, TAG, "The cosine wave generator doesn't support frequency beyond 55000 Hz");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
portENTER_CRITICAL(&dac_spinlock);
for (dac_channel_chain_t *p = handle->head; p != NULL; p = p->next) {
/* Connect DAC module to cosine wave generator */
dac_ll_cw_set_channel(p->id, true);
/* Set coefficients for cosine wave generator */
dac_ll_cw_set_freq(cw_cfg->freq_hz);
dac_ll_cw_set_scale(p->id, cw_cfg->scale);
dac_ll_cw_set_phase(p->id, cw_cfg->phase);
dac_ll_cw_set_dc_offset(p->id, cw_cfg->offset);
}
portEXIT_CRITICAL(&dac_spinlock);
handle->state = DAC_STATE_CW_READY;
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
esp_err_t dac_channel_group_deinit_cosine_output(dac_channel_group_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_CW_READY, ESP_ERR_INVALID_STATE, TAG,
"This DAC group is still running or not working at cosine wave mode");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
portENTER_CRITICAL(&dac_spinlock);
for (dac_channel_chain_t *p = handle->head; p != NULL; p = p->next) {
/* Disonnect DAC module to cosine wave generator */
dac_ll_cw_set_channel(p->id, false);
}
portEXIT_CRITICAL(&dac_spinlock);
if (handle->is_enabled) {
handle->state = DAC_STATE_OUTPUT_READY;
} else {
handle->state = DAC_STATE_INITIAL;
}
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
esp_err_t dac_channel_group_start_cosine_output(dac_channel_group_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This DAC group has not been enabled");
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_CW_READY, ESP_ERR_INVALID_STATE, TAG,
"This DAC group has started already or not working at cosine wave mode");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_acquire(handle->pm_lock);
#endif
portENTER_CRITICAL(&dac_spinlock);
dac_ll_cw_generator_enable();
portEXIT_CRITICAL(&dac_spinlock);
handle->state = DAC_STATE_CW_RUNNING;
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
esp_err_t dac_channel_group_stop_cosine_output(dac_channel_group_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This DAC group has not been enabled");
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_CW_RUNNING, ESP_ERR_INVALID_STATE, TAG,
"This DAC group has stopped already or not working at cosine wave mode");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
portENTER_CRITICAL(&dac_spinlock);
dac_ll_cw_generator_disable();
portEXIT_CRITICAL(&dac_spinlock);
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_release(handle->pm_lock);
#endif
handle->state = DAC_STATE_CW_READY;
xSemaphoreGive(handle->mutex);
return ESP_OK;
}

View File

@ -0,0 +1,102 @@
/*
* 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 outof range
* - ESP_OK Initialize DAC DMA peripheral success
*/
esp_err_t dac_dma_periph_init(uint8_t chan_num, uint32_t freq_hz, bool is_alternate, bool is_apll);
/**
* @brief Deinitialize DAC DMA peripheral
*
* @return
* - ESP_ERR_INVALID_STATE The DAC DMA has deinitialized already
* - ESP_OK Deinitialize DAC DMA peripheral success
*/
esp_err_t dac_dma_periph_deinit(void);
/**
* @brief Register the DMA interrupt
*
* @param[in] intr_handler_func DMA interrupt handler function
* @param[in] user_ctx User contex that pass to the interrupt handler
* @return
* - ESP_ERR_INVALID_STATE DAC DMA peripheral is not initialized
* - ESP_OK Register DMA interrupt success
*/
esp_err_t dac_dma_periph_register_intr(intr_handler_t intr_handler_func, void *user_ctx);
/**
* @brief Deregister the DMA interrupt
*
* @return
* - ESP_ERR_INVALID_STATE DAC DMA peripheral is not initialized
* - ESP_OK Deregister DMA interrupt success
*/
esp_err_t dac_dma_periph_deregister_intr(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
* - true TX_EOF interrupt is triggered
* - false TX_EOF interrupt is not triggered
*/
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,781 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include <sys/queue.h>
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "sdkconfig.h"
#include "rom/lldesc.h"
#include "soc/soc_caps.h"
#include "soc/dac_periph.h"
#include "hal/dac_types.h"
#include "hal/dac_ll.h"
#include "driver/rtc_io.h"
#include "driver/dac_driver.h"
#include "dac_dma.h"
#include "esp_memory_utils.h"
#include "clk_ctrl_os.h"
#if CONFIG_DAC_ENABLE_DEBUG_LOG
// The local log level must be defined before including esp_log.h
// Set the maximum log level for this source file
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#endif
#include "esp_check.h"
#if CONFIG_PM_ENABLE
#include "esp_pm.h"
#endif
#define DAC_DMA_MAX_BUF_SIZE 4092 // Max DMA buffer size is 4095 but better to align with 4 bytes, so set 4092 here
#if CONFIG_DAC_ISR_IRAM_SAFE || CONFIG_DAC_CTRL_FUNC_IN_IRAM
#define DAC_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
#define DAC_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#endif
#if CONFIG_DAC_ISR_IRAM_SAFE
#define DAC_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_SHARED)
#else
#define DAC_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_SHARED)
#endif
#define DAC_DMA_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA)
#define DAC_STAILQ_REMOVE(head, elm, type, field) do { \
if ((head)->stqh_first == (elm)) { \
STAILQ_REMOVE_HEAD((head), field); \
} else { \
struct type *curelm = (head)->stqh_first; \
while (curelm->field.stqe_next != (elm) && \
curelm->field.stqe_next != NULL) \
curelm = curelm->field.stqe_next; \
if (curelm->field.stqe_next && (curelm->field.stqe_next = \
curelm->field.stqe_next->field.stqe_next) == NULL) \
(head)->stqh_last = &(curelm)->field.stqe_next; \
} \
} while (/*CONSTCOND*/0)
typedef enum {
DAC_STATE_INITIAL, /* Initial state, dac has been registered */
DAC_STATE_OUTPUT_READY, /* DAC channels enabled and ready to output constant voltage */
DAC_STATE_DMA_READY, /* DMA mode initialized, but not started */
DAC_STATE_CW_READY, /* Cosine wave mode is initialized, but not started */
DAC_STATE_DMA_ENABLED, /* DAC DMA dma is enabled, can't switch mode in this stage */
DAC_STATE_CW_RUNNING, /* DAC cosine wave generator is running, can't switch mode in this stage */
} dac_state_t;
typedef struct {
QueueHandle_t desc_pool; /* The pool of available descriptors
* The descriptors in the pool are not linked in to pending chain */
#if CONFIG_DAC_ISR_IRAM_SAFE
StaticQueue_t *desc_pool_struct; /* Static message queue struct */
void *desc_pool_storage; /* Static message queue storage */
#endif
dac_conti_config_t cfg;
STAILQ_HEAD(desc_chain_s, lldesc_s) head; /* Head of the descriptor chain
* The descriptors in the chain are pending to be sent or sending now */
lldesc_t **desc;
uint8_t **bufs;
volatile bool is_running; /* Is DMA running or stopped */
volatile bool is_cyclic; /* Is transport data cyclicly */
} dac_dma_t;
typedef struct dac_channel_chain_node_s {
dac_channel_t id;
SLIST_ENTRY(dac_channel_chain_node_s) next;
} dac_channel_info_t;
typedef SLIST_HEAD(dac_channel_chain_s, dac_channel_chain_node_s) dac_channel_chain_t;
struct dac_channels_s {
uint32_t chan_num;
dac_state_t state;
SemaphoreHandle_t mutex;
StaticSemaphore_t *mutex_struct; /*!< Static mutex struct */
#if CONFIG_PM_ENABLE
esp_pm_lock_handle_t pm_lock;
#endif
bool is_enabled;
dac_dma_t dma;
dac_cosine_config_t sw_cfg;
dac_channel_chain_t head;
};
typedef struct {
dac_channel_info_t *chan[SOC_DAC_PERIPH_NUM];
bool dma_in_use;
} dac_platform_t;
static const char *TAG = "DAC";
static dac_platform_t s_dac = {
.chan = {
[0 ... SOC_DAC_PERIPH_NUM - 1] = NULL,
},
.dma_in_use = false,
};
/* Global dac spin lock for the whole DAC driver */
portMUX_TYPE dac_spinlock = portMUX_INITIALIZER_UNLOCKED; // TODO: check rtc_spinlock
#define DAC_NULL_POINTER_CHECK(p) ESP_RETURN_ON_FALSE((p), ESP_ERR_INVALID_ARG, TAG, "input parameter '"#p"' is NULL")
static void dac_free_dma_desc(dac_channels_handle_t handle)
{
STAILQ_EMPTY(&handle->dma.head);
if (handle->dma.desc != NULL) {
for (int i = 0; i < handle->dma.cfg.desc_num; i++) {
if (handle->dma.desc[i]) {
free(handle->dma.desc[i]);
handle->dma.desc[i] = NULL;
}
}
free(handle->dma.desc);
handle->dma.desc = NULL;
}
if (handle->dma.bufs != NULL) {
for (int i = 0; i < handle->dma.cfg.desc_num; i++) {
if (handle->dma.bufs[i]) {
free(handle->dma.bufs[i]);
handle->dma.bufs[i] = NULL;
}
}
free(handle->dma.bufs);
handle->dma.bufs = NULL;
}
}
static esp_err_t dac_alloc_dma_desc(dac_channels_handle_t handle)
{
esp_err_t ret = ESP_OK;
STAILQ_INIT(&handle->dma.head);
handle->dma.desc = (lldesc_t **) heap_caps_calloc(handle->dma.cfg.desc_num, sizeof(lldesc_t *), DAC_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(handle->dma.desc, ESP_ERR_NO_MEM, TAG, "Faild to allocate dma descriptor array");
handle->dma.bufs = (uint8_t **) heap_caps_calloc(handle->dma.cfg.desc_num, sizeof(uint8_t *), DAC_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(handle->dma.bufs, ESP_ERR_NO_MEM, TAG, "Faild to allocate dma buffer array");
for (int cnt = 0; cnt < handle->dma.cfg.desc_num; cnt++) {
/* Allocate DMA descpriptor */
handle->dma.desc[cnt] = (lldesc_t *) heap_caps_calloc(1, sizeof(lldesc_t), DAC_DMA_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(handle->dma.desc[cnt], ESP_ERR_NO_MEM, err, TAG, "failed to allocate dma descriptor");
/* Allocate DMA buffer */
handle->dma.bufs[cnt] = (uint8_t *) heap_caps_calloc(1, handle->dma.cfg.buf_size, DAC_DMA_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(handle->dma.desc[cnt], ESP_ERR_NO_MEM, err, TAG, "failed to allocate dma buffer");
/* Assign initial value */
lldesc_config(handle->dma.desc[cnt], LLDESC_HW_OWNED, 1, 0, handle->dma.cfg.buf_size);
handle->dma.desc[cnt]->size = handle->dma.cfg.buf_size;
handle->dma.desc[cnt]->buf = handle->dma.bufs[cnt];
handle->dma.desc[cnt]->offset = 0;
xQueueSend(handle->dma.desc_pool, &handle->dma.desc[cnt], portMAX_DELAY);
}
return ESP_OK;
err:
/* Free DMA buffer if failed to allocate memory */
dac_free_dma_desc(handle);
return ret;
}
static void IRAM_ATTR dac_default_intr_handler(void *arg)
{
dac_channels_handle_t handle = (dac_channels_handle_t)arg;
uint32_t dummy;
BaseType_t need_awoke = pdFALSE;
BaseType_t tmp;
uint32_t intr_mask = dac_dma_periph_intr_is_triggered();
if ((intr_mask & DAC_DMA_EOF_INTR) && (!handle->dma.is_cyclic)) {
lldesc_t *fdesc = (lldesc_t *)dac_dma_periph_intr_get_eof_desc();
/* Remove the descriptor in the chain that finished sent */
portENTER_CRITICAL(&dac_spinlock);
if (STAILQ_FIRST(&handle->dma.head) != NULL) {
DAC_STAILQ_REMOVE(&handle->dma.head, fdesc, lldesc_s, qe);
}
portEXIT_CRITICAL(&dac_spinlock);
if (xQueueIsQueueFullFromISR(handle->dma.desc_pool) == pdTRUE) {
xQueueReceiveFromISR(handle->dma.desc_pool, &dummy, &tmp);
need_awoke |= tmp;
}
xQueueSendFromISR(handle->dma.desc_pool, &fdesc, &tmp);
need_awoke |= tmp;
}
if (intr_mask & DAC_DMA_TEOF_INTR) {
/* Total end of frame interrupt received, DMA stopped */
handle->dma.is_running = false;
}
if (need_awoke == pdTRUE) {
portYIELD_FROM_ISR();
}
}
/*--------------------------------------------------------------------------
DAC common APIs
---------------------------------------------------------------------------*/
esp_err_t dac_new_channels(const dac_channels_config_t *dac_cfg, dac_channels_handle_t *handle)
{
#if CONFIG_DAC_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif
esp_err_t ret = ESP_OK;
DAC_NULL_POINTER_CHECK(dac_cfg);
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE((uint32_t)dac_cfg->chan_sel > 0, ESP_ERR_INVALID_ARG, TAG, "invalid DAC channel");
ESP_RETURN_ON_FALSE((32 - __builtin_clz((uint32_t)dac_cfg->chan_sel)) <= SOC_DAC_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid DAC channel");
dac_channels_handle_t channels = (dac_channels_handle_t)heap_caps_calloc(1, sizeof(struct dac_channels_s), DAC_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(channels, ESP_ERR_NO_MEM, TAG, "No memory for DAC channels' structure");
channels->mutex_struct = (StaticSemaphore_t *)heap_caps_calloc(1, sizeof(StaticSemaphore_t), DAC_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(channels->mutex_struct, ESP_ERR_NO_MEM, err, TAG, "No memory for channels mutex struct");
channels->mutex = xSemaphoreCreateMutexStatic(channels->mutex_struct);
ESP_GOTO_ON_FALSE(channels->mutex, ESP_ERR_NO_MEM, err, TAG, "No memory for channels mutex");
channels->chan_num = 0;
channels->is_enabled = false;
channels->state = DAC_STATE_INITIAL; // Set static output as default
/* Register selected channels and link into a chain*/
SLIST_INIT(&channels->head);
for (uint32_t msk = (uint32_t)dac_cfg->chan_sel, i = 0; msk != 0; msk >>= 1, i++) {
if (msk & 0x01) {
/* Allocate memory for the channel when it is enabled */
ESP_GOTO_ON_FALSE(!s_dac.chan[i], ESP_ERR_INVALID_STATE, err, TAG, "DAC channel %d has been registered already", i + 1);
dac_channel_info_t *node = (dac_channel_info_t *)calloc(1, sizeof(dac_channel_info_t));
ESP_GOTO_ON_FALSE(node, ESP_ERR_NO_MEM, err, TAG, "No memory for DAC channel object");
node->id = i;
s_dac.chan[i] = node;
channels->chan_num++;
/* Add the channel node into a chain */
SLIST_INSERT_HEAD(&channels->head, node, next);
}
}
*handle = channels;
return ret;
err:
/* Free the resource when error occurs */
dac_del_channels(channels);
channels = NULL;
return ret;
}
esp_err_t dac_del_channels(dac_channels_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_INITIAL, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels is not deinitialized");
ESP_RETURN_ON_FALSE(!handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels is not disabled");
if (handle->mutex) {
vSemaphoreDelete(handle->mutex);
}
if (handle->mutex_struct) {
free(handle->mutex_struct);
}
while (SLIST_FIRST(&handle->head)) {
dac_channel_info_t *node = SLIST_FIRST(&handle->head);
SLIST_REMOVE_HEAD(&handle->head, next);
s_dac.chan[node->id] = NULL;
free(node);
}
free(handle);
return ESP_OK;
}
esp_err_t dac_channels_enable(dac_channels_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(!handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels has been enabled already");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
dac_channel_info_t *p;
SLIST_FOREACH(p, &handle->head, next) {
gpio_num_t gpio_num = (gpio_num_t)dac_periph_signal.dac_channel_io_num[p->id];
rtc_gpio_init(gpio_num);
rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_DISABLED);
rtc_gpio_pullup_dis(gpio_num);
rtc_gpio_pulldown_dis(gpio_num);
portENTER_CRITICAL(&dac_spinlock);
dac_ll_power_on(p->id);
dac_ll_rtc_sync_by_adc(false);
portEXIT_CRITICAL(&dac_spinlock);
}
handle->is_enabled = true;
/* If the channels has not been intialized to other mode, set it `DAC_STATE_OUTPUT_READY` as default */
if (handle->state == DAC_STATE_INITIAL) {
handle->state = DAC_STATE_OUTPUT_READY;
}
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
esp_err_t dac_channels_disable(dac_channels_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels has not been enabled yet");
ESP_RETURN_ON_FALSE(handle->state < DAC_STATE_DMA_ENABLED, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels is still running");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
dac_channel_info_t *p;
SLIST_FOREACH(p, &handle->head, next) {
gpio_num_t gpio_num = (gpio_num_t)dac_periph_signal.dac_channel_io_num[p->id];
rtc_gpio_deinit(gpio_num);
portENTER_CRITICAL(&dac_spinlock);
dac_ll_power_down(p->id);
portEXIT_CRITICAL(&dac_spinlock);
}
handle->is_enabled = false;
/* If the channels has not been intialized to other mode, set it `DAC_STATE_INITIAL` as default */
if (handle->state == DAC_STATE_OUTPUT_READY) {
handle->state = DAC_STATE_INITIAL;
}
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
/*--------------------------------------------------------------------------
DAC direct voltage outputting APIs
---------------------------------------------------------------------------*/
#if CONFIG_DAC_ISR_IRAM_SAFE
IRAM_ATTR
#endif
esp_err_t dac_channels_set_voltage(dac_channels_handle_t handle, uint8_t value)
{
ESP_RETURN_ON_FALSE_ISR(handle, ESP_ERR_INVALID_ARG, TAG, "NULL pointer");
ESP_RETURN_ON_FALSE_ISR(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "Not enabled");
ESP_RETURN_ON_FALSE_ISR(handle->state == DAC_STATE_OUTPUT_READY, ESP_ERR_INVALID_STATE, TAG, "Incorrect mode");
/* Set the voltage for each channel in the channels */
dac_channel_info_t *p;
SLIST_FOREACH(p, &handle->head, next) {
portENTER_CRITICAL(&dac_spinlock);
dac_ll_update_output_value(p->id, value);
portEXIT_CRITICAL(&dac_spinlock);
}
return ESP_OK;
}
/*--------------------------------------------------------------------------
DAC continuous mode APIs
---------------------------------------------------------------------------*/
esp_err_t dac_channels_init_continuous_mode(dac_channels_handle_t handle, const dac_conti_config_t *conti_cfg)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->state < DAC_STATE_DMA_READY, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels has been initialized already");
ESP_RETURN_ON_FALSE(conti_cfg->desc_num > 1, ESP_ERR_INVALID_STATE, TAG, "At least two DMA descriptor needed");
ESP_RETURN_ON_FALSE(!s_dac.dma_in_use, ESP_ERR_INVALID_STATE, TAG, "Only one set of channels can use DMA output");
esp_err_t ret = ESP_OK;
xSemaphoreTake(handle->mutex, portMAX_DELAY);
#if CONFIG_DAC_ISR_IRAM_SAFE
handle->dma.desc_pool_storage = (uint8_t *)heap_caps_calloc(conti_cfg->desc_num, sizeof(lldesc_t *), DAC_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(handle->dma.desc_pool_storage, ESP_ERR_NO_MEM, err3, TAG, "No memory for message queue storage");
handle->dma.desc_pool_struct = (StaticQueue_t *)heap_caps_calloc(1, sizeof(StaticQueue_t), DAC_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(handle->dma.desc_pool_struct , ESP_ERR_NO_MEM, err3, TAG, "No memory for message queue struct");
handle->dma.desc_pool = xQueueCreateStatic(conti_cfg->desc_num, sizeof(lldesc_t *), handle->dma.desc_pool_storage, handle->dma.desc_pool_struct);
#else
handle->dma.desc_pool = xQueueCreate(conti_cfg->desc_num, sizeof(lldesc_t *));
#endif
ESP_GOTO_ON_FALSE(handle->dma.desc_pool, ESP_ERR_NO_MEM, err3, TAG, "No memory for message queue");
#if CONFIG_PM_ENABLE
esp_pm_lock_type_t pm_lock_type = conti_cfg->clk_src == DAC_DIGI_CLK_SRC_APLL ? ESP_PM_NO_LIGHT_SLEEP : ESP_PM_APB_FREQ_MAX;
ESP_GOTO_ON_ERROR(esp_pm_lock_create(pm_lock_type, 0, "dac_driver", &handle->pm_lock), err3, TAG, "Failed to create DAC pm lock");
#endif
handle->dma.is_running = false;
/* Allocate DMA buffer */
memcpy(&(handle->dma.cfg), conti_cfg, sizeof(dac_conti_config_t));
ESP_GOTO_ON_ERROR(dac_alloc_dma_desc(handle), err2, TAG, "Failed to allocate memory for DMA buffers");
/* Initialize DAC DMA peripheral */
ESP_GOTO_ON_ERROR(dac_dma_periph_init(handle->chan_num ,conti_cfg->freq_hz, conti_cfg->chan_mode == DAC_CHANNEL_MODE_ALTER, conti_cfg->clk_src == DAC_DIGI_CLK_SRC_APLL),
err2, TAG, "Failed to initialize DAC DMA peripheral");
/* Register DMA interrupt */
ESP_GOTO_ON_ERROR(dac_dma_periph_register_intr(dac_default_intr_handler, handle), err1, TAG, "Failed to register DMA interrupt");
/* Connect DAC module to the DMA peripheral */
portENTER_CRITICAL(&dac_spinlock);
dac_ll_digi_enable_dma(true);
portEXIT_CRITICAL(&dac_spinlock);
handle->state = DAC_STATE_DMA_READY;
s_dac.dma_in_use = true;
xSemaphoreGive(handle->mutex);
return ret;
err1:
dac_dma_periph_deinit();
err2:
dac_free_dma_desc(handle);
err3:
if (handle->dma.desc_pool) {
vQueueDelete(handle->dma.desc_pool);
}
#if CONFIG_DAC_ISR_IRAM_SAFE
if (handle->dma.desc_pool_struct) {
free(handle->dma.desc_pool_struct);
}
if (handle->dma.desc_pool_storage) {
free(handle->dma.desc_pool_storage);
}
#endif
xSemaphoreGive(handle->mutex);
return ret;
}
esp_err_t dac_channels_deinit_continuous_mode(dac_channels_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_DMA_READY, ESP_ERR_INVALID_STATE, TAG,
"This set of DAC channels is still running or has been configured to other mode");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
#if CONFIG_DAC_ISR_IRAM_SAFE
free(handle->dma.desc_pool_storage);
handle->dma.desc_pool_storage = NULL;
free(handle->dma.desc_pool_struct);
handle->dma.desc_pool_struct = NULL;
#endif
#if CONFIG_PM_ENABLE
if (handle->pm_lock) {
esp_pm_lock_delete(handle->pm_lock);
handle->pm_lock = NULL;
}
#endif
vQueueDelete(handle->dma.desc_pool);
/* Free DMA buffer */
dac_free_dma_desc(handle);
/* Deregister DMA interrupt */
ESP_RETURN_ON_ERROR(dac_dma_periph_deregister_intr(), TAG, "Failed to deregister DMA interrupt");
/* Deinitialize DMA peripheral */
ESP_RETURN_ON_ERROR(dac_dma_periph_deinit(), TAG, "Failed to deinitialize DAC DMA peripheral");
/* Disconnect DAC module to the DMA peripheral */
portENTER_CRITICAL(&dac_spinlock);
dac_ll_digi_enable_dma(false);
portEXIT_CRITICAL(&dac_spinlock);
if (handle->is_enabled) {
handle->state = DAC_STATE_OUTPUT_READY;
} else {
handle->state = DAC_STATE_INITIAL;
}
s_dac.dma_in_use = false;
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
esp_err_t dac_channels_enable_continuous_mode(dac_channels_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels has not been enabled");
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_DMA_READY, ESP_ERR_INVALID_STATE, TAG,
"This set of DAC channels has started already or not working at DMA mode");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_acquire(handle->pm_lock);
#endif
dac_dma_periph_enable();
portENTER_CRITICAL(&dac_spinlock);
dac_ll_digi_enable_dma(true);
portEXIT_CRITICAL(&dac_spinlock);
handle->state = DAC_STATE_DMA_ENABLED;
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
esp_err_t dac_channels_disable_continuous_mode(dac_channels_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels has not been enabled");
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_DMA_ENABLED, ESP_ERR_INVALID_STATE, TAG,
"This set of DAC channels has stopped already or not working at DMA mode");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
dac_dma_periph_disable();
portENTER_CRITICAL(&dac_spinlock);
dac_ll_digi_enable_dma(false);
portEXIT_CRITICAL(&dac_spinlock);
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_release(handle->pm_lock);
#endif
handle->state = DAC_STATE_DMA_READY;
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
static esp_err_t s_dac_load_dma_data(dac_channels_handle_t handle, uint8_t *buf, size_t buf_size, size_t *w_size, uint32_t timeout_ms)
{
lldesc_t *desc = NULL;
/* Try to get the descriptor from the pool */
ESP_RETURN_ON_FALSE(xQueueReceive(handle->dma.desc_pool, &desc, pdMS_TO_TICKS(timeout_ms)) == pdTRUE,
ESP_ERR_TIMEOUT, TAG, "Get available descriptor timeout");
if (STAILQ_FIRST(&handle->dma.head) != NULL) {
DAC_STAILQ_REMOVE(&handle->dma.head, desc, lldesc_s, qe);
}
/* Get the DMA buffer address (use 'memcpy' to avoid the 'volatile' warning) */
uint8_t *dma_buf;
memcpy(&dma_buf, &desc->buf, sizeof(desc->buf));
#if SOC_DAC_DMA_16BIT_ALIGN && CONFIG_DAC_DMA_AUTO_16BIT_ALIGN
/* Load the data to the high 8 bit in the 16-bit width slot */
size_t _w_size = (buf_size > handle->dma.cfg.buf_size / 2) ? handle->dma.cfg.buf_size / 2 : buf_size;
for (int i = 0; i < _w_size; i++) {
dma_buf[2 * i + 1] = buf[i];
}
lldesc_config(desc, LLDESC_HW_OWNED, 1, 0, _w_size * 2);
desc->size = _w_size * 2;
#else
/* Load the data into the DMA buffer */
size_t _w_size = (buf_size > handle->dma.cfg.buf_size) ? handle->dma.cfg.buf_size : buf_size;
memcpy(dma_buf, buf, _w_size);
lldesc_config(desc, LLDESC_HW_OWNED, 1, 0, _w_size);
desc->size = _w_size;
#endif
*w_size = _w_size;
/* Insert the loaded descriptor to the end of the chain, waiting to be sent */
portENTER_CRITICAL(&dac_spinlock);
STAILQ_INSERT_TAIL(&handle->dma.head, desc, qe);
portEXIT_CRITICAL(&dac_spinlock);
return ESP_OK;
}
esp_err_t dac_channels_write_continuously(dac_channels_handle_t handle, uint8_t *buf, size_t buf_size, size_t *bytes_loaded, uint32_t timeout_ms)
{
DAC_NULL_POINTER_CHECK(handle);
DAC_NULL_POINTER_CHECK(buf);
ESP_RETURN_ON_FALSE(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels has not been enabled");
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_DMA_ENABLED, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels is not started");
#if CONFIG_DAC_ISR_IRAM_SAFE
ESP_RETURN_ON_FALSE(esp_ptr_internal(buf), ESP_ERR_INVALID_ARG, TAG, "the buffer is not in internal RAM");
#endif
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(xSemaphoreTake(handle->mutex, pdMS_TO_TICKS(timeout_ms) == pdTRUE), ESP_ERR_TIMEOUT, TAG, "Take semaphore timeout");
size_t w_size = 0;
size_t src_buf_size = buf_size;
/* When there is no descriptor in the chain, DMA has stopped, load data and start the DMA link */
if (STAILQ_FIRST(&handle->dma.head) == NULL) {
/* Break the chain if DMA still running */
for (int i = 0; handle->dma.is_running && i < handle->dma.cfg.desc_num; i++) {
handle->dma.desc[i]->empty = 0;
}
for (int i = 0;
i < handle->dma.cfg.desc_num && buf_size > 0;
i++, buf += w_size, buf_size -= w_size) {
ESP_GOTO_ON_ERROR(s_dac_load_dma_data(handle, buf, buf_size, &w_size, timeout_ms), err, TAG, "Load data failed");
}
/* Wait for the previous DMA stop */
while (handle->dma.is_running) {}
handle->dma.is_cyclic = false;
dac_dma_periph_dma_trans_start((uint32_t)(STAILQ_FIRST(&handle->dma.head)));
handle->dma.is_running = true;
}
/* If the source buffer is not totally loaded, keep loading the rest data */
while (buf_size > 0) {
/* If the DMA stopped but there are still some decriptors not sent, start the DMA again */
if ((!handle->dma.is_running) && STAILQ_FIRST(&handle->dma.head)) {
dac_dma_periph_dma_trans_start((uint32_t)(STAILQ_FIRST(&handle->dma.head)));
}
ESP_GOTO_ON_ERROR(s_dac_load_dma_data(handle, buf, buf_size, &w_size, timeout_ms), err, TAG, "Load data failed");
buf += w_size;
buf_size -= w_size;
}
err:
/* The bytes number that has been loaded */
if (bytes_loaded) {
*bytes_loaded = src_buf_size - buf_size;
}
xSemaphoreGive(handle->mutex);
return ret;
}
esp_err_t dac_channels_write_cyclically(dac_channels_handle_t handle, uint8_t *buf, size_t buf_size, size_t *bytes_loaded, uint32_t timeout_ms)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels has not been enabled");
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_DMA_ENABLED, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels is not started");
#if CONFIG_DAC_ISR_IRAM_SAFE
ESP_RETURN_ON_FALSE(esp_ptr_internal(buf), ESP_ERR_INVALID_ARG, TAG, "the buffer is not in internal RAM");
#endif
esp_err_t ret = ESP_OK;
ESP_RETURN_ON_FALSE(xSemaphoreTake(handle->mutex, pdMS_TO_TICKS(timeout_ms) == pdTRUE), ESP_ERR_TIMEOUT, TAG, "Take semaphore timeout");
/* Break the DMA descriptor chain to stop the DMA first */
for (int i = 0; i < handle->dma.cfg.desc_num; i++) {
handle->dma.desc[i]->empty = 0;
}
size_t src_buf_size = buf_size;
/* If the source buffer size is smaller than the DMA buffer size, load the source buffer into two descriptors */
if (buf_size < handle->dma.cfg.buf_size) {
/* Load data */
size_t half_size = buf_size / 2;
memcpy(handle->dma.bufs[0], buf, half_size);
memcpy(handle->dma.bufs[1], buf, buf_size - half_size);
lldesc_config(handle->dma.desc[0], LLDESC_HW_OWNED, 1, 0, half_size);
lldesc_config(handle->dma.desc[1], LLDESC_HW_OWNED, 1, 0, buf_size - half_size);
buf_size = 0;
/* Link as a circle */
handle->dma.desc[0]->empty = (uint32_t)handle->dma.desc[1];
handle->dma.desc[1]->empty = (uint32_t)handle->dma.desc[0];
} else {
int i;
for (i = 0; i < handle->dma.cfg.desc_num && buf_size > 0; i++) {
size_t w_size = buf_size > handle->dma.cfg.buf_size ? handle->dma.cfg.buf_size : buf_size;
memcpy(handle->dma.bufs[i], buf, w_size);
lldesc_config(handle->dma.desc[i], LLDESC_HW_OWNED, 1, 0, w_size);
handle->dma.desc[i]->empty = (uint32_t)(i < handle->dma.cfg.desc_num - 1 ? handle->dma.desc[i+1] :0);
buf_size -= w_size;
buf += w_size;
}
handle->dma.desc[i-1]->empty = (uint32_t)(handle->dma.desc[0]);
}
/* Wait for the previous DMA stop */
while (handle->dma.is_running) {}
handle->dma.is_cyclic = true;
dac_dma_periph_dma_trans_start((uint32_t)handle->dma.desc[0]);
handle->dma.is_running = true;
if (bytes_loaded) {
*bytes_loaded = src_buf_size - buf_size;
}
if (buf_size > 0) {
ESP_LOGW(TAG, "The cyclic buffer size exceeds the total DMA buffer size: %d(desc_num) * %d(buf_size) = %d",
handle->dma.cfg.desc_num, handle->dma.cfg.buf_size, handle->dma.cfg.buf_size * handle->dma.cfg.desc_num);
}
xSemaphoreGive(handle->mutex);
return ret;
}
/*--------------------------------------------------------------------------
DAC cosine wave outputting APIs
---------------------------------------------------------------------------*/
esp_err_t dac_channels_init_cosine_mode(dac_channels_handle_t handle, const dac_cosine_config_t *cw_cfg)
{
DAC_NULL_POINTER_CHECK(handle);
DAC_NULL_POINTER_CHECK(cw_cfg);
ESP_RETURN_ON_FALSE((handle->state == DAC_STATE_INITIAL) | (handle->state == DAC_STATE_OUTPUT_READY),
ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels has been initialized already");
ESP_RETURN_ON_FALSE(cw_cfg->freq_hz >= 130, ESP_ERR_NOT_SUPPORTED, TAG, "The cosine wave generator doesn't support frequency below 130 Hz");
esp_err_t ret = ESP_OK;
xSemaphoreTake(handle->mutex, portMAX_DELAY);
#if CONFIG_PM_ENABLE
ESP_GOTO_ON_ERROR(esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "dac_driver", &handle->pm_lock), err, TAG, "Failed to create DAC pm lock");
#endif
portENTER_CRITICAL(&dac_spinlock);
dac_channel_info_t *p;
SLIST_FOREACH(p, &handle->head, next) {
/* Connect DAC module to cosine wave generator */
dac_ll_cw_set_channel(p->id, true);
/* Set coefficients for cosine wave generator */
dac_ll_cw_set_freq(cw_cfg->freq_hz, periph_rtc_dig_clk8m_get_freq());
dac_ll_cw_set_scale(p->id, cw_cfg->scale);
dac_ll_cw_set_phase(p->id, cw_cfg->phase == DAC_COSINE_PHASE_180 ? DAC_LL_CW_PHASE_180 : DAC_LL_CW_PHASE_0);
dac_ll_cw_set_dc_offset(p->id, cw_cfg->offset);
}
portEXIT_CRITICAL(&dac_spinlock);
handle->state = DAC_STATE_CW_READY;
#if CONFIG_PM_ENABLE
err:
#endif
xSemaphoreGive(handle->mutex);
return ret;
}
esp_err_t dac_channels_deinit_cosine_mode(dac_channels_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_CW_READY, ESP_ERR_INVALID_STATE, TAG,
"This set of DAC channels is still running or not working at cosine wave mode");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
#if CONFIG_PM_ENABLE
if (handle->pm_lock) {
esp_pm_lock_delete(handle->pm_lock);
handle->pm_lock = NULL;
}
#endif
portENTER_CRITICAL(&dac_spinlock);
dac_channel_info_t *p;
SLIST_FOREACH(p, &handle->head, next) {
/* Disonnect DAC module to cosine wave generator */
dac_ll_cw_set_channel(p->id, false);
}
portEXIT_CRITICAL(&dac_spinlock);
if (handle->is_enabled) {
handle->state = DAC_STATE_OUTPUT_READY;
} else {
handle->state = DAC_STATE_INITIAL;
}
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
esp_err_t dac_channels_start_cosine_output(dac_channels_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels has not been enabled");
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_CW_READY, ESP_ERR_INVALID_STATE, TAG,
"This set of DAC channels has started already or not working at cosine wave mode");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_acquire(handle->pm_lock);
#endif
portENTER_CRITICAL(&dac_spinlock);
dac_ll_cw_generator_enable();
portEXIT_CRITICAL(&dac_spinlock);
handle->state = DAC_STATE_CW_RUNNING;
xSemaphoreGive(handle->mutex);
return ESP_OK;
}
esp_err_t dac_channels_stop_cosine_output(dac_channels_handle_t handle)
{
DAC_NULL_POINTER_CHECK(handle);
ESP_RETURN_ON_FALSE(handle->is_enabled, ESP_ERR_INVALID_STATE, TAG, "This set of DAC channels has not been enabled");
ESP_RETURN_ON_FALSE(handle->state == DAC_STATE_CW_RUNNING, ESP_ERR_INVALID_STATE, TAG,
"This set of DAC channels has stopped already or not working at cosine wave mode");
xSemaphoreTake(handle->mutex, portMAX_DELAY);
portENTER_CRITICAL(&dac_spinlock);
dac_ll_cw_generator_disable();
portEXIT_CRITICAL(&dac_spinlock);
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_release(handle->pm_lock);
#endif
handle->state = DAC_STATE_CW_READY;
xSemaphoreGive(handle->mutex);
return ESP_OK;
}

View File

@ -7,20 +7,28 @@
#include "freertos/FreeRTOS.h"
#include "hal/adc_ll.h"
#include "hal/i2s_ll.h"
#include "hal/i2s_types.h"
#include "soc/i2s_periph.h"
#include "esp_private/dac_dma.h"
#include "../dac_dma.h"
#include "esp_private/i2s_platform.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 8
#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
@ -29,21 +37,51 @@ static const char *TAG = "DAC_DMA";
extern portMUX_TYPE dac_spinlock; /* Global DAC spinlock */
static esp_err_t dac_dma_periph_set_clock(uint32_t freq_hz){
static uint32_t dac_set_apll(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 %d Hz", real_freq);
}
ESP_LOGD(TAG, "APLL expected frequency is %d Hz, real frequency is %d Hz", expt_freq, real_freq);
return real_freq;
}
static esp_err_t 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 = I2S_LL_BASE_CLK; // use PLL clock as default
uint32_t sclk; // use 160M PLL clock as default, minimun support freq: 19.6 KHz maximun support freq: 2.5 MHz
if (is_apll) {
sclk = dac_set_apll(mclk);
ESP_RETURN_ON_FALSE(sclk, ESP_ERR_INVALID_ARG, TAG, "set APLL coefficients failed");
} else {
sclk = I2S_LL_BASE_CLK;
}
uint32_t mclk_div = sclk / mclk;
/* Check if the configuration is correct */
// TODO: expand the frequency range
ESP_RETURN_ON_FALSE(sclk / (float)mclk > 1.99, ESP_ERR_INVALID_ARG, TAG, "Frequency is too large, the mclk division is below minimum value 2");
ESP_RETURN_ON_FALSE(mclk_div < 256, ESP_ERR_INVALID_ARG, TAG, "Frequency is too small, the mclk division exceed the maximum value 255");
ESP_LOGD(TAG, "[sclk] %d [mclk] %d [mclk_div] %d [bclk] %d [bclk_div] %d", sclk, mclk, mclk_div, bclk, bclk_div);
portENTER_CRITICAL(&dac_spinlock);
i2s_ll_tx_clk_set_src(s_ddp->periph_dev, I2S_CLK_D2CLK);
i2s_ll_tx_clk_set_src(s_ddp->periph_dev, is_apll ? I2S_CLK_SRC_APLL : I2S_CLK_SRC_DEFAULT);
i2s_ll_tx_set_mclk(s_ddp->periph_dev, sclk, mclk, mclk_div);
i2s_ll_tx_set_bck_div_num(s_ddp->periph_dev, bclk_div);
portEXIT_CRITICAL(&dac_spinlock);
@ -51,23 +89,32 @@ static esp_err_t dac_dma_periph_set_clock(uint32_t freq_hz){
return ESP_OK;
}
esp_err_t dac_dma_periph_init(int chan_num, uint32_t freq_hz, bool is_alternate)
esp_err_t dac_dma_periph_init(uint8_t chan_num, 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_priv_register_object("dac_dma", DAC_DMA_PERIPH_I2S_NUM), TAG, "Failed to acquire DAC DMA peripheral");
ESP_RETURN_ON_ERROR(i2s_platform_acquire_occupation(DAC_DMA_PERIPH_I2S_NUM, "dac_dma"), TAG, "Failed to acquire DAC DMA peripheral");
/* Allocate DAC DMA peripheral object */
s_ddp = (dac_dma_periph_i2s_t *)calloc(1, sizeof(dac_dma_periph_i2s_t));
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);
ESP_GOTO_ON_ERROR(dac_dma_periph_set_clock(freq_hz), err, TAG, "Failed to set clock of DMA peripheral");
if (is_apll) {
periph_rtc_apll_acquire();
s_ddp->use_apll = true;
}
ESP_GOTO_ON_ERROR(dac_dma_periph_set_clock(freq_hz, is_apll), err, TAG, "Failed to set clock of DMA peripheral");
portENTER_CRITICAL(&dac_spinlock);
i2s_ll_enable_builtin_dac(s_ddp->periph_dev, true);
i2s_ll_tx_reset(s_ddp->periph_dev);
i2s_ll_tx_set_slave_mod(s_ddp->periph_dev, false);
i2s_ll_tx_set_sample_bit(s_ddp->periph_dev, DAC_DMA_PERIPH_I2S_BIT_WIDTH, DAC_DMA_PERIPH_I2S_BIT_WIDTH);
i2s_ll_tx_enable_mono_mode(s_ddp->periph_dev, !is_alternate);
i2s_ll_tx_select_std_slot(s_ddp->periph_dev, 0x03, !is_alternate);
i2s_ll_tx_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);
@ -84,11 +131,15 @@ err:
esp_err_t dac_dma_periph_deinit(void)
{
ESP_RETURN_ON_ERROR(i2s_priv_deregister_object(DAC_DMA_PERIPH_I2S_NUM), TAG, "Failed to release DAC DMA peripheral");
ESP_RETURN_ON_ERROR(i2s_platform_release_occupation(DAC_DMA_PERIPH_I2S_NUM), TAG, "Failed to release DAC DMA peripheral");
if (s_ddp) {
if (s_ddp->intr_handle) {
dac_dma_periph_deregister_intr();
}
if (s_ddp->use_apll) {
periph_rtc_apll_release();
s_ddp->use_apll = false;
}
free(s_ddp);
s_ddp = NULL;
}
@ -104,7 +155,7 @@ esp_err_t dac_dma_periph_register_intr(intr_handler_t intr_handler_func, void *u
intr_handler_func, user_ctx, &(s_ddp->intr_handle)),
TAG, "Failed to register DAC DMA interrupt");
portENTER_CRITICAL(&dac_spinlock);
i2s_ll_enable_intr(s_ddp->periph_dev, I2S_LL_EVENT_TX_EOF, true);
i2s_ll_enable_intr(s_ddp->periph_dev, I2S_LL_EVENT_TX_EOF | I2S_LL_EVENT_TX_TEOF, true);
portEXIT_CRITICAL(&dac_spinlock);
return ESP_OK;
}
@ -114,7 +165,7 @@ esp_err_t dac_dma_periph_deregister_intr(void)
ESP_RETURN_ON_FALSE(s_ddp, ESP_ERR_INVALID_STATE, TAG, "DAC DMA peripheral has not initialized yet");
if (s_ddp->intr_handle) {
portENTER_CRITICAL(&dac_spinlock);
i2s_ll_enable_intr(s_ddp->periph_dev, I2S_LL_EVENT_TX_EOF, false);
i2s_ll_enable_intr(s_ddp->periph_dev, I2S_LL_EVENT_TX_EOF | I2S_LL_EVENT_TX_TEOF, false);
portEXIT_CRITICAL(&dac_spinlock);
esp_intr_free(s_ddp->intr_handle);
s_ddp->intr_handle = NULL;
@ -132,8 +183,9 @@ void dac_dma_periph_enable(void)
/* Start */
i2s_ll_enable_dma(s_ddp->periph_dev,true);
i2s_ll_tx_enable_intr(s_ddp->periph_dev);
// i2s_ll_tx_start_link(s_ddp->periph_dev, (uint32_t)desc_addr);
// i2s_ll_tx_start(s_ddp->periph_dev);
i2s_ll_tx_start(s_ddp->periph_dev);
i2s_ll_dma_enable_eof_on_fifo_empty(s_ddp->periph_dev, true);
i2s_ll_dma_enable_auto_write_back(s_ddp->periph_dev, true);
portEXIT_CRITICAL(&dac_spinlock);
/* Enable interrupt */
esp_intr_enable(s_ddp->intr_handle);
@ -151,12 +203,14 @@ void dac_dma_periph_disable(void)
i2s_ll_tx_stop_link(s_ddp->periph_dev);
i2s_ll_tx_disable_intr(s_ddp->periph_dev);
i2s_ll_enable_dma(s_ddp->periph_dev, false);
i2s_ll_dma_enable_eof_on_fifo_empty(s_ddp->periph_dev, false);
i2s_ll_dma_enable_auto_write_back(s_ddp->periph_dev, false);
portEXIT_CRITICAL(&dac_spinlock);
/* Disable interrupt */
esp_intr_disable(s_ddp->intr_handle);
}
bool inline dac_dma_periph_intr_is_triggered(void)
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) {
@ -164,10 +218,13 @@ bool inline dac_dma_periph_intr_is_triggered(void)
return false;
}
i2s_ll_clear_intr_status(s_ddp->periph_dev, status);
return (status & I2S_LL_EVENT_TX_EOF) != 0;
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 inline dac_dma_periph_intr_get_eof_desc(void)
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);
@ -177,11 +234,6 @@ uint32_t inline dac_dma_periph_intr_get_eof_desc(void)
void inline dac_dma_periph_dma_trans_start(uint32_t desc_addr)
{
portENTER_CRITICAL(&dac_spinlock);
// TODO: check whether need to reset
i2s_ll_tx_reset(s_ddp->periph_dev);
i2s_ll_tx_reset_dma(s_ddp->periph_dev);
i2s_ll_tx_reset_fifo(s_ddp->periph_dev);
i2s_ll_tx_start_link(s_ddp->periph_dev, desc_addr);
i2s_ll_tx_start(s_ddp->periph_dev);
portEXIT_CRITICAL(&dac_spinlock);
}

View File

@ -4,14 +4,23 @@
* SPDX-License-Identifier: Apache-2.0
*/
#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 "esp_private/dac_dma.h"
#include "esp_private/periph_ctrl.h"
#include "driver/spi_common_internal.h"
#include "soc/soc.h"
#include "soc/soc_caps.h"
#include "../dac_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
@ -19,6 +28,7 @@ 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
@ -27,41 +37,74 @@ static const char *TAG = "DAC_DMA";
extern portMUX_TYPE dac_spinlock; /* Global DAC spinlock */
static uint32_t dac_set_apll_freq(uint32_t expt_freq)
{
/* 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 %d Hz", real_freq);
}
ESP_LOGD(TAG, "APLL expected frequency is %d Hz, real frequency is %d Hz", expt_freq, real_freq);
return real_freq;
}
/**
* @brief Calculate and set DAC data frequency
* @note DAC clcok shares clock devider with ADC, the clock source is APB or APLL on ESP32-S2
* freq_hz = (source_clk / (clk_div + (b / a) + 1)) / interval
* interval range: 1~4095, to avoid decimal as possible, all calculations will base on interval = 4000
* @param freq_hz DAC byte frequency
* @param freq_hz DAC byte transmit frequency
* @return
* - ESP_OK config success
* - ESP_ERR_INVALID_ARG invalid frequency
*/
// TODO: check clock again, the dma data seems abnormal
static esp_err_t dac_dma_periph_set_clock(uint32_t freq_hz){
ESP_RETURN_ON_FALSE(freq_hz >= 80, ESP_ERR_INVALID_ARG, TAG, "the DAC frequency should be greater than 80 Hz");
// TODO: replace 80000000 with APB or APLL clock frequency
// when interval = 4000, max_freq = 20k min_freq = 80
uint32_t freq_khz = freq_hz / 1000;
/* If freq_khz < 20k, interval = 4000 is enough, so mutiple = 1,
* otherwise interval need to zoom out to increase the max_freq,
* And in order to avoid decimal as possible, multiple better to be 2^n */
uint32_t multiple = freq_khz < 20 ? 1 : 1 << (32 - __builtin_clz(freq_khz / 20)); // Multiple need to be 2^n to avoid decimal
uint32_t interval = 4000 / multiple; // Zoom in the max/min supported freq by zooming out interval
ESP_RETURN_ON_FALSE(interval > 0, ESP_ERR_INVALID_ARG, TAG, "the DAC frequency is too big");
static esp_err_t dac_dma_periph_set_clock(uint32_t freq_hz, bool is_apll){
/* Step 1: Determine the digital clock source frequency */
uint32_t digi_ctrl_freq; // Digital controller clock
if (is_apll) {
/* Theoretical frequency range (due to the limitation of DAC, the maximum frequency may not reach):
* SOC_APLL_MAX_HZ: 119.24 Hz ~ 67.5 MHz
* SOC_APLL_MIN_HZ: 5.06 Hz ~ 2.65 MHz */
digi_ctrl_freq = dac_set_apll_freq(freq_hz < 120 ? SOC_APLL_MIN_HZ :SOC_APLL_MAX_HZ);
ESP_RETURN_ON_FALSE(digi_ctrl_freq, ESP_ERR_INVALID_ARG, TAG, "set APLL coefficients failed");
} else {
digi_ctrl_freq = APB_CLK_FREQ;
}
uint32_t clk_div = (80000000 / interval) / freq_hz;
uint32_t mod = (80000000 / interval) % freq_hz;
/* 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 inverval
/* 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)freq_hz) + 0.5);
uint32_t diff = (uint32_t)abs((int)(mod * tmp_b) - (int)(freq_hz * tmp_a));
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;
@ -75,28 +118,37 @@ static esp_err_t dac_dma_periph_set_clock(uint32_t freq_hz){
}
finish:
/* Step 4: Set the clock coefficients */
portENTER_CRITICAL(&dac_spinlock);
dac_ll_digi_clk_inv(true);
dac_ll_digi_set_trigger_interval(interval); // secondary clock division
adc_ll_digi_controller_clk_div(clk_div, b, a);
adc_ll_digi_clk_sel(false);
adc_ll_digi_controller_clk_div(clk_div - 1, b, a);
adc_ll_digi_clk_sel(is_apll);
portEXIT_CRITICAL(&dac_spinlock);
return ESP_OK;
}
esp_err_t dac_dma_periph_init(int chan_num, uint32_t freq_hz, bool is_alternate)
esp_err_t dac_dma_periph_init(uint8_t chan_num, 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");
// TODO: reference count, maybe only required on s2
periph_module_enable(PERIPH_SARADC_MODULE);
/* Allocate DAC DMA peripheral object */
s_ddp = (dac_dma_periph_spi_t *)calloc(1, sizeof(dac_dma_periph_spi_t));
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);
// TODO: clock may related to convert mode (mono/stereo)
ESP_GOTO_ON_ERROR(dac_dma_periph_set_clock(freq_hz), err, TAG, "Failed to set clock of DMA peripheral");
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(dac_dma_periph_set_clock(trans_freq_hz, is_apll), err, TAG, "Failed to set clock of DMA peripheral");
portENTER_CRITICAL(&dac_spinlock);
dac_ll_digi_set_convert_mode(is_alternate);
@ -110,12 +162,15 @@ err:
esp_err_t dac_dma_periph_deinit(void)
{
ESP_RETURN_ON_FALSE(spicommon_periph_free(DAC_DMA_PERIPH_SPI_HOST), ESP_FAIL, TAG, "Failed to release DAC DMA peripheral");
// TODO: reference count, maybe only required on s2
periph_module_disable(PERIPH_SARADC_MODULE);
if (s_ddp) {
if (s_ddp->intr_handle) {
dac_dma_periph_deregister_intr();
}
if (s_ddp->use_apll) {
periph_rtc_apll_release();
s_ddp->use_apll = false;
}
free(s_ddp);
s_ddp = NULL;
}
@ -132,7 +187,7 @@ esp_err_t dac_dma_periph_register_intr(intr_handler_t intr_handler_func, void *u
ESP_GOTO_ON_ERROR(esp_intr_alloc(spicommon_irqdma_source_for_host(DAC_DMA_PERIPH_SPI_HOST),
0, intr_handler_func, user_ctx, &(s_ddp->intr_handle)), err, TAG, "Failed to register DAC DMA interrupt");
portENTER_CRITICAL(&dac_spinlock);
spi_ll_enable_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_EOF);
spi_ll_enable_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_EOF | SPI_LL_INTR_OUT_TOTAL_EOF);
portEXIT_CRITICAL(&dac_spinlock);
return ret;
err:
@ -146,7 +201,7 @@ esp_err_t dac_dma_periph_deregister_intr(void)
ESP_RETURN_ON_ERROR(spicommon_dma_chan_free(DAC_DMA_PERIPH_SPI_HOST), TAG, "Failed to free dma peripheral channel");
if (s_ddp->intr_handle) {
portENTER_CRITICAL(&dac_spinlock);
spi_ll_disable_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_EOF);
spi_ll_disable_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_EOF | SPI_LL_INTR_OUT_TOTAL_EOF);
portEXIT_CRITICAL(&dac_spinlock);
esp_intr_free(s_ddp->intr_handle);
s_ddp->intr_handle = NULL;
@ -177,11 +232,14 @@ void dac_dma_periph_disable(void)
esp_intr_disable(s_ddp->intr_handle);
}
bool IRAM_ATTR dac_dma_periph_intr_is_triggered(void)
uint32_t IRAM_ATTR dac_dma_periph_intr_is_triggered(void)
{
uint32_t is_triggered = spi_ll_get_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_EOF);
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);
return is_triggered;
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)

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

@ -6,17 +6,14 @@
#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 "driver/dac_types_legacy.h"
#include "soc/dac_periph.h"
#include "hal/dac_hal.h"
#include "hal/dac_types.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.
@ -27,7 +24,7 @@ static __attribute__((unused)) const char *TAG = "DAC";
---------------------------------------------------------------*/
esp_err_t dac_pad_get_io_num(dac_channel_t channel, gpio_num_t *gpio_num)
{
ESP_RETURN_ON_FALSE(channel < DAC_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
ESP_RETURN_ON_FALSE(channel < SOC_DAC_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
*gpio_num = (gpio_num_t)dac_periph_signal.dac_channel_io_num[channel];
@ -36,7 +33,7 @@ esp_err_t dac_pad_get_io_num(dac_channel_t channel, gpio_num_t *gpio_num)
static esp_err_t dac_rtc_pad_init(dac_channel_t channel)
{
ESP_RETURN_ON_FALSE(channel < DAC_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
ESP_RETURN_ON_FALSE(channel < SOC_DAC_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
gpio_num_t gpio_num = 0;
dac_pad_get_io_num(channel, &gpio_num);
@ -50,12 +47,12 @@ static esp_err_t dac_rtc_pad_init(dac_channel_t channel)
esp_err_t dac_output_enable(dac_channel_t channel)
{
ESP_RETURN_ON_FALSE(channel < DAC_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
ESP_RETURN_ON_FALSE(channel < SOC_DAC_PERIPH_NUM, 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);
dac_ll_power_on(channel);
dac_ll_rtc_sync_by_adc(false);
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
@ -63,10 +60,10 @@ esp_err_t dac_output_enable(dac_channel_t channel)
esp_err_t dac_output_disable(dac_channel_t channel)
{
ESP_RETURN_ON_FALSE(channel < DAC_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
ESP_RETURN_ON_FALSE(channel < SOC_DAC_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
portENTER_CRITICAL(&rtc_spinlock);
dac_hal_power_down(channel);
dac_ll_power_down(channel);
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
@ -74,10 +71,10 @@ esp_err_t dac_output_disable(dac_channel_t channel)
esp_err_t dac_output_voltage(dac_channel_t channel, uint8_t dac_value)
{
ESP_RETURN_ON_FALSE(channel < DAC_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
ESP_RETURN_ON_FALSE(channel < SOC_DAC_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
portENTER_CRITICAL(&rtc_spinlock);
dac_hal_update_output_value(channel, dac_value);
dac_ll_update_output_value(channel, dac_value);
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
@ -85,10 +82,10 @@ esp_err_t dac_output_voltage(dac_channel_t channel, uint8_t dac_value)
esp_err_t dac_out_voltage(dac_channel_t channel, uint8_t dac_value)
{
ESP_RETURN_ON_FALSE(channel < DAC_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
ESP_RETURN_ON_FALSE(channel < SOC_DAC_PERIPH_NUM, ESP_ERR_INVALID_ARG, TAG, "DAC channel error");
portENTER_CRITICAL(&rtc_spinlock);
dac_hal_update_output_value(channel, dac_value);
dac_ll_update_output_value(channel, dac_value);
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
@ -97,7 +94,7 @@ esp_err_t dac_out_voltage(dac_channel_t channel, uint8_t dac_value)
esp_err_t dac_cw_generator_enable(void)
{
portENTER_CRITICAL(&rtc_spinlock);
dac_hal_cw_generator_enable();
dac_ll_cw_generator_enable();
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
@ -106,7 +103,7 @@ esp_err_t dac_cw_generator_enable(void)
esp_err_t dac_cw_generator_disable(void)
{
portENTER_CRITICAL(&rtc_spinlock);
dac_hal_cw_generator_disable();
dac_ll_cw_generator_disable();
portEXIT_CRITICAL(&rtc_spinlock);
return ESP_OK;
@ -117,7 +114,7 @@ esp_err_t dac_cw_generator_config(dac_cw_config_t *cw)
ESP_RETURN_ON_FALSE(cw, ESP_ERR_INVALID_ARG, TAG, "invalid clock configuration");
portENTER_CRITICAL(&rtc_spinlock);
dac_ll_cw_set_freq(cw->freq);
dac_ll_cw_set_freq(cw->freq, periph_rtc_dig_clk8m_get_freq());
dac_ll_cw_set_scale(cw->en_ch, cw->scale);
dac_ll_cw_set_phase(cw->en_ch, cw->phase);
dac_ll_cw_set_dc_offset(cw->en_ch, cw->offset);
@ -126,3 +123,19 @@ esp_err_t dac_cw_generator_config(dac_cw_config_t *cw)
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_new_channels()` should be NULL at runtime.
extern __attribute__((weak)) esp_err_t dac_new_channels(const void *dac_cfg, void **handle);
if ((void *)dac_new_channels != NULL) {
ESP_EARLY_LOGE(TAG, "CONFLICT! The new DAC driver is not allowed to be used together with the legacy driver");
abort();
}
ESP_EARLY_LOGW(TAG, "legacy driver is deprecated, please migrate to `driver/dac_driver.h` instead");
}

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_driver.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
#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
#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 "driver/dac_types_legacy.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

@ -6,13 +6,62 @@
#pragma once
#include "driver/dac_types.h"
#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.
*/

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

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

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

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

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

@ -0,0 +1,298 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "driver/dac_types.h"
#include "driver/gpio.h"
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
#if SOC_DAC_SUPPORTED
/**
* @brief DAC channel configuration
*
*/
typedef struct {
dac_channel_mask_t chan_sel; /*!< Using DAC channel mask to select the channels */
} dac_channels_config_t;
/**
* @brief DAC continuous mode configration
*
*/
typedef struct {
uint32_t freq_hz; /*!< The frequency of DAC conversion in continuous mode, unit: Hz
* The supported range is related to the target and the clock source.
* For the clock `DAC_DIGI_CLK_SRC_DEFAULT`: the range is 19.6 KHz to several MHz on ESP32
* and 77 Hz to several MHz on ESP32-S2.
* For the clock `DAC_DIGI_CLK_SRC_APLL`: the range is 648 Hz to several MHz on ESP32
* and 6 Hz to several MHz on ESP32-S2.
* Typically not suggest to set the frequency higher than 2 MHz, otherwise the severe distortion will appear
*/
dac_conti_clk_src_t clk_src; /*!< The clock source of digital controller, which can affect the range of supported frequency
* Currently `DAC_DIGI_CLK_SRC_DEFAULT` and `DAC_DIGI_CLK_SRC_APLL` are available
*/
uint32_t desc_num; /*!< The number of DMA descriptor, at least 2 descriptors are required
* The number of descriptors is directly proportional to the max data buffer size while converting in cyclic output
* but only need to ensure it is greater than '1' in acyclic output
* Typically, suggest to set the number bigger than 5, in case the DMA stopped while sending a short buffer
*/
uint32_t buf_size; /*!< The DMA buffer size, should be within 4092 bytes. Each DMA buffer will be attached to a DMA descriptor,
* i.e. the number of DMA buffer will be equal to the DMA descriptor number
* The DMA buffer size is not allowed to be greater than 4092 bytes
* The total DMA buffer size equal to `desc_num * buf_size`
* Typically, suggest to set the size to the multiple of 4
*/
dac_conti_channel_mode_t chan_mode; /*!< The channel mode of continuous mode, only take effect when multiple channels enabled, depends converting the buffer alternately or simultaneously */
} dac_conti_config_t;
/**
* @brief DAC cosine wave gnerator configuration
*
*/
typedef struct {
uint32_t freq_hz; /*!< The frequency of cosine wave, unit: Hz.
* The cosine wave generator is driven by RTC clock which is about SOC_CLK_RC_FAST_FREQ_APPROX Hz by default,
* With the default RTC clock, the minimun frequency of cosine wave is about 130 Hz,
* Although it can support up to serveral MHz frequency theoretically,
* the waveform will distort at high frequency due to the hardware limitation.
* Typically not suggest to set the frequency higher than 200 KHz
*/
dac_cosine_clk_src_t clk_src; /*!< The clock source of the cosine wave generator, currently only support `DAC_COSINE_CLK_SRC_DEFAULT` which comes from RTC FAST clock */
dac_cosine_scale_t scale; /*!< The scale of cosine wave amplitude */
dac_cosine_phase_t phase; /*!< The phase of cosine wave */
int8_t offset; /*!< The DC offset of cosine wave */
} dac_cosine_config_t;
typedef struct dac_channels_s *dac_channels_handle_t; /*!< DAC channels' handle of DAC peripheral, one or multiple DAC channels can be controlled by this handle */
/*--------------------------------------------------------------------------
DAC common APIs
---------------------------------------------------------------------------*/
/**
* @brief Allocate a new DAC channels' handle
* @note The driver supports to manage one single channel by enabling only one channel in the channel mask,
* or multiple channels together as a whole by enabling multiple channels in the channel mask.
* Moreover, the channels can also be managed separately if they are allocated separately.
*
* @param[in] dac_cfg DAC basic configuration
* @param[out] handle DAC channels handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_NO_MEM No memory for a new DAC handle
* - ESP_ERR_INVALID_STATE The specified DAC channel is occupied already
* - ESP_OK Success to allocate DAC channels
*/
esp_err_t dac_new_channels(const dac_channels_config_t *dac_cfg, dac_channels_handle_t *handle);
/**
* @brief Delete and free the DAC channels
*
* @param[in] handle DAC channels handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The channels are not disabled
* - ESP_OK Success to delete the channels
*/
esp_err_t dac_del_channels(dac_channels_handle_t handle);
/**
* @brief Enabled the DAC channels in the channels
* @note GPIOs of DAC channles will be enabled in this step
*
* @param[in] handle DAC channels handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The channels has enabled already or the channels are running
* - ESP_OK Success to enable the channels
*/
esp_err_t dac_channels_enable(dac_channels_handle_t handle);
/**
* @brief Disable the DAC channels in the channels
*
* @param[in] handle DAC channels handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The channels has disabled already or the channels are running
* - ESP_OK Success to enable the channels
*/
esp_err_t dac_channels_disable(dac_channels_handle_t handle);
/*--------------------------------------------------------------------------
DAC direct voltage outputting APIs
---------------------------------------------------------------------------*/
/**
* @brief DAC channels output a voltage directly
* @note This function is available when DAC chennels is enbled
* @note Please enable 'DAC ISR IRAM-Safe' in memuconfig when it is called in an IRAM safe ISR
*
* @param[in] handle DAC channels handle
* @param[in] value The digital value of the voltage
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The channels are not enabled
* - ESP_OK Success to enable the channels
*/
esp_err_t dac_channels_set_voltage(dac_channels_handle_t handle, uint8_t value);
/*--------------------------------------------------------------------------
DAC continuous outputting APIs
---------------------------------------------------------------------------*/
/**
* @brief Initialize the DAC channels to continuous mode
* @note DAC can convert digital data continuously in continuous mode
*
* @param[in] handle DAC channels handle
* @param[in] conti_cfg DAC continuous mode configuration
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC channels has been initialized already
* - ESP_ERR_NO_MEM No memory for DAC continuous mode
* - ESP_OK Success to initializing the DAC channels to continuous mode
*/
esp_err_t dac_channels_init_continuous_mode(dac_channels_handle_t handle, const dac_conti_config_t *conti_cfg);
/**
* @brief Deinitialize the continuous mode of the DAC channels
* @note It can only be deinitialized when the continuous output is disabled
*
* @param[in] handle DAC channels handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC continuous mode is not disabled yet
* - ESP_OK Success to deinitialize the DAC continuous mode
*/
esp_err_t dac_channels_deinit_continuous_mode(dac_channels_handle_t handle);
/**
* @brief Enable the DAC continuous output
*
* @param[in] handle DAC channels handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC continuous mode has been enabled already
* - ESP_OK Success to start the continuous output
*/
esp_err_t dac_channels_enable_continuous_mode(dac_channels_handle_t handle);
/**
* @brief Disable the DAC continuous output
*
* @param[in] handle DAC channels handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC continuous mode is disabled already
* - ESP_OK Success to stop the continuous output
*/
esp_err_t dac_channels_disable_continuous_mode(dac_channels_handle_t handle);
/**
* @brief Write DAC continuous data continuously
* @note The data in buffer will only be converted one time,
* This function will be blocked until all data loaded or timeout
* then the DAC output will keep outputting the voltage of the last data in the buffer
* @note On ESP32, the data bit width of DAC continuous data is fixed to 16 bits while only the high 8 bits are available,
* you can align the DAC data to 16 bits manually or set `CONFIG_DAC_DMA_AUTO_16BIT_ALIGN` to get the correct wave.
* But the data bit width is already 8 bits on ESP32-S2, each byte stands for an vailable voltage,
* no need to do any alignment.
*
* @param[in] handle DAC channels handle
* @param[in] buf The digital data buffer to convert
* @param[in] buf_size The buffer size of digital data buffer
* @param[out] bytes_loaded The bytes that has been loaded into DMA buffer, can be NULL if don't need it
* @param[in] timeout_ms The timeout time in mili-second
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC continuous mode has not been enabled yet
* - ESP_ERR_TIMEOUT Waiting for semaphore or message queue timeout
* - ESP_OK Success to output the acyclic DAC data
*/
esp_err_t dac_channels_write_continuously(dac_channels_handle_t handle, uint8_t *buf, size_t buf_size, size_t *bytes_loaded, uint32_t timeout_ms);
/**
* @brief Write DAC continuous data cyclically
* @note The data in buffer will be converted cyclically once this function is called,
* This function won't be blocked, it will return once the data loaded into DMA buffers
* @note The buffer size of cyclically output is limited by the descriptor number while initializing the continuous mode,
* Concretely, in order to load all the data into descriptors,
* the cyclic buffer size is not supposed to be greater than `desc_num * 4092`
* @note On ESP32, the data bit width of DAC continuous data is fixed to 16 bits while only the high 8 bits are available,
* you can align the DAC data to 16 bits manually or set `CONFIG_DAC_DMA_AUTO_16BIT_ALIGN` to get the correct wave.
* But the data bit width is already 8 bits on ESP32-S2, each byte stands for an vailable voltage,
* no need to do any alignment.
*
* @param[in] handle DAC channels handle
* @param[in] buf The digital data buffer to convert
* @param[in] buf_size The buffer size of digital data buffer
* @param[out] bytes_loaded The bytes that has been loaded into DMA buffer, can be NULL if don't need it
* @param[in] timeout_ms The timeout time in mili-second
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC continuous mode has not been enabled yet
* - ESP_ERR_TIMEOUT Waiting for semaphore or message queue timeout
* - ESP_OK Success to output the acyclic DAC data
*/
esp_err_t dac_channels_write_cyclically(dac_channels_handle_t handle, uint8_t *buf, size_t buf_size, size_t *bytes_loaded, uint32_t timeout_ms);
/*--------------------------------------------------------------------------
DAC cosine wave outputting APIs
---------------------------------------------------------------------------*/
/**
* @brief Initialize the DAC channels to cosine wave mode
*
* @param[in] handle DAC channels handle
* @param[in] cw_cfg DAC cosine wave generater configuration
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC channels has been initialized already
* - ESP_OK Success to initialize the DAC channels into cosine wave mode
*/
esp_err_t dac_channels_init_cosine_mode(dac_channels_handle_t handle, const dac_cosine_config_t *cw_cfg);
/**
* @brief Deinitialize the DAC channels to cosine wave mode
*
* @param[in] handle DAC channels handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC cosine wave generator is not stopped yet
* - ESP_OK Success to deinitialize the DAC cosine mode
*/
esp_err_t dac_channels_deinit_cosine_mode(dac_channels_handle_t handle);
/**
* @brief Start the DAC cosine wave generator output
*
* @param[in] handle DAC channels handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC channels has not been enabled yet or started already
* - ESP_OK Success to start cosine wave generator
*/
esp_err_t dac_channels_start_cosine_output(dac_channels_handle_t handle);
/**
* @brief Stop the DAC cosine wave generator output
*
* @param[in] handle DAC channels handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC channels has not been enabled yet or stoppped already
* - ESP_OK Success to stop cosine wave generator
*/
esp_err_t dac_channels_stop_cosine_output(dac_channels_handle_t handle);
#endif // SOC_DAC_SUPPORTED
#ifdef __cplusplus
}
#endif

View File

@ -1,260 +0,0 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "driver/dac_types.h"
#ifdef __cplusplus
extern "C" {
#endif
#if CONFIG_IDF_TARGET_ESP32
#define DAC_CHANNEL1_IO 25 /*!< ESP32 DAC channel 1 GPIO number: GPIO_NUM_25 */
#define DAC_CHANNEL2_IO 26 /*!< ESP32 DAC channel 2 GPIO number: GPIO_NUM_26 */
#elif CONFIG_IDF_TARGET_ESP32S2
#define DAC_CHANNEL1_IO 17 /*!< ESP32S2 DAC channel 1 GPIO number: GPIO_NUM_17 */
#define DAC_CHANNEL2_IO 18 /*!< ESP32S2 DAC channel 2 GPIO number: GPIO_NUM_17 */
#endif
/**
* @brief DAC channel configuration
*
*/
typedef struct {
dac_channel_mask_t chan_sel; /*!< Using DAC channel mask to select the channel in the channel group */
} dac_group_config_t;
/**
* @brief DAC DMA configration
*
*/
typedef struct {
uint32_t freq_hz; /*!< The frequency of DAC converting each data in DMA mode, unit: Hz */
uint32_t desc_num; /*!< The number of DMA descriptor , directly proportional to the max data buffer size while converting in cyclic way */
dac_dma_channel_mode_t chan_mode; /*!< DMA channel mode, only take effect when multiple channels enabled in a group, depends converting the buffer alternately or simultaneously */
} dac_dma_config_t;
/**
* @brief DAC cosine wave gnerator configuration
*
*/
typedef struct {
uint32_t freq_hz; /*!< The frequency of cosine wave, unit: Hz */
dac_cosine_scale_t scale; /*!< The scale of cosine wave amplitude */
dac_cosine_phase_t phase; /*!< The phase of cosine wave */
int8_t offset; /*!< The DC offset of cosine wave */
} dac_cosine_config_t;
typedef struct dac_channel_group_s *dac_channel_group_handle_t; /*!< DAC group handle of DAC peripheral, one or multiple DAC channels can be controlled by the group handle */
/*--------------------------------------------------------------------------
DAC common APIs
---------------------------------------------------------------------------*/
/**
* @brief Allocate a new DAC channel group
*
* @param[in] dac_cfg DAC basic configuration
* @param[out] handle DAC channel group handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_NO_MEM No memory for a new DAC handle
* - ESP_ERR_INVALID_STATE The specified DAC channel is occupied already
* - ESP_OK Success to allocate DAC channel group
*/
esp_err_t dac_new_channel_group(const dac_group_config_t *dac_cfg, dac_channel_group_handle_t *handle);
/**
* @brief Delete and free the DAC channel group
*
* @param[in] handle DAC channel group handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The channel group is not disabled
* - ESP_OK Success to delete the channel group
*/
esp_err_t dac_del_channel_group(dac_channel_group_handle_t handle);
/**
* @brief Enabled the DAC channels in the channel group
* @note GPIOs of DAC channles will be enabled in this step
*
* @param[in] handle DAC channel group handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The channel group has enabled already or the channels are running
* - ESP_OK Success to enable the channel group
*/
esp_err_t dac_channel_group_enable(dac_channel_group_handle_t handle);
/**
* @brief Disable the DAC channels in the channel group
*
* @param[in] handle DAC channel group handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The channel group has disabled already or the channels are running
* - ESP_OK Success to enable the channel group
*/
esp_err_t dac_channel_group_disable(dac_channel_group_handle_t handle);
/*--------------------------------------------------------------------------
DAC constant voltage outputting APIs
---------------------------------------------------------------------------*/
/**
* @brief DAC channel group output a constant voltage
* @note This function is available when DAC chennel group is enbled
*
* @param[in] handle DAC channel group handle
* @param[in] value The digital value of the constant voltage
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The channel group is not enabled
* - ESP_OK Success to enable the channel group
*/
esp_err_t dac_channel_group_output_constant_voltage(dac_channel_group_handle_t handle, uint8_t value);
/*--------------------------------------------------------------------------
DAC continuous outputting APIs
---------------------------------------------------------------------------*/
/**
* @brief Initialize the DAC channel group to DMA mode
* @note DAC can convert digital data continuously in DMA mode
*
* @param[in] handle DAC channel group handle
* @param[in] dma_cfg DAC DMA configuration
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC channel group has been initialized already
* - ESP_ERR_NO_MEM No memory for DAC DMA mode
* - ESP_OK Success to initializing the DAC channel group to DMA mode
*/
esp_err_t dac_channel_group_init_dma_output(dac_channel_group_handle_t handle, const dac_dma_config_t *dma_cfg);
/**
* @brief Deinitialize the DMA mode of the DAC channel group
* @note It can only be deinitialized when the DMA output is stopped
*
* @param[in] handle DAC channel group handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC DMA is not stopped yet
* - ESP_OK Success to deinitialize the DAC DMA mode
*/
esp_err_t dac_channel_group_deinit_dma_output(dac_channel_group_handle_t handle);
/**
* @brief Start the DAC DMA output
*
* @param[in] handle DAC channel group handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC DMA has not been enabled yet or started already
* - ESP_OK Success to start the DMA output
*/
esp_err_t dac_channel_group_start_dma_output(dac_channel_group_handle_t handle);
/**
* @brief Stop the DAC DMA output
*
* @param[in] handle DAC channel group handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC DMA is stopped or not disabled already
* - ESP_OK Success to stop the DMA output
*/
esp_err_t dac_channel_group_stop_dma_output(dac_channel_group_handle_t handle);
/**
* @brief Write DAC DMA data acyclicly
* @note The data in buffer will only be converted one time,
* This function will be blocked until all data sent successfully or timeout
* then the DAC output will keep outputting the voltage of the last data in the buffer
*
* @param[in] handle DAC channel group handle
* @param[in] buf The digital data buffer to convert
* @param[in] buf_size The buffer size of digital data buffer
* @param[in] timeout_ms The timeout time in mili-second
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC DMA has not been started or enabled yet
* - ESP_ERR_TIMEOUT Waiting for semaphore or message queue timeout
* - ESP_OK Success to output the acyclic DAC data by DMA
*/
esp_err_t dac_channel_group_write_acyclicly(dac_channel_group_handle_t handle, uint8_t *buf, size_t buf_size, uint32_t timeout_ms);
/**
* @brief Write DAC DMA data cyclicly
* @note The data in buffer will be converted cyclicly once this function is called,
* so the input buffer needs to stay accessable during the convertion,
* but this function won't be blocked, it will return once the data loaded into DMA descriptors
* @note The buffer size of cyclicly output is limited by the descriptor number while initializing the DMA mode,
* Concretely, in order to load all the data into descriptors,
* the cyclic buffer size is not supposed to be greater than `desc_num * 4092`
*
* @param[in] handle DAC channel group handle
* @param[in] buf The digital data buffer to convert
* @param[in] buf_size The buffer size of digital data buffer
* @param[in] timeout_ms The timeout time in mili-second
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC DMA has not been started or enabled yet
* - ESP_ERR_TIMEOUT Waiting for semaphore or message queue timeout
* - ESP_OK Success to output the acyclic DAC data by DMA
*/
esp_err_t dac_channel_group_write_cyclicly(dac_channel_group_handle_t handle, uint8_t *buf, size_t buf_size, uint32_t timeout_ms);
/*--------------------------------------------------------------------------
DAC cosine wave outputting APIs
---------------------------------------------------------------------------*/
/**
* @brief Initialize the DAC channel group to cosine wave mode
*
* @param[in] handle DAC channel group handle
* @param[in] cw_cfg DAC cosine wave generater configuration
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC channel group has been initialized already
* - ESP_OK Success to initialize the DAC channel group into cosine wave mode
*/
esp_err_t dac_channel_group_init_cosine_output(dac_channel_group_handle_t handle, const dac_cosine_config_t *cw_cfg);
/**
* @brief Deinitialize the DAC channel group to cosine wave mode
*
* @param[in] handle DAC channel group handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC cosine wave generator is not stopped yet
* - ESP_OK Success to deinitialize the DAC DMA mode
*/
esp_err_t dac_channel_group_deinit_cosine_output(dac_channel_group_handle_t handle);
/**
* @brief Start the DAC cosine wave generator output
*
* @param[in] handle DAC channel group handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC channel group has not been enabled yet or started already
* - ESP_OK Success to start cosine wave generator
*/
esp_err_t dac_channel_group_start_cosine_output(dac_channel_group_handle_t handle);
/**
* @brief Stop the DAC cosine wave generator output
*
* @param[in] handle DAC channel group handle
* @return
* - ESP_ERR_INVALID_ARG The input parameter is invalid
* - ESP_ERR_INVALID_STATE The DAC channel group has not been enabled yet or stoppped already
* - ESP_OK Success to stop cosine wave generator
*/
esp_err_t dac_channel_group_stop_cosine_output(dac_channel_group_handle_t handle);
#ifdef __cplusplus
}
#endif

View File

@ -5,51 +5,82 @@
*/
#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 "esp_bit_defs.h"
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
#if SOC_DAC_SUPPORTED
/**
* @brief DAC channel mask
*
*/
typedef enum {
DAC_CHANNEL_MASK_1 = BIT(0), /*!< DAC channel 1 is GPIO25(ESP32) / GPIO17(ESP32S2) */
DAC_CHANNEL_MASK_2 = BIT(1), /*!< DAC channel 2 is GPIO26(ESP32) / GPIO18(ESP32S2) */
DAC_CHANNEL_MASK_BOTH = BIT(0) | BIT(1), /*!< DAC channel 2 is GPIO26(ESP32) / GPIO18(ESP32S2) */
DAC_CHANNEL_MASK_CH0 = BIT(0), /*!< DAC channel 0 is GPIO25(ESP32) / GPIO17(ESP32S2) */
DAC_CHANNEL_MASK_CH1 = BIT(1), /*!< DAC channel 1 is GPIO26(ESP32) / GPIO18(ESP32S2) */
DAC_CHANNEL_MASK_BOTH = BIT(0) | BIT(1), /*!< Both DAC channel 0 and channel 1 */
} dac_channel_mask_t;
/**
* @brief DAC channel work mode in dma mode
* @note Only take effect when multiple channels enabled.
* @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_SIMULTANEOUS, /*!< The data in the DMA buffer is simultaneously output to the enable channel of the DAC. */
DAC_CHANNEL_ALTERNATE, /*!< The data in the DMA buffer is alternately output to the enable channel of the DAC. */
} dac_dma_channel_mode_t;
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 The multiple of the amplitude of the cosine wave generator. The max amplitude is VDD3P3_RTC.
* @brief DAC DMA (digitial controller) clock source
*
*/
typedef soc_periph_dac_digi_clk_src_t dac_conti_clk_src_t;
/**
* @brief DAC cosine wave generator clock source
*
*/
typedef soc_periph_dac_cosine_clk_src_t dac_cosine_clk_src_t;
/**
* @brief The attenuation of the amplitude of the cosine wave generator. The max amplitude is VDD3P3_RTC.
*/
typedef enum {
DAC_COSINE_SCALE_1 = 0x0, /*!< No scaling to the DAC cosine wave amplitude. Default. */
DAC_COSINE_SCALE_2 = 0x1, /*!< 1/2 amplitude of the DAC cosine wave */
DAC_COSINE_SCALE_4 = 0x2, /*!< 1/4 amplitude of the DAC cosine wave */
DAC_COSINE_SCALE_8 = 0x3, /*!< 1/8 amplitude of the DAC cosine wave */
DAC_COSINE_NO_ATTEN = 0x0, /*!< No attenuation to the DAC cosine wave amplitude. Default. */
DAC_COSINE_ATTEN_2 = 0x1, /*!< 1/2 amplitude of the DAC cosine wave */
DAC_COSINE_ATTEN_4 = 0x2, /*!< 1/4 amplitude of the DAC cosine wave */
DAC_COSINE_ATTEN_8 = 0x3, /*!< 1/8 amplitude of the DAC cosine wave */
} dac_cosine_scale_t;
/**
* @brief Set the phase of the cosine wave generator output.
* @note Only 0 or 180 are supported,
* it will be set to 0 as default if configured to an unsupported phase.
*/
typedef enum {
DAC_COSINE_PHASE_0 = 0x2, /*!< Phase shift +0° */
DAC_COSINE_PHASE_180 = 0x3, /*!< Phase shift +180° */
DAC_COSINE_PHASE_0 = 0, /*!< Phase shift +0° */
DAC_COSINE_PHASE_180 = 180, /*!< Phase shift +180° */
} dac_cosine_phase_t;
#endif // SOC_DAC_SUPPORTED
#ifdef __cplusplus
}
#endif

View File

@ -1,37 +0,0 @@
/*
* 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
esp_err_t dac_dma_periph_init(int chan_num, uint32_t freq_hz, bool is_alternate);
esp_err_t dac_dma_periph_deinit(void);
esp_err_t dac_dma_periph_register_intr(intr_handler_t intr_handler_func, void *user_ctx);
esp_err_t dac_dma_periph_deregister_intr(void);
void dac_dma_periph_enable(void);
void dac_dma_periph_disable(void);
bool dac_dma_periph_intr_is_triggered(void);
uint32_t dac_dma_periph_intr_get_eof_desc(void);
void dac_dma_periph_dma_trans_start(uint32_t desc_addr);
#ifdef __cplusplus
}
#endif

View File

@ -17,5 +17,7 @@ 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_driver: dac_channels_set_voltage (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,363 +0,0 @@
/*
* SPDX-FileCopyrightText: 2015-2021 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/adc.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"
/********************************************************
* This test is left for testing the legacy DAC-DMA APIs
*******************************************************/
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
static const char *TAG = "test_adc";
#define PLATFORM_SELECT (1) //0: pxp; 1: chip
#if (PLATFORM_SELECT == 0) //PXP platform
#include "soc/apb_ctrl_reg.h"
#define SET_BREAK_POINT(flag) REG_WRITE(APB_CTRL_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_RATE_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

@ -1,209 +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"
#include "soc/apb_ctrl_reg.h"
#include "math.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.
static const char *TAG = "test_adc";
/*******************************************/
/** DAC-DMA INIT CODE */
/*******************************************/
#define PI 3.14159265
static uint8_t _buf[512];
void dac_dma_test_create_buffer(dac_digi_convert_mode_t mode)
{
if (mode == DAC_CONV_ALTER) {
for(int i=0; i < 256; i++) {
if (i % 2 != 0) {
_buf[i] = i % 256;
} else {
_buf[i] = 255*(sin(i * PI / 255) + 1)/2;
}
}
for (int i=256;i < 512; i++) {
if (i % 2 != 0) {
_buf[i] = 255 - i % 256;
} else {
_buf[i] = 255*(sin((i-256) * PI / 255 - PI)+1)/2;
}
}
} else {
for(int i=0; i < 256; i++) {
_buf[i] = i % 256;
}
for (int i=256;i < 512; i++) {
_buf[i] = 255 - i % 256;
}
}
}
/**
* Testcase: Check the interrupt types of DAC-DMA.
*/
void test_dac_dma(dac_digi_convert_mode_t mode)
{
ESP_LOGI(TAG, " >> %s - dac mode %d<< ", __func__, mode);
const dac_digi_init_config_t cfg = {
.mode = mode,
.dac_chan_msk = BIT(DAC_CHANNEL_1) | BIT(DAC_CHANNEL_2),
.interval = 100,
.dac_dma_cnt = 4,
.dac_dma_length = 128,
.dac_dma_link_type = DAC_DMA_LINK_RECURSIVE,
};
dac_digi_initialize(&cfg);
dac_dma_test_create_buffer(mode);
dac_digi_start();
uint32_t length = sizeof(_buf);
dac_digi_write_bytes(length, (uint8_t*)_buf, portMAX_DELAY);
// /* Check interrupt type */
ESP_LOGI(TAG, "DAC-DMA intr test over");
dac_digi_stop();
dac_digi_deinitialize();
}
TEST_CASE("DAC with DMA test", "[dac]")
{
test_dac_dma(DAC_CONV_NORMAL);
test_dac_dma(DAC_CONV_ALTER);
}
/** 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_legacy(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(legacy api)", "[dac]")
{
test_dac_dig_dma_intr_check_legacy(DAC_CONV_NORMAL);
test_dac_dig_dma_intr_check_legacy(DAC_CONV_ALTER);
}
#endif // DAC_DMA_LEGACY_IMPL
#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,368 @@
/*
* 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 "driver/dac_driver.h"
#include "driver/adc.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 ADC2_CHANNEL_8 // GPIO25, same as DAC channel 0
#define ADC_TEST_WIDTH ADC_WIDTH_BIT_12
#elif CONFIG_IDF_TARGET_ESP32S2
#define ADC_TEST_CHANNEL_NUM ADC2_CHANNEL_6 // GPIO17, same as DAC channel 0
#define ADC_TEST_WIDTH ADC_WIDTH_BIT_13
#endif
#define ADC_TEST_ATTEN ADC_ATTEN_DB_11
TEST_CASE("DAC_API_basic_logic_test", "[dac]")
{
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
dac_conti_config_t dma_cfg = {
.freq_hz = 20000,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
.desc_num = 10,
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
};
dac_cosine_config_t cos_cfg = {
.freq_hz = 1000,
.clk_src = DAC_COSINE_CLK_SRC_DEFAULT,
.offset = 0,
.phase = DAC_COSINE_PHASE_0,
.scale = DAC_COSINE_NO_ATTEN,
};
/* Constant API test */
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_set_voltage(handle, 100));
TEST_ESP_OK(dac_channels_disable(handle));
/* DMA API test */
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ASSERT(dac_channels_enable_continuous_mode(handle) == ESP_ERR_INVALID_STATE);
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
TEST_ASSERT(dac_channels_disable(handle) == ESP_ERR_INVALID_STATE);
TEST_ASSERT(dac_channels_deinit_continuous_mode(handle) == ESP_ERR_INVALID_STATE);
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
/* Cosine wave API test */
TEST_ESP_OK(dac_channels_init_cosine_mode(handle, &cos_cfg));
TEST_ASSERT(dac_del_channels(handle) == ESP_ERR_INVALID_STATE);
TEST_ESP_OK(dac_channels_start_cosine_output(handle));
TEST_ASSERT(dac_channels_disable(handle) == ESP_ERR_INVALID_STATE);
TEST_ESP_OK(dac_channels_stop_cosine_output(handle));
TEST_ESP_OK(dac_channels_deinit_cosine_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
/* DMA peripheral availability test */
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
#if CONFIG_IDF_TARGET_ESP32
TEST_ESP_OK(i2s_platform_acquire_occupation(0, "dac_test"));
#elif CONFIG_IDF_TARGET_ESP32S2
TEST_ASSERT(spicommon_periph_claim(SPI3_HOST, "dac_test"));
#endif
TEST_ASSERT(dac_channels_init_continuous_mode(handle, &dma_cfg) == ESP_ERR_NOT_FOUND);
#if CONFIG_IDF_TARGET_ESP32
TEST_ESP_OK(i2s_platform_release_occupation(0));
#elif CONFIG_IDF_TARGET_ESP32S2
TEST_ASSERT(spicommon_periph_free(SPI3_HOST));
#endif
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
}
TEST_CASE("DAC_memory_leak_test", "[dac]")
{
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
dac_conti_config_t dma_cfg = {
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
.desc_num = 10,
.freq_hz = 20000,
};
/* Some resources will be lazy installed, ignore the first around */
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
int initial_size = esp_get_free_heap_size();
printf("Initial free heap size: %d\n", initial_size);
for (int i = 0; i < 20; i++) {
printf("# %d: ---------------------------------\n", i + 1);
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
printf("current heap size: %d\n", esp_get_free_heap_size());
TEST_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_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
/* Prepare ADC2 */
TEST_ESP_OK(adc2_config_channel_atten(ADC_TEST_CHANNEL_NUM, ADC_TEST_ATTEN));
int curr_adc = 0;
int last_adc = 0;
for (uint8_t i = 0; i <= 200; i += 20) {
TEST_ESP_OK(dac_channels_set_voltage(handle, i));
vTaskDelay(pdMS_TO_TICKS(20));
TEST_ESP_OK(adc2_get_raw(ADC_TEST_CHANNEL_NUM, ADC_TEST_WIDTH, &curr_adc));
printf("DAC: %d - ADC: %d\n", i, curr_adc);
if (last_adc != 0) {
TEST_ASSERT_GREATER_THAN(last_adc, curr_adc);
}
last_adc = curr_adc;
}
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
}
TEST_CASE("DAC_dma_write_test", "[dac]")
{
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
dac_conti_config_t dma_cfg = {
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
.desc_num = 10,
.freq_hz = 20000,
};
uint8_t *data = (uint8_t *)calloc(1, 2000);
TEST_ASSERT(data);
for (int i = 0; i < 2000; i++) {
data[i] = i % 256;
}
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_write_continuously(handle, data, 2000, NULL, 1000));
vTaskDelay(pdMS_TO_TICKS(200));
TEST_ESP_OK(dac_channels_write_cyclically(handle, data, 2000, NULL, 1000));
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
free(data);
}
/* Test 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);
uint8_t *data = (uint8_t *)calloc(1, 2000);
TEST_ASSERT(data);
/* Register DAC DMA using PLL */
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
dac_conti_config_t dma_cfg = {
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
.desc_num = 10,
.freq_hz = 20000,
};
/* Initialize DAC to test default PLL clock */
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
/* Start transmitting data on line */
TEST_ESP_OK(dac_channels_write_cyclically(handle, data, 2000, NULL, 1000));
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 handle */
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
printf("[PLL | 20000 Hz] %d pulses, expected %d, err %d\n", real_pulse, expt_pulse, real_pulse - expt_pulse);
TEST_ASSERT_INT_WITHIN(expt_pulse * 0.01, expt_pulse, real_pulse);
dma_cfg.clk_src = DAC_DIGI_CLK_SRC_APLL;
/* Initialize DAC to test APLL clock */
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
/* Start transmitting data on line */
TEST_ESP_OK(dac_channels_write_cyclically(handle, data, 2000, NULL, 1000));
/* 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_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
printf("[APLL | 20000 Hz] %d pulses, expected %d, err %d\n", real_pulse, expt_pulse, real_pulse - expt_pulse);
TEST_ASSERT_INT_WITHIN(expt_pulse * 0.01, expt_pulse, real_pulse);
free(data);
/* Free PCNT */
TEST_ESP_OK(pcnt_del_channel(pcnt_chan));
TEST_ESP_OK(pcnt_unit_stop(pcnt_unit));
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_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
dac_cosine_config_t cos_cfg = {
.freq_hz = 1000,
.clk_src = DAC_COSINE_CLK_SRC_DEFAULT,
.offset = 0,
.phase = DAC_COSINE_PHASE_0,
.scale = DAC_COSINE_NO_ATTEN,
};
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_init_cosine_mode(handle, &cos_cfg));
TEST_ESP_OK(dac_channels_start_cosine_output(handle));
vTaskDelay(pdMS_TO_TICKS(200));
TEST_ESP_OK(dac_channels_stop_cosine_output(handle));
TEST_ESP_OK(dac_channels_deinit_cosine_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
}
static volatile bool task_run_flag;
static void dac_acyclicly_write_task(void *arg)
{
dac_channels_handle_t dac_handle = (dac_channels_handle_t)arg;
uint8_t buf[1000];
for (int i = 0; i < 1000; i++) {
buf[i] = i % 256;
}
while (task_run_flag) {
if (dac_channels_write_continuously(dac_handle, buf, 100, NULL, 1000) == ESP_OK) {
printf("DAC write data success\n");
}
vTaskDelay(20);
}
vTaskDelete(NULL);
}
TEST_CASE("DAC_DMA_thread_safe", "[dac]")
{
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_CH0};
dac_conti_config_t dma_cfg = {
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
.desc_num = 10,
.freq_hz = 20000,
};
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
task_run_flag = true;
xTaskCreate(dac_acyclicly_write_task, "dac_acyclicly_write_task", 4096, handle, 5, NULL);
for (int i = 0; i < 5; i++) {
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
printf("DAC stopped\n");
vTaskDelay(pdMS_TO_TICKS(100));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
printf("DAC started\n");
vTaskDelay(pdMS_TO_TICKS(300));
}
task_run_flag = false;
vTaskDelay(pdMS_TO_TICKS(100));
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
}

View File

@ -0,0 +1,70 @@
/*
* 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 "driver/dac_driver.h"
#include "esp_private/spi_flash_os.h"
#include "esp_heap_caps.h"
#include "esp_err.h"
#define BUF_SIZE 2000
static void IRAM_ATTR test_dac_direct_set_safety(dac_channels_handle_t handle)
{
spi_flash_guard_get()->start();
dac_channels_set_voltage(handle, 128);
spi_flash_guard_get()->end();
}
static void IRAM_ATTR test_dac_dma_iram_safety(dac_channels_handle_t handle, uint8_t *data, uint32_t len)
{
spi_flash_guard_get()->start();
// Change the data of DMA
for (int i = 0; i < len; i++) {
data[i] = i % 100;
}
spi_flash_guard_get()->end();
}
TEST_CASE("DAC_IRAM_safe_test", "[dac]")
{
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
dac_conti_config_t dma_cfg = {
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
.desc_num = 10,
.freq_hz = 40000,
};
/* 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;
}
/* Get ready for dma transmition */
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
/* Test direct voltage setting safety */
test_dac_direct_set_safety(handle);
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
/* Simulate cache off */
dac_channels_write_cyclically(handle, data, BUF_SIZE, NULL, 1000);
test_dac_dma_iram_safety(handle, data, BUF_SIZE);
/* Deregister DAC DMA channel group */
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
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,5 @@
CONFIG_COMPILER_DUMP_RTL_FILES=y
CONFIG_DAC_ISR_IRAM_SAFE=y
CONFIG_DAC_CTRL_FUNC_IN_IRAM=y
# silent the error check, as the error string are stored in rodata, causing RTL check failure
CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y

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

@ -9,17 +9,16 @@
#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 +33,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 +45,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 +71,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 +80,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 +117,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 +141,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) {
@ -187,5 +182,3 @@ TEST_CASE("esp32s2 adc2-dac with adc2 calibration", "[adc-dac]")
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"

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 1
}
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 1
}
if (channel == ADC_CHANNEL_7) {
dac_ll_power_down(DAC_CHANNEL_2);
dac_ll_power_down(DAC_CHAN_1);
}
}
#else

View File

@ -1,16 +0,0 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// #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.
*
@ -55,10 +58,10 @@ static inline void dac_ll_power_down(dac_channel_t channel)
*/
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);
}
@ -103,9 +106,9 @@ static inline void dac_ll_cw_generator_disable(void)
*/
static inline void dac_ll_cw_set_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 +117,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: 130(130Hz)
* @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)(((float)freq / (float)rtc8m_freq) * 65536);
HAL_FORCE_MODIFY_U32_REG_FIELD(SENS.sar_dac_ctrl1, sw_fstep, (sw_freq > 0xFFFF) ? 0xFFFF : sw_freq);
}
@ -130,9 +134,9 @@ static inline void dac_ll_cw_set_freq(uint32_t freq)
*/
static inline void dac_ll_cw_set_scale(dac_channel_t channel, uint32_t scale)
{
if (channel == DAC_CHANNEL_1) {
if (channel == DAC_CHAN_0) {
SENS.sar_dac_ctrl2.dac_scale1 = scale;
} else if (channel == DAC_CHANNEL_2) {
} else if (channel == DAC_CHAN_1) {
SENS.sar_dac_ctrl2.dac_scale2 = scale;
}
}
@ -141,13 +145,13 @@ static inline void dac_ll_cw_set_scale(dac_channel_t channel, uint32_t scale)
* 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, uint32_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 +166,14 @@ static inline void dac_ll_cw_set_phase(dac_channel_t channel, uint32_t phase)
*/
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,99 +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
---------------------------------------------------------------*/
#define dac_ll_dma_clear_intr(dev, mask) spi_ll_clear_intr(dev, mask)
#define dac_ll_dma_enable_intr(dev, mask) spi_ll_enable_intr(dev, mask)
#define dac_ll_dma_fifo_reset(dev) spi_ll_dma_tx_fifo_reset(dev)
#define dac_ll_dma_disable_intr(dev, mask) spi_ll_disable_intr(dev, mask)
#define dac_ll_dma_get_intr(dev, mask) spi_ll_get_intr(dev, mask)
#define dac_ll_dma_disable(dev) spi_dma_ll_tx_disable(dev)
#define dac_ll_dma_reset(dev, chan) spi_dma_ll_tx_reset(dev, chan)
#define dac_ll_dma_start(dev, chan, desc) spi_dma_ll_tx_start(dev, chan, desc)
void dac_dma_hal_clr_intr(dac_hal_context_t *hal)
{
spi_ll_clear_intr(hal->dev, DAC_DMA_HAL_INTR);
}
bool dac_dma_hal_get_intr_status(dac_hal_context_t *hal)
{
return spi_ll_get_intr(hal->dev, DAC_DMA_HAL_INTR);
}
void dac_dma_hal_init(dac_hal_context_t *hal)
{
dac_ll_dma_clear_intr(hal->dev, DAC_DMA_HAL_INTR);
dac_ll_dma_enable_intr(hal->dev, DAC_DMA_HAL_INTR);
}
void dac_dma_hal_deinit(dac_hal_context_t *hal)
{
dac_ll_digi_trigger_output(false);
dac_ll_digi_enable_dma(false);
dac_ll_dma_clear_intr(hal->dev, DAC_DMA_HAL_INTR);
dac_ll_dma_disable(hal->dev);
}
void dac_dma_hal_trans_start(dac_hal_context_t *hal, lldesc_t *desc)
{
dac_ll_dma_reset(hal->dev, hal->dma_chan);
dac_ll_dma_fifo_reset(hal->dev);
dac_ll_dma_start(hal->dev, hal->dma_chan, desc);
}
void dac_hal_digi_controller_configure(const dac_hal_ctrl_config_t *cfg)
{
dac_ll_digi_clk_inv(true);
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);
}
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);
}
void __attribute__((deprecated)) 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 __attribute__((deprecated)) dac_hal_digi_controller_config(const dac_digi_config_t *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_controller_clk_enable(cfg->dig_clk.use_apll);
}
void __attribute__((deprecated)) dac_hal_digi_init(void)
{
dac_ll_digi_clk_inv(true);
}

View File

@ -1,128 +0,0 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*******************************************************************************
* 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 "hal/spi_ll.h"
#include "soc/lldesc.h"
#include_next "hal/dac_hal.h"
#ifdef __cplusplus
extern "C" {
#endif
#define DAC_DMA_HAL_INTR (SPI_LL_INTR_OUT_TOTAL_EOF)
typedef struct {
void *dev;
uint32_t dma_chan;
} dac_hal_context_t;
typedef struct {
dac_digi_convert_mode_t mode;
uint32_t interval;
} dac_hal_ctrl_config_t;
/*---------------------------------------------------------------
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_configure(const dac_hal_ctrl_config_t *ctrl_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()
/*******************************************************
* DAC-DMA hal layer functions.
* On ESP32-S2, DAC shares the DMA with SPI3.
*******************************************************/
/**
* DAC DMA HAL initialization
*
* @param hal Context of the HAL layer
*/
void dac_dma_hal_init(dac_hal_context_t *hal);
/**
* DAC DMA HAL interrupt clear.
*
* @param hal Context of the HAL layer
*/
void dac_dma_hal_clr_intr(dac_hal_context_t *hal);
/**
* DAC DMA HAL transaction start.
*
* @param hal Context of the HAL layer
*/
void dac_dma_hal_trans_start(dac_hal_context_t *hal, lldesc_t *desc);
/**
* Get if interrupt is triggered or not.
*
* @param hal Context of the HAL layer
*
* @return if the intr is triggered. 1: triggered, 0: not triggered.
*/
bool dac_dma_hal_get_intr_status(dac_hal_context_t *hal);
/**
* DAC DMA HAL deinitialization
*
* @param hal Context of the HAL layer
*/
void dac_dma_hal_deinit(dac_hal_context_t *hal);
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
void __attribute__((deprecated)) dac_hal_digi_controller_config(const dac_digi_config_t *cfg);
#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
---------------------------------------------------------------*/
@ -67,10 +70,10 @@ static inline void dac_ll_power_down(dac_channel_t channel)
*/
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);
}
@ -124,9 +127,9 @@ static inline void dac_ll_cw_generator_disable(void)
*/
static inline void dac_ll_cw_set_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 +138,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: >= 130(130Hz)
* @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)(((float)freq / (float)rtc8m_freq) * 65536);
HAL_FORCE_MODIFY_U32_REG_FIELD(SENS.sar_dac_ctrl1, sw_fstep, (sw_freq > 0xFFFF) ? 0xFFFF : sw_freq);
}
@ -151,9 +155,9 @@ static inline void dac_ll_cw_set_freq(uint32_t freq)
*/
static inline void dac_ll_cw_set_scale(dac_channel_t channel, uint32_t scale)
{
if (channel == DAC_CHANNEL_1) {
if (channel == DAC_CHAN_0) {
SENS.sar_dac_ctrl2.dac_scale1 = scale;
} else if (channel == DAC_CHANNEL_2) {
} else if (channel == DAC_CHAN_1) {
SENS.sar_dac_ctrl2.dac_scale2 = scale;
}
}
@ -162,13 +166,13 @@ static inline void dac_ll_cw_set_scale(dac_channel_t channel, uint32_t scale)
* 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, uint32_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 +187,14 @@ static inline void dac_ll_cw_set_phase(dac_channel_t channel, uint32_t phase)
*/
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

@ -1,71 +0,0 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*******************************************************************************
* 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"
#include "hal/dac_types.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,54 +1,16 @@
#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.
*/
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_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] = DAC_CHAN_0_GPIO_NUM,
.dac_channel_io_num[1] = DAC_CHAN_1_GPIO_NUM,
};

View File

@ -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_CLK_SRC_PLLD2 = SOC_MOD_CLK_PLL_D2,
DAC_DIGI_CLK_SRC_APLL = SOC_MOD_CLK_APLL,
DAC_DIGI_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_D2,
} soc_periph_dac_digi_clk_src_t;
/**
* @brief Array initializer for all supported clock sources of DAC cosine wave generator
*/
#define SOC_DAC_COSINE_CLKS {DAC_COSINE_CLK_SRC_RTC}
/**
* @brief DAC cosine wave generator clock source
*
*/
typedef enum {
DAC_COSINE_CLK_SRC_RTC = SOC_MOD_CLK_RTC_FAST,
DAC_COSINE_CLK_SRC_DEFAULT = SOC_MOD_CLK_RTC_FAST,
} 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
// 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: 2010-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#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_CHAN_0_GPIO_NUM 25
#define DAC_GPIO26_CHANNEL DAC_CHANNEL_2
#define DAC_CHANNEL_2_GPIO_NUM 26
#define DAC_GPIO26_CHANNEL DAC_CHAN_1
#define DAC_CHAN_1_GPIO_NUM 26
#endif

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_PERIPH_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] = DAC_CHAN_0_GPIO_NUM,
.dac_channel_io_num[1] = DAC_CHAN_1_GPIO_NUM,
};

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,
DAC_DIGI_CLK_SRC_APLL = SOC_MOD_CLK_APLL,
DAC_DIGI_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB,
} soc_periph_dac_digi_clk_src_t;
/**
* @brief Array initializer for all supported clock sources of DAC cosine wave generator
*/
#define SOC_DAC_COSINE_CLKS {DAC_COSINE_CLK_SRC_RTC}
/**
* @brief DAC cosine wave generator clock source
*
*/
typedef enum {
DAC_COSINE_CLK_SRC_RTC = SOC_MOD_CLK_RTC_FAST,
DAC_COSINE_CLK_SRC_DEFAULT = SOC_MOD_CLK_RTC_FAST,
} 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
// 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: 2010-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _SOC_DAC_CHANNEL_H
#define _SOC_DAC_CHANNEL_H
#define DAC_GPIO17_CHANNEL DAC_CHANNEL_1
#define DAC_CHANNEL_1_GPIO_NUM 17
#define DAC_GPIO17_CHANNEL DAC_CHAN_0
#define DAC_CHAN_0_GPIO_NUM 17
#define DAC_GPIO18_CHANNEL DAC_CHANNEL_2
#define DAC_CHANNEL_2_GPIO_NUM 18
#define DAC_GPIO18_CHANNEL DAC_CHAN_1
#define DAC_CHAN_1_GPIO_NUM 18
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

View File

@ -61,7 +61,6 @@ 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/dedic_gpio.h \
$(PROJECT_PATH)/components/driver/include/driver/gpio.h \
$(PROJECT_PATH)/components/driver/include/driver/gptimer.h \
@ -178,7 +177,6 @@ INPUT = \
$(PROJECT_PATH)/components/freertos/FreeRTOS-Kernel/include/freertos/task.h \
$(PROJECT_PATH)/components/freertos/FreeRTOS-Kernel/include/freertos/timers.h \
$(PROJECT_PATH)/components/hal/include/hal/adc_types.h \
$(PROJECT_PATH)/components/hal/include/hal/dac_types.h \
$(PROJECT_PATH)/components/hal/include/hal/esp_flash_err.h \
$(PROJECT_PATH)/components/hal/include/hal/gpio_types.h \
$(PROJECT_PATH)/components/hal/include/hal/i2c_types.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,311 @@ 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 generateor. The DAC channel can output a cosing wave with specified frequency and amplitude.
For other analog output options, see the :doc:`Sigma-delta Modulation module <sigmadelta>` and the :doc:`LED Control module <ledc>`. Both these 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 header file of legacy DAC driver (for apps using legacy driver)
- ``dac_driver.h``: The header file of new DAC driver (for apps using new DAC driver)
.. note::
The legacy driver can't coexist with the new driver. Including ``dac.h`` to use the legacy driver or ``dac_driver.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, which can be managed by the :cpp:type:`dac_channels_handle_t`. One or both two channels can be registered to a group by calling :cpp:func:`dac_new_channels`, it will return a channels handle so that the channels in a same group can be operated at the same time with this handle. While the channels in a group are not used any more, please call :cpp:func:`dac_del_channels` to free the resources and reset the hardware.
Direct Voltage Output (Direct Mode)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The DAC channels in the group can convert a 8-bit digital value into the analog every time calling :cpp:func:`dac_channels_set_voltage` (it can be called in ISR), and then the analog voltage will keep on corresponding pins until next convert start. But before starting to convert the voltage, the DAC channels and pins should be enabled by calling :cpp:func:`dac_channels_enable`.
Continuous Wave Output (DMA Mode)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
DAC channels in the group can also convert digital data continuously via the DMA.
To convert data continuously, the channels need to be initialized into DMA mode by calling :cpp:func:`dac_channels_init_continuous_mode` and then enable DMA conversion by :cpp:func:`dac_channels_enable_continuous_mode`.
After start up the DMA, there are two methods to transmit the rerial digital data. One method is transmitting only one time by :cpp:func:`dac_channels_write_continuously`, it will be blocked until all data transmitted, and the voltage will be kept according to the last conversion value. Another method is transmitting repeatly by :cpp:func:`dac_channels_write_cyclically`, the data in the buffer will be converted cyclicly without block, but note that the buffer size is limited by the descriptor number, and it has to stay available during the whole conversion period, otherwise invalid data might be transmitted.
.. 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. So the data buffer need to be expanded before passing it into the write function. Besides, if the both two channels are enabled, the data will be transmitted with a fixed phase diferrence between these channels.
The clock of DAC digital controller comes from I2S0 as well, so there are two kinds of clock source can be selected, they are :cpp:enumerator:`dac_conti_clk_src_t::DAC_DIGI_CLK_SRC_DEFAULT` which comes from ``CPU_D2_PLL`` and :cpp:enumerator:`dac_conti_clk_src_t::DAC_DIGI_CLK_SRC_APLL`. Theoretically, :cpp:enumerator:`dac_conti_clk_src_t::DAC_DIGI_CLK_SRC_DEFAULT` can support frequency between 19.6 KHz to several MHz and :cpp:enumerator:`dac_conti_clk_src_t::DAC_DIGI_CLK_SRC_DEFAULT` can support frequency between 648 Hz to several MHz, however, the latter clock source 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.
.. 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 difital controller can be choosen to :cpp:enumerator:`dac_conti_clk_src_t::DAC_DIGI_CLK_SRC_DEFAULT` (which comes from ``APB``) or :cpp:enumerator:`dac_conti_clk_src_t::DAC_DIGI_CLK_SRC_APLL`. Theoretically, :cpp:enumerator:`dac_conti_clk_src_t::DAC_DIGI_CLK_SRC_DEFAULT` can support frequency between 77 Hz to several MHz and :cpp:enumerator:`dac_conti_clk_src_t::DAC_DIGI_CLK_SRC_DEFAULT` can support frequency between 6 Hz to several MHz,however, the latter clock source 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.
Cosine Wave Output (Cosine Mode)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The DAC peripheral has a cosine wave generateor on each channel, it can generate cosine wave on these channels, users can specify the frequency, amplitude and phase of the cosine wave. To output the cosine wave, please initialize the DAC to cosine mode by :cpp:func:`dac_channels_init_cosine_mode` first, and then start the cosine wave generator by :cpp:func:`dac_channels_start_cosine_output`.
Currently, the source clock of the consine wave generator only comes from ``RTC FAST`` which can be choosen by :cpp:enumerator:`dac_cosine_clk_src_t::DAC_COSINE_CLK_SRC_DEFAULT`.
Finite-State Machine
^^^^^^^^^^^^^^^^^^^^
The DAC driver adopts a finite-state machine, the following diagram shows the relationship of the public APIs and the driver internal states.
.. figure:: ../../../_static/diagrams/dac/dac_state_machine.png
:align: center
:alt: DAC Finite-State Machine
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.
DAC driver 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 cosing wave generator is working), the driver will guarantee that the power management lock is acquired. Likewise, the driver releases the lock after conversion stopped.
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. However, before enable :ref:`CONFIG_DAC_ISR_IRAM_SAFE` please make sure that the data buffer is allocated in the internal RAM by :cpp:func:`heap_caps_calloc` and set the last parameter to ``MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT``.
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 these APIs are not allowed to be used in ISR.
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.
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.
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.
The basic examples for the ``Direct Mode``, ``DMA Mode`` and ``Consine Mode`` can be found in :example:`peripherals/dac/dac_basic`, :example:`peripherals/dac/dac_continuous` and :example:`peripherals/dac/dac_cosine_wave`. Here is a general overview of how to use these modes:
Direct Output Example
^^^^^^^^^^^^^^^^^^^^^
.. code:: c
#include <driver/dac.h>
#include <driver/dac_driver.h>
...
dac_output_enable(DAC_CHANNEL_1);
dac_output_voltage(DAC_CHANNEL_1, 200);
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
/* Allocate a channels handle for the choosen channels */
ESP_ERROR_CHECK(dac_new_channels(&cfg, &handle));
/* Must enable the channels before any outputting */
ESP_ERROR_CHECK(dac_channels_enable(handle));
/* Output '100', the DAC pin will output about (100 / 255) * 3.3 = 1.29 V */
ESP_ERROR_CHECK(dac_channels_set_voltage(handle, 100));
/* Disable the channels to stop outputting */
ESP_ERROR_CHECK(dac_channels_disable(handle));
/* Delete and free the channels */
ESP_ERROR_CHECK(dac_del_channels(handle));
Continuous Output Example
^^^^^^^^^^^^^^^^^^^^^^^^^
.. only:: esp32
.. code:: c
#include "driver/dac_driver.h"
#if CONFIG_DAC_ISR_IRAM_SAFE
/* If DAC IRAM safe enabled in Kconfig, include "esp_heap_caps.h" to allocate memory on internal RAM */
#include "esp_heap_caps.h"
#endif
...
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
dac_conti_config_t dma_cfg = {
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
.desc_num = 10,
.freq_hz = 20000,
};
/* Allocate a channels handle for the choosen channels */
ESP_ERROR_CHECK(dac_new_channels(&cfg, &handle));
/* Must enable the channels before any outputting */
ESP_ERROR_CHECK(dac_channels_enable(handle));
/* Initialize the channels to DMA mode */
ESP_ERROR_CHECK(dac_channels_init_continuous_mode(handle, &dma_cfg));
/* Must enable DMA before writing data */
ESP_ERROR_CHECK(dac_channels_enable_continuous_mode(handle));
/* Allocate the memory for the buffer to write */
uint32_t buf_size = 2000;
uint32_t timeout_ms = 1000;
#if CONFIG_DAC_ISR_IRAM_SAFE
/* If DAC IRAM SAFE is enabled, allocate the buffer on the internal RAM */
uint8_t *data = (uint8_t *)heap_caps_calloc(1, buf_size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
#else
uint8_t *data = (uint8_t *)calloc(1, buf_size);
#endif
assert(data);
/* Load the 16-bit aligned data */
for (int i = 0; i < buf_size; i += 2) {
data[i] = 0; // Fill 0 to the low 8 bits
data[i+1] = (i / 2) % 255; // Only the high 8 bits will be conveted
}
/* Write the data acyclicly, it will be blocked untill finishing sending all the data */
ESP_ERROR_CHECK(dac_channels_write_continuously(handle, data, buf_size, NULL, timeout_ms));
/* Write the data acyclicly, it will start output the buffer cyclicly without block, it can only be timeout when failed to aquire the lock */
ESP_ERROR_CHECK(dac_channels_write_continuously(handle, data, buf_size, NULL, timeout_ms));
/* Disable the DMA before deinitializing DMA mode */
ESP_ERROR_CHECK(dac_channels_disable_continuous_mode(handle));
/* Deinitialize DMA mode before disabling the channels */
ESP_ERROR_CHECK(dac_channels_deinit_continuous_mode(handle));
/* Disable the channels before deleting it */
ESP_ERROR_CHECK(dac_channels_disable(handle));
/* Delete the channels */
ESP_ERROR_CHECK(dac_del_channels(handle));
.. only:: esp32s2
.. code:: c
#include "driver/dac_driver.h"
#if CONFIG_DAC_ISR_IRAM_SAFE
/* If DAC IRAM safe enabled in Kconfig, include "esp_heap_caps.h" to allocate memory on internal RAM */
#include "esp_heap_caps.h"
#endif
...
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
dac_conti_config_t dma_cfg = {
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
.desc_num = 10,
.freq_hz = 20000,
};
/* Allocate a channels handle for the choosen channels */
ESP_ERROR_CHECK(dac_new_channels(&cfg, &handle));
/* Must enable the channels before any outputting */
ESP_ERROR_CHECK(dac_channels_enable(handle));
/* Initialize the channels to DMA mode */
ESP_ERROR_CHECK(dac_channels_init_continuous_mode(handle, &dma_cfg));
/* Must enable DMA before writing data */
ESP_ERROR_CHECK(dac_channels_enable_continuous_mode(handle));
/* Allocate the memory for the buffer to write */
uint32_t buf_size = 2000;
uint32_t timeout_ms = 1000;
#if CONFIG_DAC_ISR_IRAM_SAFE
/* If DAC IRAM SAFE is enabled, allocate the buffer on the internal RAM */
uint8_t *data = (uint8_t *)heap_caps_calloc(1, buf_size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
#else
uint8_t *data = (uint8_t *)calloc(1, buf_size);
#endif
assert(data);
for (int i = 0; i < buf_size; i++) {
data[i] = i % 255;
}
/* Write the data acyclicly, it will be blocked untill finishing sending all the data */
ESP_ERROR_CHECK(dac_channels_write_continuously(handle, data, buf_size, NULL, timeout_ms));
/* Write the data acyclicly, it will start output the buffer cyclicly without block, it can only be timeout when failed to aquire the lock */
ESP_ERROR_CHECK(dac_channels_write_continuously(handle, data, buf_size, NULL, timeout_ms));
/* Disable the DMA before deinitializing DMA mode */
ESP_ERROR_CHECK(dac_channels_disable_continuous_mode(handle));
/* Deinitialize DMA mode before disabling the channels */
ESP_ERROR_CHECK(dac_channels_deinit_continuous_mode(handle));
/* Disable the channels before deleting it */
ESP_ERROR_CHECK(dac_channels_disable(handle));
/* Delete the channels */
ESP_ERROR_CHECK(dac_del_channels(handle));
Cosine Wave Output Example
^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code:: c
#include "driver/dac_driver.h"
...
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
dac_cosine_config_t cos_cfg = {
.freq_hz = 1000,
.clk_src = DAC_COSINE_CLK_SRC_DEFAULT,
.offset = 0,
.phase = DAC_COSINE_PHASE_0,
.scale = DAC_COSINE_NO_ATTEN,
};
/* Allocate a channels handle for the choosen channels */
ESP_ERROR_CHECK(dac_new_channels(&cfg, &handle));
/* Must enable the channels before any outputting */
ESP_ERROR_CHECK(dac_channels_enable(handle));
/* Initialize the channels to cosine wave mode */
ESP_ERROR_CHECK(dac_channels_init_cosine_mode(handle, &cos_cfg));
/* Start outputting the cosine wave */
ESP_ERROR_CHECK(dac_channels_start_cosine_output(handle));
/* Stop the cosine wave generator before deinitializing cosine mode */
ESP_ERROR_CHECK(dac_channels_stop_cosine_output(handle));
/* Deinitialize consine mode before disabling the channels */
ESP_ERROR_CHECK(dac_channels_deinit_cosine_mode(handle));
/* Disable the channels before deleting it */
ESP_ERROR_CHECK(dac_channels_disable(handle));
/* Delete the channels */
ESP_ERROR_CHECK(dac_del_channels(handle));
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_driver.inc
.. include-build-file:: inc/dac_channel.inc
.. include-build-file:: inc/dac_types.inc

View File

@ -527,6 +527,65 @@ LCD
The deprecated ``CAN`` peripheral driver is removed. Please use ``TWAI`` driver instead (i.e. include ``driver/twai.h`` in your application).
.. 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_driver.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:
.. only:: esp32
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_scale_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.
Breaking Changes in Usage
~~~~~~~~~~~~~~~~~~~~~~~~~
- ``dac_pad_get_io_num`` is removed.
- ``dac_output_voltage`` is replaced by :cpp:func:`dac_channels_set_voltage`.
- ``dac_output_enable`` is replaced by :cpp:func:`dac_channels_enable`.
- ``dac_output_disable`` is replaced by :cpp:func:`dac_channels_disable`.
- ``dac_cw_generator_enable`` is replaced by :cpp:func:`dac_channels_start_cosine_output`, but it need to initialize the DAC channel group to cosine mode first by :cpp:func:`dac_channels_start_cosine_output`, and :cpp:func:`dac_channels_enable` should be called as well.
- ``dac_cw_generator_disable`` is replaced by :cpp:func:`dac_channels_stop_cosine_output`, it is also only allowed to be called under cosine mode.
- ``dac_cw_generator_config`` is replaced by :cpp:func:`dac_channels_init_cosine_mode`, when it is called, the driver will work at cosine mode.
- ``dac_i2s_enable`` is replaced by :cpp:func:`dac_channels_enable_continuous_mode`, but it need to initialize the DAC channel group to DMA mode first by :cpp:func:`dac_channels_init_continuous_mode`, and :cpp:func:`dac_channels_enable` should be called as well.
- ``dac_i2s_disable`` is replaced by :cpp:func:`dac_channels_disable_continuous_mode`, it is also only allowed to be called under DMA mode.
.. only:: esp32s2
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_scale_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_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_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.
- ``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_channels_set_voltage`.
- ``dac_output_enable`` is replaced by :cpp:func:`dac_channels_enable`.
- ``dac_output_disable`` is replaced by :cpp:func:`dac_channels_disable`.
- ``dac_cw_generator_enable`` is replaced by :cpp:func:`dac_channels_start_cosine_output`, but it need to initialize the DAC channel group to cosine mode first by :cpp:func:`dac_channels_start_cosine_output`, and :cpp:func:`dac_channels_enable` should be called as well.
- ``dac_cw_generator_disable`` is replaced by :cpp:func:`dac_channels_stop_cosine_output`, it is also only allowed to be called under cosine mode.
- ``dac_cw_generator_config`` is replaced by :cpp:func:`dac_channels_init_cosine_mode`, when it is called, the driver will work at cosine mode.
- ``dac_digi_init`` and ``dac_digi_controller_config`` is merged into :cpp:func:`dac_channels_init_continuous_mode`.
- ``dac_digi_deinit`` is replaced by :cpp:func:`dac_channels_deinit_continuous_mode`.
- ``dac_digi_start``, ``dac_digi_fifo_reset`` and ``dac_digi_reset`` are merged into :cpp:func:`dac_channels_enable_continuous_mode`.
- ``dac_digi_stop`` is replaced by :cpp:func:`dac_channels_disable_continuous_mode`.
Register Access Macros
----------------------

View File

@ -22,7 +22,7 @@
#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_driver.h"
#else
#include "driver/i2s_std.h"
#endif
@ -89,6 +89,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_channels_handle_t tx_chan;
#endif
/********************************
@ -169,23 +171,24 @@ 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_channels_config_t cfg = {
.chan_sel = DAC_CHANNEL_MASK_BOTH,
};
/* 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));
dac_conti_config_t conti_cfg = {
.freq_hz = 44100,
.chan_mode = DAC_CHANNEL_MODE_ALTER,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT, // If the frequency is out of range, try 'DAC_DIGI_CLK_SRC_APLL'
.desc_num = 6,
.buf_size = 2048,
};
/* Allocate the channel group */
ESP_ERROR_CHECK(dac_new_channels(&cfg, &tx_chan));
/* Enable the channels in the group */
ESP_ERROR_CHECK(dac_channels_enable(tx_chan));
/* Initialize DAC DMA peripheral */
ESP_ERROR_CHECK(dac_channels_init_continuous_mode(tx_chan, &conti_cfg));
/* Start the DAC DMA peripheral */
ESP_ERROR_CHECK(dac_channels_enable_continuous_mode(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 +218,10 @@ 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_channels_disable_continuous_mode(tx_chan));
ESP_ERROR_CHECK(dac_channels_deinit_continuous_mode(tx_chan));
ESP_ERROR_CHECK(dac_channels_disable(tx_chan));
ESP_ERROR_CHECK(dac_del_channels(tx_chan));
#else
ESP_ERROR_CHECK(i2s_channel_disable(tx_chan));
ESP_ERROR_CHECK(i2s_del_channel(tx_chan));
@ -316,7 +322,17 @@ 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_config_t conti_cfg = {
.freq_hz = sample_rate,
.chan_mode = ch_count == 1 ? DAC_CHANNEL_MODE_SIMUL : DAC_CHANNEL_MODE_ALTER,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT, // If the frequency is out of range, try 'DAC_DIGI_CLK_SRC_APLL'
.desc_num = 6,
.buf_size = 2048,
};
dac_channels_disable_continuous_mode(tx_chan);
dac_channels_deinit_continuous_mode(tx_chan);
dac_channels_init_continuous_mode(tx_chan, &conti_cfg);
dac_channels_enable_continuous_mode(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_driver.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_channels_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_channels_write_continuously(tx_chan, data, item_size, &bytes_written, portMAX_DELAY);
#else
i2s_channel_write(tx_chan, data, item_size, &bytes_written, portMAX_DELAY);
#endif

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_driver.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_channels_handle_t tx_chan;
#endif
/* callback for A2DP sink */
@ -171,7 +172,17 @@ 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_config_t conti_cfg = {
.freq_hz = sample_rate,
.chan_mode = DAC_CHANNEL_MODE_ALTER,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT, // If the frequency is out of range, try 'DAC_DIGI_CLK_SRC_APLL'
.desc_num = 6,
.buf_size = 2048,
};
dac_channels_disable_continuous_mode(tx_chan);
dac_channels_deinit_continuous_mode(tx_chan);
dac_channels_init_continuous_mode(tx_chan, &conti_cfg);
dac_channels_enable_continuous_mode(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_driver.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_channels_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_channels_write_continuously(tx_chan, data, item_size, &bytes_written, portMAX_DELAY);
#else
i2s_channel_write(tx_chan, data, item_size, &bytes_written, portMAX_DELAY);
#endif

View File

@ -37,7 +37,7 @@
#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_driver.h"
#else
#include "driver/i2s_std.h"
#endif
@ -77,6 +77,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_channels_handle_t tx_chan;
#endif
//Declare the static function
@ -691,23 +693,24 @@ 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_channels_config_t cfg = {
.chan_sel = DAC_CHANNEL_MASK_BOTH,
};
/* 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));
dac_conti_config_t conti_cfg = {
.freq_hz = 44100,
.chan_mode = DAC_CHANNEL_MODE_ALTER,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT, // If the frequency is out of range, try 'DAC_DIGI_CLK_SRC_APLL'
.desc_num = 6,
.buf_size = 2048,
};
/* Allocate the channel group */
ESP_ERROR_CHECK(dac_new_channels(&cfg, &tx_chan));
/* Enable the channels in the group */
ESP_ERROR_CHECK(dac_channels_enable(tx_chan));
/* Initialize DAC DMA peripheral */
ESP_ERROR_CHECK(dac_channels_init_continuous_mode(tx_chan, conti_cfg));
/* Start the DAC DMA peripheral */
ESP_ERROR_CHECK(dac_channels_enable_continuous_mode(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

@ -6,6 +6,22 @@ examples/peripherals/adc/continuous_read:
temporary: true
reason: adc dma mode isn't supported on these targets
examples/peripherals/dac/dac_audio:
disable:
- if: SOC_DAC_SUPPORTED != 1
examples/peripherals/dac/dac_basic:
disable:
- if: SOC_DAC_SUPPORTED != 1
examples/peripherals/dac/dac_continuous:
disable:
- if: SOC_DAC_SUPPORTED != 1
examples/peripherals/dac/dac_cosine_wave:
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.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,73 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/dac_driver.h"
#include "esp_check.h"
#include "audio_example_file.h"
#define EXAMPLE_CONVERT_FREQ_HZ 16000 // DAC conversion frequency, it determines how fast to play the audio
static const char *TAG = "dac audio";
void app_main(void)
{
ESP_LOGI(TAG, "DAC audio example start");
ESP_LOGI(TAG, "--------------------------------------");
dac_channels_handle_t dac_handle;
dac_channels_config_t cfg = {
.chan_sel = DAC_CHANNEL_MASK_BOTH,
};
dac_conti_config_t dma_cfg = {
.freq_hz = EXAMPLE_CONVERT_FREQ_HZ,
/* 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,
.clk_src = DAC_DIGI_CLK_SRC_APLL, // Using APLL as clock source to get a wider frequency range
.desc_num = 5, // At least 2 descriptions
};
/* Allocate the channel group */
ESP_ERROR_CHECK(dac_new_channels(&cfg, &dac_handle));
/* Enable the channels in the group */
ESP_ERROR_CHECK(dac_channels_enable(dac_handle));
/* Initialize DAC DMA peripheral */
ESP_ERROR_CHECK(dac_channels_init_continuous_mode(dac_handle, &dma_cfg));
/* Start the DAC DMA peripheral */
ESP_ERROR_CHECK(dac_channels_enable_continuous_mode(dac_handle));
ESP_LOGI(TAG, "DAC initialized success, DAC DMA is ready");
uint8_t *audio = NULL;
size_t audio_size = sizeof(audio_table);
#if CONFIG_IDF_TARGET_ESP32
audio = (uint8_t *)calloc(1, audio_size * 2);
assert(audio);
/* On ESP32, the data have to align with 16 bits, and only the high 8 bit will be converted by DAC */
for (int i = 0; i < audio_size; i++) {
audio[2 * i + 1] = audio_table[i];
}
#else
audio = (uint8_t *)calloc(1, audio_size);
assert(audio);
/* 'audio_table' is a const buffer which can't be sent by DMA directly, copy it into a new buffer */
memcpy(audio, audio_table, audio_size);
#endif
while (1) {
ESP_LOGI(TAG, "Audio size %d bytes, played at frequency %d Hz", audio_size, EXAMPLE_CONVERT_FREQ_HZ);
ESP_ERROR_CHECK(dac_channels_write_continuously(dac_handle, audio, audio_size, NULL, portMAX_DELAY));
vTaskDelay(pdMS_TO_TICKS(1000));
}
}

View File

@ -0,0 +1,16 @@
# 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
def test_dac_audio_exampl(dut: Dut) -> None:
dut.expect('I \\(([0-9]+)\\) dac audio: DAC audio example start', timeout=10)
dut.expect('I \\(([0-9]+)\\) dac audio: --------------------------------------', timeout=5)
dut.expect('I \\(([0-9]+)\\) dac audio: DAC initialized success, DAC DMA is ready', timeout=5)
dut.expect('I \\(([0-9]+)\\) dac audio: Audio size ([0-9]+) bytes, played at frequency ([0-9]+) Hz', timeout=5)

View File

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

View File

@ -0,0 +1,71 @@
| Supported Targets | ESP32 | ESP32-S2 |
| ----------------- | ----- | -------- |
# DAC Basic Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
This example shows the basic usage of outputting a voltage directly by the DAC driver.
The output voltage will increase a step every 500 ms, and it will reset to 0 periodically.
## 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.
* (Optional) An oscilloscope to monitor the output wave
### Configure the Project
There is a macro `EXAMPLE_DAC_USE_SEPARATE_CHANNEL` in the example to choose whether put the two DAC channels into a same group
- If `EXAMPLE_DAC_USE_SEPARATE_CHANNEL` is 1, DAC channel 0 and channel 2 can be set to different voltage separately by their own group handle.
- If `EXAMPLE_DAC_USE_SEPARATE_CHANNEL` is 0, DAC channel 0 and channel 2 will be set to a same voltage by the same group handle.
### 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
The DAC channels can be read by ADC channels internally. The ADC read period is 100 ms, the following log is the raw ADC value read from the DAC channels, it shows the output voltage is increasing every 500 ms.
```
DAC channel 0 vaule: 37 DAC channel 1 vaule: 0
DAC channel 0 vaule: 37 DAC channel 1 vaule: 0
DAC channel 0 vaule: 38 DAC channel 1 vaule: 0
DAC channel 0 vaule: 38 DAC channel 1 vaule: 0
DAC channel 0 vaule: 34 DAC channel 1 vaule: 0
DAC channel 0 vaule: 179 DAC channel 1 vaule: 117
DAC channel 0 vaule: 176 DAC channel 1 vaule: 117
DAC channel 0 vaule: 178 DAC channel 1 vaule: 122
DAC channel 0 vaule: 179 DAC channel 1 vaule: 118
DAC channel 0 vaule: 177 DAC channel 1 vaule: 115
DAC channel 0 vaule: 316 DAC channel 1 vaule: 261
DAC channel 0 vaule: 317 DAC channel 1 vaule: 263
DAC channel 0 vaule: 311 DAC channel 1 vaule: 261
DAC channel 0 vaule: 317 DAC channel 1 vaule: 260
DAC channel 0 vaule: 317 DAC channel 1 vaule: 262
DAC channel 0 vaule: 458 DAC channel 1 vaule: 406
DAC channel 0 vaule: 456 DAC channel 1 vaule: 406
DAC channel 0 vaule: 454 DAC channel 1 vaule: 403
DAC channel 0 vaule: 457 DAC channel 1 vaule: 406
DAC channel 0 vaule: 459 DAC channel 1 vaule: 407
...
```
If monitoring the DAC channels with an oscilloscope, there will be a direct voltage on the screen and it will be updated every 500 ms.

View File

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

View File

@ -0,0 +1,96 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/dac_driver.h"
#include "driver/adc.h"
#include "esp_check.h"
#define EXAMPLE_DAC_USE_SEPARATE_CHANNEL 0 // Whether to register two DAC channels in separate control group
#if CONFIG_IDF_TARGET_ESP32
#define EXAMPLE_DAC_CHAN1_ADC_CHAN ADC2_CHANNEL_8 // GPIO25, same as DAC channel 0
#define EXAMPLE_DAC_CHAN2_ADC_CHAN ADC2_CHANNEL_9 // GPIO26, same as DAC channel 1
#define EXAMPLE_ADC_WIDTH ADC_WIDTH_BIT_12
#elif CONFIG_IDF_TARGET_ESP32S2
#define EXAMPLE_DAC_CHAN1_ADC_CHAN ADC2_CHANNEL_6 // GPIO17, same as DAC channel 0
#define EXAMPLE_DAC_CHAN2_ADC_CHAN ADC2_CHANNEL_7 // GPIO18, same as DAC channel 1
#define EXAMPLE_ADC_WIDTH ADC_WIDTH_BIT_13
#endif
#define EXAMPLE_ADC_ATTEN ADC_ATTEN_DB_11
static void adc_monitor_task(void *args)
{
/* Set the ADC channels, these channels are connected to the DAC channels internally */
ESP_ERROR_CHECK(adc2_config_channel_atten(EXAMPLE_DAC_CHAN1_ADC_CHAN, EXAMPLE_ADC_ATTEN));
ESP_ERROR_CHECK(adc2_config_channel_atten(EXAMPLE_DAC_CHAN2_ADC_CHAN, EXAMPLE_ADC_ATTEN));
int chan1_val = 0;
int chan2_val = 0;
while (1) {
/* Read the DAC output voltage */
ESP_ERROR_CHECK(adc2_get_raw(EXAMPLE_DAC_CHAN1_ADC_CHAN, EXAMPLE_ADC_WIDTH, &chan1_val));
ESP_ERROR_CHECK(adc2_get_raw(EXAMPLE_DAC_CHAN2_ADC_CHAN, EXAMPLE_ADC_WIDTH, &chan2_val));
printf("DAC channel 0 vaule: %4d\tDAC channel 1 vaule: %4d\n", chan1_val, chan2_val);
vTaskDelay(pdMS_TO_TICKS(100));
}
}
static void dac_output_task(void *args)
{
dac_channels_handle_t handle = (dac_channels_handle_t)args;
uint32_t val = 0;
while (1) {
/* Set the voltage every 100 ms */
ESP_ERROR_CHECK(dac_channels_set_voltage(handle, val));
val += 10;
val %= 250;
vTaskDelay(pdMS_TO_TICKS(500));
}
}
static void dac_init_channel(dac_channel_mask_t mask, dac_channels_handle_t *dac_handle)
{
dac_channels_handle_t handle = NULL;
dac_channels_config_t cfg = {
.chan_sel = mask,
};
/* Allocate the channel group */
ESP_ERROR_CHECK(dac_new_channels(&cfg, &handle));
/* Enable the channels in the group */
ESP_ERROR_CHECK(dac_channels_enable(handle));
*dac_handle = handle;
}
static void __attribute__((unused)) dac_deinit_channel(dac_channels_handle_t dac_handle)
{
/* Disable the DAC channels */
ESP_ERROR_CHECK(dac_channels_disable(dac_handle));
/* Delete the channel group */
ESP_ERROR_CHECK(dac_del_channels(dac_handle));
}
void app_main(void)
{
#if EXAMPLE_DAC_USE_SEPARATE_CHANNEL
dac_channels_handle_t chan1_handle;
dac_channels_handle_t chan2_handle;
/* Initialize the two channels separately */
dac_init_channel(DAC_CHANNEL_MASK_CH0, &chan1_handle);
dac_init_channel(DAC_CHANNEL_MASK_CH1, &chan2_handle);
xTaskCreate(dac_output_task, "dac_chan1_output_task", 4096, chan1_handle, 5, NULL);
vTaskDelay(pdMS_TO_TICKS(500)); // To differential the output of two channels
xTaskCreate(dac_output_task, "dac_chan2_output_task", 4096, chan2_handle, 5, NULL);
xTaskCreate(adc_monitor_task, "adc_monitor_task", 4096, NULL, 5, NULL);
#else
dac_channels_handle_t chan12_handle;
/* Initialize the two channels in a same group */
dac_init_channel(DAC_CHANNEL_MASK_BOTH, &chan12_handle);
xTaskCreate(dac_output_task, "dac_output_task", 4096, chan12_handle, 5, NULL);
xTaskCreate(adc_monitor_task, "adc_monitor_task", 4096, NULL, 5, NULL);
#endif
}

View File

@ -0,0 +1,34 @@
# 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
def test_dac_basic_example(dut: Dut) -> None:
res = []
for _ in range(10):
res.append(dut.expect(r'DAC channel 0 vaule:( +)(\d+)(.*)DAC channel 1 vaule:( +)(\d+)', timeout=10))
avg1_ch1 = 0
avg1_ch2 = 0
avg2_ch1 = 0
avg2_ch2 = 0
for val in res[0:5]:
avg1_ch1 = avg1_ch1 + int(val.group(2))
avg1_ch2 = avg1_ch2 + int(val.group(5))
for val in res[5:10]:
avg2_ch1 = avg1_ch1 + int(val.group(2))
avg2_ch2 = avg1_ch2 + int(val.group(5))
avg1_ch1 = int(avg1_ch1 / 5)
avg1_ch2 = int(avg1_ch2 / 5)
avg2_ch1 = int(avg2_ch1 / 5)
avg2_ch2 = int(avg2_ch2 / 5)
assert avg2_ch1 > avg1_ch1
assert avg2_ch2 > avg1_ch2

View File

@ -0,0 +1 @@
CONFIG_ADC_DISABLE_DAC=n

View File

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(dac_continuous)

View File

@ -0,0 +1,153 @@
| 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 the basic usage of outputting continuous voltage by the DAC driver. There are two ways to realize continuous output, one is outputting by DMA transmission and another is by timer interrupt.
### Timer Interrupt
While using timer interrupt to output the waves, the digital value will be set in every timer interrupt callback, it means the conversion frequency is equal to the timer interrupt frequency. Obviously, the conversion frequency is limited by the interrupt, which relies on the CPU scheduling, thus it can't reach a high frequency in this mode. But it can be used as a supplementary way while the conversion frequency is too low to use DMA mode.
### DMA transmission
While using DMA to transmit the wave buffers, the digital values are put into a DMA buffer wait for transmitting and converting, it means the conversion frequency is equal to the frequency that DMA transmitting the data. We can set the DMA frequency directly, and the digital data int the buffer will be sent automatically when the buffer has been loaded onto the DMA. So the conversion frequency can reach even several MHz while using DMA mode. But the wave can be distorted if the frequency is too high.
## 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.
* (Optional) An oscilloscope to monitor the output wave
### Configure the Project
You can switch the output method by setting the macro `EXAMPLE_DAC_CONTINUOUS_MODE` to `EXAMPLE_DAC_CONTINUOUS_BY_TIMER` or `EXAMPLE_DAC_CONTINUOUS_BY_DMA`.
There are four waves: sine, triangle, saw tooth and square. These waves are stored in corresponding buffers, and each wave has 400 points as default, which can be modified by `EXAMPLE_ARRAY_LEN`, reduce the point number can increase the wave frequency.
### 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
This example can output sine wave, triangle wave, saw tooth wave and square wave periodically, each wave will last for 3 seconds.
The DAC channels can be read by ADC channels internally. The ADC read period is 500 ms, the following log is the raw ADC value read from the DAC channels. But since the ADC sample-rate is lower than the DAC output-rate, the sampling value can only indicate that the voltage is changing.
### Timer Triggered Output
You can see sine wave, triangle wave, saw tooth wave and square wave at 50 Hz on the oscilloscope.
```
I (333) dac continuous: --------------------------------------------------
I (343) dac continuous: DAC continuous output by Timer
I (343) dac continuous: DAC channel 0 io: GPIO_NUM_25
I (353) dac continuous: DAC channel 1 io: GPIO_NUM_26
I (353) dac continuous: Waveform: SINE -> TRIANGLE -> SAWTOOTH -> SQUARE
I (363) dac continuous: DAC conversion frequency (Hz): 20000
I (373) dac continuous: DAC wave frequency (Hz): 50
I (373) dac continuous: --------------------------------------------------
DAC channel 0 vaule: 2291 DAC channel 1 vaule: 2331
DAC channel 0 vaule: 43 DAC channel 1 vaule: 3
DAC channel 0 vaule: 55 DAC channel 1 vaule: 32
DAC channel 0 vaule: 57 DAC channel 1 vaule: 33
DAC channel 0 vaule: 56 DAC channel 1 vaule: 34
DAC channel 0 vaule: 59 DAC channel 1 vaule: 34
DAC channel 0 vaule: 56 DAC channel 1 vaule: 33
I (3393) dac continuous: triangle wave start
DAC channel 0 vaule: 2258 DAC channel 1 vaule: 2243
DAC channel 0 vaule: 2257 DAC channel 1 vaule: 2242
DAC channel 0 vaule: 2259 DAC channel 1 vaule: 2242
DAC channel 0 vaule: 2257 DAC channel 1 vaule: 2245
DAC channel 0 vaule: 2257 DAC channel 1 vaule: 2243
DAC channel 0 vaule: 2258 DAC channel 1 vaule: 2240
I (6393) dac continuous: sawtooth wave start
DAC channel 0 vaule: 2704 DAC channel 1 vaule: 2735
DAC channel 0 vaule: 2704 DAC channel 1 vaule: 2735
DAC channel 0 vaule: 2704 DAC channel 1 vaule: 2736
DAC channel 0 vaule: 2704 DAC channel 1 vaule: 2717
DAC channel 0 vaule: 2704 DAC channel 1 vaule: 2734
DAC channel 0 vaule: 2704 DAC channel 1 vaule: 2736
I (9393) dac continuous: square wave start
DAC channel 0 vaule: 0 DAC channel 1 vaule: 0
DAC channel 0 vaule: 0 DAC channel 1 vaule: 0
DAC channel 0 vaule: 0 DAC channel 1 vaule: 0
DAC channel 0 vaule: 0 DAC channel 1 vaule: 0
DAC channel 0 vaule: 0 DAC channel 1 vaule: 0
DAC channel 0 vaule: 0 DAC channel 1 vaule: 0
I (12393) dac continuous: sine wave start
DAC channel 0 vaule: 82 DAC channel 1 vaule: 62
DAC channel 0 vaule: 83 DAC channel 1 vaule: 62
DAC channel 0 vaule: 82 DAC channel 1 vaule: 62
DAC channel 0 vaule: 87 DAC channel 1 vaule: 62
DAC channel 0 vaule: 84 DAC channel 1 vaule: 63
DAC channel 0 vaule: 83 DAC channel 1 vaule: 64
...
```
### DMA Output
You can see sine wave, triangle wave, saw tooth wave and square wave at 2 KHz on the oscilloscope.
```
I (335) dac continuous: --------------------------------------------------
I (345) dac continuous: DAC continuous output by DMA
I (345) dac continuous: DAC channel 0 io: GPIO_NUM_25
I (355) dac continuous: DAC channel 1 io: GPIO_NUM_26
I (355) dac continuous: Waveform: SINE -> TRIANGLE -> SAWTOOTH -> SQUARE
I (365) dac continuous: DAC conversion frequency (Hz): 800000
I (375) dac continuous: DAC wave frequency (Hz): 2000
I (375) dac continuous: --------------------------------------------------
DAC channel 0 vaule: 3131 DAC channel 1 vaule: 1634
DAC channel 0 vaule: 1712 DAC channel 1 vaule: 2531
DAC channel 0 vaule: 1716 DAC channel 1 vaule: 2535
DAC channel 0 vaule: 1715 DAC channel 1 vaule: 2544
DAC channel 0 vaule: 1715 DAC channel 1 vaule: 2533
DAC channel 0 vaule: 1712 DAC channel 1 vaule: 2539
I (3395) dac continuous(DMA): triangle wave start
DAC channel 0 vaule: 592 DAC channel 1 vaule: 1190
DAC channel 0 vaule: 4095 DAC channel 1 vaule: 3518
DAC channel 0 vaule: 4095 DAC channel 1 vaule: 3515
DAC channel 0 vaule: 4095 DAC channel 1 vaule: 3516
DAC channel 0 vaule: 4095 DAC channel 1 vaule: 3514
DAC channel 0 vaule: 4095 DAC channel 1 vaule: 3515
I (6395) dac continuous(DMA): sawtooth wave start
DAC channel 0 vaule: 294 DAC channel 1 vaule: 560
DAC channel 0 vaule: 2861 DAC channel 1 vaule: 3227
DAC channel 0 vaule: 2860 DAC channel 1 vaule: 3216
DAC channel 0 vaule: 2861 DAC channel 1 vaule: 3227
DAC channel 0 vaule: 2861 DAC channel 1 vaule: 3216
DAC channel 0 vaule: 2859 DAC channel 1 vaule: 3183
I (9395) dac continuous(DMA): square wave start
DAC channel 0 vaule: 4095 DAC channel 1 vaule: 4095
DAC channel 0 vaule: 0 DAC channel 1 vaule: 0
DAC channel 0 vaule: 0 DAC channel 1 vaule: 0
DAC channel 0 vaule: 0 DAC channel 1 vaule: 0
DAC channel 0 vaule: 0 DAC channel 1 vaule: 0
DAC channel 0 vaule: 0 DAC channel 1 vaule: 0
I (12395) dac continuous(DMA): sine wave start
DAC channel 0 vaule: 2864 DAC channel 1 vaule: 3691
DAC channel 0 vaule: 0 DAC channel 1 vaule: 204
DAC channel 0 vaule: 0 DAC channel 1 vaule: 202
DAC channel 0 vaule: 0 DAC channel 1 vaule: 193
DAC channel 0 vaule: 0 DAC channel 1 vaule: 181
DAC channel 0 vaule: 0 DAC channel 1 vaule: 194
...
```

View File

@ -0,0 +1,6 @@
set(srcs "dac_continuous_main.c"
"dac_continuous_dma.c"
"dac_continuous_timer.c")
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,41 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
#include "driver/dac_driver.h"
#define CONST_PERIOD_2_PI 6.2832 // 2 * PI
#define EXAMPLE_ARRAY_LEN 400 // Length of wave array
#define EXAMPLE_DAC_AMPLITUDE 255 // Amplitude of DAC voltage. If it's more than 256 will causes dac_output_voltage() output 0.
#define EXAMPLE_WAVE_PERIOD_SEC 3 // Switch wave every 3 senconds
#define EXAMPLE_DAC_CHANNEL DAC_CHANNEL_MASK_BOTH // DAC_CHANNEL_MASK_CH0 & DAC_CHANNEL_MASK_CH1
typedef enum {
DAC_SINE_WAVE,
DAC_TRIANGLE_WAVE,
DAC_SAWTOOTH_WAVE,
DAC_SQUARE_WAVE,
DAC_WAVE_MAX,
} dac_example_wave_type_t;
/**
* @brief Use DMA to convert continuously
*
*/
void dac_continuous_by_dma(void);
/**
* @brief Use timer to convert continuously
*
*/
void dac_continuous_by_timer(void);
/**
* @brief Print the example log information
*
* @param conv_freq DAC conversion frequency
* @param wave_freq The frequency of the wave
*/
void example_log_info(uint32_t conv_freq, uint32_t wave_freq);

View File

@ -0,0 +1,130 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_check.h"
#include "dac_continuous.h"
#define EXAMPLE_WAVE_FREQ_HZ 2000 // Default wave frequency 50 Hz, it can't be too high
#define EXAMPLE_CONVERT_FREQ_HZ (EXAMPLE_ARRAY_LEN * EXAMPLE_WAVE_FREQ_HZ) // The frequency that DAC convert every data in the wave array
static uint8_t sin_wav[EXAMPLE_ARRAY_LEN]; // Used to store sine wave values
static uint8_t tri_wav[EXAMPLE_ARRAY_LEN]; // Used to store triangle wave values
static uint8_t saw_wav[EXAMPLE_ARRAY_LEN]; // Used to store sawtooth wave values
static uint8_t squ_wav[EXAMPLE_ARRAY_LEN]; // Used to store square wave values
static const char *TAG = "dac continuous(DMA)";
static const char wav_name[DAC_WAVE_MAX][15] = {"sine", "triangle", "sawtooth", "square"};
static void dac_dma_write_task(void *args)
{
dac_channels_handle_t handle = (dac_channels_handle_t)args;
dac_example_wave_type_t wav_sel = DAC_SINE_WAVE; // Start from sine wave
size_t buf_len = EXAMPLE_ARRAY_LEN;
while (1) {
/* The wave in the buffer will be converted cyclicly
* but take care the data buffer need to be available during the conversion */
switch (wav_sel) {
case DAC_SINE_WAVE:
ESP_ERROR_CHECK(dac_channels_write_cyclically(handle, (uint8_t *)sin_wav, buf_len, NULL, 1000));
break;
case DAC_TRIANGLE_WAVE:
ESP_ERROR_CHECK(dac_channels_write_cyclically(handle, (uint8_t *)tri_wav, buf_len, NULL, 1000));
break;
case DAC_SAWTOOTH_WAVE:
ESP_ERROR_CHECK(dac_channels_write_cyclically(handle, (uint8_t *)saw_wav, buf_len, NULL, 1000));
break;
case DAC_SQUARE_WAVE:
ESP_ERROR_CHECK(dac_channels_write_cyclically(handle, (uint8_t *)squ_wav, buf_len, NULL, 1000));
break;
default:
break;
}
/* Switch wave every EXAMPLE_WAVE_PERIOD_SEC seconds */
vTaskDelay(pdMS_TO_TICKS(EXAMPLE_WAVE_PERIOD_SEC * 1000));
wav_sel++;
wav_sel %= DAC_WAVE_MAX;
ESP_LOGI(TAG, "%s wave start", wav_name[wav_sel]);
}
}
static void dac_init_channel(dac_channel_mask_t mask, dac_conti_config_t *conti_cfg, dac_channels_handle_t *dac_handle)
{
dac_channels_handle_t handle = NULL;
dac_channels_config_t cfg = {
.chan_sel = mask,
};
/* Allocate the channel group */
ESP_ERROR_CHECK(dac_new_channels(&cfg, &handle));
/* Enable the channels in the group */
ESP_ERROR_CHECK(dac_channels_enable(handle));
/* Initialize DAC DMA peripheral */
ESP_ERROR_CHECK(dac_channels_init_continuous_mode(handle, conti_cfg));
/* Start the DAC DMA peripheral */
ESP_ERROR_CHECK(dac_channels_enable_continuous_mode(handle));
*dac_handle = handle;
}
/* Unused DAC de-initialize example, to show how to delete the DAC resources */
static void __attribute__((unused)) dac_deinit_channel(dac_channels_handle_t dac_handle)
{
/* Stop the DAC DMA peripheral */
ESP_ERROR_CHECK(dac_channels_disable_continuous_mode(dac_handle));
/* Deinitialize the DAC DMA peripheral */
ESP_ERROR_CHECK(dac_channels_deinit_continuous_mode(dac_handle));
/* Disable the DAC channels */
ESP_ERROR_CHECK(dac_channels_disable(dac_handle));
/* Delete the channel group */
ESP_ERROR_CHECK(dac_del_channels(dac_handle));
}
static void example_generate_wave(void)
{
uint32_t pnt_num = EXAMPLE_ARRAY_LEN;
for (int i = 0; i < pnt_num; i ++) {
sin_wav[i] = (uint8_t)((sin( i * CONST_PERIOD_2_PI / pnt_num) + 1) * (double)(EXAMPLE_DAC_AMPLITUDE) / 2 + 0.5);
tri_wav[i] = (i > (pnt_num / 2)) ? (2 * EXAMPLE_DAC_AMPLITUDE * (pnt_num - i) / pnt_num) : (2 * EXAMPLE_DAC_AMPLITUDE * i / pnt_num);
saw_wav[i] = (i == pnt_num) ? 0 : (i * EXAMPLE_DAC_AMPLITUDE / pnt_num);
squ_wav[i] = (i < (pnt_num / 2)) ? EXAMPLE_DAC_AMPLITUDE : 0;
}
}
void dac_continuous_by_dma(void)
{
dac_channels_handle_t chan12_handle;
dac_conti_config_t conti_cfg = {
.freq_hz = EXAMPLE_CONVERT_FREQ_HZ,
/* 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,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT, // If the frequency is out of range, try 'DAC_DIGI_CLK_SRC_APLL'
.desc_num = 10,
};
/* For Continuous(DMA) Mode, only one group can access the DMA periphral,
* which means the two channels can't be initialized to DMA mode separately */
dac_init_channel(DAC_CHANNEL_MASK_BOTH, &conti_cfg, &chan12_handle);
example_log_info(EXAMPLE_CONVERT_FREQ_HZ, EXAMPLE_WAVE_FREQ_HZ);
/* Generate the data buffer, the data is a sawtooth wave every 256 point,
* With the the data frequency at 20 KHz, the sawtooth wave frequency is about 20 KHz / 256 = 78.125 Hz */
example_generate_wave();
/* Start to convert wave */
xTaskCreate(dac_dma_write_task, "dac_dma_write_task", 4096, chan12_handle, 5, NULL);
}

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