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

This commit is contained in:
laokaiyao
2022-05-24 17:26:36 +08:00
parent 351a18415c
commit f9f9a09dfb
129 changed files with 4163 additions and 8227 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,368 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include "unity.h"
#include "unity_test_utils.h"
#include "driver/dac_driver.h"
#include "driver/adc.h"
#include "esp_err.h"
#if CONFIG_IDF_TARGET_ESP32
#include "esp_private/i2s_platform.h"
// Following headers are used to test the conversion frequency
#include "soc/i2s_periph.h"
#include "hal/gpio_hal.h"
#include "driver/pulse_cnt.h"
#include "soc/pcnt_periph.h"
#elif CONFIG_IDF_TARGET_ESP32S2
#include "esp_private/spi_common_internal.h"
#endif
#if CONFIG_IDF_TARGET_ESP32
#define ADC_TEST_CHANNEL_NUM ADC2_CHANNEL_8 // GPIO25, same as DAC channel 0
#define ADC_TEST_WIDTH ADC_WIDTH_BIT_12
#elif CONFIG_IDF_TARGET_ESP32S2
#define ADC_TEST_CHANNEL_NUM ADC2_CHANNEL_6 // GPIO17, same as DAC channel 0
#define ADC_TEST_WIDTH ADC_WIDTH_BIT_13
#endif
#define ADC_TEST_ATTEN ADC_ATTEN_DB_11
TEST_CASE("DAC_API_basic_logic_test", "[dac]")
{
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
dac_conti_config_t dma_cfg = {
.freq_hz = 20000,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
.desc_num = 10,
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
};
dac_cosine_config_t cos_cfg = {
.freq_hz = 1000,
.clk_src = DAC_COSINE_CLK_SRC_DEFAULT,
.offset = 0,
.phase = DAC_COSINE_PHASE_0,
.scale = DAC_COSINE_NO_ATTEN,
};
/* Constant API test */
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_set_voltage(handle, 100));
TEST_ESP_OK(dac_channels_disable(handle));
/* DMA API test */
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ASSERT(dac_channels_enable_continuous_mode(handle) == ESP_ERR_INVALID_STATE);
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
TEST_ASSERT(dac_channels_disable(handle) == ESP_ERR_INVALID_STATE);
TEST_ASSERT(dac_channels_deinit_continuous_mode(handle) == ESP_ERR_INVALID_STATE);
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
/* Cosine wave API test */
TEST_ESP_OK(dac_channels_init_cosine_mode(handle, &cos_cfg));
TEST_ASSERT(dac_del_channels(handle) == ESP_ERR_INVALID_STATE);
TEST_ESP_OK(dac_channels_start_cosine_output(handle));
TEST_ASSERT(dac_channels_disable(handle) == ESP_ERR_INVALID_STATE);
TEST_ESP_OK(dac_channels_stop_cosine_output(handle));
TEST_ESP_OK(dac_channels_deinit_cosine_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
/* DMA peripheral availability test */
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
#if CONFIG_IDF_TARGET_ESP32
TEST_ESP_OK(i2s_platform_acquire_occupation(0, "dac_test"));
#elif CONFIG_IDF_TARGET_ESP32S2
TEST_ASSERT(spicommon_periph_claim(SPI3_HOST, "dac_test"));
#endif
TEST_ASSERT(dac_channels_init_continuous_mode(handle, &dma_cfg) == ESP_ERR_NOT_FOUND);
#if CONFIG_IDF_TARGET_ESP32
TEST_ESP_OK(i2s_platform_release_occupation(0));
#elif CONFIG_IDF_TARGET_ESP32S2
TEST_ASSERT(spicommon_periph_free(SPI3_HOST));
#endif
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
}
TEST_CASE("DAC_memory_leak_test", "[dac]")
{
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
dac_conti_config_t dma_cfg = {
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
.desc_num = 10,
.freq_hz = 20000,
};
/* Some resources will be lazy installed, ignore the first around */
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
int initial_size = esp_get_free_heap_size();
printf("Initial free heap size: %d\n", initial_size);
for (int i = 0; i < 20; i++) {
printf("# %d: ---------------------------------\n", i + 1);
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
printf("current heap size: %d\n", esp_get_free_heap_size());
TEST_ASSERT(initial_size == esp_get_free_heap_size());
}
vTaskDelay(100 / portTICK_PERIOD_MS);
TEST_ASSERT(initial_size == esp_get_free_heap_size());
}
TEST_CASE("DAC_set_voltage_test", "[dac]")
{
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
/* Prepare ADC2 */
TEST_ESP_OK(adc2_config_channel_atten(ADC_TEST_CHANNEL_NUM, ADC_TEST_ATTEN));
int curr_adc = 0;
int last_adc = 0;
for (uint8_t i = 0; i <= 200; i += 20) {
TEST_ESP_OK(dac_channels_set_voltage(handle, i));
vTaskDelay(pdMS_TO_TICKS(20));
TEST_ESP_OK(adc2_get_raw(ADC_TEST_CHANNEL_NUM, ADC_TEST_WIDTH, &curr_adc));
printf("DAC: %d - ADC: %d\n", i, curr_adc);
if (last_adc != 0) {
TEST_ASSERT_GREATER_THAN(last_adc, curr_adc);
}
last_adc = curr_adc;
}
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
}
TEST_CASE("DAC_dma_write_test", "[dac]")
{
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
dac_conti_config_t dma_cfg = {
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
.desc_num = 10,
.freq_hz = 20000,
};
uint8_t *data = (uint8_t *)calloc(1, 2000);
TEST_ASSERT(data);
for (int i = 0; i < 2000; i++) {
data[i] = i % 256;
}
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_write_continuously(handle, data, 2000, NULL, 1000));
vTaskDelay(pdMS_TO_TICKS(200));
TEST_ESP_OK(dac_channels_write_cyclically(handle, data, 2000, NULL, 1000));
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
free(data);
}
/* Test the conversion frequency by counting the pulse of WS signal
* The frequency test is currently only supported on ESP32
* because there is no such signal to monitor on ESP32-S2 */
#if CONFIG_IDF_TARGET_ESP32
TEST_CASE("DAC_dma_conver_frequency_test", "[dac]")
{
/* Prepare configuration for the PCNT unit */
pcnt_unit_handle_t pcnt_unit = NULL;
pcnt_channel_handle_t pcnt_chan = NULL;
pcnt_unit_config_t unit_config = {
.high_limit = (int16_t)0x7fff,
.low_limit = (int16_t)0x8000,
};
pcnt_chan_config_t chan_config = {
.edge_gpio_num = GPIO_NUM_4,
.level_gpio_num = -1,
};
TEST_ESP_OK(pcnt_new_unit(&unit_config, &pcnt_unit));
TEST_ESP_OK(pcnt_unit_set_glitch_filter(pcnt_unit, NULL));
TEST_ESP_OK(pcnt_new_channel(pcnt_unit, &chan_config, &pcnt_chan));
TEST_ESP_OK(pcnt_channel_set_edge_action(pcnt_chan, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD));
TEST_ESP_OK(pcnt_channel_set_level_action(pcnt_chan, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_KEEP));
TEST_ESP_OK(pcnt_unit_enable(pcnt_unit));
// Connect the clock signal to pcnt input signal
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[GPIO_NUM_4], PIN_FUNC_GPIO);
gpio_set_direction(GPIO_NUM_4, GPIO_MODE_INPUT_OUTPUT);
// The DAC conversion frequency is equal to I2S bclk.
esp_rom_gpio_connect_out_signal(GPIO_NUM_4, i2s_periph_signal[0].m_tx_ws_sig, 0, 0);
esp_rom_gpio_connect_in_signal(GPIO_NUM_4, pcnt_periph_signals.groups[0].units[0].channels[0].pulse_sig, 0);
uint8_t *data = (uint8_t *)calloc(1, 2000);
TEST_ASSERT(data);
/* Register DAC DMA using PLL */
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
dac_conti_config_t dma_cfg = {
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
.desc_num = 10,
.freq_hz = 20000,
};
/* Initialize DAC to test default PLL clock */
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
/* Start transmitting data on line */
TEST_ESP_OK(dac_channels_write_cyclically(handle, data, 2000, NULL, 1000));
int expt_pulse = 2000;
int real_pulse;
/* Count pulse by PCNT */
TEST_ESP_OK(pcnt_unit_clear_count(pcnt_unit));
TEST_ESP_OK(pcnt_unit_start(pcnt_unit));
vTaskDelay(pdMS_TO_TICKS(100));
TEST_ESP_OK(pcnt_unit_stop(pcnt_unit));
TEST_ESP_OK(pcnt_unit_get_count(pcnt_unit, &real_pulse));
/* Delete DAC handle */
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
printf("[PLL | 20000 Hz] %d pulses, expected %d, err %d\n", real_pulse, expt_pulse, real_pulse - expt_pulse);
TEST_ASSERT_INT_WITHIN(expt_pulse * 0.01, expt_pulse, real_pulse);
dma_cfg.clk_src = DAC_DIGI_CLK_SRC_APLL;
/* Initialize DAC to test APLL clock */
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
/* Start transmitting data on line */
TEST_ESP_OK(dac_channels_write_cyclically(handle, data, 2000, NULL, 1000));
/* Count pulse by PCNT */
TEST_ESP_OK(pcnt_unit_clear_count(pcnt_unit));
TEST_ESP_OK(pcnt_unit_start(pcnt_unit));
vTaskDelay(pdMS_TO_TICKS(100));
TEST_ESP_OK(pcnt_unit_stop(pcnt_unit));
TEST_ESP_OK(pcnt_unit_get_count(pcnt_unit, &real_pulse));
/* Delete DAC handle */
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
printf("[APLL | 20000 Hz] %d pulses, expected %d, err %d\n", real_pulse, expt_pulse, real_pulse - expt_pulse);
TEST_ASSERT_INT_WITHIN(expt_pulse * 0.01, expt_pulse, real_pulse);
free(data);
/* Free PCNT */
TEST_ESP_OK(pcnt_del_channel(pcnt_chan));
TEST_ESP_OK(pcnt_unit_stop(pcnt_unit));
TEST_ESP_OK(pcnt_unit_disable(pcnt_unit));
TEST_ESP_OK(pcnt_del_unit(pcnt_unit));
}
#endif
TEST_CASE("DAC_cosine_wave_test", "[dac]")
{
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
dac_cosine_config_t cos_cfg = {
.freq_hz = 1000,
.clk_src = DAC_COSINE_CLK_SRC_DEFAULT,
.offset = 0,
.phase = DAC_COSINE_PHASE_0,
.scale = DAC_COSINE_NO_ATTEN,
};
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_init_cosine_mode(handle, &cos_cfg));
TEST_ESP_OK(dac_channels_start_cosine_output(handle));
vTaskDelay(pdMS_TO_TICKS(200));
TEST_ESP_OK(dac_channels_stop_cosine_output(handle));
TEST_ESP_OK(dac_channels_deinit_cosine_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
}
static volatile bool task_run_flag;
static void dac_acyclicly_write_task(void *arg)
{
dac_channels_handle_t dac_handle = (dac_channels_handle_t)arg;
uint8_t buf[1000];
for (int i = 0; i < 1000; i++) {
buf[i] = i % 256;
}
while (task_run_flag) {
if (dac_channels_write_continuously(dac_handle, buf, 100, NULL, 1000) == ESP_OK) {
printf("DAC write data success\n");
}
vTaskDelay(20);
}
vTaskDelete(NULL);
}
TEST_CASE("DAC_DMA_thread_safe", "[dac]")
{
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_CH0};
dac_conti_config_t dma_cfg = {
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
.desc_num = 10,
.freq_hz = 20000,
};
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
task_run_flag = true;
xTaskCreate(dac_acyclicly_write_task, "dac_acyclicly_write_task", 4096, handle, 5, NULL);
for (int i = 0; i < 5; i++) {
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
printf("DAC stopped\n");
vTaskDelay(pdMS_TO_TICKS(100));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
printf("DAC started\n");
vTaskDelay(pdMS_TO_TICKS(300));
}
task_run_flag = false;
vTaskDelay(pdMS_TO_TICKS(100));
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
}

