一、关于Delta-sigma(ΔΣ)调制器

  Delta-sigma(ΔΣ)调制器是Delta-sigma转换器的核心部件。如下所示为一个简单的一阶Delta-sigma调制器,该调制器产生一个1bit比特流。将该比特流脉冲输入低通滤波器,从滤波器输出端口可以获得比特流信号的平均电平,该电平代表了调制器的输入电平。

  一阶Delta-sigma调制器的实例时序如下:

  在该实例中,时钟频率(此处等于采样率)为输入信号的64倍。根据采样定理,转换器需要至少2倍于有效信号最高频率的采样率,而Delta-sigma转换器则需要比传统转换器高得多的采样频率来产生足够数量的比特流脉冲,相同条件下提高调制器的时钟频率,则可以获得更高的精度。

二、降低量化噪声

  可以看出,比特流的平均电平(经过低通滤波后的电平)只会在某个精度范围内接近于原始电平,而永远不会完全相同,输出信号永远叠加着量化噪声。

  一种降低量化噪音的方法是:增加时钟频率(过采样)。例如,假设现有一个带宽为20kHz的音频信号,一个典型的PCM采样率为48kHz,而典型的Delta-sigma转换器的时钟频率高达 64 x 48kHz = 3.072 MHz,即相当于64倍的过采样。

  另一种更好的解决方案是:采用高阶的Delta-sigma调制器。一般来说,一阶调制器在高频段产生较多噪声功率,如果输入信号电平接近动态范围,则这种效应最为显著。采样高阶调制器可获得较一阶调制器更低的噪声。

三、二阶Delta-sigma调制器

  典型的二阶delta-sigma调制器如下图所示:

  该类型的调制器产生的比特流更加接近于理想的比例脉冲信号,因此可带来如下优势:增大输入信号带宽、降低采样频率、增加转换精度(抑制量化噪声)。

  理论上高于2阶的delta-sigma调制器是可行的,但无法通过简单地增加差分积分单元实现,其根本原因是由2个以上积分器产生的相位误差将使降低系统的稳定性。

  成熟的商业的音频ADC往往采用5阶甚至以上的delta-sigma调制器,使用64倍的过采样,其架构与本文所述有所不同,但基本原理是相同的。

四、软件实现二阶Delta-sigma调制器

  通过上文对二阶delta-sigma调制器的描述,我们可以容易得出该调制器的数字实现。这里采用C语言实现DSP(digital signal process)。

 int32_t w;                     /* Data Flow path */
 , i2v = ;   /* Integrator 1 and 2 */
 ;      /* Latch */

  对每个比特位的处理过程如下。对输入电平w迭代n次可得出任意采样率的调制输出。

 #define HW (32767)

 [Input: w]

 ) w -= HW; else w += HW; /* Difference 1 */
 w += i1v; i1v = w; /* Integrator 1 */
 ) w -= HW; else w += HW; /* Difference 2 */
 w += i2v; i2v = w; /* Integrator 2 */
 latch_reg = w;   /* Latch */
 b = (w > );     /* Comparator */

 [Output to bitstream: b]

