Sigma-Delta 调制器 (SDM)
简介
ESP32-C5 具备二阶 Sigma-Delta 调制器,可以为多个通道生成独立的脉冲密度调制 (PDM) 脉冲。请参阅技术参考手册,查看可用的硬件通道数量。[1]
Sigma-Delta 调制器可以将模拟电压信号转换为脉冲频率或脉冲密度,该过程称为脉冲密度调制 (PDM)(请参阅 维基百科上有关 Sigma-Delta 调制的介绍)。
Sigma-Delta 调制通道通常应用于以下场景:
LED 调光
使用有源 RC 低通滤波器,实现简单的数模转换(8 位分辨率)
结合半桥或全桥回路,以及 LC 低通滤波器,实现 D 级功率放大
功能概述
下文将分节概述安装和操作 SDM 通道的一般步骤:
资源分配 - 介绍如何初始化和配置 SDM 通道,以及在通道完成任务后如何回收相关资源。
启用和禁用通道 - 介绍如何启用和禁用 SDM 通道。
设置脉冲密度 - 介绍如何设置 PDM 脉冲的等效占空比。
电源管理 - 介绍不同时钟源对功耗的影响。
IRAM 安全 - 介绍禁用 cache 时仍可使用的功能。
线程安全 - 介绍由驱动程序认证为线程安全的 API。
Kconfig 选项 - 介绍 SDM 驱动程序支持的各种 Kconfig 选项,这些选项可以给驱动程序的行为造成不同影响。
资源分配
在 ESP-IDF 中,SDM 通道的信息和属性通过特定的数据结构进行管理和访问,该数据结构表示为 sdm_channel_handle_t
。每个通道都可以输出由硬件生成的二进制信号,且这些信号都经过 Sigma-Delta 调制。所有可用通道均存放在资源池中,由驱动程序管理,无需手动将固定通道分配给 GPIO。
要安装 SDM 通道,请调用 sdm_new_channel()
获取通道句柄。通道的具体配置信息由结构体 sdm_config_t
传递。
sdm_config_t::gpio_num
设置 PDM 脉冲输出的 GPIO 管脚号。sdm_config_t::clk_src
选择 SDM 模块的时钟源。注意,所有通道选择的时钟源应保持一致。sdm_config_t::sample_rate_hz
设置 SDM 模块的采样率。提高采样率可以提高输出信号的信噪比,更容易在后级通过滤波获取高精度的原始信号。sdm_config_t::invert_out
设置是否反转输出信号。
函数 sdm_new_channel()
可能因为各种原因失败,如内存不足、参数无效等。当缺少空闲通道(即所有的硬件 SDM 通道均在使用中)时,将返回 ESP_ERR_NOT_FOUND
。
SDM 通道完成任务后,请调用 sdm_del_channel()
回收相应资源,以便底层硬件通道用于其他目的。在删除 SDM 通道句柄前,请通过 sdm_channel_disable()
禁用要删除的通道,或确保该通道尚未由 sdm_channel_enable()
启用,再继续删除操作。
创建采样率为 1 MHz 的 SDM 通道
sdm_channel_handle_t chan = NULL;
sdm_config_t config = {
.clk_src = SDM_CLK_SRC_DEFAULT,
.sample_rate_hz = 1 * 1000 * 1000,
.gpio_num = 0,
};
ESP_ERROR_CHECK(sdm_new_channel(&config, &chan));
启用和禁用通道
在对 SDM 通道进行进一步的 IO 控制之前,需要先调用 sdm_channel_enable()
启用通道。在内部,该函数实现了以下操作:
将通道状态从 init 切换到 enable
如果选择了特定时钟源(如 APB 锁),则会获取合适的电源管理锁。要了解更多有关信息,请参阅 电源管理。
调用 sdm_channel_disable()
则执行相反操作,即将通道恢复到 init 状态,并释放电源管理锁。
设置脉冲密度
在 PDM 中,脉冲密度决定了低通滤波器转换后的输出模拟电压,该模拟电压可以通过公式 Vout = VDD_IO / 256 * duty + VDD_IO / 2
计算。使用函数 sdm_channel_set_pulse_density()
时,需要传入一个名为 density
的参数。这个参数是一个整数值,范围在 -128 到 127 之间,表示一个 8 位有符号整数。根据 density
参数的不同取值,输出信号的占空比也会相应改变。例如,如果将 density
参数设置为零,输出信号的占空比约为 50%。
电源管理
启用电源管理(即启用 CONFIG_PM_ENABLE)时,在进入 Light-sleep 模式前,系统会调整 APB 频率,这可能会改变 Sigma-Delta 调制器的采样率。
但是,通过获取类型为 ESP_PM_APB_FREQ_MAX
的电源管理锁,驱动程序可以防止系统改变 APB 频率。每当驱动程序创建 SDM 通道,且该通道选择 SDM_CLK_SRC_APB
作为其时钟源时,在通过 sdm_channel_enable()
启用通道的过程中,驱动程序会确保获取类型为 ESP_PM_APB_FREQ_MAX
的电源管理锁。反之,调用 sdm_channel_disable()
禁用通道时,驱动程序释放该锁。
IRAM 安全
Kconfig 选项 CONFIG_SDM_CTRL_FUNC_IN_IRAM 支持将常用的 IO 控制函数存放在 IRAM 中,以保证在禁用 cache 时可以正常使用函数。IO 控制函数如下所示:
线程安全
驱动使用了临界区保证了对寄存器的原子操作。句柄内部的关键成员也受临界区保护。驱动内部的状态机使用了原子指令保证了线程安全,通过状态检查还能进一步防止一些不合法的并发操作(例如 enable 和 delete 冲突)。因此, SDM 驱动的 API 可以在多线程环境下使用,无需自行加锁。
同时,以下这些函数还允许在中断上下文中使用:
Kconfig 选项
CONFIG_SDM_CTRL_FUNC_IN_IRAM 控制 SDM 通道控制函数的存放位置(IRAM 或 flash)。更多信息请参阅 IRAM 安全。
CONFIG_SDM_ENABLE_DEBUG_LOG 用于启用调试日志输出。启用此选项将增加固件的二进制文件大小。
转换为模拟信号(可选)
一般而言,Sigma-Delta 信号连接到 LED 用来调节明暗时,无需在信号和 LED 之间添加滤波器,因为人眼本身对光强变化有低通滤波作用。但是,如果你想测量实际电压,或观察模拟波形,就需要设计一个模拟低通滤波器。此外,建议使用有源滤波器,相较于无源滤波器,有源滤波器在处理信号时具有更强的抗干扰性,且损失的电压较少。
请参阅如下示例 Sallen-Key 拓扑低通滤波器,了解滤波器的相关知识。

