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

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,16 @@
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32
@pytest.mark.esp32s2
@pytest.mark.generic
def test_dac_audio_exampl(dut: Dut) -> None:
dut.expect('I \\(([0-9]+)\\) dac audio: DAC audio example start', timeout=10)
dut.expect('I \\(([0-9]+)\\) dac audio: --------------------------------------', timeout=5)
dut.expect('I \\(([0-9]+)\\) dac audio: DAC initialized success, DAC DMA is ready', timeout=5)
dut.expect('I \\(([0-9]+)\\) dac audio: Audio size ([0-9]+) bytes, played at frequency ([0-9]+) Hz', timeout=5)

View File

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

View File

@@ -0,0 +1,71 @@
| Supported Targets | ESP32 | ESP32-S2 |
| ----------------- | ----- | -------- |
# DAC Basic Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
This example shows the basic usage of outputting a voltage directly by the DAC driver.
The output voltage will increase a step every 500 ms, and it will reset to 0 periodically.
## How to use the Example
### Hardware Required
* A development board with ESP32 or ESP32-S2 SoC
- Note that some ESP32-S2 DevKits have LED on it which is connected to GPIO18 (same pin as DAC channel2), so the output voltage of DAC channel 1 can't go down due the this LED.
* (Optional) An oscilloscope to monitor the output wave
### Configure the Project
There is a macro `EXAMPLE_DAC_USE_SEPARATE_CHANNEL` in the example to choose whether put the two DAC channels into a same group
- If `EXAMPLE_DAC_USE_SEPARATE_CHANNEL` is 1, DAC channel 0 and channel 2 can be set to different voltage separately by their own group handle.
- If `EXAMPLE_DAC_USE_SEPARATE_CHANNEL` is 0, DAC channel 0 and channel 2 will be set to a same voltage by the same group handle.
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
The DAC channels can be read by ADC channels internally. The ADC read period is 100 ms, the following log is the raw ADC value read from the DAC channels, it shows the output voltage is increasing every 500 ms.
```
DAC channel 0 vaule: 37 DAC channel 1 vaule: 0
DAC channel 0 vaule: 37 DAC channel 1 vaule: 0
DAC channel 0 vaule: 38 DAC channel 1 vaule: 0
DAC channel 0 vaule: 38 DAC channel 1 vaule: 0
DAC channel 0 vaule: 34 DAC channel 1 vaule: 0
DAC channel 0 vaule: 179 DAC channel 1 vaule: 117
DAC channel 0 vaule: 176 DAC channel 1 vaule: 117
DAC channel 0 vaule: 178 DAC channel 1 vaule: 122
DAC channel 0 vaule: 179 DAC channel 1 vaule: 118
DAC channel 0 vaule: 177 DAC channel 1 vaule: 115
DAC channel 0 vaule: 316 DAC channel 1 vaule: 261
DAC channel 0 vaule: 317 DAC channel 1 vaule: 263
DAC channel 0 vaule: 311 DAC channel 1 vaule: 261
DAC channel 0 vaule: 317 DAC channel 1 vaule: 260
DAC channel 0 vaule: 317 DAC channel 1 vaule: 262
DAC channel 0 vaule: 458 DAC channel 1 vaule: 406
DAC channel 0 vaule: 456 DAC channel 1 vaule: 406
DAC channel 0 vaule: 454 DAC channel 1 vaule: 403
DAC channel 0 vaule: 457 DAC channel 1 vaule: 406
DAC channel 0 vaule: 459 DAC channel 1 vaule: 407
...
```
If monitoring the DAC channels with an oscilloscope, there will be a direct voltage on the screen and it will be updated every 500 ms.

View File

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

View File

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

View File

@@ -0,0 +1,34 @@
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32
@pytest.mark.esp32s2
@pytest.mark.generic
def test_dac_basic_example(dut: Dut) -> None:
res = []
for _ in range(10):
res.append(dut.expect(r'DAC channel 0 vaule:( +)(\d+)(.*)DAC channel 1 vaule:( +)(\d+)', timeout=10))
avg1_ch1 = 0
avg1_ch2 = 0
avg2_ch1 = 0
avg2_ch2 = 0
for val in res[0:5]:
avg1_ch1 = avg1_ch1 + int(val.group(2))
avg1_ch2 = avg1_ch2 + int(val.group(5))
for val in res[5:10]:
avg2_ch1 = avg1_ch1 + int(val.group(2))
avg2_ch2 = avg1_ch2 + int(val.group(5))
avg1_ch1 = int(avg1_ch1 / 5)
avg1_ch2 = int(avg1_ch2 / 5)
avg2_ch1 = int(avg2_ch1 / 5)
avg2_ch2 = int(avg2_ch2 / 5)
assert avg2_ch1 > avg1_ch1
assert avg2_ch2 > avg1_ch2

View File

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

View File

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

View File

