diff --git a/components/driver/deprecated/sigma_delta_legacy.c b/components/driver/deprecated/sigma_delta_legacy.c index d490d6f186..96c8f45d79 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 ad58251bb1..52f270c191 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 8492030bfe..54670adf28 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 fb98b6ca24..361e02574b 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 776f2416fe..06b46c5d0e 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 9c749ed09d..70db7bdc2e 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 d8110c639b..a705f4208b 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 fbf387e258..7bdad37119 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 d8110c639b..a705f4208b 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 38d334714c..b3f5d9ab22 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 86bcac7f04..de27b3fd9b 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 d712dd5eef..2b7e9f07e0 100644 --- a/docs/en/api-reference/peripherals/sdm.rst +++ b/docs/en/api-reference/peripherals/sdm.rst @@ -6,13 +6,13 @@ 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)). +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, 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; +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: @@ -73,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 ^^^^^^^^^^^^^^^^ @@ -90,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 ^^^^^^^^^^^^^ @@ -98,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. @@ -108,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) ----------------------------------- @@ -138,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 73fe9a8d27..75fc3cb8ed 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 7d9bc5847b..ed3569de68 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/sdm_dac/README.md b/examples/peripherals/sigma_delta/sdm_dac/README.md index 42f9cc8e36..424ad644d2 100644 --- a/examples/peripherals/sigma_delta/sdm_dac/README.md +++ b/examples/peripherals/sigma_delta/sdm_dac/README.md @@ -13,13 +13,13 @@ This example uses the sigma-delta driver to generate modulated output on a GPIO. * 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: +* An active or passive low-pass filter. Connect them as below: ``` ┌──────────────────────────────┐ │ ESP │ ┌───────────────────┐ │ │ │ Active or Passive │ Sine Wave -│ EXAMPLE_SIGMA_DELTA_GPIO_NUM ├────►│ Filter ├────────────── +│ EXAMPLE_SIGMA_DELTA_GPIO_NUM ├────►│ Low-pass Filter ├────────────── └──────────────────────────────┘ └───────────────────┘ ``` @@ -52,10 +52,18 @@ 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. +After the output stated, you can monitor the output signal by an oscilloscope. -![output_wave](output_wave.jpeg) +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 0000000000..9fd43f993a 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/sdm_dac_example_main.c b/examples/peripherals/sigma_delta/sdm_dac/main/sdm_dac_example_main.c index 18eb687e9c..657819e5f1 100644 --- 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 @@ -14,28 +14,29 @@ #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_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 -#define EXAMPLE_SINE_WAVE_AMPLITUDE (127.0f) // 1 ~ 127 +#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 -_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"); +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 example_timer_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx) +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_duty(sdm_chan, sine_wave[cnt]); + sdm_channel_set_pulse_density(sdm_chan, sine_wave[cnt++]); /* Loop the sine wave data buffer */ - cnt++; - cnt %= EXAMPLE_SINE_WAVE_POINT_NUM; + if (cnt >= EXAMPLE_SINE_WAVE_POINT_NUM) { + cnt = 0; + } return false; } @@ -47,7 +48,6 @@ static gptimer_handle_t example_init_gptimer(void* args) .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); @@ -108,9 +108,4 @@ void app_main(void) /* 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 deleted file mode 100644 index e8aabe5630..0000000000 Binary files a/examples/peripherals/sigma_delta/sdm_dac/output_wave.jpeg and /dev/null differ 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 0000000000..1ac85e522b 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/main/sdm_led_example_main.c b/examples/peripherals/sigma_delta/sdm_led/main/sdm_led_example_main.c index 0b555b9f38..075a783c61 100644 --- a/examples/peripherals/sigma_delta/sdm_led/main/sdm_led_example_main.c +++ b/examples/peripherals/sigma_delta/sdm_led/main/sdm_led_example_main.c @@ -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));