diff --git a/docs/en/api-reference/peripherals/sdm.rst b/docs/en/api-reference/peripherals/sdm.rst index 190d1dc89e..d712dd5eef 100644 --- a/docs/en/api-reference/peripherals/sdm.rst +++ b/docs/en/api-reference/peripherals/sdm.rst @@ -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 diff --git a/examples/peripherals/sigma_delta/main/CMakeLists.txt b/examples/peripherals/sigma_delta/main/CMakeLists.txt deleted file mode 100644 index a429e2c395..0000000000 --- a/examples/peripherals/sigma_delta/main/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -idf_component_register(SRCS "sdm_example_main.c" - INCLUDE_DIRS ".") diff --git a/examples/peripherals/sigma_delta/CMakeLists.txt b/examples/peripherals/sigma_delta/sdm_dac/CMakeLists.txt similarity index 89% rename from examples/peripherals/sigma_delta/CMakeLists.txt rename to examples/peripherals/sigma_delta/sdm_dac/CMakeLists.txt index 0f4904e345..633eb379be 100644 --- a/examples/peripherals/sigma_delta/CMakeLists.txt +++ b/examples/peripherals/sigma_delta/sdm_dac/CMakeLists.txt @@ -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) diff --git a/examples/peripherals/sigma_delta/sdm_dac/README.md b/examples/peripherals/sigma_delta/sdm_dac/README.md new file mode 100644 index 0000000000..42f9cc8e36 --- /dev/null +++ b/examples/peripherals/sigma_delta/sdm_dac/README.md @@ -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. diff --git a/examples/peripherals/sigma_delta/sdm_dac/main/CMakeLists.txt b/examples/peripherals/sigma_delta/sdm_dac/main/CMakeLists.txt new file mode 100644 index 0000000000..c597f431d5 --- /dev/null +++ b/examples/peripherals/sigma_delta/sdm_dac/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "sdm_dac_example_main.c" + INCLUDE_DIRS ".") diff --git a/examples/peripherals/sigma_delta/sdm_dac/main/sdm_dac_example_main.c b/examples/peripherals/sigma_delta/sdm_dac/main/sdm_dac_example_main.c new file mode 100644 index 0000000000..18eb687e9c --- /dev/null +++ b/examples/peripherals/sigma_delta/sdm_dac/main/sdm_dac_example_main.c @@ -0,0 +1,116 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#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)); + } +} diff --git a/examples/peripherals/sigma_delta/sdm_dac/output_wave.jpeg b/examples/peripherals/sigma_delta/sdm_dac/output_wave.jpeg new file mode 100644 index 0000000000..e8aabe5630 Binary files /dev/null and b/examples/peripherals/sigma_delta/sdm_dac/output_wave.jpeg differ diff --git a/examples/peripherals/sigma_delta/sdm_dac/pytest_sdm_dac_example.py b/examples/peripherals/sigma_delta/sdm_dac/pytest_sdm_dac_example.py new file mode 100644 index 0000000000..6d9335cc93 --- /dev/null +++ b/examples/peripherals/sigma_delta/sdm_dac/pytest_sdm_dac_example.py @@ -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') diff --git a/examples/peripherals/sigma_delta/sdm_led/CMakeLists.txt b/examples/peripherals/sigma_delta/sdm_led/CMakeLists.txt new file mode 100644 index 0000000000..c0fe198f89 --- /dev/null +++ b/examples/peripherals/sigma_delta/sdm_led/CMakeLists.txt @@ -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) diff --git a/examples/peripherals/sigma_delta/README.md b/examples/peripherals/sigma_delta/sdm_led/README.md similarity index 96% rename from examples/peripherals/sigma_delta/README.md rename to examples/peripherals/sigma_delta/sdm_led/README.md index 17da05e942..930d01e062 100644 --- a/examples/peripherals/sigma_delta/README.md +++ b/examples/peripherals/sigma_delta/sdm_led/README.md @@ -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 diff --git a/examples/peripherals/sigma_delta/sdm_led/main/CMakeLists.txt b/examples/peripherals/sigma_delta/sdm_led/main/CMakeLists.txt new file mode 100644 index 0000000000..ecad9cff66 --- /dev/null +++ b/examples/peripherals/sigma_delta/sdm_led/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "sdm_led_example_main.c" + INCLUDE_DIRS ".") diff --git a/examples/peripherals/sigma_delta/main/sdm_example_main.c b/examples/peripherals/sigma_delta/sdm_led/main/sdm_led_example_main.c similarity index 93% rename from examples/peripherals/sigma_delta/main/sdm_example_main.c rename to examples/peripherals/sigma_delta/sdm_led/main/sdm_led_example_main.c index 60a6cd5794..0b555b9f38 100644 --- a/examples/peripherals/sigma_delta/main/sdm_example_main.c +++ b/examples/peripherals/sigma_delta/sdm_led/main/sdm_led_example_main.c @@ -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) { diff --git a/examples/peripherals/sigma_delta/pytest_sdm_example.py b/examples/peripherals/sigma_delta/sdm_led/pytest_sdm_led_example.py similarity index 55% rename from examples/peripherals/sigma_delta/pytest_sdm_example.py rename to examples/peripherals/sigma_delta/sdm_led/pytest_sdm_led_example.py index 4a233013bc..3d0e30df37 100644 --- a/examples/peripherals/sigma_delta/pytest_sdm_example.py +++ b/examples/peripherals/sigma_delta/sdm_led/pytest_sdm_led_example.py @@ -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')