kopia lustrzana https://github.com/espressif/esp-idf
sdm: add wave output example
rodzic
751fa28919
commit
59984e7d1f
|
@ -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
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
idf_component_register(SRCS "sdm_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
|
@ -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)
|
|
@ -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.
|
|
@ -0,0 +1,2 @@
|
|||
idf_component_register(SRCS "sdm_dac_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
|
@ -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 |
|
@ -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')
|
|
@ -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)
|
|
@ -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
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
idf_component_register(SRCS "sdm_led_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
|
@ -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)
|
||||
{
|
|
@ -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')
|
Ładowanie…
Reference in New Issue