前面我们已经详细介绍了es8388的功能,重点是关注其ADC和DAC链路的配置,同时也实现了其寄存器的读写驱动,并实现了es8388功能配置的驱动接口es8388.c/h。现在开始就可以实现具体应用了。前面我们也分享了UAC的诸多实例,现在我们就基于UAC和es8388实现麦克风和扬声器,录音和播放。
Uac相关实例参考:https://mp.weixin.qq.com/s/bLSLwPjl5cC_8X-YxZo89Q 《USB系列之-UAC扬声器+麦克风实例分享》
首先控制器需要实现IIS驱动,这是平台相关这里就不再赘述。为了保证数据不断流,需要IIS驱动能实?现ping-pang传输,一般由DMA+Half中断实现。
同时确保前面寄存器读写驱动接口移植好,添加es8388.c/h这部分驱动代码,参考如下配置,16K,差分输入输出,单声道。如果ES8388的IIS无输出,或者给了输入DAC无输出,参考前面分享的文章进行分析。
static int es8388_itf_cfg(void)
{
/* 见ES8388 user Guide.pdf的10.1 The Sequence for Start up codec */
/* 1.设置ES8388为从模式 */
es8388_set_mode_bck(ES8388_MODE_SLAVE, 0, 0, 0);
/* 2.关闭DEM STM等电源 */
es8388_set_chip_pwr_bits(ES8388_ADC_DIGPDN | ES8388_DAC_DIGPDN | ES8388_ADC_STM_RST | \
ES8388_DAC_STM_RST | ES8388_ADCVREF_PDN | ES8388_DACVREF_PDN);
/* 3.设置ADC DAC使用同样的LRCK */
es8388_set_lrck(ES8388_SLRCK_SAME,ES8388_LRCKSEL_DAC_LRCK);
/* 4.Set Chip to Play&Record Mode */
es8388_set_enref(1);
es8388_set_vmidsel(ES8388_VMIDSEL_50K);
/* 5.Power Up Analog and Ibias */
es8388_clr_chip_ctrl2_bits(ES8388_PDNIBIASGEN | ES8388_PDNANA | ES8388_LPVREFBUF);
/* 6.Power up ADC / Analog Input /Micbias for Record */
es8388_clr_adc_pwr_bits(ES8388_PDNAINL | ES8388_PDNAINR | ES8388_PDNADCL |
ES8388_PDNADCR | ES8388_PDNMICB | ES8388_PDNADCBIASGEN);
/* 7.Power up DAC and Enable LOUT/ROUIT */
es8388_set_dac_pwr(ES8388_CH_L,1);
es8388_set_dac_pwr(ES8388_CH_R,1);
es8388_set_dac_out_en(ES8388_OUT_L1,1);
es8388_set_dac_out_en(ES8388_OUT_L2,1);
es8388_set_dac_out_en(ES8388_OUT_R1,1);
es8388_set_dac_out_en(ES8388_OUT_R2,1);
/* ADC部分 */
es8388_set_adc_gain(ES8388_CH_L, 21); /* PGA增益 24db */
es8388_set_adc_gain(ES8388_CH_R, 21);
es8388_set_adc_ds(ES8388_DSSEL_LIN1_RIN1, ES8388_DSSEL_LIN2_RIN2);
es8388_set_adc_in(ES8388_CH_L,ES8388_INSEL_LR);
es8388_set_adc_in(ES8388_CH_R,ES8388_INSEL_LR);
es8388_set_adc_monomix(ES8388_MONOMIXSEL_STEREO);
es8388_set_adc_format(DATSEL_LDATA_LADC_RDATA_RADC, LRP_NORMAL_2ND, WL_24, FORMAT_I2S);
es8388_set_adc_fs(ES8388_FS_MODE_SINGLE, ES8388_FS_RADIO_1000);
es8388_set_adc_vol_attenuates(ES8388_CH_L, 86); /* 先设置衰减大一点,避免ADC噪声过大或者超量程 */
es8388_set_adc_vol_attenuates(ES8388_CH_R, 86);
es8388_set_adc_pgagain_th(47, 0);
es8388_alc_cfg_st alccfg=
{
.alcsel = ES8388_ALCSEL_LR,
.alclvl = -9,
.alchld = 0,
.alcdcy = 1,
.alcatk = 2,
.alcmode = ES8388_ALCMODE_NORMAL,
.alczc = 0,
.time_out = 0,
.win_size = 96,
};
es8388_set_adc_alc_cfg(&alccfg);
es8388_set_adc_noisegate(1, ES8388_NOISE_GATE_TYPE_MUTE, 81);
/* DAC部分*/
es8388_set_dac_format(0, LRP_NORMAL_2ND, WL_24, FORMAT_I2S); /* 设置DAC数据格式 */
es8388_set_dac_fs(ES8388_FS_MODE_SINGLE, ES8388_FS_RADIO_1000);
es8388_set_dac_vol(ES8388_CH_L, 0); /* 设置L通道音量 0db即不衰减,最大音量 */
es8388_set_dac_vol(ES8388_CH_R, 0);
es8388_set_dac_mixen(ES8388_CH_L,ES8388_OUT_SRC_MIXSEL,0); /* 设置OUT只来源于DAC,不来源于MIXSEL */
es8388_set_dac_mixen(ES8388_CH_L,ES8388_OUT_SRC_DAC,1);
es8388_set_dac_mixen(ES8388_CH_R,ES8388_OUT_SRC_MIXSEL,0);
es8388_set_dac_mixen(ES8388_CH_R,ES8388_OUT_SRC_DAC,1);
es8388_set_dac_outvol(ES8388_OUT_L1,9); /* 设置对应引脚的输出音量,使用L的1和2引脚, 先设置最小音量-90/2db避免输出过大 */
es8388_set_dac_outvol(ES8388_OUT_L2,9);
es8388_set_dac_outvol(ES8388_OUT_R1,9);
es8388_set_dac_outvol(ES8388_OUT_R2,9);
/* 开DEM STM等电源 */
es8388_clr_chip_pwr_bits(ES8388_ADC_DIGPDN | ES8388_DAC_DIGPDN | ES8388_ADC_STM_RST | \
ES8388_DAC_STM_RST | ES8388_ADCVREF_PDN | ES8388_DACVREF_PDN);
return 0;
}
使用逻辑分析仪抓取IIS通讯数据确认收发是否正确。
对于DAC可以输出正弦波数据进行测试,示波器查看输出是否是正弦波,并且可以检查数据是否有断流。一个比较常见的问题是驱动设计不合理无法保证两次传输之间的无缝连接,两笔传输之间可能存在抖动,可以通过正弦波测试,示波器分析输出信号的傅里叶变换通过谐波看出来。
对于ADC可以用手机下载固定频率声音播放器,播放固定频率音频,然后抓取IIS的数据看是否正确,或者通过控制器其他手段比如dumo内存的数据通过串口打印出来,到电脑上画出曲线进行分析。
整个应用的核心就是播放和录音的两条数据流了,音频重点要保证的是数据的流式传输,必须保证不断流,无抖动。这里涉及到几个核心技术FIFO设计,FIFO池设计,ping-pang传输设计。
FIFO相关设计我们之前已经造过相关的轮子了,并且多出应用了,现在又派上用场了。
参考
https://mp.weixin.qq.com/s/MvL9eDesyuxD60fnbl1nag 《超级精简系列之十三:超级精简的循环FIFO,C实现》
https://mp.weixin.qq.com/s/PV-sUxzTEKbobgyt4BKRlA《超级精简系列之十九:超级精简的循环FIFO池,C实现》
整个数据流分为两个方向,设计思路是一样的,
播放的数据流
PC(或其他主机) -> USB(UAC) - > FIFO - > 算法处理 -> 发送ping-pang缓存 -> IIS发送 ->ES8388 - >PA -> 喇叭。
整个流程有两个重点节点:一个是接收FIFO,UAC收到数据存入该FIFO,一是发送ping-pang缓存,算法任务从接收FIFO中取出数据进行算法处理,然后写入ping-pang缓存进行发送。注意这里算法任务等待上一次IIS的DMA传输完才进行下一次处理,所以是由IIS的DMA发送中断驱动的,IIS的DMA在两个缓存之间无缝连接(依赖于硬件的HALF中断或者DMA描述符链表机制),DMA中断通过信号通知任务。为了启动DMA的流,所以一开始必须先提交两笔传输,即ping-pang缓存的两个缓存都处于待发送状态,一个发送完后硬件自动切换到下一个,并且产生中断发送信号通知任务运算,准备下一个发送。任务需要从接收FIFO获取数据进行处理存在DMA发送完一笔,但是接收FIFO中还不足一笔数据的情况,这个时候就是断流的情况,可以先接收FIFO积蓄至少两笔以上才进行IIS DMA发送,这样足够的冗余避免断流,详见下一节断流分析。
录音的数据流
MIC->ES8388>IIS->接收-ping-pang缓存->发送FIFO-> UAC->USB主机
整个流程有两个重点节点:一个是发送FIFO,UAC从该FIFO读出数据进行发送,一是接收ping-pang缓存,算法任务从ping-pang缓存中取出数据进行算法处理,然后写入发送FIFO等待UAC发送。注意这里算法任务等待上一次IIS的DMA传输完才进行下一次处理,所以是由IIS的DMA接收中断驱动的,IIS的DMA在两个缓存之间无缝连接(依赖于硬件的HALF中断或者DMA描述符链表机制),DMA中断通过信号通知任务。为了启动DMA的流,所以一开始必须先提交两笔传输,即ping-pang缓存的两个缓存都处于待接收状态,一个接收完后硬件自动切换到下一个,并且产生中断发送信号通知任务运算,准备下一个接收。任务需要从ping-pang缓存获取数据进行处理存入发送FIFO待UAC发送。UAC发现发送FIFO不足一笔数据的情况,这个时候就是断流的情况,可以先等待发送FIFO积蓄至少两笔以上才进行UAC发送,这样足够的冗余避免断流,详见下一节断流分析。
前面数据流设计中提到了两个方向的数据断流的问题,这里再详细分析下。
UAC发送FIFO中时可能存在断流问题,因为发送FIFO是一个水池,IIS DMA接收驱动任务算法处理往该FIFO写数据。理论上IIS采样率和UAC发送率是匹配的,但是不可避免存在一点抖动偏差,这个抖动偏差就可能使得错过,比如往发送FIFO写数据第一次的时间间隔是9mS,下一次是11mS,那么UAC发送间隔时可能写数据还差一点时间才到就读不到数据导致断流,如下图生产者消费者模型所示
解决方法是消费者延迟消费,即等待生产生产两笔以上数据,再进行消费,那么水池里就继续了两笔以上数据下次消费时由于抖动,来的快一点,或者生产下一次生产的慢一点也不会导致断流,因为里面有两次传输的冗余可以协调。只要抖动不超过这个水池的积蓄量就不会断流,因为平均速率是基本一样的,所以不会导致太大的偏差,只会偶尔小的偏差,所以这个积蓄量就可以保证不断流。
从接收FIFO读数据处理也有同样的断流问题,也是一样的,不再赘述。
本篇是es8388应用案例分享系列的完结篇,以一个具体的应用结尾,该应用完全按照产品应用级设计,直接可以应用到自己的产品设计当中去。通过以上应用,其中IO模拟IIC,FIFO设计等基础技术,也可以体验到之前我们设计自己趁手的轮子的必要性了。