Sigma-Delta 调制器 (SDM)
简介
ESP32-S3 具备二阶 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 控制函数如下所示:
线程安全
驱动程序会确保工厂函数 sdm_new_channel() 的线程安全,使用时,可以直接从不同的 RTOS 任务中调用此类函数,无需额外锁保护。
驱动程序设置了临界区,以防函数同时在任务和 ISR 中调用。因此,以下函数支持在 ISR 上下文运行:
其他以 sdm_channel_handle_t 作为第一个位置参数的函数均非线程安全,因此应避免从多个任务中调用这类函数。
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_sdmcomponent. To declare that your component depends on- esp_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, while- CONFIG_PM_ENABLEis 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_IRAMis 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_densityhas a more appropriate name compare this alias function, suggest to turn to- sdm_channel_set_pulse_densityinstead- 参数:
- 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 - 
int 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 io_loop_back
- For debug/test, the signal output from the GPIO will be fed to the input path as well 
 - 
struct sdm_config_t flags
- Extra flags 
 
- 
int 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