五、ESP8266的I2S输出端口

  由于I2S接口具有串行数据连续传输而不断流的特性,因此在提高时钟频率的情况下,I2S接口可用于输出delta-sigma调制器的比特流。除I2S接口外,其它具有连续串行输出特性的接口同样可以承担输出比特流的工作。

  ESP8266芯片提供了I2S PHY及接口,可配置工作于较高的时钟频率,SER并串转换器(串行移位器)可将并行字节通过端口串行地发送出去。对于比特流输出的实现而言,我们只关心I2S的数据输出端口SDAT,至于LRCLK和BCLK信号则完全抛弃。将I2S传输格式配置为每声道16bit全数据(双声道),则每个I2S传送周期(传输一次所有声道的PCM采样)可发送32个比特位。

  这里先将delta-sigma调制输出的比特流的每32位伪装成4字节的PCM音频采样数据,通过DMA传输到I2S总线,而I2S模块中的ser模块再将伪装的PCM采样数据串行化输出,最终从SDAT端口得到完整的比特流信号。

  调制得出的比特流可预先压入FIFO缓冲器,在I2S时钟的驱动下,以慢于软件调制速率的节拍从FIFO取出数据,这样就保证了输出比特流的连续性。

  函数samp_to_delta_sigma()实现了将输入电平s调制为32bit比特流。简单计算调制器的采样率如下:

    Fs = Fs(Input) * 32,其中Fs(Input)为输入数字信号的采样率。

  对于PCM音频数据,若原始采样率为48kHz,则该delta-sigma调制器的采样率可达:

    Fs = 48kHz * 32 = 1.536MHz

  则要求I2S总线的BCLK时钟频率至少达到:

    Fmin = 1.536MHz

 int32_t
 samp_to_delta_sigma(short s)
 {
   int w;
   , i2v = ;
   ;

   int i;
   ;

   ; i<; i++)
     {
       val<<=;
       w=s;
       ) w -= HW; else w += HW; /* Difference 1 */
       w += i1v; i1v = w; /* Integrator 1 */
       ) w -= HW; else w += HW; /* Difference 2 */
       w += i2v; i2v = w; /* Integrator 2 */
       latch_reg = w;   /* Latch */
       ) val|=; /* comparator */
     }
   return val;
 }

  从上面简单计算可以看出,当调制器迭代次数与I2S传输周期的有效位数相同时,将I2S驱动请求的采样率配置为输入信号的原始采样率即可确保时钟正确。

  本文采用espressif NONOS SDK提供的I2S驱动库。首先调用I2sInit()初始化I2S接口,再调用I2sSetRate()设置采样率。对于每个输入信号采样s,通过调用i2sPushSample( samp_to_delta_sigma(s) )即可实现delta-sigma调制和比特流输出。

  如下为一个实例,该例程调制采样率为48kHZ的PCM音频信号并输出:

 I2sInit();
 I2sSetRate(, );

 ; i < size; i++)
   i2sPushSample( samp_to_delta_sigma(s[i]) );

六、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与W1、W2满足如下关系(正常情况下W1 = Wf,以确保正确还原比特流信号的时钟频率):

    W1 <= Wf < W2

  一旦条件满足,CPU将得到空闲时间:

    Tfree = 1 / [(W2 - Wf) * 1024^2] (s)

  这是因为系统运行中,除起始状态外FIFO将永远保持非空状态。Tfree可以代表用于运行其它任务的空闲时间长度(包括任务调度切换的时间)。若其它操作占用超过Tfree的运行时间,则可能导致FIFO输入侧带宽不足,从而造成FIFO下溢出,使得输出中断。

  T0的决定因素非常复杂,需要考虑生成的汇编指令、整个CPU的运行状态(如时钟主频,cache状态等)和输入的数字信号,因此只能得出平均值T0,将T0代入上式即可得出近似的Tfree时间。

七、应用价值

  在音频应用领域,该方法可通过简单的一阶RC滤波电路,直接从I2S接口得出模拟音频信号,从而省去成本较高的DAC芯片。除此之外,加入简单的功率输出和滤波电路,即可实现Class-D数字功放,如下图所示。