@@ -0,0 +1,153 @@
| Supported Targets | ESP32 | ESP32-S2 |
| ----------------- | ----- | -------- |
# DAC Constant Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
This example shows the basic usage of outputting continuous voltage by the DAC driver. There are two ways to realize continuous output, one is outputting by DMA transmission and another is by timer interrupt.
### Timer Interrupt
While using timer interrupt to output the waves, the digital value will be set in every timer interrupt callback, it means the conversion frequency is equal to the timer interrupt frequency. Obviously, the conversion frequency is limited by the interrupt, which relies on the CPU scheduling, thus it can't reach a high frequency in this mode. But it can be used as a supplementary way while the conversion frequency is too low to use DMA mode.
### DMA transmission
While using DMA to transmit the wave buffers, the digital values are put into a DMA buffer wait for transmitting and converting, it means the conversion frequency is equal to the frequency that DMA transmitting the data. We can set the DMA frequency directly, and the digital data int the buffer will be sent automatically when the buffer has been loaded onto the DMA. So the conversion frequency can reach even several MHz while using DMA mode. But the wave can be distorted if the frequency is too high.
## How to use the Example
### Hardware Required
* A development board with ESP32 or ESP32-S2 SoC
- Note that some ESP32-S2 DevKits have LED on it which is connected to GPIO18 (same pin as DAC channel2), so the output voltage of DAC channel 1 can't go down due the this LED.
* (Optional) An oscilloscope to monitor the output wave
### Configure the Project
You can switch the output method by setting the macro `EXAMPLE_DAC_CONTINUOUS_MODE` to `EXAMPLE_DAC_CONTINUOUS_BY_TIMER` or `EXAMPLE_DAC_CONTINUOUS_BY_DMA`.
There are four waves: sine, triangle, saw tooth and square. These waves are stored in corresponding buffers, and each wave has 400 points as default, which can be modified by `EXAMPLE_ARRAY_LEN`, reduce the point number can increase the wave frequency.
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
This example can output sine wave, triangle wave, saw tooth wave and square wave periodically, each wave will last for 3 seconds.
The DAC channels can be read by ADC channels internally. The ADC read period is 500 ms, the following log is the raw ADC value read from the DAC channels. But since the ADC sample-rate is lower than the DAC output-rate, the sampling value can only indicate that the voltage is changing.
### Timer Triggered Output
You can see sine wave, triangle wave, saw tooth wave and square wave at 50 Hz on the oscilloscope.
```
I (333) dac continuous: --------------------------------------------------
I (343) dac continuous: DAC continuous output by Timer
I (343) dac continuous: DAC channel 0 io: GPIO_NUM_25
I (353) dac continuous: DAC channel 1 io: GPIO_NUM_26
I (353) dac continuous: Waveform: SINE -> TRIANGLE -> SAWTOOTH -> SQUARE
I (363) dac continuous: DAC conversion frequency (Hz): 20000
I (373) dac continuous: DAC wave frequency (Hz): 50
I (373) dac continuous: --------------------------------------------------
DAC channel 0 vaule: 2291 DAC channel 1 vaule: 2331
DAC channel 0 vaule: 43 DAC channel 1 vaule: 3
DAC channel 0 vaule: 55 DAC channel 1 vaule: 32
DAC channel 0 vaule: 57 DAC channel 1 vaule: 33
DAC channel 0 vaule: 56 DAC channel 1 vaule: 34
DAC channel 0 vaule: 59 DAC channel 1 vaule: 34
DAC channel 0 vaule: 56 DAC channel 1 vaule: 33
I (3393) dac continuous: triangle wave start
DAC channel 0 vaule: 2258 DAC channel 1 vaule: 2243
DAC channel 0 vaule: 2257 DAC channel 1 vaule: 2242
DAC channel 0 vaule: 2259 DAC channel 1 vaule: 2242
DAC channel 0 vaule: 2257 DAC channel 1 vaule: 2245
DAC channel 0 vaule: 2257 DAC channel 1 vaule: 2243
DAC channel 0 vaule: 2258 DAC channel 1 vaule: 2240
I (6393) dac continuous: sawtooth wave start
DAC channel 0 vaule: 2704 DAC channel 1 vaule: 2735
DAC channel 0 vaule: 2704 DAC channel 1 vaule: 2735
DAC channel 0 vaule: 2704 DAC channel 1 vaule: 2736
DAC channel 0 vaule: 2704 DAC channel 1 vaule: 2717
DAC channel 0 vaule: 2704 DAC channel 1 vaule: 2734
DAC channel 0 vaule: 2704 DAC channel 1 vaule: 2736
I (9393) dac continuous: square wave start
DAC channel 0 vaule: 0 DAC channel 1 vaule: 0
DAC channel 0 vaule: 0 DAC channel 1 vaule: 0
DAC channel 0 vaule: 0 DAC channel 1 vaule: 0
DAC channel 0 vaule: 0 DAC channel 1 vaule: 0
DAC channel 0 vaule: 0 DAC channel 1 vaule: 0
DAC channel 0 vaule: 0 DAC channel 1 vaule: 0
I (12393) dac continuous: sine wave start
DAC channel 0 vaule: 82 DAC channel 1 vaule: 62
DAC channel 0 vaule: 83 DAC channel 1 vaule: 62
DAC channel 0 vaule: 82 DAC channel 1 vaule: 62
DAC channel 0 vaule: 87 DAC channel 1 vaule: 62
DAC channel 0 vaule: 84 DAC channel 1 vaule: 63
DAC channel 0 vaule: 83 DAC channel 1 vaule: 64
...
```
### DMA Output
You can see sine wave, triangle wave, saw tooth wave and square wave at 2 KHz on the oscilloscope.
```
I (335) dac continuous: --------------------------------------------------
I (345) dac continuous: DAC continuous output by DMA
I (345) dac continuous: DAC channel 0 io: GPIO_NUM_25
I (355) dac continuous: DAC channel 1 io: GPIO_NUM_26
I (355) dac continuous: Waveform: SINE -> TRIANGLE -> SAWTOOTH -> SQUARE
I (365) dac continuous: DAC conversion frequency (Hz): 800000
I (375) dac continuous: DAC wave frequency (Hz): 2000
I (375) dac continuous: --------------------------------------------------
DAC channel 0 vaule: 3131 DAC channel 1 vaule: 1634
DAC channel 0 vaule: 1712 DAC channel 1 vaule: 2531
DAC channel 0 vaule: 1716 DAC channel 1 vaule: 2535
DAC channel 0 vaule: 1715 DAC channel 1 vaule: 2544
DAC channel 0 vaule: 1715 DAC channel 1 vaule: 2533
DAC channel 0 vaule: 1712 DAC channel 1 vaule: 2539
I (3395) dac continuous(DMA): triangle wave start
DAC channel 0 vaule: 592 DAC channel 1 vaule: 1190
DAC channel 0 vaule: 4095 DAC channel 1 vaule: 3518
DAC channel 0 vaule: 4095 DAC channel 1 vaule: 3515
DAC channel 0 vaule: 4095 DAC channel 1 vaule: 3516
DAC channel 0 vaule: 4095 DAC channel 1 vaule: 3514
DAC channel 0 vaule: 4095 DAC channel 1 vaule: 3515
I (6395) dac continuous(DMA): sawtooth wave start
DAC channel 0 vaule: 294 DAC channel 1 vaule: 560
DAC channel 0 vaule: 2861 DAC channel 1 vaule: 3227
DAC channel 0 vaule: 2860 DAC channel 1 vaule: 3216
DAC channel 0 vaule: 2861 DAC channel 1 vaule: 3227
DAC channel 0 vaule: 2861 DAC channel 1 vaule: 3216
DAC channel 0 vaule: 2859 DAC channel 1 vaule: 3183
I (9395) dac continuous(DMA): square wave start
DAC channel 0 vaule: 4095 DAC channel 1 vaule: 4095
DAC channel 0 vaule: 0 DAC channel 1 vaule: 0
DAC channel 0 vaule: 0 DAC channel 1 vaule: 0
DAC channel 0 vaule: 0 DAC channel 1 vaule: 0
DAC channel 0 vaule: 0 DAC channel 1 vaule: 0
DAC channel 0 vaule: 0 DAC channel 1 vaule: 0
I (12395) dac continuous(DMA): sine wave start
DAC channel 0 vaule: 2864 DAC channel 1 vaule: 3691
DAC channel 0 vaule: 0 DAC channel 1 vaule: 204
DAC channel 0 vaule: 0 DAC channel 1 vaule: 202
DAC channel 0 vaule: 0 DAC channel 1 vaule: 193
DAC channel 0 vaule: 0 DAC channel 1 vaule: 181
DAC channel 0 vaule: 0 DAC channel 1 vaule: 194
...
```

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,89 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/adc.h"
#include "esp_check.h"
#include "dac_continuous.h"
/**
* There are two ways to convert digital data to analog signal continuously:
* - Using a timer: setting DAC voltage periodically in the timer interrupt
* in this way, DAC can achieve a relatively low conversion frequency
* but it is not a efficient way comparing to using the DMA
* - Using DMA: tansmitting the data buffer via DMA,
* the conversion frequency is controlled by how fast it is transmitted by DMA
* in this way, the conversion frequency can reach sevral MHz,
* but it can't achieve a very low conversion frequency because it is limited by the DMA clock source
* Generally, recommand to use DMA, if the DMA peripheral is occupied or the required conversion frequency is very low,
* then use timer instead
*/
#define EXAMPLE_DAC_CONTINUOUS_BY_TIMER 0
#define EXAMPLE_DAC_CONTINUOUS_BY_DMA 1
#define EXAMPLE_DAC_CONTINUOUS_MODE EXAMPLE_DAC_CONTINUOUS_BY_DMA
/* ADC configuration */
#if CONFIG_IDF_TARGET_ESP32
#define EXAMPLE_DAC_CHAN0_ADC_CHAN ADC2_CHANNEL_8 // GPIO25, same as DAC channel 0
#define EXAMPLE_DAC_CHAN1_ADC_CHAN ADC2_CHANNEL_9 // GPIO26, same as DAC channel 1
#define EXAMPLE_DAC_CHAN0_IO (25) // DAC channel 0 io number
#define EXAMPLE_DAC_CHAN1_IO (26) // DAC channel 1 io number
#define EXAMPLE_ADC_WIDTH ADC_WIDTH_BIT_12
#elif CONFIG_IDF_TARGET_ESP32S2
#define EXAMPLE_DAC_CHAN0_ADC_CHAN ADC2_CHANNEL_6 // GPIO17, same as DAC channel 0
#define EXAMPLE_DAC_CHAN1_ADC_CHAN ADC2_CHANNEL_7 // GPIO18, same as DAC channel 1
#define EXAMPLE_DAC_CHAN0_IO (17) // DAC channel 0 io number
#define EXAMPLE_DAC_CHAN1_IO (18) // DAC channel 1 io number
#define EXAMPLE_ADC_WIDTH ADC_WIDTH_BIT_13
#endif
#define EXAMPLE_ADC_ATTEN ADC_ATTEN_DB_11
_Static_assert(EXAMPLE_DAC_AMPLITUDE < 256, "The DAC accuracy is 8 bit-width, doesn't support the amplitude beyond 255");
static const char *TAG = "dac continuous";
void example_log_info(uint32_t conv_freq, uint32_t wave_freq)
{
ESP_LOGI(TAG, "--------------------------------------------------");
ESP_LOGI(TAG, "DAC continuous output by %s", EXAMPLE_DAC_CONTINUOUS_MODE ? "DMA" : "Timer");
ESP_LOGI(TAG, "DAC channel 0 io: GPIO_NUM_%d", EXAMPLE_DAC_CHAN0_IO);
ESP_LOGI(TAG, "DAC channel 1 io: GPIO_NUM_%d", EXAMPLE_DAC_CHAN1_IO);
ESP_LOGI(TAG, "Waveform: SINE -> TRIANGLE -> SAWTOOTH -> SQUARE");
ESP_LOGI(TAG, "DAC conversion frequency (Hz): %d", conv_freq);
ESP_LOGI(TAG, "DAC wave frequency (Hz): %d", wave_freq);
ESP_LOGI(TAG, "--------------------------------------------------");
}
static void adc_monitor_task(void *args)
{
/* Set the ADC channels, these channels are connected to the DAC channels internally */
ESP_ERROR_CHECK(adc2_config_channel_atten(EXAMPLE_DAC_CHAN0_ADC_CHAN, EXAMPLE_ADC_ATTEN));
ESP_ERROR_CHECK(adc2_config_channel_atten(EXAMPLE_DAC_CHAN1_ADC_CHAN, EXAMPLE_ADC_ATTEN));
int chan1_val = 0;
int chan2_val = 0;
while (1) {
/* Read the DAC output voltage */
ESP_ERROR_CHECK(adc2_get_raw(EXAMPLE_DAC_CHAN0_ADC_CHAN, EXAMPLE_ADC_WIDTH, &chan1_val));
ESP_ERROR_CHECK(adc2_get_raw(EXAMPLE_DAC_CHAN1_ADC_CHAN, EXAMPLE_ADC_WIDTH, &chan2_val));
printf("DAC channel 0 vaule: %4d\tDAC channel 1 vaule: %4d\n", chan1_val, chan2_val);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
void app_main(void)
{
#if EXAMPLE_DAC_CONTINUOUS_MODE == EXAMPLE_DAC_CONTINUOUS_BY_DMA
/* Output 2 kHz waves using DMA */
dac_continuous_by_dma();
#else
/* Output 50 Hz waves using timer interrupt */
dac_continuous_by_timer();
#endif
/* Create ADC monitor task to detect the voltage on DAC pin every 500 ms */
xTaskCreate(adc_monitor_task, "adc_monitor_task", 4096, NULL, 5, NULL);
}

View File

@@ -0,0 +1,113 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "driver/adc.h"
#include "driver/gptimer.h"
#include "esp_log.h"
#include "dac_continuous.h"
#define EXAMPLE_TIEMR_RESOLUTION 1000000 // 1MHz, 1 tick = 1us
#define EXAMPLE_WAVE_FREQ_HZ 50 // Default wave frequency 50 Hz, it can't be too high
#define EXAMPLE_CONVERT_FREQ_HZ (EXAMPLE_ARRAY_LEN * EXAMPLE_WAVE_FREQ_HZ) // The frequency that DAC convert every data in the wave array
#define EXAMPLE_TIMER_ALARM_COUNT (EXAMPLE_TIEMR_RESOLUTION / EXAMPLE_CONVERT_FREQ_HZ) // The count value that trigger the timer alarm callback
static const char *TAG = "dac continuous(timer)";
static const char wav_name[DAC_WAVE_MAX][15] = {"sine", "triangle", "sawtooth", "square"};
static dac_channels_handle_t dac_handle;
static uint8_t sin_wav[EXAMPLE_ARRAY_LEN]; // sine wave values
static uint8_t tri_wav[EXAMPLE_ARRAY_LEN]; // triangle wave values
static uint8_t saw_wav[EXAMPLE_ARRAY_LEN]; // sawtooth wave values
static uint8_t squ_wav[EXAMPLE_ARRAY_LEN]; // square wave values
/* Timer interrupt service routine */
static bool IRAM_ATTR on_timer_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
{
static uint32_t point_cnt = 0; // For counting the output points of one wave
static uint32_t index = 0; // The current index of the wave buffer
static dac_example_wave_type_t wav_sel = DAC_SINE_WAVE; // Start from sine wave
// Switch wave every EXAMPLE_WAVE_PERIOD_SEC sencond
if (point_cnt < EXAMPLE_CONVERT_FREQ_HZ * EXAMPLE_WAVE_PERIOD_SEC) {
switch (wav_sel) {
case DAC_SINE_WAVE:
ESP_ERROR_CHECK(dac_channels_set_voltage(dac_handle, sin_wav[index]));
break;
case DAC_TRIANGLE_WAVE:
ESP_ERROR_CHECK(dac_channels_set_voltage(dac_handle, tri_wav[index]));
break;
case DAC_SAWTOOTH_WAVE:
ESP_ERROR_CHECK(dac_channels_set_voltage(dac_handle, saw_wav[index]));
break;
case DAC_SQUARE_WAVE:
ESP_ERROR_CHECK(dac_channels_set_voltage(dac_handle, squ_wav[index]));
break;
default:
break;
}
point_cnt++;
index++;
index %= EXAMPLE_ARRAY_LEN;
} else {
wav_sel++;
wav_sel %= DAC_WAVE_MAX;
point_cnt = 0;
index = 0;
ESP_EARLY_LOGI(TAG, "%s wave start", wav_name[wav_sel]);
}
return false;
}
static void example_generate_wave(void)
{
uint32_t pnt_num = EXAMPLE_ARRAY_LEN;
for (int i = 0; i < pnt_num; i ++) {
sin_wav[i] = (uint8_t)((sin( i * CONST_PERIOD_2_PI / pnt_num) + 1) * (double)(EXAMPLE_DAC_AMPLITUDE) / 2 + 0.5);
tri_wav[i] = (i > (pnt_num / 2)) ? (2 * EXAMPLE_DAC_AMPLITUDE * (pnt_num - i) / pnt_num) : (2 * EXAMPLE_DAC_AMPLITUDE * i / pnt_num);
saw_wav[i] = (i == pnt_num) ? 0 : (i * EXAMPLE_DAC_AMPLITUDE / pnt_num);
squ_wav[i] = (i < (pnt_num / 2)) ? EXAMPLE_DAC_AMPLITUDE : 0;
}
}
void dac_continuous_by_timer(void)
{
gptimer_handle_t gptimer = NULL;
gptimer_config_t timer_config = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = EXAMPLE_TIEMR_RESOLUTION, // 1MHz, 1 tick = 1us
};
ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));
dac_channels_config_t dac_cfg = {
.chan_sel = EXAMPLE_DAC_CHANNEL,
};
ESP_ERROR_CHECK(dac_new_channels(&dac_cfg, &dac_handle));
ESP_ERROR_CHECK(dac_channels_enable(dac_handle));
example_log_info(EXAMPLE_CONVERT_FREQ_HZ, EXAMPLE_WAVE_FREQ_HZ);
example_generate_wave();
gptimer_alarm_config_t alarm_config = {
.reload_count = 0,
.alarm_count = EXAMPLE_TIMER_ALARM_COUNT,
.flags.auto_reload_on_alarm = true,
};
gptimer_event_callbacks_t cbs = {
.on_alarm = on_timer_alarm_cb,
};
ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, NULL));
ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config));
ESP_ERROR_CHECK(gptimer_enable(gptimer));
ESP_ERROR_CHECK(gptimer_start(gptimer));
}

