Просмотр исходного кода

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.
ziyuan_yin 6 лет назад
Родитель
Сommit
e8ac0bd429

+ 7 - 0
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)

+ 9 - 0
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
+

+ 112 - 0
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.

+ 2 - 0
examples/peripherals/wave_gen/main/CMakeLists.txt

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

+ 103 - 0
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

+ 3 - 0
examples/peripherals/wave_gen/main/component.mk

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

+ 149 - 0
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 <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
+    }
+}