From e8ac0bd42944800fed22cd3ea74e4094c3e726ff Mon Sep 17 00:00:00 2001 From: ziyuan_yin Date: Wed, 14 Aug 2019 17:18:28 +0800 Subject: [PATCH] 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. --- examples/peripherals/wave_gen/CMakeLists.txt | 7 + examples/peripherals/wave_gen/Makefile | 9 ++ examples/peripherals/wave_gen/README.md | 112 +++++++++++++ .../peripherals/wave_gen/main/CMakeLists.txt | 2 + .../wave_gen/main/Kconfig.projbuild | 103 ++++++++++++ .../peripherals/wave_gen/main/component.mk | 3 + .../wave_gen/main/wave_gen_example_main.c | 149 ++++++++++++++++++ 7 files changed, 385 insertions(+) create mode 100644 examples/peripherals/wave_gen/CMakeLists.txt create mode 100644 examples/peripherals/wave_gen/Makefile create mode 100644 examples/peripherals/wave_gen/README.md create mode 100644 examples/peripherals/wave_gen/main/CMakeLists.txt create mode 100644 examples/peripherals/wave_gen/main/Kconfig.projbuild create mode 100644 examples/peripherals/wave_gen/main/component.mk create mode 100644 examples/peripherals/wave_gen/main/wave_gen_example_main.c diff --git a/examples/peripherals/wave_gen/CMakeLists.txt b/examples/peripherals/wave_gen/CMakeLists.txt new file mode 100644 index 0000000000..5c8812bb09 --- /dev/null +++ b/examples/peripherals/wave_gen/CMakeLists.txt @@ -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) diff --git a/examples/peripherals/wave_gen/Makefile b/examples/peripherals/wave_gen/Makefile new file mode 100644 index 0000000000..4e87bca116 --- /dev/null +++ b/examples/peripherals/wave_gen/Makefile @@ -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 + diff --git a/examples/peripherals/wave_gen/README.md b/examples/peripherals/wave_gen/README.md new file mode 100644 index 0000000000..810e99354c --- /dev/null +++ b/examples/peripherals/wave_gen/README.md @@ -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. \ No newline at end of file diff --git a/examples/peripherals/wave_gen/main/CMakeLists.txt b/examples/peripherals/wave_gen/main/CMakeLists.txt new file mode 100644 index 0000000000..c260ce8feb --- /dev/null +++ b/examples/peripherals/wave_gen/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "wave_gen_example_main.c" + INCLUDE_DIRS ".") diff --git a/examples/peripherals/wave_gen/main/Kconfig.projbuild b/examples/peripherals/wave_gen/main/Kconfig.projbuild new file mode 100644 index 0000000000..b4868bd9db --- /dev/null +++ b/examples/peripherals/wave_gen/main/Kconfig.projbuild @@ -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 diff --git a/examples/peripherals/wave_gen/main/component.mk b/examples/peripherals/wave_gen/main/component.mk new file mode 100644 index 0000000000..44bd2b5273 --- /dev/null +++ b/examples/peripherals/wave_gen/main/component.mk @@ -0,0 +1,3 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# diff --git a/examples/peripherals/wave_gen/main/wave_gen_example_main.c b/examples/peripherals/wave_gen/main/wave_gen_example_main.c new file mode 100644 index 0000000000..9cead7cc2d --- /dev/null +++ b/examples/peripherals/wave_gen/main/wave_gen_example_main.c @@ -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 +#include +#include +#include +#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 + } +} \ No newline at end of file