View File

@@ -0,0 +1,33 @@
# 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.generic
def test_dac_continuous_example_with_12bit_adc(dut: Dut) -> None:
dut.expect('I \\(([0-9]+)\\) dac continuous: --------------------------------------------------', timeout=10)
dut.expect('I \\(([0-9]+)\\) dac continuous: DAC continuous output by DMA', timeout=10)
dut.expect('I \\(([0-9]+)\\) dac continuous: DAC channel 0 io: GPIO_NUM_25', timeout=10)
dut.expect('I \\(([0-9]+)\\) dac continuous: DAC channel 1 io: GPIO_NUM_26', timeout=10)
dut.expect('I \\(([0-9]+)\\) dac continuous: Waveform: SINE -> TRIANGLE -> SAWTOOTH -> SQUARE', timeout=10)
dut.expect('I \\(([0-9]+)\\) dac continuous: DAC conversion frequency \\(Hz\\): ([0-9]+)', timeout=10)
dut.expect('I \\(([0-9]+)\\) dac continuous: DAC wave frequency \\(Hz\\): ([0-9]+)', timeout=10)
dut.expect('I \\(([0-9]+)\\) dac continuous: --------------------------------------------------', timeout=10)
dut.expect(r'DAC channel 0 vaule:( +)(\d+)(.*)DAC channel 1 vaule:( +)(\d+)', timeout=10)
@pytest.mark.esp32s2
@pytest.mark.generic
def test_dac_continuous_example_with_13bit_adc(dut: Dut) -> None:
dut.expect('I \\(([0-9]+)\\) dac continuous: --------------------------------------------------', timeout=10)
dut.expect('I \\(([0-9]+)\\) dac continuous: DAC continuous output by DMA', timeout=10)
dut.expect('I \\(([0-9]+)\\) dac continuous: DAC channel 0 io: GPIO_NUM_17', timeout=10)
dut.expect('I \\(([0-9]+)\\) dac continuous: DAC channel 1 io: GPIO_NUM_18', timeout=10)
dut.expect('I \\(([0-9]+)\\) dac continuous: Waveform: SINE -> TRIANGLE -> SAWTOOTH -> SQUARE', timeout=10)
dut.expect('I \\(([0-9]+)\\) dac continuous: DAC conversion frequency \\(Hz\\): ([0-9]+)', timeout=10)
dut.expect('I \\(([0-9]+)\\) dac continuous: DAC wave frequency \\(Hz\\): ([0-9]+)', timeout=10)
dut.expect('I \\(([0-9]+)\\) dac continuous: --------------------------------------------------', timeout=10)
dut.expect(r'DAC channel 0 vaule:( +)(\d+)(.*)DAC channel 1 vaule:( +)(\d+)', timeout=10)

