diff --git a/components/driver/CMakeLists.txt b/components/driver/CMakeLists.txt index 6315559855..2b63897d6f 100644 --- a/components/driver/CMakeLists.txt +++ b/components/driver/CMakeLists.txt @@ -99,7 +99,14 @@ if(CONFIG_SOC_SPI_SUPPORT_SLAVE_HD_VER2) endif() if(CONFIG_SOC_TOUCH_SENSOR_SUPPORTED) - list(APPEND srcs "touch_sensor_common.c" "${target}/touch_sensor.c") + list(APPEND srcs "touch_sensor_common.c" + "${target}/touch_sensor.c") +endif() + +if(CONFIG_SOC_DAC_SUPPORTED) + list(APPEND srcs "dac.c" + "deprecated/dac_common_legacy.c") + list(APPEND includes "deprecated/${target}") endif() if(CONFIG_SOC_SDIO_SLAVE_SUPPORTED) @@ -107,14 +114,12 @@ if(CONFIG_SOC_SDIO_SLAVE_SUPPORTED) endif() if(${target} STREQUAL "esp32") - list(APPEND srcs "dac_common.c" - "deprecated/adc_i2s_deprecated.c" - "esp32/dac.c") + list(APPEND srcs "deprecated/adc_i2s_deprecated.c" + "deprecated/esp32/dac_legacy.c") endif() if(${target} STREQUAL "esp32s2") - list(APPEND srcs "dac_common.c" - "esp32s2/dac.c") + list(APPEND srcs "deprecated/esp32s2/dac_legacy.c") endif() if(BOOTLOADER_BUILD) diff --git a/components/driver/Kconfig b/components/driver/Kconfig index 7888cd5380..33417b07bf 100644 --- a/components/driver/Kconfig +++ b/components/driver/Kconfig @@ -399,4 +399,29 @@ menu "Driver Configurations" Note that, this option only controls the I2S driver log, will not affect other drivers. endmenu # I2S Configuration + menu "DAC Configuration" + depends on SOC_DAC_SUPPORTED + config DAC_ISR_IRAM_SAFE + bool "DAC ISR IRAM-Safe" + default n + help + Ensure the DAC interrupt is IRAM-Safe by allowing the interrupt handler to be + executable when the cache is disabled (e.g. SPI Flash write). + + config DAC_SUPPRESS_DEPRECATE_WARN + bool "Suppress legacy driver deprecated warning" + default n + help + Wether to suppress the deprecation warnings when using legacy DAC driver (driver/DAC.h). + If you want to continue using the legacy driver, and don't want to see related deprecation warnings, + you can enable this option. + + config DAC_ENABLE_DEBUG_LOG + bool "Enable debug log" + default n + help + Wether to enable the debug log message for DAC driver. + Note that, this option only controls the DAC driver log, won't affect other drivers. + endmenu # DAC Configuration + endmenu # Driver configurations diff --git a/components/driver/dac.c b/components/driver/dac.c new file mode 100644 index 0000000000..75cc6e220c --- /dev/null +++ b/components/driver/dac.c @@ -0,0 +1,643 @@ +/* + * SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#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; +} diff --git a/components/driver/dac_common.c b/components/driver/deprecated/dac_common_legacy.c similarity index 92% rename from components/driver/dac_common.c rename to components/driver/deprecated/dac_common_legacy.c index 9ccf89ada2..ee8744f24a 100644 --- a/components/driver/dac_common.c +++ b/components/driver/deprecated/dac_common_legacy.c @@ -16,6 +16,7 @@ #include "driver/dac.h" #include "soc/dac_periph.h" #include "hal/dac_hal.h" +#include "hal/dac_types.h" extern portMUX_TYPE rtc_spinlock; //TODO: Will be placed in the appropriate position after the rtc module is finished. @@ -116,7 +117,11 @@ esp_err_t dac_cw_generator_config(dac_cw_config_t *cw) ESP_RETURN_ON_FALSE(cw, ESP_ERR_INVALID_ARG, TAG, "invalid clock configuration"); portENTER_CRITICAL(&rtc_spinlock); - dac_hal_cw_generator_config(cw); + 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); portEXIT_CRITICAL(&rtc_spinlock); return ESP_OK; diff --git a/components/driver/include/driver/dac_common.h b/components/driver/deprecated/driver/dac_common.h similarity index 98% rename from components/driver/include/driver/dac_common.h rename to components/driver/deprecated/driver/dac_common.h index f0f2ecadae..a5e6e2b5e0 100644 --- a/components/driver/include/driver/dac_common.h +++ b/components/driver/deprecated/driver/dac_common.h @@ -13,7 +13,7 @@ extern "C" { #include #include "esp_err.h" #include "driver/gpio.h" -#include "hal/dac_types.h" +#include "driver/dac_types_legacy.h" /** * @brief Get the GPIO number of a specific DAC channel. diff --git a/components/driver/deprecated/driver/dac_types_legacy.h b/components/driver/deprecated/driver/dac_types_legacy.h new file mode 100644 index 0000000000..af77e377f1 --- /dev/null +++ b/components/driver/deprecated/driver/dac_types_legacy.h @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "driver/dac_types.h" +#include "hal/dac_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Config the cosine wave generator function in DAC module. + */ +typedef struct { + dac_channel_t en_ch; /*!< Enable the cosine wave generator of DAC channel. */ + dac_cw_scale_t scale; /*!< Set the amplitude of the cosine wave generator output. */ + dac_cw_phase_t phase; /*!< Set the phase of the cosine wave generator output. */ + uint32_t freq; /*!< Set frequency of cosine wave generator output. Range: 130(130Hz) ~ 55000(100KHz). */ + int8_t offset; /*!< Set the voltage value of the DC component of the cosine wave generator output. + Note: Unreasonable settings can cause waveform to be oversaturated. Range: -128 ~ 127. */ +} dac_cw_config_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/driver/esp32/dac.c b/components/driver/deprecated/esp32/dac_legacy.c similarity index 100% rename from components/driver/esp32/dac.c rename to components/driver/deprecated/esp32/dac_legacy.c diff --git a/components/driver/esp32/include/driver/dac.h b/components/driver/deprecated/esp32/driver/dac.h similarity index 100% rename from components/driver/esp32/include/driver/dac.h rename to components/driver/deprecated/esp32/driver/dac.h diff --git a/components/driver/deprecated/esp32s2/dac_legacy.c b/components/driver/deprecated/esp32s2/dac_legacy.c new file mode 100644 index 0000000000..b10ee0c125 --- /dev/null +++ b/components/driver/deprecated/esp32s2/dac_legacy.c @@ -0,0 +1,132 @@ +/* + * SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#include +#include "esp_log.h" +#include "esp_err.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"; + +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) +#define DAC_EXIT_CRITICAL() portEXIT_CRITICAL(&rtc_spinlock) + +#ifdef CONFIG_PM_ENABLE +static esp_pm_lock_handle_t s_dac_digi_lock = NULL; +#endif //CONFIG_PM_ENABLE + +/*--------------------------------------------------------------- + Digital controller setting +---------------------------------------------------------------*/ + +esp_err_t dac_digi_init(void) +{ + DAC_ENTER_CRITICAL(); + dac_hal_digi_init(); + DAC_EXIT_CRITICAL(); + + return ESP_OK; +} + +esp_err_t dac_digi_deinit(void) +{ +#ifdef CONFIG_PM_ENABLE + if (s_dac_digi_lock) { + esp_pm_lock_delete(s_dac_digi_lock); + s_dac_digi_lock = NULL; + } +#endif + DAC_ENTER_CRITICAL(); + dac_hal_digi_deinit(); + DAC_EXIT_CRITICAL(); + + return ESP_OK; +} + +esp_err_t dac_digi_controller_config(const dac_digi_config_t *cfg) +{ + ESP_RETURN_ON_FALSE(cfg->mode <= DAC_CONV_ALTER, ESP_ERR_INVALID_ARG, TAG, "DAC mode error"); + ESP_RETURN_ON_FALSE(cfg->interval > 0 && cfg->interval < 4096, ESP_ERR_INVALID_ARG, TAG, "DAC interval error"); + ESP_RETURN_ON_FALSE(cfg->dig_clk.div_num < 256, ESP_ERR_INVALID_ARG, TAG, "DAC clk div_num error"); + ESP_RETURN_ON_FALSE(cfg->dig_clk.div_b > 0 && cfg->dig_clk.div_b < 64, ESP_ERR_INVALID_ARG, TAG, "DAC clk div_b error"); + ESP_RETURN_ON_FALSE(cfg->dig_clk.div_a < 64, ESP_ERR_INVALID_ARG, TAG, "DAC clk div_a error"); +#ifdef CONFIG_PM_ENABLE + esp_err_t err; + if (s_dac_digi_lock == NULL) { + if (cfg->dig_clk.use_apll) { + err = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "dac_dma", &s_dac_digi_lock); + } else { + err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "dac_dma", &s_dac_digi_lock); + } + if (err != ESP_OK) { + s_dac_digi_lock = NULL; + ESP_LOGE(TAG, "DAC-DMA pm lock error"); + return err; + } + } +#endif //CONFIG_PM_ENABLE + + DAC_ENTER_CRITICAL(); + dac_hal_digi_controller_config(cfg); + DAC_EXIT_CRITICAL(); + + return ESP_OK; +} + +esp_err_t dac_digi_start(void) +{ +#ifdef CONFIG_PM_ENABLE + ESP_RETURN_ON_FALSE(s_dac_digi_lock, ESP_FAIL, TAG, "Should start after call `dac_digi_controller_config`"); + esp_pm_lock_acquire(s_dac_digi_lock); +#endif + DAC_ENTER_CRITICAL(); + dac_hal_digi_start(); + DAC_EXIT_CRITICAL(); + + return ESP_OK; +} + +esp_err_t dac_digi_stop(void) +{ +#ifdef CONFIG_PM_ENABLE + if (s_dac_digi_lock) { + esp_pm_lock_release(s_dac_digi_lock); + } +#endif + DAC_ENTER_CRITICAL(); + dac_hal_digi_stop(); + DAC_EXIT_CRITICAL(); + + return ESP_OK; +} + +esp_err_t dac_digi_fifo_reset(void) +{ + DAC_ENTER_CRITICAL(); + dac_hal_digi_fifo_reset(); + DAC_EXIT_CRITICAL(); + + return ESP_OK; +} + +esp_err_t dac_digi_reset(void) +{ + DAC_ENTER_CRITICAL(); + dac_hal_digi_reset(); + DAC_EXIT_CRITICAL(); + + return ESP_OK; +} diff --git a/components/driver/deprecated/esp32s2/driver/dac.h b/components/driver/deprecated/esp32s2/driver/dac.h new file mode 100644 index 0000000000..39f3e7e068 --- /dev/null +++ b/components/driver/deprecated/esp32s2/driver/dac.h @@ -0,0 +1,73 @@ +/* + * 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 diff --git a/components/driver/esp32/dac_dma.c b/components/driver/esp32/dac_dma.c new file mode 100644 index 0000000000..e043f248a0 --- /dev/null +++ b/components/driver/esp32/dac_dma.c @@ -0,0 +1,187 @@ +/* + * SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "freertos/FreeRTOS.h" + + +#include "hal/i2s_ll.h" +#include "hal/i2s_types.h" +#include "soc/i2s_periph.h" +#include "esp_private/dac_dma.h" +#include "esp_private/i2s_platform.h" + +#include "esp_check.h" + +#define DAC_DMA_PERIPH_I2S_NUM 0 +#define DAC_DMA_PERIPH_I2S_BIT_WIDTH 8 + +typedef struct { + void *periph_dev; /* DMA peripheral device address */ + intr_handle_t intr_handle; /* Interrupt handle */ +} dac_dma_periph_i2s_t; + +static dac_dma_periph_i2s_t *s_ddp = NULL; // Static DAC DMA peripheral structure pointer + +static const char *TAG = "DAC_DMA"; + +extern portMUX_TYPE dac_spinlock; /* Global DAC spinlock */ + +static esp_err_t dac_dma_periph_set_clock(uint32_t freq_hz){ + /* 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 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"); + + portENTER_CRITICAL(&dac_spinlock); + i2s_ll_tx_clk_set_src(s_ddp->periph_dev, I2S_CLK_D2CLK); + i2s_ll_tx_set_mclk(s_ddp->periph_dev, sclk, mclk, mclk_div); + i2s_ll_tx_set_bck_div_num(s_ddp->periph_dev, bclk_div); + portEXIT_CRITICAL(&dac_spinlock); + + return ESP_OK; +} + +esp_err_t dac_dma_periph_init(int chan_num, uint32_t freq_hz, bool is_alternate) +{ + 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"); + /* 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"); + + portENTER_CRITICAL(&dac_spinlock); + 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_enable_msb_shift(s_ddp->periph_dev, false); + i2s_ll_tx_set_ws_width(s_ddp->periph_dev, DAC_DMA_PERIPH_I2S_BIT_WIDTH); + i2s_ll_tx_enable_msb_right(s_ddp->periph_dev, false); + i2s_ll_tx_enable_right_first(s_ddp->periph_dev, true); + /* Should always enable fifo */ + i2s_ll_tx_force_enable_fifo_mod(s_ddp->periph_dev, true); + portEXIT_CRITICAL(&dac_spinlock); + + return ret; +err: + dac_dma_periph_deinit(); + return ret; +} + +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"); + if (s_ddp) { + if (s_ddp->intr_handle) { + dac_dma_periph_deregister_intr(); + } + free(s_ddp); + s_ddp = NULL; + } + + return ESP_OK; +} + +esp_err_t dac_dma_periph_register_intr(intr_handler_t intr_handler_func, void *user_ctx) +{ + ESP_RETURN_ON_FALSE(s_ddp, ESP_ERR_INVALID_STATE, TAG, "DAC DMA peripheral has not initialized yet"); + /* Regigster interrupt */ + ESP_RETURN_ON_ERROR(esp_intr_alloc(i2s_periph_signal[DAC_DMA_PERIPH_I2S_NUM].irq, ESP_INTR_FLAG_LEVEL1, + intr_handler_func, user_ctx, &(s_ddp->intr_handle)), + TAG, "Failed to register DAC DMA interrupt"); + portENTER_CRITICAL(&dac_spinlock); + i2s_ll_enable_intr(s_ddp->periph_dev, I2S_LL_EVENT_TX_EOF, true); + portEXIT_CRITICAL(&dac_spinlock); + return ESP_OK; +} + +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); + portEXIT_CRITICAL(&dac_spinlock); + esp_intr_free(s_ddp->intr_handle); + s_ddp->intr_handle = NULL; + } + return ESP_OK; +} + +void dac_dma_periph_enable(void) +{ + portENTER_CRITICAL(&dac_spinlock); + /* Reset */ + i2s_ll_tx_reset(s_ddp->periph_dev); + i2s_ll_tx_reset_dma(s_ddp->periph_dev); + i2s_ll_tx_reset_fifo(s_ddp->periph_dev); + /* Start */ + 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); + portEXIT_CRITICAL(&dac_spinlock); + /* Enable interrupt */ + esp_intr_enable(s_ddp->intr_handle); +} + +void dac_dma_periph_disable(void) +{ + portENTER_CRITICAL(&dac_spinlock); + /* Reset */ + i2s_ll_tx_reset(s_ddp->periph_dev); + i2s_ll_tx_reset_dma(s_ddp->periph_dev); + i2s_ll_tx_reset_fifo(s_ddp->periph_dev); + /* Stop */ + i2s_ll_tx_stop(s_ddp->periph_dev); + i2s_ll_tx_stop_link(s_ddp->periph_dev); + i2s_ll_tx_disable_intr(s_ddp->periph_dev); + i2s_ll_enable_dma(s_ddp->periph_dev, false); + portEXIT_CRITICAL(&dac_spinlock); + /* Disable interrupt */ + esp_intr_disable(s_ddp->intr_handle); +} + +bool inline dac_dma_periph_intr_is_triggered(void) +{ + uint32_t status = i2s_ll_get_intr_status(s_ddp->periph_dev); + if (status == 0) { + //Avoid spurious interrupt + return false; + } + i2s_ll_clear_intr_status(s_ddp->periph_dev, status); + return (status & I2S_LL_EVENT_TX_EOF) != 0; +} + +uint32_t inline dac_dma_periph_intr_get_eof_desc(void) +{ + uint32_t finish_desc; + i2s_ll_tx_get_eof_des_addr(s_ddp->periph_dev, &finish_desc); + return finish_desc; +} + +void 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); +} diff --git a/components/driver/esp32s2/dac.c b/components/driver/esp32s2/dac.c deleted file mode 100644 index 19688fa41f..0000000000 --- a/components/driver/esp32s2/dac.c +++ /dev/null @@ -1,414 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - - -#include -#include "esp_log.h" -#include "esp_err.h" -#include "esp_check.h" -#include "esp_pm.h" -#include "esp_check.h" -#include "freertos/FreeRTOS.h" -#include "freertos/semphr.h" -#include "freertos/timers.h" -#include "freertos/queue.h" -#include "driver/rtc_io.h" -#include "driver/dac.h" -#include "soc/dac_periph.h" -#include "soc/lldesc.h" -#include "hal/dac_hal.h" -#include "periph_ctrl.h" -#include "driver/spi_common_internal.h" - -static const char *DAC_TAG = "DAC"; - -/* On ESP32-S2, DAC-DMA shares the SPI3-DMA channel */ -#define DAC_USE_SPI3_DMA_CHANNEL SPI3_HOST - -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) -#define DAC_EXIT_CRITICAL() portEXIT_CRITICAL(&rtc_spinlock) - -typedef struct { - bool intr_trigger; - uint8_t *data; - uint32_t data_len; -} dac_dma_event_t; - -typedef struct { - dac_hal_context_t *hal; /*!< HAL pointer of DAC */ - QueueHandle_t que_dac_hdl; /*!< DAC queue handler */ - uint32_t dma_buffer_cnt; /*!< DMA buffer count, number of buffer. */ - uint32_t dma_buffer_length; /*!< DMA buffer length, length of each buffer. */ - lldesc_t **desc; /*!< Pointer to DMA descriptor*/ - bool dac_start_en; /*!< The status of the DAC, 0: stop, 1: start */ - dac_dma_link_type_t dac_dma_link_type; /*!< The type of the link, see `dac_dma_link_type_t` */ - esp_pm_lock_handle_t pm_lock; /*!< Spinlock for DAC */ - spi_host_device_t spi_host; /*!< spi host */ - intr_handle_t dac_isr_handle; /*!< DAC interrupt handler */ -} dac_digi_context_t; - -static dac_digi_context_t *s_dac_digi_ctx = NULL; - -/*--------------------------------------------------------------- - INTERRUPT HANDLER ----------------------------------------------------------------*/ - -static IRAM_ATTR void dac_dma_isr_default(void *arg) -{ - dac_digi_context_t *p_dac = (dac_digi_context_t *) arg; - bool status = dac_dma_hal_get_intr_status(p_dac->hal); - dac_dma_hal_clr_intr(p_dac->hal); - int task_awoken = pdFALSE; - dac_dma_event_t dac_evt; - dac_evt.intr_trigger = status; - xQueueSendFromISR(s_dac_digi_ctx->que_dac_hdl, &dac_evt, &task_awoken); -} - -/*--------------------------------------------------------------- - Digital controller setting ----------------------------------------------------------------*/ -static lldesc_t** dac_dma_desc_buf_create(int desc_cnt, size_t buf_size) -{ - lldesc_t** pdesc = (lldesc_t**)heap_caps_calloc(1, sizeof(lldesc_t*) * desc_cnt, MALLOC_CAP_DMA); - if (pdesc == NULL) { - goto _exit; - } - for (int i = 0; i < desc_cnt; i++) { - pdesc[i] = (lldesc_t*)heap_caps_malloc(sizeof(lldesc_t), MALLOC_CAP_DMA); - if (pdesc[i] == NULL) { - goto _exit; - } - memset(pdesc[i], 0, sizeof(lldesc_t)); - } - return pdesc; -_exit: - for (int i = 0; i < desc_cnt; i++) { - free(pdesc[i]); - } - free(pdesc); - return NULL; -} - -static void dac_dma_write(lldesc_t** pdesc, int desc_cnt, size_t buf_size, const void *buffer) -{ - for (int bux_idx = 0; bux_idx < desc_cnt; bux_idx++) { - pdesc[bux_idx]->owner = 1; - pdesc[bux_idx]->eof = 1; - pdesc[bux_idx]->length = buf_size; - pdesc[bux_idx]->size = buf_size; - pdesc[bux_idx]->buf = (uint8_t *) buffer; - buffer += buf_size; - if (s_dac_digi_ctx->dac_dma_link_type == DAC_DMA_LINK_RECURSIVE) { - pdesc[bux_idx]->qe.stqe_next = ((bux_idx < (desc_cnt - 1)) ? (pdesc[bux_idx + 1]) : pdesc[0]); - } else { - pdesc[bux_idx]->qe.stqe_next = ((bux_idx < (desc_cnt - 1)) ? (pdesc[bux_idx + 1]) : NULL); - } - } -} - -esp_err_t dac_digi_initialize(const dac_digi_init_config_t *init_cfg) -{ - ESP_RETURN_ON_FALSE(init_cfg->mode < DAC_CONV_MAX, ESP_ERR_INVALID_ARG, DAC_TAG, "DAC mode error"); - ESP_RETURN_ON_FALSE((init_cfg->interval > 0) && (init_cfg->interval < 4096), ESP_ERR_INVALID_ARG, DAC_TAG, "DAC interval error"); - - esp_err_t err = ESP_OK; - uint32_t dma_chan = 0; - uint32_t dac_chan = 0; - if (s_dac_digi_ctx != NULL) { - ESP_LOGE(DAC_TAG, "DAC has been installed"); - err = ESP_FAIL; - goto _exit; - } - - s_dac_digi_ctx = calloc(1, sizeof(dac_digi_context_t)); - s_dac_digi_ctx->dac_start_en = false; - if(s_dac_digi_ctx == NULL){ - err = ESP_ERR_NO_MEM; - goto _exit; - } - -#ifdef CONFIG_PM_ENABLE - if (s_dac_digi_ctx->pm_lock == NULL) { - err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "dac_dma", &s_dac_digi_ctx->pm_lock); - if (err != ESP_OK) { - s_dac_digi_ctx->pm_lock = NULL; - ESP_LOGE(DAC_TAG, "DAC-DMA pm lock error"); - goto _exit; - } - } -#endif //CONFIG_PM_ENABLE - - if (s_dac_digi_ctx->que_dac_hdl == NULL) { - s_dac_digi_ctx->que_dac_hdl = xQueueCreate(5, sizeof(dac_dma_event_t)); - } else { - xQueueReset(s_dac_digi_ctx->que_dac_hdl); - } - - periph_module_enable(PERIPH_SARADC_MODULE); - - do { - if ((init_cfg->dac_chan_msk >> dac_chan) & BIT(0)) { - dac_output_enable(dac_chan); - dac_chan++; - } - } while (dac_chan < DAC_CHANNEL_MAX); - - dac_hal_ctrl_config_t ctrl_hal_cfg = { - .mode = init_cfg->mode, - .interval = init_cfg->interval, - }; - - dac_hal_digi_controller_configure(&ctrl_hal_cfg); - s_dac_digi_ctx->dma_buffer_cnt = init_cfg->dac_dma_cnt; - s_dac_digi_ctx->dma_buffer_length = init_cfg->dac_dma_length; - s_dac_digi_ctx->dac_dma_link_type = init_cfg->dac_dma_link_type; - s_dac_digi_ctx->spi_host = DAC_USE_SPI3_DMA_CHANNEL; - - spicommon_periph_claim(s_dac_digi_ctx->spi_host, "dac"); - err = spicommon_slave_dma_chan_alloc(s_dac_digi_ctx->spi_host, SPI_DMA_CH_AUTO, &dma_chan, &dma_chan); - if (err != ESP_OK) { - goto _exit; - } - - s_dac_digi_ctx->hal = calloc(1, sizeof(dac_hal_context_t)); - s_dac_digi_ctx->hal->dev = (void *)SPI_LL_GET_HW(s_dac_digi_ctx->spi_host); - s_dac_digi_ctx->hal->dma_chan = dma_chan; - - dac_dma_hal_init(s_dac_digi_ctx->hal); - err = esp_intr_alloc(spicommon_irqdma_source_for_host(s_dac_digi_ctx->spi_host), 0, dac_dma_isr_default, s_dac_digi_ctx, &s_dac_digi_ctx->dac_isr_handle); - if(err != ESP_OK){ - goto _exit; - } - s_dac_digi_ctx->desc = dac_dma_desc_buf_create(s_dac_digi_ctx->dma_buffer_cnt, s_dac_digi_ctx->dma_buffer_length); - - return err; -_exit: - dac_digi_deinitialize(); - return err; -} - -esp_err_t dac_digi_write_bytes(uint32_t length, const void *buffer, TickType_t ticks_to_wait) -{ - ESP_RETURN_ON_FALSE(length <= (s_dac_digi_ctx->dma_buffer_cnt * s_dac_digi_ctx->dma_buffer_length), ESP_ERR_INVALID_ARG, DAC_TAG, "DAC DMA buffer length is larger than DMA buffer."); - dac_dma_event_t dac_evt; - bool dac_isr_flag = 1; - - - dac_dma_write(s_dac_digi_ctx->desc, s_dac_digi_ctx->dma_buffer_cnt, s_dac_digi_ctx->dma_buffer_length, buffer); - - dac_dma_hal_trans_start(s_dac_digi_ctx->hal, s_dac_digi_ctx->desc[0]); - while (dac_isr_flag) { - xQueueReceive(s_dac_digi_ctx->que_dac_hdl, &dac_evt, ticks_to_wait); - if (dac_evt.intr_trigger & dac_isr_flag) { - dac_isr_flag &= (!dac_evt.intr_trigger); - } - } - return ESP_OK; -} - -esp_err_t dac_digi_deinitialize(void) -{ - if (!s_dac_digi_ctx) { - return ESP_ERR_INVALID_STATE; - } - - if (s_dac_digi_ctx->dac_start_en == true) { - ESP_LOGE(DAC_TAG, "DAC is still working"); - return ESP_ERR_INVALID_STATE; - } - periph_module_disable(PERIPH_SARADC_MODULE); - - spicommon_periph_free(s_dac_digi_ctx->spi_host); - spicommon_slave_free_dma(s_dac_digi_ctx->spi_host); - - - if (s_dac_digi_ctx->que_dac_hdl) { - vQueueDelete(s_dac_digi_ctx->que_dac_hdl); - s_dac_digi_ctx->que_dac_hdl = NULL; - } - - dac_dma_hal_deinit(s_dac_digi_ctx->hal); - - for (int i = 0; i < s_dac_digi_ctx->dma_buffer_cnt; i++) { - free(s_dac_digi_ctx->desc[i]); - } - - free(s_dac_digi_ctx->hal); - free(s_dac_digi_ctx); - s_dac_digi_ctx = NULL; -#ifdef CONFIG_PM_ENABLE - if (s_dac_digi_ctx->pm_lock) { - esp_pm_lock_delete(s_dac_digi_ctx->pm_lock); - s_dac_digi_ctx->pm_lock = NULL; - } -#endif - - DAC_ENTER_CRITICAL(); - dac_hal_digi_deinit(); - DAC_EXIT_CRITICAL(); - return ESP_OK; -} - -esp_err_t __attribute__((unused)) dac_digi_deinit(void) -{ -#ifdef CONFIG_PM_ENABLE - if (s_dac_digi_ctx->pm_lock) { - esp_pm_lock_delete(s_dac_digi_ctx->pm_lock); - s_dac_digi_ctx->pm_lock = NULL; - } -#endif - free(s_dac_digi_ctx); - DAC_ENTER_CRITICAL(); - dac_hal_digi_deinit(); - DAC_EXIT_CRITICAL(); - - return ESP_OK; -} - -esp_err_t __attribute__((unused)) dac_digi_controller_config(const dac_digi_config_t *cfg) -{ - ESP_RETURN_ON_FALSE(cfg->mode < DAC_CONV_MAX, ESP_ERR_INVALID_ARG, TAG, "DAC mode error"); - ESP_RETURN_ON_FALSE(cfg->interval > 0 && cfg->interval < 4096, ESP_ERR_INVALID_ARG, TAG, "DAC interval error"); - ESP_RETURN_ON_FALSE(cfg->dig_clk.div_num < 256, ESP_ERR_INVALID_ARG, TAG, "DAC clk div_num error"); - ESP_RETURN_ON_FALSE(cfg->dig_clk.div_b > 0 && cfg->dig_clk.div_b < 64, ESP_ERR_INVALID_ARG, TAG, "DAC clk div_b error"); - ESP_RETURN_ON_FALSE(cfg->dig_clk.div_a < 64, ESP_ERR_INVALID_ARG, TAG, "DAC clk div_a error"); -#ifdef CONFIG_PM_ENABLE - esp_err_t err; - if (s_dac_digi_ctx->pm_lock == NULL) { - if (cfg->dig_clk.use_apll) { - err = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "dac_dma", &s_dac_digi_ctx->pm_lock); - } else { - err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "dac_dma", &s_dac_digi_ctx->pm_lock); - } - if (err != ESP_OK) { - s_dac_digi_ctx->pm_lock = NULL; - ESP_LOGE(DAC_TAG, "DAC-DMA pm lock error"); - return err; - } - } -#endif //CONFIG_PM_ENABLE - - DAC_ENTER_CRITICAL(); - dac_hal_digi_controller_config(cfg); - DAC_EXIT_CRITICAL(); - - return ESP_OK; -} - -esp_err_t dac_digi_start(void) -{ - if (s_dac_digi_ctx->dac_start_en == true) { - ESP_LOGE(DAC_TAG, "DAC is already started"); - return ESP_ERR_INVALID_STATE; - } - s_dac_digi_ctx->dac_start_en = true; -#ifdef CONFIG_PM_ENABLE - ESP_RETURN_ON_FALSE((s_dac_digi_ctx->pm_lock), ESP_ERR_INVALID_STATE, DAC_TAG, "Should start after call `dac_digi_controller_config`"); - esp_pm_lock_acquire(s_dac_digi_ctx->pm_lock); -#endif - DAC_ENTER_CRITICAL(); - dac_hal_digi_start(); - DAC_EXIT_CRITICAL(); - - return ESP_OK; -} - -esp_err_t dac_digi_stop(void) -{ - if (s_dac_digi_ctx->dac_start_en == false) { - ESP_LOGE(DAC_TAG, "DAC is already stopped"); - return ESP_ERR_INVALID_STATE; - } - s_dac_digi_ctx->dac_start_en = false; -#ifdef CONFIG_PM_ENABLE - if (s_dac_digi_ctx->pm_lock) { - esp_pm_lock_release(s_dac_digi_ctx->pm_lock); - } -#endif - DAC_ENTER_CRITICAL(); - dac_hal_digi_stop(); - DAC_EXIT_CRITICAL(); - - return ESP_OK; -} - -esp_err_t dac_digi_fifo_reset(void) -{ - DAC_ENTER_CRITICAL(); - dac_hal_digi_fifo_reset(); - DAC_EXIT_CRITICAL(); - - return ESP_OK; -} - -esp_err_t dac_digi_reset(void) -{ - DAC_ENTER_CRITICAL(); - dac_hal_digi_reset(); - DAC_EXIT_CRITICAL(); - - return ESP_OK; -} - -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -esp_err_t __attribute__((deprecated)) dac_digi_init(void) -{ - s_dac_digi_ctx = calloc(1, sizeof(dac_digi_context_t)); - s_dac_digi_ctx->dac_start_en = false; - DAC_ENTER_CRITICAL(); - dac_hal_digi_init(); - DAC_EXIT_CRITICAL(); - - return ESP_OK; -} - -esp_err_t __attribute__((deprecated)) dac_digi_deinit(void) -{ -#ifdef CONFIG_PM_ENABLE - if (s_dac_digi_ctx->pm_lock) { - esp_pm_lock_delete(s_dac_digi_ctx->pm_lock); - s_dac_digi_ctx->pm_lock = NULL; - } - -#endif - free(s_dac_digi_ctx); - DAC_ENTER_CRITICAL(); - dac_hal_digi_deinit(); - DAC_EXIT_CRITICAL(); - - return ESP_OK; -} - -esp_err_t __attribute__((deprecated)) dac_digi_controller_config(const dac_digi_config_t *cfg) -{ - ESP_RETURN_ON_FALSE(cfg->mode < DAC_CONV_MAX, ESP_ERR_INVALID_ARG, DAC_TAG, "DAC mode error"); - ESP_RETURN_ON_FALSE(cfg->interval > 0 && cfg->interval < 4096, ESP_ERR_INVALID_ARG, DAC_TAG, "DAC interval error"); - ESP_RETURN_ON_FALSE(cfg->dig_clk.div_num < 256, ESP_ERR_INVALID_ARG, DAC_TAG, "DAC clk div_num error"); - ESP_RETURN_ON_FALSE(cfg->dig_clk.div_b > 0 && cfg->dig_clk.div_b < 64, ESP_ERR_INVALID_ARG, DAC_TAG, "DAC clk div_b error"); - ESP_RETURN_ON_FALSE(cfg->dig_clk.div_a < 64, ESP_ERR_INVALID_ARG, DAC_TAG, "DAC clk div_a error"); -#ifdef CONFIG_PM_ENABLE - esp_err_t err; - if (s_dac_digi_ctx->pm_lock == NULL) { - if (cfg->dig_clk.use_apll) { - err = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "dac_dma", &s_dac_digi_ctx->pm_lock); - } else { - err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "dac_dma", &s_dac_digi_ctx->pm_lock); - } - if (err != ESP_OK) { - s_dac_digi_ctx->pm_lock = NULL; - ESP_LOGE(DAC_TAG, "DAC-DMA pm lock error"); - return err; - } - } -#endif //CONFIG_PM_ENABLE - - DAC_ENTER_CRITICAL(); - dac_hal_digi_controller_config(cfg); - DAC_EXIT_CRITICAL(); - - return ESP_OK; -} diff --git a/components/driver/esp32s2/dac_dma.c b/components/driver/esp32s2/dac_dma.c new file mode 100644 index 0000000000..a6b2adf004 --- /dev/null +++ b/components/driver/esp32s2/dac_dma.c @@ -0,0 +1,199 @@ +/* + * SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#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 "esp_check.h" + +#define DAC_DMA_PERIPH_SPI_HOST SPI3_HOST + +typedef struct { + void *periph_dev; /* DMA peripheral device address */ + uint32_t dma_chan; + intr_handle_t intr_handle; /* Interrupt handle */ +} dac_dma_periph_spi_t; + +static dac_dma_periph_spi_t *s_ddp = NULL; // Static DAC DMA peripheral structure pointer + +static const char *TAG = "DAC_DMA"; + +extern portMUX_TYPE dac_spinlock; /* Global DAC spinlock */ + +/** + * @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 + * @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"); + + uint32_t clk_div = (80000000 / interval) / freq_hz; + uint32_t mod = (80000000 / interval) % freq_hz; + 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)); + if (diff == 0) { + a = tmp_a; + b = tmp_b; + goto finish; + } + if (diff < min_diff) { + min_diff = diff; + a = tmp_a; + b = tmp_b; + } + } + +finish: + 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); + 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 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"); + + portENTER_CRITICAL(&dac_spinlock); + dac_ll_digi_set_convert_mode(is_alternate); + portEXIT_CRITICAL(&dac_spinlock); + return ret; +err: + dac_dma_periph_deinit(); + return ret; +} + +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(); + } + free(s_ddp); + s_ddp = NULL; + } + return ESP_OK; +} + +esp_err_t dac_dma_periph_register_intr(intr_handler_t intr_handler_func, void *user_ctx) +{ + ESP_RETURN_ON_FALSE(s_ddp, ESP_ERR_INVALID_STATE, TAG, "DAC DMA peripheral has not initialized yet"); + ESP_RETURN_ON_ERROR(spicommon_dma_chan_alloc(DAC_DMA_PERIPH_SPI_HOST, SPI_DMA_CH_AUTO, &s_ddp->dma_chan, &s_ddp->dma_chan), + TAG, "Failed to allocate dma peripheral channel"); + esp_err_t ret = ESP_OK; + /* Regigster interrupt */ + ESP_GOTO_ON_ERROR(esp_intr_alloc(spicommon_irqdma_source_for_host(DAC_DMA_PERIPH_SPI_HOST), + 0, intr_handler_func, user_ctx, &(s_ddp->intr_handle)), err, TAG, "Failed to register DAC DMA interrupt"); + portENTER_CRITICAL(&dac_spinlock); + spi_ll_enable_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_EOF); + portEXIT_CRITICAL(&dac_spinlock); + return ret; +err: + spicommon_dma_chan_free(DAC_DMA_PERIPH_SPI_HOST); + return ret; +} + +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"); + 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); + portEXIT_CRITICAL(&dac_spinlock); + esp_intr_free(s_ddp->intr_handle); + s_ddp->intr_handle = NULL; + } + return ESP_OK; +} + +void dac_dma_periph_enable(void) +{ + portENTER_CRITICAL(&dac_spinlock); + spi_dma_ll_tx_reset(s_ddp->periph_dev, s_ddp->dma_chan); + spi_ll_dma_tx_fifo_reset(s_ddp->periph_dev); + dac_ll_digi_trigger_output(true); + portEXIT_CRITICAL(&dac_spinlock); + /* Enable interrupt */ + esp_intr_enable(s_ddp->intr_handle); +} + +void dac_dma_periph_disable(void) +{ + portENTER_CRITICAL(&dac_spinlock); + spi_dma_ll_tx_reset(s_ddp->periph_dev, s_ddp->dma_chan); + spi_ll_dma_tx_fifo_reset(s_ddp->periph_dev); + spi_dma_ll_tx_stop(s_ddp->periph_dev, s_ddp->dma_chan); + dac_ll_digi_trigger_output(false); + portEXIT_CRITICAL(&dac_spinlock); + /* Disable interrupt */ + esp_intr_disable(s_ddp->intr_handle); +} + +bool 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); + spi_ll_clear_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_EOF); + return is_triggered; +} + +uint32_t IRAM_ATTR dac_dma_periph_intr_get_eof_desc(void) +{ + return spi_dma_ll_get_out_eof_desc_addr(s_ddp->periph_dev, s_ddp->dma_chan); +} + +void dac_dma_periph_dma_trans_start(uint32_t desc_addr) +{ + portENTER_CRITICAL(&dac_spinlock); + spi_dma_ll_tx_reset(s_ddp->periph_dev, s_ddp->dma_chan); + spi_ll_dma_tx_fifo_reset(s_ddp->periph_dev); + spi_dma_ll_tx_start(s_ddp->periph_dev, s_ddp->dma_chan, (lldesc_t *)desc_addr); + portEXIT_CRITICAL(&dac_spinlock); +} diff --git a/components/driver/esp32s2/include/driver/dac.h b/components/driver/esp32s2/include/driver/dac.h deleted file mode 100644 index 695e5c3f9f..0000000000 --- a/components/driver/esp32s2/include/driver/dac.h +++ /dev/null @@ -1,130 +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 - -/** - * @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 dac_chan_msk; /*!< DAC channel select mask, use BIT(CHAN_X) to mask the channesl */ - 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. */ - uint32_t dac_dma_cnt; /*!< DMA buffer count, number of buffer. */ - uint32_t dac_dma_length; /*!< DMA buffer length, length of each buffer. */ - dac_dma_link_type_t dac_dma_link_type; /*!< The type of the link, see `dac_dma_link_type_t` */ -} dac_digi_init_config_t; - -/*--------------------------------------------------------------- - Digital controller setting ----------------------------------------------------------------*/ - -/** - * @brief Initialize the Digital DAC controller. - * - * @param init_cfg Pointer to Digital DAC initialization config. Refer to ``dac_digi_config_t``. - * - * @return - * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid. - * - ESP_ERR_NOT_FOUND No free interrupt found with the specified flags - * - ESP_ERR_NO_MEM If out of memory - * - ESP_OK On success - */ -esp_err_t dac_digi_initialize(const dac_digi_init_config_t *init_cfg); - -/** - * @brief Deinitialize the Digital DAC controller. - * - * @return - * - ESP_ERR_INVALID_STATE Driver state is invalid. - * - ESP_OK On success - */ -esp_err_t dac_digi_deinitialize(void); - -/** - * @brief Write bytes to Digital DAC through DMA. - * - * @param length the buffer length. - * @param[in] buffer Buffer to write to DAC. - * @param ticks_to_wait Ticks to wait until there's room in the queue; use portMAX_DELAY to - * never time out. - * - * @return - * - ESP_ERR_INVALID_STATE Driver state is invalid. Usually it means the ADC sampling rate is faster than the task processing rate. - * - ESP_ERR_TIMEOUT Operation timed out - * - ESP_OK On success - */ -esp_err_t dac_digi_write_bytes(uint32_t length, const void *buffer, TickType_t ticks_to_wait); - -/** - * @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); - -/************************************************************ - * Deprecated APIs - ***********************************************************/ -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -/** - * @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 __attribute__((deprecated)) dac_digi_controller_config(const dac_digi_config_t *cfg); - -/** - * @brief DAC digital controller deinitialization. - * @return - * - ESP_OK success - */ -esp_err_t __attribute__((deprecated)) dac_digi_deinit(void); - -/** - * @brief DAC digital controller initialization. - * @return - * - ESP_OK success - */ -esp_err_t __attribute__((deprecated)) dac_digi_init(void); - -#ifdef __cplusplus -} -#endif diff --git a/components/driver/include/driver/dac_new.h b/components/driver/include/driver/dac_new.h new file mode 100644 index 0000000000..f4ad111487 --- /dev/null +++ b/components/driver/include/driver/dac_new.h @@ -0,0 +1,260 @@ +/* + * 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 diff --git a/components/driver/include/driver/dac_types.h b/components/driver/include/driver/dac_types.h new file mode 100644 index 0000000000..c38a955564 --- /dev/null +++ b/components/driver/include/driver/dac_types.h @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "soc/soc_caps.h" +#include "hal/adc_types.h" +#include "sdkconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @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_t; + +/** + * @brief DAC channel work mode in dma mode + * @note Only take effect when multiple channels enabled. + */ +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; + +/** + * @brief The multiple 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_scale_t; + +/** + * @brief Set the phase of the cosine wave generator output. + */ +typedef enum { + DAC_COSINE_PHASE_0 = 0x2, /*!< Phase shift +0° */ + DAC_COSINE_PHASE_180 = 0x3, /*!< Phase shift +180° */ +} dac_cosine_phase_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/driver/include/esp_private/dac_dma.h b/components/driver/include/esp_private/dac_dma.h new file mode 100644 index 0000000000..66735534fa --- /dev/null +++ b/components/driver/include/esp_private/dac_dma.h @@ -0,0 +1,37 @@ +/* + * 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 diff --git a/components/hal/CMakeLists.txt b/components/hal/CMakeLists.txt index deb55e1ea1..8a9ffa9685 100644 --- a/components/hal/CMakeLists.txt +++ b/components/hal/CMakeLists.txt @@ -125,7 +125,6 @@ if(NOT BOOTLOADER_BUILD) "esp32s2/brownout_hal.c" "esp32s2/cp_dma_hal.c" "esp32s2/touch_sensor_hal.c" - "esp32s2/dac_hal.c" "usb_dwc_hal.c") endif() diff --git a/components/hal/dac_hal.c b/components/hal/dac_hal.c index 51e4a2727a..765d1fcc8f 100644 --- a/components/hal/dac_hal.c +++ b/components/hal/dac_hal.c @@ -1,24 +1,16 @@ -// 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 "hal/dac_hal.h" +// #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); -} +// 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); +// } diff --git a/components/hal/esp32/include/hal/dac_ll.h b/components/hal/esp32/include/hal/dac_ll.h index 3bf220c8e2..2c15ede3bd 100644 --- a/components/hal/esp32/include/hal/dac_ll.h +++ b/components/hal/esp32/include/hal/dac_ll.h @@ -128,7 +128,7 @@ static inline void dac_ll_cw_set_freq(uint32_t freq) * @param channel DAC channel num. * @param scale The multiple of the amplitude. The max amplitude is VDD3P3_RTC. */ -static inline void dac_ll_cw_set_scale(dac_channel_t channel, dac_cw_scale_t scale) +static inline void dac_ll_cw_set_scale(dac_channel_t channel, uint32_t scale) { if (channel == DAC_CHANNEL_1) { SENS.sar_dac_ctrl2.dac_scale1 = scale; @@ -143,7 +143,7 @@ static inline void dac_ll_cw_set_scale(dac_channel_t channel, dac_cw_scale_t sca * @param channel DAC channel num. * @param scale Phase value. */ -static inline void dac_ll_cw_set_phase(dac_channel_t channel, dac_cw_phase_t phase) +static inline void dac_ll_cw_set_phase(dac_channel_t channel, uint32_t phase) { if (channel == DAC_CHANNEL_1) { SENS.sar_dac_ctrl2.dac_inv1 = phase; diff --git a/components/hal/esp32s2/dac_hal.c b/components/hal/esp32s2/dac_hal.c index 357688f73e..123ade4d53 100644 --- a/components/hal/esp32s2/dac_hal.c +++ b/components/hal/esp32s2/dac_hal.c @@ -59,7 +59,7 @@ void dac_dma_hal_trans_start(dac_hal_context_t *hal, lldesc_t *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_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); @@ -87,7 +87,7 @@ void __attribute__((deprecated)) dac_hal_digi_deinit(void) void __attribute__((deprecated)) dac_hal_digi_controller_config(const dac_digi_config_t *cfg) { - dac_ll_digi_set_convert_mode(cfg->mode); + 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); diff --git a/components/hal/esp32s2/include/hal/clk_gate_ll.h b/components/hal/esp32s2/include/hal/clk_gate_ll.h index 45ce590207..cde957997f 100644 --- a/components/hal/esp32s2/include/hal/clk_gate_ll.h +++ b/components/hal/esp32s2/include/hal/clk_gate_ll.h @@ -83,8 +83,6 @@ static inline uint32_t periph_ll_get_clk_en_mask(periph_module_t periph) return DPORT_CRYPTO_DMA_CLK_EN | DPORT_CRYPTO_SHA_CLK_EN; case PERIPH_AES_DMA_MODULE: return DPORT_CRYPTO_DMA_CLK_EN | DPORT_CRYPTO_AES_CLK_EN; - case PERIPH_SARADC_MODULE: - return DPORT_APB_SARADC_CLK_EN_M; default: return 0; } diff --git a/components/hal/esp32s2/include/hal/dac_ll.h b/components/hal/esp32s2/include/hal/dac_ll.h index daf861dc66..cf5f9a3cc4 100644 --- a/components/hal/esp32s2/include/hal/dac_ll.h +++ b/components/hal/esp32s2/include/hal/dac_ll.h @@ -149,7 +149,7 @@ static inline void dac_ll_cw_set_freq(uint32_t freq) * @param channel DAC channel num. * @param scale The multiple of the amplitude. The max amplitude is VDD3P3_RTC. */ -static inline void dac_ll_cw_set_scale(dac_channel_t channel, dac_cw_scale_t scale) +static inline void dac_ll_cw_set_scale(dac_channel_t channel, uint32_t scale) { if (channel == DAC_CHANNEL_1) { SENS.sar_dac_ctrl2.dac_scale1 = scale; @@ -164,7 +164,7 @@ static inline void dac_ll_cw_set_scale(dac_channel_t channel, dac_cw_scale_t sca * @param channel DAC channel num. * @param scale Phase value. */ -static inline void dac_ll_cw_set_phase(dac_channel_t channel, dac_cw_phase_t phase) +static inline void dac_ll_cw_set_phase(dac_channel_t channel, uint32_t phase) { if (channel == DAC_CHANNEL_1) { SENS.sar_dac_ctrl2.dac_inv1 = phase; @@ -251,13 +251,9 @@ static inline void dac_ll_digi_trigger_output(bool enable) * * @param mode Conversion mode select. See ``dac_digi_convert_mode_t``. */ -static inline void dac_ll_digi_set_convert_mode(dac_digi_convert_mode_t mode) +static inline void dac_ll_digi_set_convert_mode(bool is_alternate) { - if (mode == DAC_CONV_NORMAL) { - APB_SARADC.apb_dac_ctrl.apb_dac_alter_mode = 0; - } else { - APB_SARADC.apb_dac_ctrl.apb_dac_alter_mode = 1; - } + APB_SARADC.apb_dac_ctrl.apb_dac_alter_mode = is_alternate; } /** diff --git a/components/hal/include/hal/dac_hal.h b/components/hal/include/hal/dac_hal.h index e0c4d9a23f..40d56a57ba 100644 --- a/components/hal/include/hal/dac_hal.h +++ b/components/hal/include/hal/dac_hal.h @@ -13,6 +13,7 @@ #pragma once #include "hal/dac_ll.h" +#include "hal/dac_types.h" /** * Power on dac module and start output voltage. @@ -62,7 +63,7 @@ * * @param cw Configuration. */ -void dac_hal_cw_generator_config(dac_cw_config_t *cw); +// void dac_hal_cw_generator_config(dac_cw_config_t *cw); /** * Enable/disable DAC output data from DMA. diff --git a/components/hal/include/hal/dac_types.h b/components/hal/include/hal/dac_types.h index d52c72ed7c..266bdbc453 100644 --- a/components/hal/include/hal/dac_types.h +++ b/components/hal/include/hal/dac_types.h @@ -28,18 +28,6 @@ typedef enum { DAC_CW_PHASE_180 = 0x3, /*!< Phase shift +180° */ } dac_cw_phase_t; -/** - * @brief Config the cosine wave generator function in DAC module. - */ -typedef struct { - dac_channel_t en_ch; /*!< Enable the cosine wave generator of DAC channel. */ - dac_cw_scale_t scale; /*!< Set the amplitude of the cosine wave generator output. */ - dac_cw_phase_t phase; /*!< Set the phase of the cosine wave generator output. */ - uint32_t freq; /*!< Set frequency of cosine wave generator output. Range: 130(130Hz) ~ 55000(100KHz). */ - int8_t offset; /*!< Set the voltage value of the DC component of the cosine wave generator output. - Note: Unreasonable settings can cause waveform to be oversaturated. Range: -128 ~ 127. */ -} dac_cw_config_t; - #if CONFIG_IDF_TARGET_ESP32S2 /** @@ -48,17 +36,8 @@ typedef struct { 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 The type of the DAC DMA link. -*/ -typedef enum { - DAC_DMA_LINK_LINE = BIT(0), /*!< The link is Linear. */ - DAC_DMA_LINK_RECURSIVE = BIT(1), /*!< The link is recursive. */ -} dac_dma_link_type_t; - /** * @brief DAC digital controller (DMA mode) configuration parameters. */ @@ -70,6 +49,6 @@ typedef struct { 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; /*!