本文提出一种彻底由“软件定义”的Delta-sigma调制器,用于取代硬件生成PDM(Pulse Density Modulation信号,从而下降成本。缓存
该方案在低成本音频应用中具备必定价值:经过调研咱们发现:在一些方案中,音频DAC的成本居然与主控芯片成本至关,而加入PA电路后成本则更高。这些应用并不要求极高的音频性能,在少许硬件辅助的状况下,软件定义的数模转换器彻底能够胜任过去由纯硬件完成的工做。函数
本文针对该问题给出的方案以下:一、利用软件实现delta-sigma调制器;二、利用普通的I2S接口输出PDM信号;三、经过简单的RC低通滤波器还原出模拟音频信号。这种方法在保证原始音质和较低的CPU运算负载的前提下,取消了DAC芯片并简化了后级PA电路,进一步优化了成本。性能
该方案在espressif公司的ESP8266平台上测试经过。测试
1、一阶Delta-sigma(ΔΣ)调制器的黑盒模型优化
Delta-sigma(ΔΣ)调制器是Delta-sigma模数转换器的核心部件,以下所示为其最简的一阶形式的具体实现。ui
为了便于说明问题,咱们先暂时将其视做黑盒。因而,从A/D的角度看,它将输入的模拟量调制为1bit脉冲流,从而实现模拟到数字的转换;而从D/A的角度看,调制输出的脉冲通过低通滤波后便可还原回模拟信号,这是本方案的基础所在。spa
1、一阶Delta-sigma(ΔΣ)调制器的实现原理设计
为了更加清楚地描述整个调制器的工做过程,咱们在具体实现的基础上,从Z域模型的角度去研究:code
首先,由于转换精度是有限的,这就不可避免地引入量化噪声e(Z):blog
e(Z) = y(Z) - x(Z)
若是能将量化噪声减小到必定范围,则y(Z)能表明x(Z),从而实现调制。
Delta-sigma使用了过采样(即以远高于Nyquist频率的采样率R*Fs/2进行采样),过采样后本来均匀分布在0 ~ Fs/2频带内的量化噪声被分散到了0 ~ R*Fs/2的频带上,所以减少了基带内的量化噪声。
但有了过采样还远远不够,还有必要进行噪声整形。因为积分器的存在,调制器的传输函数为:
y(Z) = x(Z) + (1-Z-1)e(Z)
输出对量化噪声e(Z)呈高通形式,这就实现了量化噪声的整形。
经过上述方法,量化噪声基本趋向了高频段并远离基带,经过一个低通滤波器便可基本滤除这些多余的噪声。
从另外一种角度也能够解释调制器的工做原理。对整个调制过程作整体分析,咱们看到积分器的输入为:
x(Z) - X'(Z)
即系统对量化偏差进行了积分,其结果是:调制输出积分后的波形不断向着输入波形逼近,这也许就是Delta-sigma的原理所在。
为了更加形象地了解delta-sigma调制器的工做状态,咱们回到上面所提到的具体实现。输入初相位0的正弦信号,在一个周期内各个节点的时域波形以下:
观察其中1-Bit DAC的波形,在模拟输入为Vref+的时间附近, 调制输出大部分为1(图中未画出,参考“1-Bit DAC”波形);而在模拟输入为Vref-的时间附近,调制输出大部分为0。同时还能够看到,在模拟输入为0的时间附近,调制输出不断在0,1间振荡,这正是PDM(脉冲密度调制)这个名字的由来。
2、提升SNR
SNR(信噪比)是评估转换器性能的重要指标。
对于L阶的delta-sigma调制器,其理想信噪比有以下定量计算公式:
SNR(dB) = 6.02N + 10lg(2L + 1) + 10(2L + 1)lg R - 10L
其中N为量化位数,R为过采样比。
一阶的Delta-sigma调制器SNR尚达不到音频应用的水平,所以须要经过改进设计提升SNR。
从上式能够看出,若要提升信噪比,能够增长调制器的阶数L、量化位数N、或者过采样比R。
因为咱们使用的I2S接口支持的时钟频率有限,经过提升时钟频率来减小量化噪声不是一个颇有效的办法,所以考虑采用更高的量化位数来下降对采样率的要求,但受制于运算性能,难以作到很高的量化精度。
综上可知,增长调制器阶数是最有效的方法。固然阶数必须合理设置,由于提升阶数后,系统的稳定性也下降了,同时也增长了CPU的运算负载。
3、二阶Delta-sigma调制器
基于上面的考虑,咱们采用二阶调制器。一个典型的二阶调制器以下图所示:
固然,也能够采用高于2阶的delta-sigma调制器,但难以像上面这样经过简单地增长积分器实现它们。系统的复杂度增长了。
4、软件实现二阶Delta-sigma调制器
如今咱们要以“软件定义”的方式去实现上文所描述的二阶调制器。
注意到锁存器(latch)在电路中的做用:连续的信号流在时钟的做用下离散化了,所以硬件的每一个时钟周期对应到软件中即执行一趟处理函数,更新状态并将本次结果缓存起来。换句话说,硬件的每一个时钟周期都对应于软件执行一遍处理函数。由于软件不是按调制频率运行的(比调制频率快得多),是非实时的,须要引入缓存来确保硬件能以正确的时钟频率还原调制结果——I2S按调制时钟的节拍依次输出缓存内容,从而还原结果。
这里采用C语言完成DSP。
1 int32_t w; /* Data Flow path */ 2 static int i1v = 0, i2v = 0; /* Integrator 1 and 2 */ 3 static int latch_reg = 0; /* Latch */
对每一个调制周期的处理过程以下。对输入电平的采样帧w迭代n次可得出任意采样率的调制输出。
1 #define HW (32767) 2 3 [Input: w] 4 5 if (latch_reg > 0) w -= HW; else w += HW; /* Difference 1 */ 6 w += i1v; i1v = w; /* Integrator 1 */ 7 if (latch_reg > 0) w -= HW; else w += HW; /* Difference 2 */ 8 w += i2v; i2v = w; /* Integrator 2 */ 9 latch_reg = w; /* Latch */ 10 b = (w > 0); /* Comparator */ 11 12 [Output to bitstream: b]
如今假设咱们对每一采样迭代32次,则调制器的采样频率可经过以下方法计算:
Fs = Fs(Input) * 32,其中Fs(Input)为输入数字信号的原始采样率。
例如:对于PCM音频数据,若原始采样率为48kHz,则该delta-sigma调制器的时钟频率可达:
Fs = 48kHz * 32 = 1.536MHz
则要求I2S总线的BCLK时钟频率至少达到:
Fmin = Fs = 1.536MHz。
5、ESP8266的I2S输出端口
因为I2S接口具备串行数据连续传输而不断流的特性,所以I2S接口可用于输出delta-sigma调制器的PDM脉冲流。除I2S接口外,其它具备连续串行输出特性的接口也能够承担输出比特流的工做,本文仅以I2S接口为例说明。
由于I2S最初的设计目的并非输出PDM脉冲流,具体实现须要一些变通:
首先将I2S的传输格式配置为16bit x 2chs(双声道),则每一个传送周期可发送32个比特位。
Delta-sigma调制输出的PDM脉冲流是连续的,须要按每32个比特一组的分法切分为若干字节,而后“假装”成4字节的PCM音频采样数据,经过DMA传输到I2S模块,而I2S模块再将假装的PCM采样串行化输出,最终咱们即可以从芯片的SDAT端口获得完整的脉冲流信号。
写入I2S模块的数据会预先压入FIFO缓冲器(硬件为咱们隐藏了细节),在I2S时钟的驱动下,I2S会以慢于写的速率从FIFO取出数据,从而能够保证输出脉冲流的连续性。
以上即是对于I2S端口输出PDM脉冲流的分析。
接下来讲明完整的代码实现。函数samp_to_delta_sigma(s)实现了将每一个电平采样s调制为32bit脉冲流:
1 int32_t 2 samp_to_delta_sigma(short s) 3 { 4 int w; 5 static int i1v = 0, i2v = 0; 6 static int latch_reg = 0; 7 8 int i; 9 int32_t val=0; 10 11 for (i=0; i<32; i++) 12 { 13 val<<=1; 14 w=s; 15 if (latch_reg > 0) w -= HW; else w += HW; /* Difference 1 */ 16 w += i1v; i1v = w; /* Integrator 1 */ 17 if (latch_reg > 0) w -= HW; else w += HW; /* Difference 2 */ 18 w += i2v; i2v = w; /* Integrator 2 */ 19 latch_reg = w; /* Latch */ 20 if (w > 0) val|=1; /* comparator */ 21 } 22 return val; 23 }
有了脉冲流,接下来考虑如何将其输出到硬件上。本文采用espressif提供的I2S驱动库,它提供了三个函数:I2sInit()用于初始化I2S接口,I2sSetRate()用于设置采样率,i2sPushSample用于向缓冲区中压入数据。
以下为一个调用调制器的实例,核心代码只有4行,该实例调制采样率为48kHz的PCM音频信号并输出PDM.
其中:size = 音频采样帧数,s是一块连续内存,存储全部音频采样(short类型)。
1 I2sInit(); 2 I2sSetRate(48000, 0); 3 4 for(i=0; i < size; i++) 5 i2sPushSample( samp_to_delta_sigma(s[i]) );
对于每一个输入信号采样s:经过调用i2sPushSample( samp_to_delta_sigma(s) )便可实现调制输出。
6、CPU负载评估
仍以原始采样率为48kHz的PCM音频数据为例,从第五节已经得出,FIFO输出侧(I2S端口)最小带宽为 W1 = 1.536 * 10^(6) / 1024^2 ≈ 1.5 (MBits/s)。对于FIFO输入侧(软件调制器),为便于计算,先设CPU执行一次samp_to_delta_sigma()函数的平均用时为T0 (s),则软件调制的输出带宽为 W2 = 32 / T0 / 1024^2 (MBits/s)。
若系统能稳定工做,则要求I2S端口实际带宽Wf与W一、W2知足以下关系(正常状况下W1 = Wf,以确保正确还原比特流信号的时钟频率):
W1 <= Wf < W2
一旦条件知足,CPU将获得空闲时间:
Tfree = 1 / [(W2 - Wf) * 1024^2] (s)
这是由于系统运行中,除起始状态外FIFO将永远保持非空状态。Tfree能够表明用于运行其它任务的空闲时间长度(包括任务调度切换的时间)。若其它操做占用超过Tfree的运行时间,则可能致使FIFO输入侧带宽不足,从而形成FIFO下溢出,使得输出中断。
T0的决定因素很是复杂,须要取平均值T0,将T0代入上式便可得出近似的Tfree时间。
7、后级电路
一、可经过简单的一阶RC低通滤波器,直接从I2S接口得出模拟信号,从而省去成本较高的DAC芯片。
二、在要求功率较小的状况下(例如驱动小型扬声器或者耳机),彻底能够对输出脉冲流进行简单缓冲后,接入扬声器发声。
三、加入简单的功率输出和滤波电路,便可实现Class-D数字功放,以下图所示。
最后,将该方案扩展到非音频的应用是可行的,但只能在对信号的带宽,精度等的要求都不严格的场合使用。