diff --git a/components/driver/deprecated/sigma_delta_legacy.c b/components/driver/deprecated/sigma_delta_legacy.c index d490d6f18616..96c8f45d7931 100644 --- a/components/driver/deprecated/sigma_delta_legacy.c +++ b/components/driver/deprecated/sigma_delta_legacy.c @@ -35,7 +35,7 @@ static inline esp_err_t _sigmadelta_set_duty(sigmadelta_port_t sigmadelta_port, { SIGMADELTA_OBJ_CHECK(sigmadelta_port); - sdm_ll_set_duty(p_sigmadelta_obj[sigmadelta_port]->hal.dev, channel, duty); + sdm_ll_set_pulse_density(p_sigmadelta_obj[sigmadelta_port]->hal.dev, channel, duty); return ESP_OK; } diff --git a/components/driver/include/driver/sdm.h b/components/driver/include/driver/sdm.h index ad58251bb181..52f270c1916e 100644 --- a/components/driver/include/driver/sdm.h +++ b/components/driver/include/driver/sdm.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -25,7 +25,7 @@ typedef struct sdm_channel_t *sdm_channel_handle_t; typedef struct { int gpio_num; /*!< GPIO number */ sdm_clock_source_t clk_src; /*!< Clock source */ - uint32_t sample_rate_hz; /*!< Sample rate in Hz, it determines how frequent the modulator outputs a pulse */ + uint32_t sample_rate_hz; /*!< Over sample rate in Hz, it determines the frequency of the carrier pulses */ struct { uint32_t invert_out: 1; /*!< Whether to invert the output signal */ uint32_t io_loop_back: 1; /*!< For debug/test, the signal output from the GPIO will be fed to the input path as well */ @@ -88,17 +88,33 @@ esp_err_t sdm_channel_enable(sdm_channel_handle_t chan); esp_err_t sdm_channel_disable(sdm_channel_handle_t chan); /** - * @brief Set the duty cycle of the PDM output signal. + * @brief Set the pulse density of the PDM output signal. * - * @note For PDM signals, duty cycle refers to the percentage of high level cycles to the whole statistical period. - * The average output voltage could be Vout = VDD_IO / 256 * duty + VDD_IO / 2 - * @note If the duty is set to zero, the output signal is like a 50% duty cycle square wave, with a frequency around (sample_rate_hz / 4). - * @note The duty is proportional to the equivalent output voltage after a low-pass-filter. + * @note The raw output signal requires a low-pass filter to restore it into analog voltage, +* the restored analog output voltage could be Vout = VDD_IO / 256 * density + VDD_IO / 2 * @note This function is allowed to run within ISR context - * @note This function will be placed into IRAM if `CONFIG_SDM_CTRL_FUNC_IN_IRAM` is on, so that it's allowed to be executed when Cache is disabled + * @note This function will be placed into IRAM if `CONFIG_SDM_CTRL_FUNC_IN_IRAM` is on, + * so that it's allowed to be executed when Cache is disabled * * @param[in] chan SDM channel created by `sdm_new_channel` - * @param[in] duty Equivalent duty cycle of the PDM output signal, ranges from -128 to 127. But the range of [-90, 90] can provide a better randomness. + * @param[in] density Quantized pulse density of the PDM output signal, ranges from -128 to 127. + * But the range of [-90, 90] can provide a better randomness. + * @return + * - ESP_OK: Set pulse density successfully + * - ESP_ERR_INVALID_ARG: Set pulse density failed because of invalid argument + * - ESP_FAIL: Set pulse density failed because of other error + */ +esp_err_t sdm_channel_set_pulse_density(sdm_channel_handle_t chan, int8_t density); + +/** + * @brief The alias function of `sdm_channel_set_pulse_density`, it decides the pulse density of the output signal + * + * @note `sdm_channel_set_pulse_density` has a more appropriate name compare this + * alias function, suggest to turn to `sdm_channel_set_pulse_density` instead + * + * @param[in] chan SDM channel created by `sdm_new_channel` + * @param[in] duty Actually it's the quantized pulse density of the PDM output signal + * * @return * - ESP_OK: Set duty cycle successfully * - ESP_ERR_INVALID_ARG: Set duty cycle failed because of invalid argument diff --git a/components/driver/linker.lf b/components/driver/linker.lf index 8492030bfec4..54670adf28ba 100644 --- a/components/driver/linker.lf +++ b/components/driver/linker.lf @@ -17,7 +17,7 @@ entries: gpio: gpio_set_level (noflash) gpio: gpio_intr_disable (noflash) if SDM_CTRL_FUNC_IN_IRAM = y: - sdm: sdm_channel_set_duty (noflash) + sdm: sdm_channel_set_pulse_density (noflash) if DAC_CTRL_FUNC_IN_IRAM = y: dac_oneshot: dac_oneshot_output_voltage (noflash) dac_continuous: dac_continuous_write_asynchronously (noflash) diff --git a/components/driver/sdm.c b/components/driver/sdm.c index fb98b6ca24c0..361e02574bb8 100644 --- a/components/driver/sdm.c +++ b/components/driver/sdm.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: Apache-2.0 */ @@ -262,7 +262,7 @@ esp_err_t sdm_new_channel(const sdm_config_t *config, sdm_channel_handle_t *ret_ sdm_ll_set_prescale(group->hal.dev, chan_id, prescale); chan->sample_rate_hz = src_clk_hz / prescale; // preset the duty cycle to zero - sdm_ll_set_duty(group->hal.dev, chan_id, 0); + sdm_ll_set_pulse_density(group->hal.dev, chan_id, 0); // initialize other members of timer chan->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; @@ -317,7 +317,7 @@ esp_err_t sdm_channel_disable(sdm_channel_handle_t chan) return ESP_OK; } -esp_err_t sdm_channel_set_duty(sdm_channel_handle_t chan, int8_t duty) +esp_err_t sdm_channel_set_pulse_density(sdm_channel_handle_t chan, int8_t density) { ESP_RETURN_ON_FALSE_ISR(chan, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); @@ -325,8 +325,11 @@ esp_err_t sdm_channel_set_duty(sdm_channel_handle_t chan, int8_t duty) int chan_id = chan->chan_id; portENTER_CRITICAL_SAFE(&chan->spinlock); - sdm_ll_set_duty(group->hal.dev, chan_id, duty); + sdm_ll_set_pulse_density(group->hal.dev, chan_id, density); portEXIT_CRITICAL_SAFE(&chan->spinlock); return ESP_OK; } + +esp_err_t sdm_channel_set_duty(sdm_channel_handle_t chan, int8_t duty) +__attribute__((alias("sdm_channel_set_pulse_density"))); diff --git a/components/driver/test_apps/gpio_extensions/main/test_sdm.c b/components/driver/test_apps/gpio_extensions/main/test_sdm.c index 776f2416fe8a..06b46c5d0e14 100644 --- a/components/driver/test_apps/gpio_extensions/main/test_sdm.c +++ b/components/driver/test_apps/gpio_extensions/main/test_sdm.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: Apache-2.0 */ @@ -37,7 +37,7 @@ TEST_CASE("sdm_channel_install_uninstall", "[sdm]") } } -TEST_CASE("sdm_channel_set_duty", "[sdm]") +TEST_CASE("sdm_channel_set_pulse_density", "[sdm]") { const int sdm_chan_gpios[2] = {0, 2}; sdm_config_t config = { @@ -49,7 +49,7 @@ TEST_CASE("sdm_channel_set_duty", "[sdm]") config.gpio_num = sdm_chan_gpios[i]; TEST_ESP_OK(sdm_new_channel(&config, &chans[i])); // should see a ~250KHz (sample_rate/4) square wave - TEST_ESP_OK(sdm_channel_set_duty(chans[i], 0)); + TEST_ESP_OK(sdm_channel_set_pulse_density(chans[i], 0)); TEST_ESP_OK(sdm_channel_enable(chans[i])); } vTaskDelay(pdMS_TO_TICKS(500)); @@ -57,8 +57,8 @@ TEST_CASE("sdm_channel_set_duty", "[sdm]") // can't delete the channel if the channel is in the enable state TEST_ESP_ERR(ESP_ERR_INVALID_STATE, sdm_del_channel(chans[0])); - TEST_ESP_OK(sdm_channel_set_duty(chans[0], 127)); - TEST_ESP_OK(sdm_channel_set_duty(chans[1], -128)); + TEST_ESP_OK(sdm_channel_set_pulse_density(chans[0], 127)); + TEST_ESP_OK(sdm_channel_set_pulse_density(chans[1], -128)); vTaskDelay(pdMS_TO_TICKS(500)); for (size_t i = 0; i < 2; i++) { diff --git a/components/hal/esp32/include/hal/sdm_ll.h b/components/hal/esp32/include/hal/sdm_ll.h index 9c749ed09de9..70db7bdc2e1f 100644 --- a/components/hal/esp32/include/hal/sdm_ll.h +++ b/components/hal/esp32/include/hal/sdm_ll.h @@ -31,13 +31,13 @@ static inline void sdm_ll_enable_clock(gpio_sd_dev_t *hw, bool en) * * @param hw Peripheral SIGMADELTA hardware instance address. * @param channel Sigma-delta channel number - * @param duty Sigma-delta duty of one channel, the value ranges from -128 to 127, recommended range is -90 ~ 90. + * @param density Sigma-delta quantized density of one channel, the value ranges from -128 to 127, recommended range is -90 ~ 90. * The waveform is more like a random one in this range. */ __attribute__((always_inline)) -static inline void sdm_ll_set_duty(gpio_sd_dev_t *hw, int channel, int8_t duty) +static inline void sdm_ll_set_pulse_density(gpio_sd_dev_t *hw, int channel, int8_t density) { - HAL_FORCE_MODIFY_U32_REG_FIELD(hw->channel[channel], duty, (uint32_t)duty); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->channel[channel], duty, (uint32_t)density); } /** diff --git a/components/hal/esp32c3/include/hal/sdm_ll.h b/components/hal/esp32c3/include/hal/sdm_ll.h index d8110c639b5d..a705f4208b61 100644 --- a/components/hal/esp32c3/include/hal/sdm_ll.h +++ b/components/hal/esp32c3/include/hal/sdm_ll.h @@ -31,13 +31,13 @@ static inline void sdm_ll_enable_clock(gpio_sd_dev_t *hw, bool en) * * @param hw Peripheral SIGMADELTA hardware instance address. * @param channel Sigma-delta channel number - * @param duty Sigma-delta duty of one channel, the value ranges from -128 to 127, recommended range is -90 ~ 90. + * @param density Sigma-delta quantized density of one channel, the value ranges from -128 to 127, recommended range is -90 ~ 90. * The waveform is more like a random one in this range. */ __attribute__((always_inline)) -static inline void sdm_ll_set_duty(gpio_sd_dev_t *hw, int channel, int8_t duty) +static inline void sdm_ll_set_pulse_density(gpio_sd_dev_t *hw, int channel, int8_t density) { - HAL_FORCE_MODIFY_U32_REG_FIELD(hw->channel[channel], duty, (uint32_t)duty); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->channel[channel], duty, (uint32_t)density); } /** diff --git a/components/hal/esp32c6/include/hal/sdm_ll.h b/components/hal/esp32c6/include/hal/sdm_ll.h index fbf387e2581a..7bdad37119de 100644 --- a/components/hal/esp32c6/include/hal/sdm_ll.h +++ b/components/hal/esp32c6/include/hal/sdm_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -31,13 +31,13 @@ static inline void sdm_ll_enable_clock(gpio_sd_dev_t *hw, bool en) * * @param hw Peripheral SIGMADELTA hardware instance address. * @param channel Sigma-delta channel number - * @param duty Sigma-delta duty of one channel, the value ranges from -128 to 127, recommended range is -90 ~ 90. + * @param density Sigma-delta quantized density of one channel, the value ranges from -128 to 127, recommended range is -90 ~ 90. * The waveform is more like a random one in this range. */ __attribute__((always_inline)) -static inline void sdm_ll_set_duty(gpio_sd_dev_t *hw, int channel, int8_t duty) +static inline void sdm_ll_set_pulse_density(gpio_sd_dev_t *hw, int channel, int8_t density) { - HAL_FORCE_MODIFY_U32_REG_FIELD(hw->channel[channel], duty, (uint32_t)duty); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->channel[channel], duty, (uint32_t)density); } /** diff --git a/components/hal/esp32h4/include/hal/sdm_ll.h b/components/hal/esp32h4/include/hal/sdm_ll.h index d8110c639b5d..a705f4208b61 100644 --- a/components/hal/esp32h4/include/hal/sdm_ll.h +++ b/components/hal/esp32h4/include/hal/sdm_ll.h @@ -31,13 +31,13 @@ static inline void sdm_ll_enable_clock(gpio_sd_dev_t *hw, bool en) * * @param hw Peripheral SIGMADELTA hardware instance address. * @param channel Sigma-delta channel number - * @param duty Sigma-delta duty of one channel, the value ranges from -128 to 127, recommended range is -90 ~ 90. + * @param density Sigma-delta quantized density of one channel, the value ranges from -128 to 127, recommended range is -90 ~ 90. * The waveform is more like a random one in this range. */ __attribute__((always_inline)) -static inline void sdm_ll_set_duty(gpio_sd_dev_t *hw, int channel, int8_t duty) +static inline void sdm_ll_set_pulse_density(gpio_sd_dev_t *hw, int channel, int8_t density) { - HAL_FORCE_MODIFY_U32_REG_FIELD(hw->channel[channel], duty, (uint32_t)duty); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->channel[channel], duty, (uint32_t)density); } /** diff --git a/components/hal/esp32s2/include/hal/sdm_ll.h b/components/hal/esp32s2/include/hal/sdm_ll.h index 38d334714cb2..b3f5d9ab2293 100644 --- a/components/hal/esp32s2/include/hal/sdm_ll.h +++ b/components/hal/esp32s2/include/hal/sdm_ll.h @@ -31,13 +31,13 @@ static inline void sdm_ll_enable_clock(gpio_sd_dev_t *hw, bool en) * * @param hw Peripheral SIGMADELTA hardware instance address. * @param channel Sigma-delta channel number - * @param duty Sigma-delta duty of one channel, the value ranges from -128 to 127, recommended range is -90 ~ 90. + * @param density Sigma-delta quantized density of one channel, the value ranges from -128 to 127, recommended range is -90 ~ 90. * The waveform is more like a random one in this range. */ __attribute__((always_inline)) -static inline void sdm_ll_set_duty(gpio_sd_dev_t *hw, int channel, int8_t duty) +static inline void sdm_ll_set_pulse_density(gpio_sd_dev_t *hw, int channel, int8_t density) { - HAL_FORCE_MODIFY_U32_REG_FIELD(hw->channel[channel], duty, (uint32_t)duty); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->channel[channel], duty, (uint32_t)density); } /** diff --git a/components/hal/esp32s3/include/hal/sdm_ll.h b/components/hal/esp32s3/include/hal/sdm_ll.h index 86bcac7f043c..de27b3fd9b72 100644 --- a/components/hal/esp32s3/include/hal/sdm_ll.h +++ b/components/hal/esp32s3/include/hal/sdm_ll.h @@ -31,13 +31,13 @@ static inline void sdm_ll_enable_clock(gpio_sd_dev_t *hw, bool en) * * @param hw Peripheral SIGMADELTA hardware instance address. * @param channel Sigma-delta channel number - * @param duty Sigma-delta duty of one channel, the value ranges from -128 to 127, recommended range is -90 ~ 90. + * @param density Sigma-delta quantized density of one channel, the value ranges from -128 to 127, recommended range is -90 ~ 90. * The waveform is more like a random one in this range. */ __attribute__((always_inline)) -static inline void sdm_ll_set_duty(gpio_sd_dev_t *hw, int channel, int8_t duty) +static inline void sdm_ll_set_pulse_density(gpio_sd_dev_t *hw, int channel, int8_t density) { - HAL_FORCE_MODIFY_U32_REG_FIELD(hw->channel[channel], duty, (uint32_t)duty); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->channel[channel], duty, (uint32_t)density); } /** diff --git a/docs/en/api-reference/peripherals/sdm.rst b/docs/en/api-reference/peripherals/sdm.rst index 190d1dc89e5d..2b7e9f07e00f 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 |wiki_ref|_). + +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, and it can't change its output density continuously. If you have to, you can update the density in a timer's callback; +3. Base on the former two points, an external active or passive filter is required to restore the analog wave (See :ref:`convert_to_analog_signal`); + Typically, a Sigma-Delta modulated channel can be used in scenarios like: - LED dimming @@ -65,10 +73,10 @@ Before doing further IO control to the SDM channel, you should enable it first, On the contrary, calling :cpp:func:`sdm_channel_disable` will do the opposite, that is, put the channel back to the **init** state and release the power management lock. -Set Equivalent Duty Cycle -^^^^^^^^^^^^^^^^^^^^^^^^^ +Set Pulse Density +^^^^^^^^^^^^^^^^^ -For the output PDM signals, the duty cycle refers to the percentage of high level cycles to the whole statistical period. The average output voltage from the channel is calculated by ``Vout = VDD_IO / 256 * duty + VDD_IO / 2``. Thus the range of the ``duty`` input parameter of :cpp:func:`sdm_channel_set_duty` is from -128 to 127 (eight bit signed integer). For example,if zero value is set, then the output signal's duty will be about 50%. +For the output PDM signals, the pulse density decides the output analog voltage that restored by a low-pass filter. The restored analog voltage from the channel is calculated by ``Vout = VDD_IO / 256 * duty + VDD_IO / 2``. The range of the quantized ``density`` input parameter of :cpp:func:`sdm_channel_set_pulse_density` is from -128 to 127 (eight-bit signed integer). For example, if a zero value is set, then the output signal's duty will be around 50%. Power Management ^^^^^^^^^^^^^^^^ @@ -82,7 +90,7 @@ IRAM Safe There's a Kconfig option :ref:`CONFIG_SDM_CTRL_FUNC_IN_IRAM` that can put commonly used IO control functions into IRAM as well. So that these functions can also be executable when the cache is disabled. These IO control functions are listed as follows: -- :cpp:func:`sdm_channel_set_duty` +- :cpp:func:`sdm_channel_set_pulse_density` Thread Safety ^^^^^^^^^^^^^ @@ -90,7 +98,7 @@ Thread Safety The factory function :cpp:func:`sdm_new_channel` is guaranteed to be thread safe by the driver, which means, user can call it from different RTOS tasks without protection by extra locks. The following functions are allowed to run under ISR context, the driver uses a critical section to prevent them being called concurrently in both task and ISR. -- :cpp:func:`sdm_channel_set_duty` +- :cpp:func:`sdm_channel_set_pulse_density` Other functions that take the :cpp:type:`sdm_channel_handle_t` as the first positional parameter, are not treated as thread safe. Which means the user should avoid calling them from multiple tasks. @@ -100,6 +108,8 @@ Kconfig Options - :ref:`CONFIG_SDM_CTRL_FUNC_IN_IRAM` controls where to place the SDM channel control functions (IRAM or Flash), see `IRAM Safe <#iram-safe>`__ for more information. - :ref:`CONFIG_SDM_ENABLE_DEBUG_LOG` is used to enabled the debug log output. Enable this option will increase the firmware binary size. +.. _convert_to_analog_signal: + Convert to analog signal (Optional) ----------------------------------- @@ -130,3 +140,6 @@ API Reference Different ESP chip series might have different numbers of SDM channels. Please refer to Chapter `GPIO and IOMUX <{IDF_TARGET_TRM_EN_URL}#iomuxgpio>`__ in {IDF_TARGET_NAME} Technical Reference Manual for more details. The driver won't forbid you from applying for more channels, but it will return error when all available hardware resources are used up. Please always check the return value when doing resource allocation (e.g. :cpp:func:`sdm_new_channel`). .. _Sallen-Key topology Low Pass Filter: https://en.wikipedia.org/wiki/Sallen%E2%80%93Key_topology + +.. |wiki_ref| replace:: Delta-sigma modulation on Wikipedia +.. _wiki_ref: https://en.wikipedia.org/wiki/Delta-sigma_modulation diff --git a/docs/en/migration-guides/release-5.x/5.0/peripherals.rst b/docs/en/migration-guides/release-5.x/5.0/peripherals.rst index 73fe9a8d2711..75fc3cb8ed65 100644 --- a/docs/en/migration-guides/release-5.x/5.0/peripherals.rst +++ b/docs/en/migration-guides/release-5.x/5.0/peripherals.rst @@ -106,12 +106,13 @@ GPIO - SDM channel representation has changed from ``sigmadelta_channel_t`` to :cpp:type:`sdm_channel_handle_t`, which is an opaque pointer. - SDM channel configurations are stored in :cpp:type:`sdm_config_t` now, instead the previous ``sigmadelta_config_t``. - In the legacy driver, users don't have to set the clock source for SDM channel. But in the new driver, users need to set a proper one in the :cpp:member:`sdm_config_t::clk_src`. The available clock sources are listed in the :cpp:type:`soc_periph_sdm_clk_src_t`. - - In the legacy driver, users need to set a ``prescale`` for the channel, which reflects the frequency in which the modulator outputs a pulse. In the new driver, users should use :cpp:member:`sdm_config_t::sample_rate_hz`. + - In the legacy driver, users need to set a ``prescale`` for the channel, which reflects the frequency in which the modulator outputs a pulse. In the new driver, users should use :cpp:member:`sdm_config_t::sample_rate_hz` to set the over sample rate. + - In the legacy driver, users set ``duty`` to decide the output analog value, it's now renamed to a more appropriate name ``density``. Breaking Changes in Usage ^^^^^^^^^^^^^^^^^^^^^^^^^ - - Channel configuration was done by channel allocation, in :cpp:func:`sdm_new_channel`. In the new driver, only the ``duty`` can be changed at runtime, by :cpp:func:`sdm_channel_set_duty`. Other parameters like ``gpio number`` and ``prescale`` are only allowed to set during channel allocation. + - Channel configuration was done by channel allocation, in :cpp:func:`sdm_new_channel`. In the new driver, only the ``density`` can be changed at runtime, by :cpp:func:`sdm_channel_set_pulse_density`. Other parameters like ``gpio number`` and ``prescale`` are only allowed to set during channel allocation. - Before further channel operations, users should **enable** the channel in advance, by calling :cpp:func:`sdm_channel_enable`. This function will help to manage some system level services, like **Power Management**. Timer Group Driver diff --git a/docs/zh_CN/migration-guides/release-5.x/5.0/peripherals.rst b/docs/zh_CN/migration-guides/release-5.x/5.0/peripherals.rst index 7d9bc5847b0d..ed3569de6813 100644 --- a/docs/zh_CN/migration-guides/release-5.x/5.0/peripherals.rst +++ b/docs/zh_CN/migration-guides/release-5.x/5.0/peripherals.rst @@ -107,11 +107,12 @@ GPIO - SDM 通道配置原来存放于 ``sigmadelta_config_t``,现存放于 :cpp:type:`sdm_config_t`。 - 旧版驱动中,用户无需为 SDM 通道设置时钟源。但是在新驱动中,用户需要在 :cpp:member:`sdm_config_t::clk_src` 为 SDM 通道设置合适的时钟源,:cpp:type:`soc_periph_sdm_clk_src_t` 中列出了可用的时钟源。 - 旧版驱动中,用户需要为通道设置 ``prescale``,该参数会影响调制器输出脉冲的频率。在新的驱动中,用户需要使用 :cpp:member:`sdm_config_t::sample_rate_hz` 实现该功能。 + - 旧版驱动中,用户通过设置 ``duty`` 来改变输出的模拟量,现在换成了一个更贴切的名字 ``density`` 主要使用方法更新 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - 更新前,通道配置由通道分配在 :cpp:func:`sdm_new_channel` 完成。在新驱动中,只有 ``duty`` 可在运行时由 :cpp:func:`sdm_channel_set_duty` 更新。其他参数如 ``gpio number``、 ``prescale`` 只能在通道分配时进行设置。 + - 更新前,通道配置由通道分配在 :cpp:func:`sdm_new_channel` 完成。在新驱动中,只有 ``density`` 可在运行时由 :cpp:func:`sdm_channel_set_pulse_density` 更新。其他参数如 ``gpio number``、 ``prescale`` 只能在通道分配时进行设置。 - 在进行下一步通道操作前,用户应通过调用 :cpp:func:`sdm_channel_enable` 提前 **使能** 该通道。该函数有助于管理一些系统级服务,如 **电源管理**。 定时器组驱动 diff --git a/examples/peripherals/sigma_delta/main/CMakeLists.txt b/examples/peripherals/sigma_delta/main/CMakeLists.txt deleted file mode 100644 index a429e2c395c3..000000000000 --- 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 0f4904e345ec..633eb379bec6 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 000000000000..424ad644d239 --- /dev/null +++ b/examples/peripherals/sigma_delta/sdm_dac/README.md @@ -0,0 +1,69 @@ +| 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 low-pass filter. Connect them as below: + +``` +┌──────────────────────────────┐ +│ ESP │ ┌───────────────────┐ +│ │ │ Active or Passive │ Sine Wave +│ EXAMPLE_SIGMA_DELTA_GPIO_NUM ├────►│ Low-pass 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 output signal by an oscilloscope. + +If you monitor on the GPIO directly, you can see the raw SDM output, it consists by square waves (i.e. pulse) with different density + +![raw_sdm_output](raw_sdm_output.png) + +If you monitor the signal after a low-pass filter, you can see the pulses are filtered into a sine wave already + +![filtered_sine_wave](filtered_sine_wave.png) + +## 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. + +If a lot of glitches appear in the filtered sine wave, it might be the inappropriate GND that the probe connected, please try to choose the GND that nearby the filter. diff --git a/examples/peripherals/sigma_delta/sdm_dac/filtered_sine_wave.png b/examples/peripherals/sigma_delta/sdm_dac/filtered_sine_wave.png new file mode 100644 index 000000000000..9fd43f993a0b Binary files /dev/null and b/examples/peripherals/sigma_delta/sdm_dac/filtered_sine_wave.png differ 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 000000000000..c597f431d58e --- /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 000000000000..657819e5f127 --- /dev/null +++ b/examples/peripherals/sigma_delta/sdm_dac/main/sdm_dac_example_main.c @@ -0,0 +1,111 @@ +/* + * 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) // 10 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, adjust this value to decide the sine wave frequency +#define EXAMPLE_SINE_WAVE_AMPLITUDE (127.0f) // 1 ~ 127, adjust this value to decide the sine wave amplitude +#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 + +ESP_STATIC_ASSERT(EXAMPLE_SINE_WAVE_POINT_NUM > 1, "Sine wave frequency is too high"); +ESP_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 IRAM_ATTR 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_pulse_density(sdm_chan, sine_wave[cnt++]); + /* Loop the sine wave data buffer */ + if (cnt >= EXAMPLE_SINE_WAVE_POINT_NUM) { + cnt = 0; + } + 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, + }; + 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)); +} 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 000000000000..6d9335cc93c1 --- /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_dac/raw_sdm_output.png b/examples/peripherals/sigma_delta/sdm_dac/raw_sdm_output.png new file mode 100644 index 000000000000..1ac85e522bfc Binary files /dev/null and b/examples/peripherals/sigma_delta/sdm_dac/raw_sdm_output.png differ 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 000000000000..c0fe198f8906 --- /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 17da05e94245..930d01e06217 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 000000000000..ecad9cff66ed --- /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 88% 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 60a6cd5794ec..075a783c61cf 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) { @@ -36,7 +36,7 @@ void app_main(void) int8_t duty = 0; int step = EXAMPLE_LED_DIM_DUTY_STEP; while (1) { - ESP_ERROR_CHECK(sdm_channel_set_duty(sdm_chan, duty)); + ESP_ERROR_CHECK(sdm_channel_set_pulse_density(sdm_chan, duty)); /* By changing delay time, you can change the blink frequency of LED */ vTaskDelay(pdMS_TO_TICKS(EXAMPLE_LED_DIM_DELAY_MS)); 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 4a233013bcd2..3d0e30df37e0 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 @@ @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')