diff --git a/components/driver/esp32s2/dac.c b/components/driver/esp32s2/dac.c index a6815dfd79..7015ed463c 100644 --- a/components/driver/esp32s2/dac.c +++ b/components/driver/esp32s2/dac.c @@ -13,27 +13,173 @@ #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "freertos/timers.h" +#include "freertos/task.h" +#include "freertos/queue.h" #include "driver/rtc_io.h" #include "driver/dac.h" #include "soc/dac_periph.h" +#include "soc/lldesc.h" +#include "soc/system_reg.h" +#include "soc/periph_defs.h" +#include "soc/cp_dma_reg.h" #include "hal/dac_hal.h" +#include "periph_ctrl.h" +#include "sys/queue.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) +portMUX_TYPE dac_isr_handler_list_lock = portMUX_INITIALIZER_UNLOCKED; +#define DAC_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&dac_isr_handler_list_lock) +#define DAC_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&dac_isr_handler_list_lock) -#ifdef CONFIG_PM_ENABLE -static esp_pm_lock_handle_t s_dac_digi_lock = NULL; -#endif //CONFIG_PM_ENABLE +typedef struct { + uint32_t int_msk; + uint8_t *data; + uint32_t data_len; +} dac_dma_event_t; + +typedef struct { + 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 */ + intr_handle_t dac_isr_handle; /*!< DAC interrupt handler */ + uint32_t dac_isr; /*!< DAC interrupt mask */ +} dac_digi_context_t; + +dac_digi_context_t *s_dac_digi_ctx = NULL; + +/*--------------------------------------------------------------- + INTERRUPT HANDLER +---------------------------------------------------------------*/ + +typedef struct dac_dma_isr_handler { + uint32_t mask; + intr_handler_t handler; + void* handler_arg; + SLIST_ENTRY(dac_dma_isr_handler) next; +} dac_dma_isr_handler_t; + +static SLIST_HEAD(dac_dma_isr_handler_list_, dac_dma_isr_handler) s_dac_dma_isr_handler_list = + SLIST_HEAD_INITIALIZER(s_dac_dma_isr_handler_list); + +static IRAM_ATTR void dac_dma_isr(void * arg) +{ + uint32_t int_st = dac_hal_digi_read_intr_status(); + int task_awoken = pdFALSE; + dac_dma_event_t dac_evt; + dac_evt.int_msk = int_st; + REG_WRITE(SPI_DMA_INT_CLR_REG(3), int_st); + xQueueSendFromISR(s_dac_digi_ctx->que_dac_hdl, &dac_evt, &task_awoken); + + if (task_awoken == pdTRUE) { + portYIELD_FROM_ISR(); + } +} + +static IRAM_ATTR void dac_dma_isr_default(void* arg) +{ + uint32_t status = dac_hal_digi_read_intr_status(); + dac_dma_isr_handler_t* it; + DAC_ENTER_CRITICAL_ISR(); + SLIST_FOREACH(it, &s_dac_dma_isr_handler_list, next) { + if (it->mask & status) { + DAC_EXIT_CRITICAL_ISR(); + (*it->handler)(it->handler_arg); + DAC_ENTER_CRITICAL_ISR(); + } + } + DAC_EXIT_CRITICAL_ISR(); + dac_hal_digi_clear_intr(status); +} + +static esp_err_t dac_dma_isr_ensure_installed(void) +{ + esp_err_t err = ESP_OK; + DAC_ENTER_CRITICAL_ISR(); + dac_hal_digi_clear_intr(UINT32_MAX); + dac_hal_digi_enable_intr(0); + err = esp_intr_alloc(ETS_SPI3_DMA_INTR_SOURCE, 0, &dac_dma_isr_default, NULL, &s_dac_digi_ctx->dac_isr_handle); + if (err != ESP_OK) { + goto out; + } + +out: + DAC_EXIT_CRITICAL_ISR(); + return err; +} + +esp_err_t dac_dma_isr_register(intr_handler_t handler, void* handler_arg, uint32_t intr_mask) +{ + esp_err_t err = dac_dma_isr_ensure_installed(); + if (err != ESP_OK) { + return err; + } + + 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; + DAC_ENTER_CRITICAL_ISR(); + SLIST_INSERT_HEAD(&s_dac_dma_isr_handler_list, item, next); + DAC_EXIT_CRITICAL_ISR(); + return ESP_OK; +} + +esp_err_t dac_dma_isr_deregister(intr_handler_t handler, void* handler_arg) +{ + dac_dma_isr_handler_t* it; + dac_dma_isr_handler_t* prev = NULL; + bool found = false; + esp_intr_free(s_dac_digi_ctx->dac_isr_handle); + + DAC_ENTER_CRITICAL_ISR(); + SLIST_FOREACH(it, &s_dac_dma_isr_handler_list, next) { + if (it->handler == handler && it->handler_arg == handler_arg) { + if (it == SLIST_FIRST(&s_dac_dma_isr_handler_list)) { + SLIST_REMOVE_HEAD(&s_dac_dma_isr_handler_list, next); + } else { + SLIST_REMOVE_AFTER(prev, next); + } + found = true; + free(it); + break; + } + prev = it; + } + DAC_EXIT_CRITICAL_ISR(); + return found ? ESP_OK : ESP_ERR_INVALID_STATE; +} + +void dac_dma_linker_stop(void) +{ + dac_hal_dma_disable(); +} + +void dac_dma_linker_deinit(void) +{ + dac_dma_linker_stop(); + dac_hal_digi_clear_intr(UINT32_MAX); + dac_hal_digi_enable_intr(0); +} /*--------------------------------------------------------------- Digital controller setting ---------------------------------------------------------------*/ -esp_err_t dac_digi_init(void) +esp_err_t __attribute__((unused)) 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(); @@ -41,14 +187,173 @@ esp_err_t dac_digi_init(void) return ESP_OK; } -esp_err_t dac_digi_deinit(void) +static lldesc_t** dac_dma_desc_buf_create(int desc_cnt, size_t buf_size, const void *data) { + 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*)malloc(sizeof(lldesc_t)); + if (pdesc[i] == NULL) { + goto _exit; + } + memset(pdesc[i], 0, sizeof(lldesc_t)); + } + 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 *) data; + data += buf_size; + if (s_dac_digi_ctx->dac_dma_link_type == DAC_DMA_LINK_RECURSIVE) { + WRITE_PERI_REG(CP_DMA_OUTLINK_START, 1); + 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); + } + + } + return pdesc; +_exit: + for (int i = 0; i < desc_cnt; i++) { + free(pdesc[i]); + } + free(pdesc); + return NULL; +} + +esp_err_t dac_digi_initialize(const dac_digi_config_t *init_cfg) +{ + DAC_CHECK(init_cfg->mode < DAC_CONV_MAX, "DAC mode error", ESP_ERR_INVALID_ARG); + DAC_CHECK(init_cfg->interval > 0 && init_cfg->interval < 4096, "DAC interval error", ESP_ERR_INVALID_ARG); + DAC_CHECK(init_cfg->dig_clk.div_num < 256, "DAC clk div_num error", ESP_ERR_INVALID_ARG); + DAC_CHECK(init_cfg->dig_clk.div_b > 0 && init_cfg->dig_clk.div_b < 64, "DAC clk div_b error", ESP_ERR_INVALID_ARG); + DAC_CHECK(init_cfg->dig_clk.div_a < 64, "DAC clk div_a error", ESP_ERR_INVALID_ARG); + + esp_err_t err = ESP_OK; + 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_lock) { - esp_pm_lock_delete(s_dac_digi_lock); - s_dac_digi_lock = NULL; + if (s_dac_digi_ctx->pm_lock == NULL) { + if (init_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"); + 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_SPI3_DMA_MODULE); + periph_module_enable(PERIPH_HSPI_MODULE); + periph_module_enable(PERIPH_SARADC_MODULE); + + dac_hal_digi_controller_config(init_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->dac_isr = SPI_OUT_EOF_INT_ENA | SPI_OUT_TOTAL_EOF_INT_ENA; + dac_dma_isr_register(dac_dma_isr, NULL, s_dac_digi_ctx->dac_isr); + + return err; +_exit: + dac_digi_deinitialize(); + return err; +} + +esp_err_t dac_digi_write_bytes(const void *buffer) +{ + dac_hal_digi_clear_intr(UINT32_MAX); + dac_hal_digi_enable_intr(SPI_LL_INTR_OUT_TOTAL_EOF | SPI_LL_INTR_OUT_EOF); + dac_dma_event_t dac_evt; + s_dac_digi_ctx->desc = dac_dma_desc_buf_create(s_dac_digi_ctx->dma_buffer_cnt, s_dac_digi_ctx->dma_buffer_length, (uint32_t*)buffer); + + dac_hal_dma_reset(0); + dac_hal_dma_fifo_reset(); + + dac_hal_dma_start(0, s_dac_digi_ctx->desc[0]); + while (s_dac_digi_ctx->dac_isr) { + xQueueReceive(s_dac_digi_ctx->que_dac_hdl, &dac_evt, 2000 / portTICK_RATE_MS); + ESP_LOGV(DAC_TAG, "DAC-DMA intr type 0x%x", dac_evt.int_msk); + if (dac_evt.int_msk & s_dac_digi_ctx->dac_isr) { + s_dac_digi_ctx->dac_isr &= (~dac_evt.int_msk); + } + } + 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_SPI3_DMA_MODULE); + periph_module_disable(PERIPH_HSPI_MODULE); + periph_module_disable(PERIPH_SARADC_MODULE); + + dac_dma_linker_deinit(); + dac_dma_isr_deregister(dac_dma_isr, NULL); + + if (s_dac_digi_ctx->que_dac_hdl) { + vQueueDelete(s_dac_digi_ctx->que_dac_hdl); + s_dac_digi_ctx->que_dac_hdl = NULL; + } + + 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); + 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(); @@ -56,7 +361,7 @@ esp_err_t dac_digi_deinit(void) return ESP_OK; } -esp_err_t dac_digi_controller_config(const dac_digi_config_t *cfg) +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"); @@ -65,15 +370,15 @@ esp_err_t dac_digi_controller_config(const dac_digi_config_t *cfg) 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 (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_lock); + 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_lock); + 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_lock = NULL; - ESP_LOGE(TAG, "DAC-DMA pm lock error"); + s_dac_digi_ctx->pm_lock = NULL; + ESP_LOGE(DAC_TAG, "DAC-DMA pm lock error"); return err; } } @@ -88,9 +393,14 @@ esp_err_t dac_digi_controller_config(const dac_digi_config_t *cfg) 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_lock, ESP_FAIL, TAG, "Should start after call `dac_digi_controller_config`"); - esp_pm_lock_acquire(s_dac_digi_lock); + DAC_CHECK((s_dac_digi_ctx->pm_lock), "Should start after call `dac_digi_controller_config`", ESP_FAIL); + esp_pm_lock_acquire(s_dac_digi_ctx->pm_lock); #endif DAC_ENTER_CRITICAL(); dac_hal_digi_start(); @@ -101,9 +411,14 @@ esp_err_t dac_digi_start(void) 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_lock) { - esp_pm_lock_release(s_dac_digi_lock); + if (s_dac_digi_ctx->pm_lock) { + esp_pm_lock_release(s_dac_digi_ctx->pm_lock); } #endif DAC_ENTER_CRITICAL(); diff --git a/components/driver/esp32s2/include/driver/dac.h b/components/driver/esp32s2/include/driver/dac.h index 39f3e7e068..ef6bb96fa4 100644 --- a/components/driver/esp32s2/include/driver/dac.h +++ b/components/driver/esp32s2/include/driver/dac.h @@ -20,14 +20,48 @@ extern "C" { * @return * - ESP_OK success */ -esp_err_t dac_digi_init(void); +esp_err_t __attribute__((unused)) dac_digi_init(void); + +/** + * @brief Initialize the Digital DAC. + * + * @param init_cfg Pointer to Digital DAC initilization 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_config_t *init_cfg); /** * @brief DAC digital controller deinitialization. * @return * - ESP_OK success */ -esp_err_t dac_digi_deinit(void); +esp_err_t __attribute__((unused)) dac_digi_deinit(void); + +/** + * @brief Deinitialize the Digital DAC. + * + * @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[in] buffer Buffer to write to DAC. + * + * @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(const void *buffer); /** * @brief Setting the DAC digital controller. @@ -38,7 +72,7 @@ esp_err_t dac_digi_deinit(void); * - ESP_OK success * - ESP_ERR_INVALID_ARG Parameter error */ -esp_err_t dac_digi_controller_config(const dac_digi_config_t *cfg); +esp_err_t __attribute__((unused)) dac_digi_controller_config(const dac_digi_config_t *cfg); /** * @brief DAC digital controller start output voltage. diff --git a/components/driver/test/dac_dma_test/test_esp32s2.c b/components/driver/test/test_dac_dma.c similarity index 51% rename from components/driver/test/dac_dma_test/test_esp32s2.c rename to components/driver/test/test_dac_dma.c index c54101d183..12722e1679 100644 --- a/components/driver/test/dac_dma_test/test_esp32s2.c +++ b/components/driver/test/test_dac_dma.c @@ -34,6 +34,8 @@ #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"; @@ -59,6 +61,87 @@ static void test_pxp_deinit_io(void) #define SAR_SIMPLE_NUM 512 // Set out number of enabled unit. +/*******************************************/ +/** 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_dig_dma_intr_check(dac_digi_convert_mode_t mode) +{ + ESP_LOGI(TAG, " >> %s - dac mode %d<< ", __func__, mode); + + 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_dma_cnt = 4, + .dac_dma_length = 128, + .dac_dma_link_type = DAC_DMA_LINK_LINE, + }; + dac_digi_initialize(&cfg); + dac_output_enable(DAC_CHANNEL_1); + dac_output_enable(DAC_CHANNEL_2); + + dac_dma_test_create_buffer(mode); + dac_digi_start(); + dac_digi_write_bytes((uint8_t*)_buf); + // /* Check interrupt type */ + + ESP_LOGI(TAG, "DAC-DMA intr test over"); + dac_digi_stop(); + dac_digi_deinitialize(); +} + +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); +} + +/**************************************************** + * The code below is used for legacy implementation + ***************************************************/ +#ifndef DAC_DMA_LEGACY_IMPL +#define DAC_DMA_LEGACY_IMPL 1 +#endif + +#if DAC_DMA_LEGACY_IMPL + typedef struct dma_msg { uint32_t int_msk; uint8_t *data; @@ -79,7 +162,7 @@ static lldesc_t dma2 = {0}; * @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. + * @param is_alter Is alter or not. */ uint32_t dac_dma_linker_init(bool is_alter, bool is_loop) { @@ -146,7 +229,7 @@ static IRAM_ATTR void dac_dma_isr(void * arg) /** * Testcase: Check the interrupt types of DAC-DMA. */ -void test_dac_dig_dma_intr_check(dac_digi_convert_mode_t mode) +void test_dac_dig_dma_intr_check_legacy(dac_digi_convert_mode_t mode) { ESP_LOGI(TAG, " >> %s - dac mode %d<< ", __func__, mode); @@ -194,164 +277,12 @@ void test_dac_dig_dma_intr_check(dac_digi_convert_mode_t mode) TEST_ESP_OK( dac_digi_deinit() ); } -TEST_CASE("DAC-DMA interrupt test", "[dac]") +TEST_CASE("DAC-DMA interrupt test(legacy api)", "[dac]") { - test_dac_dig_dma_intr_check(DAC_CONV_NORMAL); - test_dac_dig_dma_intr_check(DAC_CONV_ALTER); + test_dac_dig_dma_intr_check_legacy(DAC_CONV_NORMAL); + test_dac_dig_dma_intr_check_legacy(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 // DAC_DMA_LEGACY_IMPL #endif // CONFIG_IDF_TARGET_ESP32S2 diff --git a/components/hal/esp32s2/include/hal/clk_gate_ll.h b/components/hal/esp32s2/include/hal/clk_gate_ll.h index cde957997f..45ce590207 100644 --- a/components/hal/esp32s2/include/hal/clk_gate_ll.h +++ b/components/hal/esp32s2/include/hal/clk_gate_ll.h @@ -83,6 +83,8 @@ 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/spi_ll.h b/components/hal/esp32s2/include/hal/spi_ll.h index 65084c8d23..13b862b5fd 100644 --- a/components/hal/esp32s2/include/hal/spi_ll.h +++ b/components/hal/esp32s2/include/hal/spi_ll.h @@ -1290,6 +1290,11 @@ static inline bool spi_ll_tx_get_empty_err(spi_dev_t *hw) return hw->dma_int_raw.outfifo_empty_err; } +static inline uint32_t spi_ll_read_intr_status(spi_dev_t *hw) +{ + return hw->dma_int_st.val; +} + #undef SPI_LL_RST_MASK #undef SPI_LL_UNUSED_INT_MASK diff --git a/components/hal/include/hal/dac_hal.h b/components/hal/include/hal/dac_hal.h index f71c95d279..ec2e8014b0 100644 --- a/components/hal/include/hal/dac_hal.h +++ b/components/hal/include/hal/dac_hal.h @@ -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 + */ /******************************************************************************* * NOTICE @@ -21,6 +13,12 @@ #pragma once #include "hal/dac_ll.h" +#if CONFIG_IDF_TARGET_ESP32S2 +#include "soc/spi_struct.h" +#include "hal/spi_ll.h" +#include "soc/spi_reg.h" +#include "soc/spi_periph.h" +#endif /** * Power on dac module and start output voltage. @@ -76,3 +74,51 @@ 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) + +#if CONFIG_IDF_TARGET_ESP32S2 +/******************************************************* + * DAC-DMA hal layer functions. + * On ESP32-S2, DAC shares the DMA with SPI3. +*******************************************************/ + +/** + * Read the interrupt status. + */ +#define dac_hal_digi_read_intr_status() spi_ll_read_intr_status(&GPSPI3) + +/** + * Clear the interrupt bit. + * @param mask spi-dma interrupt bit mask. + */ +#define dac_hal_digi_clear_intr(mask) spi_ll_clear_intr(&GPSPI3, mask) + +/** + * Enable interrupt + * @param mask spi-dma interrupt bit mask. + */ +#define dac_hal_digi_enable_intr(mask) spi_ll_enable_intr(&GPSPI3, mask) + +/** + * Disable dac dma + */ +#define dac_hal_dma_disable() spi_dma_ll_tx_disable(&GPSPI3) + +/** + * Reset dac dma + * @param chan the dma channel. + */ +#define dac_hal_dma_reset(chan) spi_dma_ll_tx_reset(&GPSPI3, chan) + +/** + * Start dac dma + * @param chan the dma channel. + * @param desc the pointer to the dma link. + */ +#define dac_hal_dma_start(chan, desc) spi_dma_ll_tx_start(&GPSPI3, chan, desc) + +/** + * Reset the dma fifo + */ +#define dac_hal_dma_fifo_reset() spi_ll_dma_tx_fifo_reset(&GPSPI3) + +#endif //CONFIG_IDF_TARGET_ESP32S2 diff --git a/components/hal/include/hal/dac_types.h b/components/hal/include/hal/dac_types.h index c07c1c8733..587ea9e4ec 100644 --- a/components/hal/include/hal/dac_types.h +++ b/components/hal/include/hal/dac_types.h @@ -51,6 +51,14 @@ typedef enum { 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. */ @@ -62,6 +70,9 @@ 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; /*!` and the :doc:`LED Control module `. 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. @@ -40,6 +40,35 @@ Setting DAC channel 1 ({IDF_TARGET_DAC_CH_1}) voltage to approx 0.78 of VDD_A vo dac_output_voltage(DAC_CHANNEL_1, 200); +.. only:: esp32s2 + +For {IDF_TARGET_NAME}, DAC support to use DMA to send digital data to convert. Here is the example: + +.. code-block:: c + + #include + + const dac_digi_config_t cfg = { + .mode = mode, + .interval = 100, + .dig_clk.use_apll = false, // APB clk + .dig_clk.div_num = 79, // See comments `adc_digi_clk_t` + .dig_clk.div_b = 1, + .dig_clk.div_a = 0, + .dac_dma_cnt = 1, // The dac dma link number for your project + .dac_dma_length = 512, // The dac dam link length for your project. Should fit the buffer you prepared with dac_dma_cnt. + .dac_dma_link_type = DAC_DMA_LINK_RECURSIVE, //The link type. + }; + + dac_digi_initialize(&cfg); + dac_output_enable(DAC_CHANNEL_X); + dac_digi_start(); + dac_digi_write_bytes((uint8_t*)buffer_you_prepared); + dac_digi_stop(); + dac_digi_deinitialize(); + + + API Reference ------------- diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index c337c380f6..fb6cabc244 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -733,7 +733,6 @@ components/hal/esp32s3/include/hal/usb_ll.h components/hal/esp32s3/include/hal/usb_serial_jtag_ll.h components/hal/include/hal/aes_hal.h components/hal/include/hal/aes_types.h -components/hal/include/hal/dac_hal.h components/hal/include/hal/dac_types.h components/hal/include/hal/ds_hal.h components/hal/include/hal/esp_flash_err.h