sdm: add wave output example

pull/10546/head
laokaiyao 2023-01-04 12:53:06 +08:00
rodzic 751fa28919
commit 59984e7d1f
13 zmienionych plików z 223 dodań i 11 usunięć

Wyświetl plik

@ -6,6 +6,14 @@ Introduction
{IDF_TARGET_NAME} has a second-order sigma-delta modulator, which can generate independent PDM pulses to multiple channels. Please refer to the TRM to check how many hardware channels are available. [1]_
Delta-sigma modulation converts an analog voltage signal into a pulse frequency, or pulse density, which can be understood as pulse-density modulation (PDM) (refer to [Delta-sigma modulation on Wikipedia](https://en.wikipedia.org/wiki/Delta-sigma_modulation)).
The main differences comparing to the PDM in I2S peripheral and DAC are:
1. SDM has no clock signal, it just like the DAC mode of PDM;
2. SDM has no DMA, it can only output the analog signal by a timer just like DAC oneshot mode, therefore, its output rate is limited by the timer callback interval;
3. Base on the former two points, an external active or passive filter is required to restore the analog wave;
Typically, a Sigma-Delta modulated channel can be used in scenarios like:
- LED dimming

Wyświetl plik

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

Wyświetl plik

@ -3,4 +3,4 @@
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(gpio_sigma_delta)
project(sdm_dac_example)

Wyświetl plik

@ -0,0 +1,61 @@
| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- |
# Sigma Delta Modulation DAC Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example uses the sigma-delta driver to generate modulated output on a GPIO. If you filter the output signal with an active or passive filter, you can get a 1 KHz sine wave.
## How to use example
### Hardware Required
* A development board with any supported Espressif SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
* A USB cable for Power supply and programming
* An active or passive filter. Connect them as below:
```
┌──────────────────────────────┐
│ ESP │ ┌───────────────────┐
│ │ │ Active or Passive │ Sine Wave
│ EXAMPLE_SIGMA_DELTA_GPIO_NUM ├────►│ Filter ├──────────────
└──────────────────────────────┘ └───────────────────┘
```
### Configure the project
The main configurations can be update by the macro that defined at the top of the [example source file](main/sdm_dac_example_main.c), including the output gpio, timer callback interval and the sine wave frequency and amplitude.
### 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](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
## Example Output
Once the upload is complete and the board is reset, the program should start running. This is reported on the monitor as below:
```
...
I (299) main_task: Calling app_main()
I (309) gpio: GPIO[0]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (309) sdm_dac: Sigma-delta output is attached to GPIO 0
I (319) sdm_dac: Timer allocated with resolution 10000000 Hz
I (329) sdm_dac: Timer callback registered, interval 10 us
I (329) sdm_dac: Timer enabled
I (339) sdm_dac: Output start
```
After the output stated, you can monitor the sine wave after the filter by an oscilloscope.
![output_wave](output_wave.jpeg)
## 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.

Wyświetl plik

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

Wyświetl plik

@ -0,0 +1,116 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/sdm.h"
#include "driver/gptimer.h"
#define EXAMPLE_SIGMA_DELTA_GPIO_NUM (0) // Select GPIO_NUM_0 as the sigma-delta output pin
#define EXAMPLE_OVER_SAMPLE_RATE (20 * 1000 * 1000) // 20 MHz over sample rate
#define EXAMPLE_TIMER_RESOLUTION (10 * 1000 * 1000) // 10 MHz timer counting resolution
#define EXAMPLE_CALLBACK_INTERVAL_US (10) // 20 us interval of each timer callback
#define EXAMPLE_ALARM_COUNT (EXAMPLE_CALLBACK_INTERVAL_US * (EXAMPLE_TIMER_RESOLUTION / 1000000))
#define EXAMPLE_SINE_WAVE_FREQ_HZ (1000) // 1 KHz wave
#define EXAMPLE_SINE_WAVE_AMPLITUDE (127.0f) // 1 ~ 127
#define EXAMPLE_SINE_WAVE_POINT_NUM (1000000 / (EXAMPLE_CALLBACK_INTERVAL_US * EXAMPLE_SINE_WAVE_FREQ_HZ))
#define CONST_PI (3.1416f) // Constant of PI, used for calculating the sine wave
_Static_assert(EXAMPLE_SINE_WAVE_POINT_NUM > 1, "Sine wave frequency is too high");
_Static_assert(EXAMPLE_CALLBACK_INTERVAL_US >= 7, "Timer callback interval is too short");
static const char *TAG = "sdm_dac";
static int8_t sine_wave[EXAMPLE_SINE_WAVE_POINT_NUM]; // Sine wave data buffer
static bool example_timer_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx)
{
static uint32_t cnt = 0;
sdm_channel_handle_t sdm_chan = (sdm_channel_handle_t)user_ctx;
/* Set the pulse density */
sdm_channel_set_duty(sdm_chan, sine_wave[cnt]);
/* Loop the sine wave data buffer */
cnt++;
cnt %= EXAMPLE_SINE_WAVE_POINT_NUM;
return false;
}
static gptimer_handle_t example_init_gptimer(void* args)
{
/* Allocate GPTimer handle */
gptimer_handle_t timer_handle;
gptimer_config_t timer_cfg = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = EXAMPLE_TIMER_RESOLUTION,
.flags.intr_shared = false,
};
ESP_ERROR_CHECK(gptimer_new_timer(&timer_cfg, &timer_handle));
ESP_LOGI(TAG, "Timer allocated with resolution %d Hz", EXAMPLE_TIMER_RESOLUTION);
/* Set the timer alarm configuration */
gptimer_alarm_config_t alarm_cfg = {
.alarm_count = EXAMPLE_ALARM_COUNT,
.reload_count = 0,
.flags.auto_reload_on_alarm = true,
};
ESP_ERROR_CHECK(gptimer_set_alarm_action(timer_handle, &alarm_cfg));
/* Register the alarm callback */
gptimer_event_callbacks_t cbs = {
.on_alarm = example_timer_callback,
};
ESP_ERROR_CHECK(gptimer_register_event_callbacks(timer_handle, &cbs, args));
ESP_LOGI(TAG, "Timer callback registered, interval %d us", EXAMPLE_CALLBACK_INTERVAL_US);
/* Clear the timer raw count value, make sure it'll count from 0 */
ESP_ERROR_CHECK(gptimer_set_raw_count(timer_handle, 0));
/* Enable the timer */
ESP_ERROR_CHECK(gptimer_enable(timer_handle));
ESP_LOGI(TAG, "Timer enabled");
return timer_handle;
}
static sdm_channel_handle_t example_init_sdm(void)
{
/* Allocate sdm channel handle */
sdm_channel_handle_t sdm_chan = NULL;
sdm_config_t config = {
.clk_src = SDM_CLK_SRC_DEFAULT,
.gpio_num = EXAMPLE_SIGMA_DELTA_GPIO_NUM,
.sample_rate_hz = EXAMPLE_OVER_SAMPLE_RATE,
};
ESP_ERROR_CHECK(sdm_new_channel(&config, &sdm_chan));
/* Enable the sdm channel */
ESP_ERROR_CHECK(sdm_channel_enable(sdm_chan));
ESP_LOGI(TAG, "Sigma-delta output is attached to GPIO %d", EXAMPLE_SIGMA_DELTA_GPIO_NUM);
return sdm_chan;
}
void app_main(void)
{
/* Assign sine wave data */
for (int i = 0; i < EXAMPLE_SINE_WAVE_POINT_NUM; i++) {
sine_wave[i] = (int8_t)((sin(2 * (float)i * CONST_PI / EXAMPLE_SINE_WAVE_POINT_NUM)) * EXAMPLE_SINE_WAVE_AMPLITUDE);
}
/* Initialize sigma-delta modulation on the specific GPIO */
sdm_channel_handle_t sdm_chan = example_init_sdm();
/* Initialize GPTimer and register the timer alarm callback */
gptimer_handle_t timer_handle = example_init_gptimer(sdm_chan);
/* Start the GPTimer */
ESP_LOGI(TAG, "Output start");
ESP_ERROR_CHECK(gptimer_start(timer_handle));
/* Forever loop */
while (1) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
}

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 279 KiB