View File

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

View File

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

View File

@@ -0,0 +1,70 @@
| Supported Targets | ESP32 | ESP32-S2 |
| ----------------- | ----- | -------- |
# DAC Constant Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
## Overview
This example shows the basic usage of outputting cosine wave by the DAC driver. The cosine wave is generated by the hardware cosine wave generator in the DAC module.
This example will output cosine wave on both channels.
## How to use the Example
### Hardware Required
* A development board with ESP32 or ESP32-S2 SoC
- Note that some ESP32-S2 DevKits have LED on it which is connected to GPIO18 (same pin as DAC channel2), so the output voltage of DAC channel 1 can't go down due the this LED.
* (Optional) An oscilloscope to monitor the output wave
### Configure the Project
There is a macro `EXAMPLE_DAC_USE_SEPARATE_CHANNEL` in the example to choose whether put the two DAC channels into a same group
- If `EXAMPLE_DAC_USE_SEPARATE_CHANNEL` is 1, DAC channel 0 and channel 2 can be set to different cosine wave (but the frequency are same) by their own group handle.
- If `EXAMPLE_DAC_USE_SEPARATE_CHANNEL` is 0, DAC channel 0 and channel 2 will be set to a same cosine wave by the same group handle.
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
The DAC channels can be read by ADC channels internally. The ADC read period is 100 ms, the following log is the raw ADC value read from the DAC channels. But since the ADC sample-rate might be lower than the DAC cosine period, the sampling value can only indicate that the voltage is changing.
```
DAC channel 0 vaule: 647 DAC channel 1 vaule: 1728
DAC channel 0 vaule: 2112 DAC channel 1 vaule: 2166
DAC channel 0 vaule: 778 DAC channel 1 vaule: 2483
DAC channel 0 vaule: 4095 DAC channel 1 vaule: 1922
DAC channel 0 vaule: 238 DAC channel 1 vaule: 1282
DAC channel 0 vaule: 3187 DAC channel 1 vaule: 2609
DAC channel 0 vaule: 627 DAC channel 1 vaule: 1068
DAC channel 0 vaule: 3168 DAC channel 1 vaule: 2624
DAC channel 0 vaule: 225 DAC channel 1 vaule: 1286
DAC channel 0 vaule: 4095 DAC channel 1 vaule: 2083
DAC channel 0 vaule: 89 DAC channel 1 vaule: 1934
DAC channel 0 vaule: 3603 DAC channel 1 vaule: 1434
DAC channel 0 vaule: 725 DAC channel 1 vaule: 2469
DAC channel 0 vaule: 2277 DAC channel 1 vaule: 960
DAC channel 0 vaule: 1306 DAC channel 1 vaule: 2670
DAC channel 0 vaule: 1670 DAC channel 1 vaule: 899
DAC channel 0 vaule: 3189 DAC channel 1 vaule: 2609
DAC channel 0 vaule: 86 DAC channel 1 vaule: 1459
DAC channel 0 vaule: 4095 DAC channel 1 vaule: 2258
...
```
If monitoring the DAC channels with an oscilloscope, there will be two same cosine waves at 1000 Hz if `EXAMPLE_DAC_USE_SEPARATE_CHANNEL` is 0, and two cosine waves with opposite phase and different amplitude at 8000 Hz if `EXAMPLE_DAC_USE_SEPARATE_CHANNEL` is 1.

View File

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

View File

@@ -0,0 +1,111 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/dac_driver.h"
#include "driver/adc.h"
#include "esp_check.h"
#define EXAMPLE_DAC_USE_SEPARATE_CHANNEL 1 // Whether to register two DAC channels in separate control group
#if CONFIG_IDF_TARGET_ESP32
#define EXAMPLE_DAC_CHAN1_ADC_CHAN ADC2_CHANNEL_8 // GPIO25, same as DAC channel 0
#define EXAMPLE_DAC_CHAN2_ADC_CHAN ADC2_CHANNEL_9 // GPIO26, same as DAC channel 1
#define EXAMPLE_ADC_WIDTH ADC_WIDTH_BIT_12
#elif CONFIG_IDF_TARGET_ESP32S2
#define EXAMPLE_DAC_CHAN1_ADC_CHAN ADC2_CHANNEL_6 // GPIO17, same as DAC channel 0
#define EXAMPLE_DAC_CHAN2_ADC_CHAN ADC2_CHANNEL_7 // GPIO18, same as DAC channel 1
#define EXAMPLE_ADC_WIDTH ADC_WIDTH_BIT_13
#endif
#define EXAMPLE_ADC_ATTEN ADC_ATTEN_DB_11
static void adc_monitor_task(void *args)
{
/* Set the ADC channels, these channels are connected to the DAC channels internally */
ESP_ERROR_CHECK(adc2_config_channel_atten(EXAMPLE_DAC_CHAN1_ADC_CHAN, EXAMPLE_ADC_ATTEN));
ESP_ERROR_CHECK(adc2_config_channel_atten(EXAMPLE_DAC_CHAN2_ADC_CHAN, EXAMPLE_ADC_ATTEN));
int chan1_val = 0;
int chan2_val = 0;
while (1) {
/* Read the DAC output voltage */
ESP_ERROR_CHECK(adc2_get_raw(EXAMPLE_DAC_CHAN1_ADC_CHAN, EXAMPLE_ADC_WIDTH, &chan1_val));
ESP_ERROR_CHECK(adc2_get_raw(EXAMPLE_DAC_CHAN2_ADC_CHAN, EXAMPLE_ADC_WIDTH, &chan2_val));
printf("DAC channel 0 vaule: %4d\tDAC channel 1 vaule: %4d\n", chan1_val, chan2_val);
vTaskDelay(pdMS_TO_TICKS(100));
}
}
static void dac_init_channel(dac_channel_mask_t mask, dac_cosine_config_t *cos_cfg, dac_channels_handle_t *dac_handle)
{
dac_channels_handle_t handle = NULL;
dac_channels_config_t cfg = {
.chan_sel = mask,
};
/* Allocate the channel group */
ESP_ERROR_CHECK(dac_new_channels(&cfg, &handle));
/* Enable the channels in the group */
ESP_ERROR_CHECK(dac_channels_enable(handle));
/* Initialize DAC cosine wave genarator */
ESP_ERROR_CHECK(dac_channels_init_cosine_mode(handle, cos_cfg));
/* Start to output the cosine wave */
ESP_ERROR_CHECK(dac_channels_start_cosine_output(handle));
*dac_handle = handle;
}
static void __attribute__((unused)) dac_deinit_channel(dac_channels_handle_t dac_handle)
{
/* Stop outputting the cosine wave */
ESP_ERROR_CHECK(dac_channels_stop_cosine_output(dac_handle));
/* Deinitialize the cosine wave genarator */
ESP_ERROR_CHECK(dac_channels_deinit_cosine_mode(dac_handle));
/* Disable the DAC channels */
ESP_ERROR_CHECK(dac_channels_disable(dac_handle));
/* Delete the channel group */
ESP_ERROR_CHECK(dac_del_channels(dac_handle));
}
void app_main(void)
{
#if EXAMPLE_DAC_USE_SEPARATE_CHANNEL
dac_channels_handle_t chan1_handle;
dac_channels_handle_t chan2_handle;
/* Adopt different cosine wave configuration to the two DAC channel
* But take care that the two channels can only be configured to one frequence
* If they are differt, the latter frequency will cover the former one
* The example here will produce cosine wave at 8 KHz on both channels */
dac_cosine_config_t cos1_cfg = {
.freq_hz = 1000, // It will be covered by 8000 in the latter configuration
.clk_src = DAC_COSINE_CLK_SRC_DEFAULT,
.offset = 0,
.phase = DAC_COSINE_PHASE_0,
.scale = DAC_COSINE_NO_ATTEN,
};
dac_cosine_config_t cos2_cfg = {
.freq_hz = 8000,
.clk_src = DAC_COSINE_CLK_SRC_DEFAULT,
.offset = 0,
.phase = DAC_COSINE_PHASE_180,
.scale = DAC_COSINE_ATTEN_2,
};
dac_init_channel(DAC_CHANNEL_MASK_CH0, &cos1_cfg, &chan1_handle);
dac_init_channel(DAC_CHANNEL_MASK_CH1, &cos2_cfg, &chan2_handle);
xTaskCreate(adc_monitor_task, "adc_monitor_task", 4096, NULL, 5, NULL);
#else
dac_channels_handle_t chan12_handle;
dac_cosine_config_t cos_cfg = {
.freq_hz = 1000, // 130 Hz ~ 200 KHz
.clk_src = DAC_COSINE_CLK_SRC_DEFAULT,
.offset = 0,
.phase = DAC_COSINE_PHASE_0,
.scale = DAC_COSINE_NO_ATTEN,
};
dac_init_channel(DAC_CHANNEL_MASK_BOTH, &cos_cfg, &chan12_handle);
xTaskCreate(adc_monitor_task, "adc_monitor_task", 4096, NULL, 5, NULL);
#endif
}

