wave_gen example: analog signal generator

This wave generator example does following:

 - An analog signal generator.
 - Offering four kinds of waveform:
 sine, triangle, sawtooth, square.
 - Customer can select their expected waveform, frequency, etc. All of them can be configured in menuconfig.
This commit is contained in:
ziyuan_yin 2019-08-14 17:18:28 +08:00 committed by bot
parent 647cb628a1
commit e8ac0bd429
7 changed files with 385 additions and 0 deletions

View 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)

View 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

View 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.

View file

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

View 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

View file

@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View 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
}
}