ESP8266 软件实现 Delta-sigma(ΔΣ)调制器 并通过I2S接口输出编码流的更多相关文章

  1. C#软件设计——小话设计模式原则之:接口隔离原则ISP

    前言:有朋友问我,设计模式原则这些东西在园子里都讨论烂了,一搜一大把的资料,还花这么大力气去整这个干嘛.博主不得不承认,园子里确实很多这方面的文章,并且不乏出色的博文.博主的想法是,既然要完善知识体系 ...

  2. ESP8266开发之旅 基础篇② 如何安装ESP8266的Arduino开发环境

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  3. ESP8266开发之旅 基础篇③ ESP8266与Arduino的开发说明

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  4. sigma网格中水平压力梯度误差及其修正

    1.水平梯度误差产生 sigma坐标系下,笛卡尔坐标内水平梯度项对应形式为 \[\begin{equation} \left. \frac{\partial }{\partial x} \right| ...

  5. 水平梯度在sigma坐标对应形式

    sigma 坐标变换 一般 \(\sigma\) 坐标转换方程为 \[\sigma = \frac{z-\eta}{D} = \frac{z-\eta}{H+\eta} \] 转换后水深 z 范围由原 ...

  6. C#软件设计——小话设计模式原则之:依赖倒置原则DIP

    前言:很久之前就想动笔总结下关于软件设计的一些原则,或者说是设计模式的一些原则,奈何被各种bootstrap组件所吸引,一直抽不开身.群里面有朋友问博主是否改行做前端了,呵呵,其实博主是想做“全战”, ...

  7. C#软件设计——小话设计模式原则之:单一职责原则SRP

    前言:上篇C#软件设计——小话设计模式原则之:依赖倒置原则DIP简单介绍了下依赖倒置的由来以及使用,中间插了两篇WebApi的文章,这篇还是回归正题,继续来写写设计模式另一个重要的原则:单一职责原则. ...

  8. C#软件设计——小话设计模式原则之:开闭原则OCP

    前言:这篇继续来看看开闭原则.废话少说,直接入正题. 软件设计原则系列文章索引 C#软件设计——小话设计模式原则之:依赖倒置原则DIP C#软件设计——小话设计模式原则之:单一职责原则SRP C#软件 ...

  9. Google首席软件工程师Joshua Bloch谈如何设计一款优秀的API【附PPT】

    编者按]随着近来软件规模的日益庞大,API编程接口的设计变的越来越重要.良好的接口设计可以降低系统各部分之间的相互依赖,提高组成单元的内聚性,降低组成单元间的耦合度,从而提高系统的维护性和稳定性. J ...

随机推荐

  1. 获取邮箱使用情况、以及最后一次logon时间

    # 每天收发邮件数 # https://gallery.technet.microsoft.com/scriptcenter/Count-sent-and-recceived-f9c66cf7 # 获 ...

  2. GNU make 汇总

    = 是最基本的赋值 := 是覆盖之前的值?= 是如果没有被赋值过就赋予等号后面的值+= 是添加等号后面的值 $@--目标文件,$^--所有的依赖文件,$<--第一个依赖文件 makefile获取 ...

  3. dubbo 用来做什么

    1.各个独立app之间的通信问题怎么解决? 2.怎么做到统一调度.协调处理. 3.如果计费模块是并发最大的模块,但是其他模块并发不是很大.则需要对计费进行负载均衡,怎么实现?

  4. 文件下载及header方法介绍

    文件下载: 文件下载是浏览器一个功能,我们用php,把一个文件转化成浏览器无法解析的文件,浏览器就会认为,他是下载文件或无效文件. 主要依靠:header() 函数: header() 方法用于客户端 ...

  5. To be taught if i am fortunate

    此博客算是我自娱自乐的海洋球池吧. 由于我十分的菜并且文笔拙劣,所以您可能并不能在这找到什么有用的信息或者好玩的东西(或者exciting的内容). 如果您能指出我的一些错误,我将十分感激.

  6. Collection 和 Collections的区别。(转)

    Collection 和 Collections的区别. Collections是个java.util下的类,它包含有各种有关集合操作的静态方法. Collection是个java.util下的接口, ...

  7. 71.纯 CSS 创作一个跳 8 字型舞的 loader

    原文地址:https://segmentfault.com/a/1190000015534639#articleHeader0 感想:rotateX() 和rotateZ()一起使用好懵呀. HTML ...

  8. 关于单元测试时加载spring-context.xml文件的问题

    在进行web开发的时候,通常我们都会使用Spring框架,使用spring容器管理java bean. 而spring的配置文件有时候放在classpath下面,有时候放在WEB-INF下面. 一般在 ...

  9. 《2018面向对象程序设计(Java)课程学习进度条》

    周次 (阅读/编写)代码行数 发布博客量/博客评论数量 课堂/课余学习时间(小时) 最满意的编程任务 第一周 50/40 1/0 6/4 九九乘法表 第二周 100/80 1/0 6/8 实验5,6, ...

  10. Could not read document: Can not deserialize instance of java.lang.String out of START_ARRAY

    线上问题: { "timestamp": "1544510665", "status": 400, "error": & ...