View File

@@ -0,0 +1,35 @@
# 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.generic
def test_dac_cosine_wave_example_with_12bit_adc(dut: Dut) -> None:
res = []
for _ in range(20):
res.append(dut.expect(r'DAC channel 0 vaule:( +)(\d+)(.*)DAC channel 1 vaule:( +)(\d+)', timeout=10))
chan1_val = []
for val in res:
chan1_val.append(int(val.group(2)))
assert min(chan1_val) < 300
assert max(chan1_val) > 3500
@pytest.mark.esp32s2
@pytest.mark.generic
def test_dac_cosine_wave_example_with_13bit_adc(dut: Dut) -> None:
res = []
for _ in range(20):
res.append(dut.expect(r'DAC channel 0 vaule:( +)(\d+)(.*)DAC channel 1 vaule:( +)(\d+)', timeout=10))
chan1_val = []
for val in res:
chan1_val.append(int(val.group(2)))
assert min(chan1_val) < 600
assert max(chan1_val) > 7000

View File

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

View File

@@ -1,94 +0,0 @@
| Supported Targets | ESP32 |
| ----------------- | ----- |
# I2S Built-in ADC/DAC Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
In this example, we configure I2S to work in I2S_ADC and I2S_DAC modes and then:
* recording sound from ADC,
* playing the recorded sound,
* playing an audio file in flash via DAC.
#### Note:
The `tools` directory contains `generate_audio_file.py` script for generating audio files:
* The script provides an example of generating audio tables from `.wav` files.
* In this example, the `wav` file must be in 16k/16bit mono format.
* The script will bundle the `wav` files into a single table named `audio_example_file.h`.
* Since the ADC can only play 8-bit data, the script will scale each 16-bit value to a 8-bit value.
* The script will covert all signed values into unsigned values because only positive values will be output by the ADC.
## How to Use Example
### Hardware Required
* A development board with ESP32 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
* A USB cable for power supply and programming
* A microphone (with amplifier) and one or two speaker(s) for testing.
The following is the hardware connection:
|hardware|module|GPIO|
|:---:|:---:|:---:|
|Microphone|ADC1_CH0|GPIO36|
|speaker(R)|DAC1|GPIO25|
|speaker(L)|DAC2|GPIO26|
### Configure the Project
```
idf.py menuconfig
```
* Set the flash size to 4 MB under Serial Flasher Options.
* Select "Custom partition table CSV" and rename "Custom partition CSV file" to "partitions_adc_dac_example.csv".
(Note that you can use `sdkconfig.defaults`)
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
Reset your development board. The application it will first record the sound through the microphone. Then it will play the recorded sound, and finally a piece of audio stored in the flash. The following is the output log:
```
partiton addr: 0x00110000; size: 2097152; label: storage
Erasing flash
partiton addr: 0x00110000; size: 2097152; label: storage
Erase size: 323584 Bytes
I2S: PLL_D2: Req RATE: 16000, real rate: 1004.000, BITS: 16, CLKM: 83, BCK: 60, MCLK: 83.333, SCLK: 32128.000000, diva: 64, divb: 21
Sound recording 5%
Sound recording 10%
...
Sound recording 97%
Sound recording 102%
playing: 0 %
playing: 1 %
playing: 2 %
...
playing: 96 %
playing: 97 %
playing: 98 %
Playing file example:
I2S: PLL_D2: Req RATE: 16000, real rate: 1004.000, BITS: 16, CLKM: 83, BCK: 60, MCLK: 83.333, SCLK: 32128.000000, diva: 64, divb: 21
```
## Troubleshooting
* Program upload failure
* Hardware connection is not correct: run `idf.py -p PORT monitor`, and reboot your board to see if there are any output logs.
* The baud rate for downloading is too high: lower your baud rate in the `menuconfig` menu, and try again.
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

View File

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

View File