Wyświetl plik

@ -0,0 +1,19 @@
# 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.esp32s3
@pytest.mark.esp32c3
@pytest.mark.esp32c6
@pytest.mark.generic
def test_sdm_dac_example(dut: Dut) -> None:
dut.expect(r'sdm_dac: Sigma-delta output is attached to GPIO \w+')
dut.expect(r'sdm_dac: Timer allocated with resolution \w+ Hz')
dut.expect(r'sdm_dac: Timer callback registered, interval \w+ us')
dut.expect_exact('sdm_dac: Timer enabled')
dut.expect_exact('sdm_dac: Output start')

Wyświetl plik

@ -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(sdm_led_example)

Wyświetl plik

@ -1,7 +1,7 @@
| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- |
# Sigma Delta Modulation Example
# Sigma Delta Modulation LED Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
@ -22,7 +22,7 @@ EXAMPLE_SIGMA_DELTA_GPIO_NUM +----/\/\/\----+------|>|-----+ GND
### Configure the project
You can change the GPIO number by [EXAMPLE_SIGMA_DELTA_GPIO_NUM](main/sdm_example_main.c).
You can change the GPIO number by [EXAMPLE_SIGMA_DELTA_GPIO_NUM](main/sdm_led_example_main.c).
### Build and Flash

Wyświetl plik

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

Wyświetl plik

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@ -16,7 +16,7 @@
#define EXAMPLE_LED_DIM_DUTY_MAX 90
#define EXAMPLE_LED_DIM_DUTY_MIN (EXAMPLE_LED_DIM_DUTY_MAX - EXAMPLE_LED_DIM_PERIOD_MS / EXAMPLE_LED_DIM_DELAY_MS * EXAMPLE_LED_DIM_DUTY_STEP)
static const char *TAG = "example";
static const char *TAG = "sdm_led";
void app_main(void)
{

Wyświetl plik

@ -11,7 +11,7 @@ from pytest_embedded import Dut
@pytest.mark.esp32c3
@pytest.mark.esp32c6
@pytest.mark.generic
def test_sdm_example(dut: Dut) -> None:
dut.expect_exact('example: Install sigma delta channel')
dut.expect_exact('example: Enable sigma delta channel')
dut.expect_exact('example: Change duty cycle continuously')
def test_sdm_led_example(dut: Dut) -> None:
dut.expect_exact('sdm_led: Install sigma delta channel')
dut.expect_exact('sdm_led: Enable sigma delta channel')
dut.expect_exact('sdm_led: Change duty cycle continuously')