ESP8266 软件实现 Delta-sigma(ΔΣ)调制器 并经过I2S接口输出PDM信号

  本文提出一种彻底由“软件定义”的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数字功放,以下图所示。

 

  最后,将该方案扩展到非音频的应用是可行的,但只能在对信号的带宽,精度等的要求都不严格的场合使用。