@@ -1,303 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* ADC/DAC are not supported in the new I2S driver, but still available in the legacy I2S driver for backward compatibility
* Please turn to the dedicated ADC/DAC driver instead */
#pragma message("ADC/DAC on ESP32 will no longer supported via I2S driver")
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "spi_flash_mmap.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_partition.h"
#include "driver/i2s.h"
#include "audio_example_file.h"
#include "esp_rom_sys.h"
#include "driver/adc.h"
#include "esp_adc_cal.h"
#if CONFIG_IDF_TARGET_ESP32
static const char* TAG = "ad/da";
#define V_REF 1100
#define ADC1_TEST_CHANNEL (ADC1_CHANNEL_7)
#define PARTITION_NAME "storage"
/*---------------------------------------------------------------
EXAMPLE CONFIG
---------------------------------------------------------------*/
//enable record sound and save in flash
#define RECORD_IN_FLASH_EN (1)
//enable replay recorded sound in flash
#define REPLAY_FROM_FLASH_EN (1)
//i2s number
#define EXAMPLE_I2S_NUM (0)
//i2s sample rate
#define EXAMPLE_I2S_SAMPLE_RATE (16000)
//i2s data bits
#define EXAMPLE_I2S_SAMPLE_BITS (16)
//enable display buffer for debug
#define EXAMPLE_I2S_BUF_DEBUG (0)
//I2S read buffer length
#define EXAMPLE_I2S_READ_LEN (16 * 1024)
//I2S data format
#define EXAMPLE_I2S_FORMAT (I2S_CHANNEL_FMT_RIGHT_LEFT)
//I2S channel number
#define EXAMPLE_I2S_CHANNEL_NUM ((EXAMPLE_I2S_FORMAT < I2S_CHANNEL_FMT_ONLY_RIGHT) ? (2) : (1))
//I2S built-in ADC unit
#define I2S_ADC_UNIT ADC_UNIT_1
//I2S built-in ADC channel
#define I2S_ADC_CHANNEL ADC1_CHANNEL_0
//flash record size, for recording 5 seconds' data
#define FLASH_RECORD_SIZE (EXAMPLE_I2S_CHANNEL_NUM * EXAMPLE_I2S_SAMPLE_RATE * EXAMPLE_I2S_SAMPLE_BITS / 8 * 5)
#define FLASH_ERASE_SIZE (FLASH_RECORD_SIZE % FLASH_SECTOR_SIZE == 0) ? FLASH_RECORD_SIZE : FLASH_RECORD_SIZE + (FLASH_SECTOR_SIZE - FLASH_RECORD_SIZE % FLASH_SECTOR_SIZE)
//sector size of flash
#define FLASH_SECTOR_SIZE (0x1000)
//flash read / write address
#define FLASH_ADDR (0x200000)
/**
* @brief I2S ADC/DAC mode init.
*/
void example_i2s_init(void)
{
int i2s_num = EXAMPLE_I2S_NUM;
i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN,
.sample_rate = EXAMPLE_I2S_SAMPLE_RATE,
.bits_per_sample = EXAMPLE_I2S_SAMPLE_BITS,
.communication_format = I2S_COMM_FORMAT_STAND_MSB,
.channel_format = EXAMPLE_I2S_FORMAT,
.intr_alloc_flags = 0,
.dma_buf_count = 6,
.dma_buf_len = 256,
.use_apll = 1,
};
//install and start i2s driver
i2s_driver_install(i2s_num, &i2s_config, 0, NULL);
//init DAC pad
i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
//init ADC pad
i2s_set_adc_mode(I2S_ADC_UNIT, I2S_ADC_CHANNEL);
}
/*
* @brief erase flash for recording
*/
void example_erase_flash(void)
{
#if RECORD_IN_FLASH_EN
printf("Erasing flash \n");
const esp_partition_t *data_partition = NULL;
data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
ESP_PARTITION_SUBTYPE_DATA_FAT, PARTITION_NAME);
if (data_partition != NULL) {
printf("partiton addr: 0x%08"PRIx32"; size: %"PRIu32"; label: %s\n", data_partition->address, data_partition->size, data_partition->label);
}
printf("Erase size: %d Bytes\n", FLASH_ERASE_SIZE);
ESP_ERROR_CHECK(esp_partition_erase_range(data_partition, 0, FLASH_ERASE_SIZE));
#else
printf("Skip flash erasing...\n");
#endif
}
/**
* @brief debug buffer data
*/
void example_disp_buf(uint8_t* buf, int length)
{
#if EXAMPLE_I2S_BUF_DEBUG
printf("======\n");
for (int i = 0; i < length; i++) {
printf("%02x ", buf[i]);
if ((i + 1) % 8 == 0) {
printf("\n");
}
}
printf("======\n");
#endif
}
/**
* @brief Reset i2s clock and mode
*/
void example_reset_play_mode(void)
{
i2s_set_clk(EXAMPLE_I2S_NUM, EXAMPLE_I2S_SAMPLE_RATE, EXAMPLE_I2S_SAMPLE_BITS, EXAMPLE_I2S_CHANNEL_NUM);
}
/**
* @brief Set i2s clock for example audio file
*/
void example_set_file_play_mode(void)
{
i2s_set_clk(EXAMPLE_I2S_NUM, 16000, EXAMPLE_I2S_SAMPLE_BITS, 1);
}
/**
* @brief Scale data to 16bit/32bit for I2S DMA output.
* DAC can only output 8bit data value.
* I2S DMA will still send 16 bit or 32bit data, the highest 8bit contains DAC data.
*/
int example_i2s_dac_data_scale(uint8_t* d_buff, uint8_t* s_buff, uint32_t len)
{
uint32_t j = 0;
#if (EXAMPLE_I2S_SAMPLE_BITS == 16)
for (int i = 0; i < len; i++) {
d_buff[j++] = 0;
d_buff[j++] = s_buff[i];
}
return (len * 2);
#else
for (int i = 0; i < len; i++) {
d_buff[j++] = 0;
d_buff[j++] = 0;
d_buff[j++] = 0;
d_buff[j++] = s_buff[i];
}
return (len * 4);
#endif
}
/**
* @brief Scale data to 8bit for data from ADC.
* Data from ADC are 12bit width by default.
* DAC can only output 8 bit data.
* Scale each 12bit ADC data to 8bit DAC data.
*/
void example_i2s_adc_data_scale(uint8_t * d_buff, uint8_t* s_buff, uint32_t len)
{
uint32_t j = 0;
uint32_t dac_value = 0;
#if (EXAMPLE_I2S_SAMPLE_BITS == 16)
for (int i = 0; i < len; i += 2) {
dac_value = ((((uint16_t) (s_buff[i + 1] & 0xf) << 8) | ((s_buff[i + 0]))));
d_buff[j++] = 0;
d_buff[j++] = dac_value * 256 / 4096;
}
#else
for (int i = 0; i < len; i += 4) {
dac_value = ((((uint16_t)(s_buff[i + 3] & 0xf) << 8) | ((s_buff[i + 2]))));
d_buff[j++] = 0;
d_buff[j++] = 0;
d_buff[j++] = 0;
d_buff[j++] = dac_value * 256 / 4096;
}
#endif
}
/**
* @brief I2S ADC/DAC example
* 1. Erase flash
* 2. Record audio from ADC and save in flash
* 3. Read flash and replay the sound via DAC
* 4. Play an example audio file(file format: 8bit/8khz/single channel)
* 5. Loop back to step 3
*/
void example_i2s_adc_dac(void*arg)
{
const esp_partition_t *data_partition = NULL;
data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
ESP_PARTITION_SUBTYPE_DATA_FAT, PARTITION_NAME);
if (data_partition != NULL) {
printf("partiton addr: 0x%08"PRIx32"; size: %"PRIu32"; label: %s\n", data_partition->address, data_partition->size, data_partition->label);
} else {
ESP_LOGE(TAG, "Partition error: can't find partition name: %s\n", PARTITION_NAME);
vTaskDelete(NULL);
}
//1. Erase flash
example_erase_flash();
int i2s_read_len = EXAMPLE_I2S_READ_LEN;
int flash_wr_size = 0;
size_t bytes_read, bytes_written;
//2. Record audio from ADC and save in flash
#if RECORD_IN_FLASH_EN
char* i2s_read_buff = (char*) calloc(i2s_read_len, sizeof(char));
uint8_t* flash_write_buff = (uint8_t*) calloc(i2s_read_len, sizeof(char));
i2s_adc_enable(EXAMPLE_I2S_NUM);
while (flash_wr_size < FLASH_RECORD_SIZE) {
//read data from I2S bus, in this case, from ADC.
i2s_read(EXAMPLE_I2S_NUM, (void*) i2s_read_buff, i2s_read_len, &bytes_read, portMAX_DELAY);
example_disp_buf((uint8_t*) i2s_read_buff, 64);
//save original data from I2S(ADC) into flash.
esp_partition_write(data_partition, flash_wr_size, i2s_read_buff, i2s_read_len);
flash_wr_size += i2s_read_len;
esp_rom_printf("Sound recording %u%%\n", flash_wr_size * 100 / FLASH_RECORD_SIZE);
}
i2s_adc_disable(EXAMPLE_I2S_NUM);
free(i2s_read_buff);
i2s_read_buff = NULL;
free(flash_write_buff);
flash_write_buff = NULL;
#endif
uint8_t* flash_read_buff = (uint8_t*) calloc(i2s_read_len, sizeof(char));
uint8_t* i2s_write_buff = (uint8_t*) calloc(i2s_read_len, sizeof(char));
while (1) {
//3. Read flash and replay the sound via DAC
#if REPLAY_FROM_FLASH_EN
for (int rd_offset = 0; rd_offset < flash_wr_size; rd_offset += FLASH_SECTOR_SIZE) {
//read I2S(ADC) original data from flash
esp_partition_read(data_partition, rd_offset, flash_read_buff, FLASH_SECTOR_SIZE);
//process data and scale to 8bit for I2S DAC.
example_i2s_adc_data_scale(i2s_write_buff, flash_read_buff, FLASH_SECTOR_SIZE);
//send data
i2s_write(EXAMPLE_I2S_NUM, i2s_write_buff, FLASH_SECTOR_SIZE, &bytes_written, portMAX_DELAY);
printf("playing: %d %%\n", rd_offset * 100 / flash_wr_size);
}
#endif
//4. Play an example audio file(file format: 8bit/16khz/single channel)
printf("Playing file example: \n");
int offset = 0;
int tot_size = sizeof(audio_table);
example_set_file_play_mode();
while (offset < tot_size) {
int play_len = ((tot_size - offset) > (4 * 1024)) ? (4 * 1024) : (tot_size - offset);
int i2s_wr_len = example_i2s_dac_data_scale(i2s_write_buff, (uint8_t*)(audio_table + offset), play_len);
i2s_write(EXAMPLE_I2S_NUM, i2s_write_buff, i2s_wr_len, &bytes_written, portMAX_DELAY);
offset += play_len;
example_disp_buf((uint8_t*) i2s_write_buff, 32);
}
vTaskDelay(100 / portTICK_PERIOD_MS);
example_reset_play_mode();
}
free(flash_read_buff);
free(i2s_write_buff);
vTaskDelete(NULL);
}
void adc_read_task(void* arg)
{
adc1_config_width(ADC_WIDTH_BIT_12);
adc1_config_channel_atten(ADC1_TEST_CHANNEL, ADC_ATTEN_DB_11);
esp_adc_cal_characteristics_t characteristics;
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, V_REF, &characteristics);
while(1) {
uint32_t voltage;
vTaskDelay(200 / portTICK_PERIOD_MS);
esp_adc_cal_get_voltage(ADC1_TEST_CHANNEL, &characteristics, &voltage);
ESP_LOGI(TAG, "%"PRIu32" mV", voltage);
}
}
esp_err_t app_main(void)
{
example_i2s_init();
esp_log_level_set("I2S", ESP_LOG_INFO);
xTaskCreate(example_i2s_adc_dac, "example_i2s_adc_dac", 1024 * 2, NULL, 5, NULL);
xTaskCreate(adc_read_task, "ADC read task", 2048, NULL, 5, NULL);
return ESP_OK;
}
#endif