Sallen-Key 拓扑低通滤波器
(滤波前后的波形请参阅文档 peripherals/sigma_delta/sdm_dac/README.md)
应用示例
peripherals/sigma_delta/sdm_dac 演示了如何使用 sigma-delta 驱动器作为 8 位 DAC(数字模拟转换器),并输出 100 Hz 的正弦波。
peripherals/sigma_delta/sdm_led 演示了如何使用 sigma-delta 驱动器来控制 LED 或 LCD 背光的亮度。
API 参考
Header File
This header file can be included with:
#include "driver/sdm.h"
This header file is a part of the API provided by the
esp_driver_sdm
component. To declare that your component depends onesp_driver_sdm
, add the following to your CMakeLists.txt:REQUIRES esp_driver_sdm
or
PRIV_REQUIRES esp_driver_sdm
Functions
-
esp_err_t sdm_new_channel(const sdm_config_t *config, sdm_channel_handle_t *ret_chan)
Create a new Sigma Delta channel.
- 参数:
config -- [in] SDM configuration
ret_chan -- [out] Returned SDM channel handle
- 返回:
ESP_OK: Create SDM channel successfully
ESP_ERR_INVALID_ARG: Create SDM channel failed because of invalid argument
ESP_ERR_NO_MEM: Create SDM channel failed because out of memory
ESP_ERR_NOT_FOUND: Create SDM channel failed because all channels are used up and no more free one
ESP_FAIL: Create SDM channel failed because of other error
-
esp_err_t sdm_del_channel(sdm_channel_handle_t chan)
Delete the Sigma Delta channel.
- 参数:
chan -- [in] SDM channel created by
sdm_new_channel
- 返回:
ESP_OK: Delete the SDM channel successfully
ESP_ERR_INVALID_ARG: Delete the SDM channel failed because of invalid argument
ESP_ERR_INVALID_STATE: Delete the SDM channel failed because the channel is not in init state
ESP_FAIL: Delete the SDM channel failed because of other error
-
esp_err_t sdm_channel_enable(sdm_channel_handle_t chan)
Enable the Sigma Delta channel.
备注
This function will transit the channel state from init to enable.
备注
This function will acquire a PM lock, if a specific source clock (e.g. APB) is selected in the
sdm_config_t
, whileCONFIG_PM_ENABLE
is enabled.- 参数:
chan -- [in] SDM channel created by
sdm_new_channel
- 返回:
ESP_OK: Enable SDM channel successfully
ESP_ERR_INVALID_ARG: Enable SDM channel failed because of invalid argument
ESP_ERR_INVALID_STATE: Enable SDM channel failed because the channel is already enabled
ESP_FAIL: Enable SDM channel failed because of other error
-
esp_err_t sdm_channel_disable(sdm_channel_handle_t chan)
Disable the Sigma Delta channel.
备注
This function will do the opposite work to the
sdm_channel_enable()
- 参数:
chan -- [in] SDM channel created by
sdm_new_channel
- 返回:
ESP_OK: Disable SDM channel successfully
ESP_ERR_INVALID_ARG: Disable SDM channel failed because of invalid argument
ESP_ERR_INVALID_STATE: Disable SDM channel failed because the channel is not enabled yet
ESP_FAIL: Disable SDM channel failed because of other error
-
esp_err_t sdm_channel_set_pulse_density(sdm_channel_handle_t chan, int8_t density)
Set the pulse density of the PDM output signal.
备注
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
备注
This function is allowed to run within ISR context
备注
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- 参数:
chan -- [in] SDM channel created by
sdm_new_channel
density -- [in] Quantized pulse density of the PDM output signal, ranges from -128 to 127. But the range of [-90, 90] can provide a better randomness.
- 返回:
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_duty(sdm_channel_handle_t chan, int8_t duty)
The alias function of
sdm_channel_set_pulse_density
, it decides the pulse density of the output signal.备注
sdm_channel_set_pulse_density
has a more appropriate name compare this alias function, suggest to turn tosdm_channel_set_pulse_density
instead- 参数:
chan -- [in] SDM channel created by
sdm_new_channel
duty -- [in] Actually it's the quantized pulse density of the PDM output signal
- 返回:
ESP_OK: Set duty cycle successfully
ESP_ERR_INVALID_ARG: Set duty cycle failed because of invalid argument
ESP_FAIL: Set duty cycle failed because of other error
Structures
-
struct sdm_config_t
Sigma Delta channel configuration.
Public Members
-
gpio_num_t gpio_num
GPIO number
-
sdm_clock_source_t clk_src
Clock source
-
uint32_t sample_rate_hz
Over sample rate in Hz, it determines the frequency of the carrier pulses
-
uint32_t invert_out
Whether to invert the output signal
-
uint32_t allow_pd
If set, driver allows the power domain to be powered off when system enters sleep mode. This can save power, but at the expense of more RAM being consumed to save register context.
-
struct sdm_config_t flags
Extra flags
-
gpio_num_t gpio_num
Type Definitions
-
typedef struct sdm_channel_t *sdm_channel_handle_t
Type of Sigma Delta channel handle.
Header File
This header file can be included with:
#include "hal/sdm_types.h"
Type Definitions
-
typedef soc_periph_sdm_clk_src_t sdm_clock_source_t