View File

@@ -0,0 +1,70 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include "unity.h"
#include "unity_test_utils.h"
#include "driver/dac_driver.h"
#include "esp_private/spi_flash_os.h"
#include "esp_heap_caps.h"
#include "esp_err.h"
#define BUF_SIZE 2000
static void IRAM_ATTR test_dac_direct_set_safety(dac_channels_handle_t handle)
{
spi_flash_guard_get()->start();
dac_channels_set_voltage(handle, 128);
spi_flash_guard_get()->end();
}
static void IRAM_ATTR test_dac_dma_iram_safety(dac_channels_handle_t handle, uint8_t *data, uint32_t len)
{
spi_flash_guard_get()->start();
// Change the data of DMA
for (int i = 0; i < len; i++) {
data[i] = i % 100;
}
spi_flash_guard_get()->end();
}
TEST_CASE("DAC_IRAM_safe_test", "[dac]")
{
dac_channels_handle_t handle;
dac_channels_config_t cfg = {.chan_sel = DAC_CHANNEL_MASK_BOTH};
dac_conti_config_t dma_cfg = {
.chan_mode = DAC_CHANNEL_MODE_SIMUL,
.clk_src = DAC_DIGI_CLK_SRC_DEFAULT,
.desc_num = 10,
.freq_hz = 40000,
};
/* Real data in internal memory */
uint8_t *data = (uint8_t *)heap_caps_calloc(1, BUF_SIZE, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
TEST_ASSERT(data);
for (int i = 0; i < BUF_SIZE; i++) {
data[i] = i % 256;
}
/* Get ready for dma transmition */
TEST_ESP_OK(dac_new_channels(&cfg, &handle));
TEST_ESP_OK(dac_channels_enable(handle));
/* Test direct voltage setting safety */
test_dac_direct_set_safety(handle);
TEST_ESP_OK(dac_channels_init_continuous_mode(handle, &dma_cfg));
TEST_ESP_OK(dac_channels_enable_continuous_mode(handle));
/* Simulate cache off */
dac_channels_write_cyclically(handle, data, BUF_SIZE, NULL, 1000);
test_dac_dma_iram_safety(handle, data, BUF_SIZE);
/* Deregister DAC DMA channel group */
TEST_ESP_OK(dac_channels_disable_continuous_mode(handle));
TEST_ESP_OK(dac_channels_deinit_continuous_mode(handle));
TEST_ESP_OK(dac_channels_disable(handle));
TEST_ESP_OK(dac_del_channels(handle));
free(data);
}

View File

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

View File

@@ -0,0 +1,5 @@
CONFIG_COMPILER_DUMP_RTL_FILES=y
CONFIG_DAC_ISR_IRAM_SAFE=y
CONFIG_DAC_CTRL_FUNC_IN_IRAM=y
# silent the error check, as the error string are stored in rodata, causing RTL check failure
CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y

View File

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

View File

@@ -0,0 +1,4 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT=n
# Disable this config, otherwise DAC will be disabled when ADC initialized
CONFIG_ADC_DISABLE_DAC=n

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,184 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
Tests for the dac device driver
*/
#include "esp_system.h"
#include "unity.h"
#include "unity_test_utils.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "soc/soc_caps.h"
#define CONFIG_ADC_SUPPRESS_DEPRECATE_WARN 1
#include "driver/adc.h"
#include "driver/dac.h"
#include "esp_adc_cal.h"
static const char *TAG = "test_dac";
#ifdef CONFIG_IDF_TARGET_ESP32
#define ADC_TEST_WIDTH ADC_WIDTH_BIT_12
#elif defined CONFIG_IDF_TARGET_ESP32S2
#define ADC_TEST_WIDTH ADC_WIDTH_BIT_13 //ESP32S2 only support 13 bit width
#endif
#define ADC_TEST_ATTEN ADC_ATTEN_DB_11
#if CONFIG_IDF_TARGET_ESP32
#define ADC_TEST_CHANNEL_NUM ADC2_CHANNEL_8 // GPIO25
#define DAC_TEST_CHANNEL_NUM DAC_CHAN_0 // GPIO25
#elif CONFIG_IDF_TARGET_ESP32S2
#define ADC_TEST_CHANNEL_NUM ADC2_CHANNEL_6 // GPIO17
#define DAC_TEST_CHANNEL_NUM DAC_CHAN_0 // GPIO17
#endif
#define DAC_OUT_MAX (200)
#define DAC_OUT_TIMES (10)
#define DAC_OUT_STEP (DAC_OUT_MAX / DAC_OUT_TIMES)
#define DAC_TEST_TIMES (100)
TEST_CASE("DAC_output(RTC)_check_by_adc", "[dac_legacy]")
{
gpio_num_t adc_gpio_num, dac_gpio_num;
TEST_ESP_OK( adc2_pad_get_io_num( ADC_TEST_CHANNEL_NUM, &adc_gpio_num ) );
TEST_ESP_OK( dac_pad_get_io_num( DAC_TEST_CHANNEL_NUM, &dac_gpio_num ) );
printf("Please connect ADC2 CH%d-GPIO%d <--> DAC CH%d-GPIO%d.\n", ADC_TEST_CHANNEL_NUM, adc_gpio_num,
DAC_TEST_CHANNEL_NUM + 1, dac_gpio_num );
TEST_ESP_OK( dac_output_enable( DAC_TEST_CHANNEL_NUM ) );
//be sure to do the init before using adc2.
printf("adc2_init...\n");
TEST_ESP_OK( adc2_config_channel_atten( ADC_TEST_CHANNEL_NUM, ADC_TEST_ATTEN ) );
vTaskDelay(2 * portTICK_PERIOD_MS);
printf("start conversion.\n");
int output_data = 0;
int read_raw = 0, read_old = 0;
for (int i = 0; i < DAC_OUT_TIMES; i++) {
TEST_ESP_OK( dac_output_voltage( DAC_TEST_CHANNEL_NUM, output_data ) );
output_data += DAC_OUT_STEP;
vTaskDelay(2 * portTICK_PERIOD_MS);
TEST_ESP_OK( adc2_get_raw( ADC_TEST_CHANNEL_NUM, ADC_TEST_WIDTH, &read_raw) );
ESP_LOGI(TAG, "DAC: %d - ADC: %d", output_data, read_raw);
if (read_old != 0) {
TEST_ASSERT_GREATER_THAN(read_old, read_raw);
}
read_old = read_raw;
}
TEST_ESP_OK( dac_output_disable( DAC_TEST_CHANNEL_NUM ) );
}
TEST_CASE("DAC_cw_generator_output(RTC)_check_by_adc", "[dac_legacy]")
{
gpio_num_t adc_gpio_num, dac_gpio_num;
TEST_ESP_OK( adc2_pad_get_io_num( ADC_TEST_CHANNEL_NUM, &adc_gpio_num ) );
TEST_ESP_OK( dac_pad_get_io_num( DAC_TEST_CHANNEL_NUM, &dac_gpio_num ) );
printf("Please connect ADC2 CH%d-GPIO%d <--> DAC CH%d-GPIO%d.\n", ADC_TEST_CHANNEL_NUM, adc_gpio_num,
DAC_TEST_CHANNEL_NUM + 1, dac_gpio_num );
dac_cw_config_t cw = {
.en_ch = DAC_TEST_CHANNEL_NUM,
.scale = DAC_CW_SCALE_2,
.phase = DAC_CW_PHASE_0,
.freq = 1000,
#if CONFIG_IDF_TARGET_ESP32
.offset = 64,
#elif CONFIG_IDF_TARGET_ESP32S2
.offset = 16,
#endif
};
TEST_ESP_OK( dac_cw_generator_config(&cw) );
TEST_ESP_OK( dac_cw_generator_enable() );
TEST_ESP_OK( dac_output_enable( DAC_TEST_CHANNEL_NUM ) );
//be sure to do the init before using adc2.
printf("adc2_init...\n");
TEST_ESP_OK( adc2_config_channel_atten( ADC_TEST_CHANNEL_NUM, ADC_TEST_ATTEN ) );
vTaskDelay(2 * portTICK_PERIOD_MS);
printf("start conversion.\n");
int read_raw[3] = {0};
for (int i = 0; i < DAC_TEST_TIMES; i++) {
vTaskDelay(10 * portTICK_PERIOD_MS);
TEST_ESP_OK( adc2_get_raw( ADC_TEST_CHANNEL_NUM, ADC_TEST_WIDTH, &read_raw[0]) );
ESP_LOGI(TAG, "ADC: %d", read_raw[0]);
read_raw[2] = read_raw[1];
read_raw[1] = read_raw[0];
}
TEST_ESP_OK( dac_cw_generator_disable() );
TEST_ESP_OK( dac_output_disable( DAC_TEST_CHANNEL_NUM ) );
}
#if CONFIG_IDF_TARGET_ESP32S2
static int helper_calc_dac_output(int mV)
{
return mV * 0.07722;
}
static bool subtest_adc_dac(int mV_ref, esp_adc_cal_characteristics_t * chars)
{
dac_output_voltage(DAC_TEST_CHANNEL_NUM, helper_calc_dac_output(mV_ref));
vTaskDelay(pdMS_TO_TICKS(80));
int raw;
adc2_get_raw((adc2_channel_t)ADC_TEST_CHANNEL_NUM, ADC_WIDTH_BIT_13, &raw);
uint32_t voltage = esp_adc_cal_raw_to_voltage(raw, chars);
TEST_ASSERT_INT_WITHIN( 200, mV_ref, voltage ); // 200 mV error allowance, because both DAC and ADC have error
return true;
}
TEST_CASE("esp32s2_adc2-dac_with_adc2_calibration", "[dac_legacy]")
{
gpio_num_t adc_gpio_num, dac_gpio_num;
if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) != ESP_OK) {
TEST_IGNORE_MESSAGE("Warning: This esp32s2 board does not support calibration. This test will be skipped.\n");
}
TEST_ESP_OK( adc2_pad_get_io_num( ADC_TEST_CHANNEL_NUM, &adc_gpio_num ) );
TEST_ESP_OK( dac_pad_get_io_num( DAC_TEST_CHANNEL_NUM, &dac_gpio_num ) );
printf("Please connect ADC2 CH%d-GPIO%d <--> DAC CH%d-GPIO%d.\n", ADC_TEST_CHANNEL_NUM, adc_gpio_num,
DAC_TEST_CHANNEL_NUM + 1, dac_gpio_num );
TEST_ESP_OK( dac_output_enable( DAC_TEST_CHANNEL_NUM ) );
esp_adc_cal_characteristics_t chars;
printf("Test 0dB atten...\n");
adc2_config_channel_atten((adc2_channel_t)ADC_TEST_CHANNEL_NUM, ADC_ATTEN_DB_0);
esp_adc_cal_characterize(ADC_UNIT_2, ADC_ATTEN_DB_0, ADC_WIDTH_BIT_13, 0, &chars);
printf("a %d, b %d\n", chars.coeff_a, chars.coeff_b);
subtest_adc_dac(750, &chars);
printf("Test 2.5dB atten...\n");
adc2_config_channel_atten((adc2_channel_t)ADC_TEST_CHANNEL_NUM, ADC_ATTEN_DB_2_5);
esp_adc_cal_characterize(ADC_UNIT_2, ADC_ATTEN_DB_2_5, ADC_WIDTH_BIT_13, 0, &chars);
printf("a %d, b %d\n", chars.coeff_a, chars.coeff_b);
subtest_adc_dac(1100, &chars);
printf("Test 6dB atten...\n");
adc2_config_channel_atten((adc2_channel_t)ADC_TEST_CHANNEL_NUM, ADC_ATTEN_DB_6);
esp_adc_cal_characterize(ADC_UNIT_2, ADC_ATTEN_DB_6, ADC_WIDTH_BIT_13, 0, &chars);
printf("a %d, b %d\n", chars.coeff_a, chars.coeff_b);
subtest_adc_dac(800, &chars);
subtest_adc_dac(1250, &chars);
printf("Test 11dB atten...\n");
adc2_config_channel_atten((adc2_channel_t)ADC_TEST_CHANNEL_NUM, ADC_ATTEN_DB_11);
esp_adc_cal_characterize(ADC_UNIT_2, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_13, 0, &chars);
printf("a %d, b %d\n", chars.coeff_a, chars.coeff_b);
subtest_adc_dac(1500, &chars);
subtest_adc_dac(2500, &chars);
}
#endif

View File

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

View File

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

View File

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

View File

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

View File

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