View File

@@ -1,6 +0,0 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
storage, data, fat, , 2M,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 1M,
6 storage, data, fat, , 2M,

View File

@@ -1,15 +0,0 @@
# 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.generic
def test_i2s_adc_dac_example(dut: Dut) -> None:
dut.expect('partiton addr: 0x([0-9a-fA-F]+); size: ([0-9]+); label: storage', timeout=30)
dut.expect_exact('Erasing flash', timeout=30)
dut.expect('partiton addr: 0x([0-9a-fA-F]+); size: ([0-9]+); label: storage', timeout=30)
dut.expect('Erase size: ([0-9]+) Bytes', timeout=30)
dut.expect('I \\(([0-9]+)\\) ad/da: ([0-9]+) mV', timeout=30)

View File

@@ -1,7 +0,0 @@
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_adc_dac_example.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions_adc_dac_example.csv"
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
# ADC/DAC are only supported in the legacy driver, suppress the warnings while using the legacy driver
CONFIG_I2S_SUPPRESS_DEPRECATE_WARN=y
CONFIG_ADC_SUPPRESS_DEPRECATE_WARN=y

View File

@@ -1,112 +0,0 @@
| Supported Targets | ESP32 |
| ----------------- | ----- |
# Wave Generator Example
(See the README.md file in the upper level 'examples' directory for more information about examples)
This example demonstrates how to implement a software controlled signal generator by utilizing the DAC and Timer Group drivers. All waveforms demonstrated in this example are generated by software.
Users can connect DAC output channel to their devices and use it as a simple analog signal output source.
## How to use this example
### Hardware Required
* A development board with ESP32 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
* A USB cable for power supply and programming
* Target device or oscilloscope (not required)
Make sure the DAC output pin (which is the GPIO25 if channel 1 set, GPIO26 if channel 2 set) is connected to the target device correctly.
### Configure the project
Open the project configuration menu (`idf.py menuconfig`).
In the `Example Configuration` menu:
* Use `DAC Channel Num` to select the DAC number.
* Use `Waveform` to select the waveform type.
* Select `Sine` (*default*), `Triangle`, `Sawtooth` or `Square` wave type.
* Select `Wave frequency` from the available range.
* Set the `Enable output voltage log` if you want to print the log in the terminal.
#### Wave frequency
For this example, the range of frequency is from 1 kHz to 17 kHz. **3 kHz is selected by default.**
If you modify the frequency, you will change the number of DAC output points. This will affect the smoothness of the curve as well.
Each output point value is calculated by the DAC resolution of 8-bits (0~255). All of these raw values are stored in an array.
Based on the given frequency, the number of DAC output points for each cycle can be calculated through the following formula:
```num_of_output_points = 1000000(us)/(7 us * frequency)```
For example, with high frequency, 20 kHz will generate only 10 output points; the curve will be edgy.
On the other hand, 500 Hz, a relative low frequency, will result in many DAC output points and the array will not be able to store all of the generated data.
Thus, there will be less output points per cycle in higher frequency, and more points in lower frequency.
After the raw value calculation, the real output voltage can be converted through the following formula (VDD is 3.3 V):
```points_voltage = (VDD * DAC_OUTPUT / 255)```
The voltage is within the range of 0~3300 mV.
#### Enable output voltage log
**Disabled selected by default.**
If enabled, the expected voltage of each point will be printed on the terminal.
### Build and Flash
Build the project and flash it to the board, then run the monitor tool to view the serial output:
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for all the steps to configure and use the ESP-IDF to build projects.
* [ESP-IDF Getting Started Guide on ESP32](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html)
## Example Output
If an oscilloscope is available, the target wave will be displayed in it after running this example.
#### Sine:
![Sine](image/sine.png)
#### Triangle:
![Triangle](image/triangle.png)
#### Sawtooth:
![Sawtooth](image/sawtooth.png)
#### Square:
![Square](image/square.png)
The output log will be shown in the terminal as the following (if `Enable output voltage log` is enabled):
```
I (318) Wave generator: DAC output channel: 1
I (318) Wave generator: GPIO:25
I (328) Wave generator: Waveform: Sine
I (328) Wave generator: Frequency(Hz): 3000
I (338) Wave generator: Output points num: 47
I (438) Wave generator: Output voltage(mV): 1656
I (538) Wave generator: Output voltage(mV): 1863
I (638) Wave generator: Output voltage(mV): 2083
I (738) Wave generator: Output voltage(mV): 2290
I (838) Wave generator: Output voltage(mV): 2484
I (938) Wave generator: Output voltage(mV): 2678
I (1038) Wave generator: Output voltage(mV): 2834
I (1138) Wave generator: Output voltage(mV): 2976
I (1238) Wave generator: Output voltage(mV): 3092
I (1338) Wave generator: Output voltage(mV): 3183
....
```
## Troubleshooting
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

View File

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

View File

