mirror of
https://github.com/espressif/esp-idf.git
synced 2025-11-27 13:03:51 +00:00
dac: update unit-test docs and examples for driver-NG
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(wave_gen)
|
||||
project(dac_audio)
|
||||
53
examples/peripherals/dac/dac_audio/README.md
Normal file
53
examples/peripherals/dac/dac_audio/README.md
Normal 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.
|
||||
2
examples/peripherals/dac/dac_audio/main/CMakeLists.txt
Normal file
2
examples/peripherals/dac/dac_audio/main/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "dac_audio.c"
|
||||
INCLUDE_DIRS ".")
|
||||
73
examples/peripherals/dac/dac_audio/main/dac_audio.c
Normal file
73
examples/peripherals/dac/dac_audio/main/dac_audio.c
Normal 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));
|
||||
}
|
||||
}
|
||||
16
examples/peripherals/dac/dac_audio/pytest_dac_audio.py
Normal file
16
examples/peripherals/dac/dac_audio/pytest_dac_audio.py
Normal 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)
|
||||
@@ -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)
|
||||
71
examples/peripherals/dac/dac_basic/README.md
Normal file
71
examples/peripherals/dac/dac_basic/README.md
Normal 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.
|
||||
2
examples/peripherals/dac/dac_basic/main/CMakeLists.txt
Normal file
2
examples/peripherals/dac/dac_basic/main/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "dac_basic.c"
|
||||
INCLUDE_DIRS ".")
|
||||
96
examples/peripherals/dac/dac_basic/main/dac_basic.c
Normal file
96
examples/peripherals/dac/dac_basic/main/dac_basic.c
Normal 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
|
||||
}
|
||||
34
examples/peripherals/dac/dac_basic/pytest_dac_basic.py
Normal file
34
examples/peripherals/dac/dac_basic/pytest_dac_basic.py
Normal 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
|
||||
1
examples/peripherals/dac/dac_basic/sdkconfig.defaults
Normal file
1
examples/peripherals/dac/dac_basic/sdkconfig.defaults
Normal file
@@ -0,0 +1 @@
|
||||
CONFIG_ADC_DISABLE_DAC=n
|
||||
6
examples/peripherals/dac/dac_continuous/CMakeLists.txt
Normal file
6
examples/peripherals/dac/dac_continuous/CMakeLists.txt
Normal 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)
|
||||
153
examples/peripherals/dac/dac_continuous/README.md
Normal file
153
examples/peripherals/dac/dac_continuous/README.md
Normal 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
|
||||
...
|
||||
```
|
||||
@@ -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 ".")
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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)
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_ADC_DISABLE_DAC=n
|
||||
6
examples/peripherals/dac/dac_cosine_wave/CMakeLists.txt
Normal file
6
examples/peripherals/dac/dac_cosine_wave/CMakeLists.txt
Normal 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)
|
||||
70
examples/peripherals/dac/dac_cosine_wave/README.md
Normal file
70
examples/peripherals/dac/dac_cosine_wave/README.md
Normal 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.
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "dac_cosine_wave.c"
|
||||
INCLUDE_DIRS ".")
|
||||
111
examples/peripherals/dac/dac_cosine_wave/main/dac_cosine_wave.c
Normal file
111
examples/peripherals/dac/dac_cosine_wave/main/dac_cosine_wave.c
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_ADC_DISABLE_DAC=n
|
||||
@@ -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.
|
||||
@@ -1,2 +0,0 @@
|
||||
idf_component_register(SRCS "app_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -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
|
||||
@@ -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,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)
|
||||
@@ -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
|
||||
@@ -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:
|
||||

|
||||
#### Triangle:
|
||||

|
||||
#### Sawtooth:
|
||||

|
||||
#### Square:
|
||||

|
||||
|
||||
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 |
@@ -1,2 +0,0 @@
|
||||
idf_component_register(SRCS "wave_gen_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user