mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/wave_generator_example' into 'master'
example: software controlled analog signal generator See merge request espressif/esp-idf!5994
This commit is contained in:
commit
ed45760ca7
7
examples/peripherals/wave_gen/CMakeLists.txt
Normal file
7
examples/peripherals/wave_gen/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
||||
# 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.5)
|
||||
|
||||
set(SUPPORTED_TARGETS esp32)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(wave_gen)
|
9
examples/peripherals/wave_gen/Makefile
Normal file
9
examples/peripherals/wave_gen/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := wave_gen
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
112
examples/peripherals/wave_gen/README.md
Normal file
112
examples/peripherals/wave_gen/README.md
Normal file
@ -0,0 +1,112 @@
|
||||
# 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 an 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
|
||||
* A bunch of cables
|
||||
* Target device
|
||||
|
||||
Make sure DAC output pin which is GPIO25 if channel 1 set, GPIO26 if channel 2 set, be connected to target device correctly.
|
||||
|
||||
### Configure the project
|
||||
Under example folder, right click and select 'open terminal here'
|
||||
|
||||
Execute following statements in terminal:
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
In `Example Configuration`, set the following options:
|
||||
|
||||
#### DAC channel Num
|
||||
|
||||
ESP32 DAC contains two channels:
|
||||
* **DAC_CHANNEL_1 (GPIO25), selected by default.**
|
||||
* DAC_CHANNEL_2 (GPIO26)
|
||||
|
||||
#### Wave form
|
||||
|
||||
This example demonstrates one of the following waveforms:
|
||||
* **Sine, selected by default.**
|
||||
* Triangle
|
||||
* Sawtooth
|
||||
* Square
|
||||
|
||||
#### Wave frequency
|
||||
|
||||
About this option:
|
||||
|
||||
This signal generator has a range of frequency is 1kHz to 17kHz. **3kHz selected by default.**
|
||||
|
||||
Modify the frequency will change the number of DAC output points. That will affect the smoothness of curve as well. Those output points are used to calculate the raw value(0~255) of each output point. All of these raw value are stored in an array.
|
||||
|
||||
Based on the given frequency, the number of DAC output points for each cycle can be caluculated by following formula:
|
||||
```num_of_output_points = 1000000(us)/(7 us * frequency)```
|
||||
|
||||
For example, with high frequency, 20kHz will results in generating only 10 output points, the curve will be sharp and zigzag.
|
||||
|
||||
On the contrary, 500 Hz, low frequency relatively, will results in many DAC output points and the array cannot stores so many values that it will causes array overboundary.
|
||||
|
||||
In short, there will be less output points per cycle in higher frequency, and more points in lower frequency.
|
||||
|
||||
After got the raw value, the real output voltage can be calculated through following formula (VDD is 3.3V):
|
||||
|
||||
```points_voltage = (VDD * DAC_OUTPUT / 255)```
|
||||
|
||||
The voltage is in range of 0~3300mV.
|
||||
|
||||
#### Enable output voltage log
|
||||
|
||||
**Disabled selected by default.**
|
||||
|
||||
If enabled, expected voltage of each points will be shown in terminal. It's intuitive for debugging and testing. If output example is needed, read **Example Output** chapter below.
|
||||
|
||||
### Build and Flash
|
||||
After configure step is done, build project and flash it to the board:
|
||||
|
||||
```
|
||||
$ 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
|
||||
If oscilloscope is available, the target wave will show on oscilloscope after running this example.
|
||||
|
||||
Additionally, if more specific output voltage information is needed, run menuconfig and set "Enable print output voltage" to "Enabled". Then, more information will show as log in terminal.
|
||||
|
||||
For example, set wave frequency, waveform to 3kHz and sine respectively, and also enable print output voltage option. The output information will show in log in terminal as following:
|
||||
|
||||
```
|
||||
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
|
||||
....
|
||||
|
||||
```
|
||||
`Output voltage` in log means real voltage, in mV, which is output through GPIO by device.
|
2
examples/peripherals/wave_gen/main/CMakeLists.txt
Normal file
2
examples/peripherals/wave_gen/main/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "wave_gen_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
103
examples/peripherals/wave_gen/main/Kconfig.projbuild
Normal file
103
examples/peripherals/wave_gen/main/Kconfig.projbuild
Normal file
@ -0,0 +1,103 @@
|
||||
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
|
3
examples/peripherals/wave_gen/main/component.mk
Normal file
3
examples/peripherals/wave_gen/main/component.mk
Normal file
@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
149
examples/peripherals/wave_gen/main/wave_gen_example_main.c
Normal file
149
examples/peripherals/wave_gen/main/wave_gen_example_main.c
Normal file
@ -0,0 +1,149 @@
|
||||
/* 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/timer.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 WITH_RELOAD 1
|
||||
#define TIMER_INTR_US 7 // Execution time of each ISR interval in micro-seconds
|
||||
#define TIMER_DIVIDER 16
|
||||
#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 SEC_TO_MICRO_SEC(x) ((x) / 1000 / 1000) // Convert second to micro-second
|
||||
#define UNUSED_PARAM __attribute__((unused)) // A const period parameter which equals 2 * pai, used to calculate raw dac output value.
|
||||
#define TIMER_TICKS (TIMER_BASE_CLK / TIMER_DIVIDER) // TIMER_BASE_CLK = APB_CLK = 80MHz
|
||||
#define ALARM_VAL_US SEC_TO_MICRO_SEC(TIMER_INTR_US * TIMER_TICKS) // Alarm value in micro-seconds
|
||||
#define OUTPUT_POINT_NUM (int)(1000000 / (TIMER_INTR_US * FREQ) + 0.5) // The number of output wave points.
|
||||
|
||||
#define DAC_CHAN CONFIG_EXAMPLE_DAC_CHANNEL // DAC_CHANNEL_1 (GPIO25) by default
|
||||
#define FREQ CONFIG_EXAMPLE_WAVE_FREQUENCY // 3kHz by default
|
||||
|
||||
_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 void IRAM_ATTR timer0_ISR(void *ptr)
|
||||
{
|
||||
timer_group_clr_intr_status_in_isr(TIMER_GROUP_0, TIMER_0);
|
||||
timer_group_enable_alarm_in_isr(TIMER_GROUP_0, TIMER_0);
|
||||
|
||||
int *head = (int*)ptr;
|
||||
|
||||
/* 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++;
|
||||
}
|
||||
|
||||
/* Timer group0 TIMER_0 initialization */
|
||||
static void example_timer_init(int timer_idx, bool auto_reload)
|
||||
{
|
||||
esp_err_t ret;
|
||||
timer_config_t config = {
|
||||
.divider = TIMER_DIVIDER,
|
||||
.counter_dir = TIMER_COUNT_UP,
|
||||
.counter_en = TIMER_PAUSE,
|
||||
.alarm_en = TIMER_ALARM_EN,
|
||||
.intr_type = TIMER_INTR_LEVEL,
|
||||
.auto_reload = auto_reload,
|
||||
};
|
||||
|
||||
ret = timer_init(TIMER_GROUP_0, timer_idx, &config);
|
||||
ESP_ERROR_CHECK(ret);
|
||||
ret = timer_set_counter_value(TIMER_GROUP_0, timer_idx, 0x00000000ULL);
|
||||
ESP_ERROR_CHECK(ret);
|
||||
ret = timer_set_alarm_value(TIMER_GROUP_0, timer_idx, ALARM_VAL_US);
|
||||
ESP_ERROR_CHECK(ret);
|
||||
ret = timer_enable_intr(TIMER_GROUP_0, TIMER_0);
|
||||
ESP_ERROR_CHECK(ret);
|
||||
/* Register an ISR handler */
|
||||
timer_isr_register(TIMER_GROUP_0, timer_idx, timer0_ISR, (void *)raw_val, ESP_INTR_FLAG_IRAM, NULL);
|
||||
}
|
||||
|
||||
static void prepare_data(int pnt_num)
|
||||
{
|
||||
timer_pause(TIMER_GROUP_0, TIMER_0);
|
||||
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);
|
||||
}
|
||||
timer_start(TIMER_GROUP_0, TIMER_0);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
esp_err_t ret;
|
||||
example_timer_init(TIMER_0, WITH_RELOAD);
|
||||
|
||||
ret = dac_output_enable(DAC_CHAN);
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
log_info();
|
||||
g_index = 0;
|
||||
prepare_data(OUTPUT_POINT_NUM);
|
||||
|
||||
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
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user