@@ -1,103 +0,0 @@
menu "Example Configuration"
choice EXAMPLE_DAC_CHANNEL
bool "DAC Channel Num"
default EXAMPLE_DAC_CHANNEL_1
help
Select DAC channel used by the wave generator.
config EXAMPLE_DAC_CHANNEL_1
bool "DAC Channel 1 (GPIO25)"
config EXAMPLE_DAC_CHANNEL_2
bool "DAC Channel 2 (GPIO26)"
endchoice
config EXAMPLE_DAC_CHANNEL
int
default 0 if EXAMPLE_DAC_CHANNEL_1
default 1 if EXAMPLE_DAC_CHANNEL_2
choice EXAMPLE_WAVEFORM
bool "Waveform"
default EXAMPLE_WAVEFORM_SINE
help
Select waveform
config EXAMPLE_WAVEFORM_SINE
bool "Sine selected"
config EXAMPLE_WAVEFORM_TRIANGLE
bool "Triangle selected"
config EXAMPLE_WAVEFORM_SAWTOOTH
bool "Sawtooth selected"
config EXAMPLE_WAVEFORM_SQUARE
bool "Square selected"
endchoice
choice EXAMPLE_WAVE_FREQUENCY
bool "Wave frequency"
default EXAMPLE_WAVE_FREQ_3000
help
Select output wave frequency.
config EXAMPLE_WAVE_FREQ_1000
bool "1000 Hz"
config EXAMPLE_WAVE_FREQ_2000
bool "2000 Hz"
config EXAMPLE_WAVE_FREQ_3000
bool "3000 Hz"
config EXAMPLE_WAVE_FREQ_4000
bool "4000 Hz"
config EXAMPLE_WAVE_FREQ_5000
bool "5000 Hz"
config EXAMPLE_WAVE_FREQ_6000
bool "6000 Hz"
config EXAMPLE_WAVE_FREQ_7000
bool "7000 Hz"
config EXAMPLE_WAVE_FREQ_8000
bool "8000 Hz"
config EXAMPLE_WAVE_FREQ_9000
bool "9000 Hz"
config EXAMPLE_WAVE_FREQ_10000
bool "10000 Hz"
config EXAMPLE_WAVE_FREQ_11000
bool "11000 Hz"
config EXAMPLE_WAVE_FREQ_12000
bool "12000 Hz"
config EXAMPLE_WAVE_FREQ_13000
bool "13000 Hz"
config EXAMPLE_WAVE_FREQ_14000
bool "14000 Hz"
config EXAMPLE_WAVE_FREQ_15000
bool "15000 Hz"
config EXAMPLE_WAVE_FREQ_16000
bool "16000 Hz"
config EXAMPLE_WAVE_FREQ_17000
bool "17000 Hz"
endchoice
config EXAMPLE_WAVE_FREQUENCY
int
default 1000 if EXAMPLE_WAVE_FREQ_1000
default 2000 if EXAMPLE_WAVE_FREQ_2000
default 3000 if EXAMPLE_WAVE_FREQ_3000
default 4000 if EXAMPLE_WAVE_FREQ_4000
default 5000 if EXAMPLE_WAVE_FREQ_5000
default 6000 if EXAMPLE_WAVE_FREQ_6000
default 7000 if EXAMPLE_WAVE_FREQ_7000
default 8000 if EXAMPLE_WAVE_FREQ_8000
default 9000 if EXAMPLE_WAVE_FREQ_9000
default 10000 if EXAMPLE_WAVE_FREQ_10000
default 11000 if EXAMPLE_WAVE_FREQ_11000
default 12000 if EXAMPLE_WAVE_FREQ_12000
default 13000 if EXAMPLE_WAVE_FREQ_13000
default 14000 if EXAMPLE_WAVE_FREQ_14000
default 15000 if EXAMPLE_WAVE_FREQ_15000
default 16000 if EXAMPLE_WAVE_FREQ_16000
default 17000 if EXAMPLE_WAVE_FREQ_17000
config EXAMPLE_LOG_VOLTAGE
bool "Enable output voltage log"
default n
help
If enabled, the output voltage(in mV) will show in log.
endmenu

View File

@@ -1,131 +0,0 @@
/* Wave Generator Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
DAC output channel, waveform, wave frequency can be customized in menuconfig.
If any questions about this example or more information is needed, please read README.md before your start.
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "driver/dac.h"
#include "driver/gptimer.h"
#include "esp_log.h"
/* The timer ISR has an execution time of 5.5 micro-seconds(us).
Therefore, a timer period less than 5.5 us will cause trigger the interrupt watchdog.
7 us is a safe interval that will not trigger the watchdog. No need to customize it.
*/
#define TIMER_INTR_US 7 // Execution time of each ISR interval in micro-seconds
#define POINT_ARR_LEN 200 // Length of points array
#define AMP_DAC 255 // Amplitude of DAC voltage. If it's more than 256 will causes dac_output_voltage() output 0.
#define VDD 3300 // VDD is 3.3V, 3300mV
#define CONST_PERIOD_2_PI 6.2832
#define DAC_CHAN CONFIG_EXAMPLE_DAC_CHANNEL // DAC_CHANNEL_1 (GPIO25) by default
#define FREQ CONFIG_EXAMPLE_WAVE_FREQUENCY // 3kHz by default
#define OUTPUT_POINT_NUM (int)(1000000 / (TIMER_INTR_US * FREQ) + 0.5) // The number of output wave points.
_Static_assert(OUTPUT_POINT_NUM <= POINT_ARR_LEN, "The CONFIG_EXAMPLE_WAVE_FREQUENCY is too low and using too long buffer.");
static int raw_val[POINT_ARR_LEN]; // Used to store raw values
static int volt_val[POINT_ARR_LEN]; // Used to store voltage values(in mV)
static const char *TAG = "wave_gen";
static int g_index = 0;
/* Timer interrupt service routine */
static bool IRAM_ATTR on_timer_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data)
{
int *head = (int *)user_data;
/* DAC output ISR has an execution time of 4.4 us*/
if (g_index >= OUTPUT_POINT_NUM) {
g_index = 0;
}
dac_output_voltage(DAC_CHAN, *(head + g_index));
g_index++;
return false;
}
static void prepare_data(int pnt_num)
{
for (int i = 0; i < pnt_num; i ++) {
#ifdef CONFIG_EXAMPLE_WAVEFORM_SINE
raw_val[i] = (int)((sin( i * CONST_PERIOD_2_PI / pnt_num) + 1) * (double)(AMP_DAC) / 2 + 0.5);
#elif CONFIG_EXAMPLE_WAVEFORM_TRIANGLE
raw_val[i] = (i > (pnt_num / 2)) ? (2 * AMP_DAC * (pnt_num - i) / pnt_num) : (2 * AMP_DAC * i / pnt_num);
#elif CONFIG_EXAMPLE_WAVEFORM_SAWTOOTH
raw_val[i] = (i == pnt_num) ? 0 : (i * AMP_DAC / pnt_num);
#elif CONFIG_EXAMPLE_WAVEFORM_SQUARE
raw_val[i] = (i < (pnt_num / 2)) ? AMP_DAC : 0;
#endif
volt_val[i] = (int)(VDD * raw_val[i] / (float)AMP_DAC);
}
}
static void log_info(void)
{
ESP_LOGI(TAG, "DAC output channel: %d", DAC_CHAN);
if (DAC_CHAN == DAC_CHANNEL_1) {
ESP_LOGI(TAG, "GPIO:%d", GPIO_NUM_25);
} else {
ESP_LOGI(TAG, "GPIO:%d", GPIO_NUM_26);
}
#ifdef CONFIG_EXAMPLE_WAVEFORM_SINE
ESP_LOGI(TAG, "Waveform: SINE");
#elif CONFIG_EXAMPLE_WAVEFORM_TRIANGLE
ESP_LOGI(TAG, "Waveform: TRIANGLE");
#elif CONFIG_EXAMPLE_WAVEFORM_SAWTOOTH
ESP_LOGI(TAG, "Waveform: SAWTOOTH");
#elif CONFIG_EXAMPLE_WAVEFORM_SQUARE
ESP_LOGI(TAG, "Waveform: SQUARE");
#endif
ESP_LOGI(TAG, "Frequency(Hz): %d", FREQ);
ESP_LOGI(TAG, "Output points num: %d\n", OUTPUT_POINT_NUM);
}
void app_main(void)
{
g_index = 0;
gptimer_handle_t gptimer = NULL;
gptimer_config_t timer_config = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 1000000, // 1MHz, 1 tick = 1us
};
ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer));
ESP_ERROR_CHECK(dac_output_enable(DAC_CHAN));
log_info();
prepare_data(OUTPUT_POINT_NUM);
gptimer_alarm_config_t alarm_config = {
.reload_count = 0,
.alarm_count = TIMER_INTR_US,
.flags.auto_reload_on_alarm = true,
};
gptimer_event_callbacks_t cbs = {
.on_alarm = on_timer_alarm_cb,
};
ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, raw_val));
ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config));
ESP_ERROR_CHECK(gptimer_enable(gptimer));
ESP_ERROR_CHECK(gptimer_start(gptimer));
while (1) {
vTaskDelay(10);
#if CONFIG_EXAMPLE_LOG_VOLTAGE
if (g_index < OUTPUT_POINT_NUM) {
ESP_LOGI(TAG, "Output voltage(mV): %d", volt_val[g_index]);
ESP_LOGD(TAG, "g_index: %d\n", g_index);
}
#endif
}
}