智能手表上的音频(二):驱动

上一篇讲了智能手表上音频系统的架构和应用场景。从本篇开始讲具体的,首先讲音频相关的驱动,主要包括IPC(inter-processor communication,核间通信, 即AP/CP/ADSP之间的通信)的driver 和audio的driver。首先说明一下,由于codec是我们公司自己设计的,内置在SOC里,且driver在ADSP(ADSP用的是RTOS)上,driver代码不会像Linux ALSA那样普适。

讲驱动之前,先看用了哪些中断和memory。 用到的中断主要有IPC中断和ADMA(Audio DMA)中断。IPC本质上是中断加Ring Buffer,用到内置codec以及蓝牙通话时,系统就是靠ADMA中断(这个中断等间隔来)驱动的,即等间隔的ADMA 中断驱动音频系统转起来,后面具体讲。用到的memory主要有5块,具体如下:1,ITCM(片内,放代码)。2,DTCM(片内,放数据)。 3,ADSP与其他core做数据交互用的share memory,这块memory在SRAM上,对于做数据交互的双方来说,都要能访问,且双方都要设成uncacheable。4,存放codec采集到的音频数据的memory,这块memory ASIC设计时就是音频专用的,对audio来说latency很小,不会有其他竞争导致audio 被卡。5,DDR中给audio用的memory,这块memory属于片外,在设计时就分配给audio用,既可以放代码,也可以放数据,根据需要将其中的部分设成cacheable或者uncacheable。下图显示了哪些memory要设成cacheable,哪些要设成uncacheable:

1,  IPC driver

IPC就是核间通信(AP/CP/ADSP之间的通信),即核间进行数据交互。要进行数据交互,首先得定义好数据格式,让交互的双方都能解析。 交互数据包括控制数据(如使能一个stream)和音频数据。上面说过,IPC本质上就是中断加Ring Buffer(既然是Ring Buffer,就得有读写index)。Ring Buffer及其控制变量(读写index等)都在要通信的两核都能访问的share memory上(上面说的第3种memory)。如果核间是单向通信,则只需要一个Ring Buffer。如果是双向通信,就需要两个Ring Buffer。下图给出了常见的双向交互的示意图:

发送方把数据按照规定的格式组好后放在Ring Buffer内write index开始的地方,同时更新write index,然后给接收方发一个中断。接收方收到中断后进入中断服务程序,从Ring Buffer内read index开始的地方取指定大小的数据,同时更新read index,然后解析收到的数据,进行下一步的操作。

AP与ADSP之间就是以如上的方式交互控制命令和音频数据的。此外IPC还有的作用包括输出ADSP的log到UART以及把要dump的音频数据从ADSP发给AP保存成文件等。由于ADSP的log没法直接输出,就需要把log通过IPC发给AP,AP再输出到UART等。Dump音频数据是音频调试的一个非常重要的手段。ADSP上也没法直接dump,只能借助于IPC发到AP上保存成文件。调驱动时首先调的就是AP与ADSP之间的IPC,确保AP和ADSP之间通信正常,ADSP的log输出正常,能正常dump音频数据,在IPC好的基础上再去调其他的。新手在调IPC时常犯错的是没把用到的share memory设成uncacheable,导致有时候通信正确,有时候通信不正确,从而花不少时间去调查,有经验的一看现象就大概知道原因了。

ADSP与CP之间仅仅交互语音数据(只有语音通话时才会用到,语音控制命令相关的都是ADSP与AP之间的交互),就对IPC进行了简化,用固定的buffer(这块buffer也是在双方都能访问的share memory上)代替Ring Buffer,且只用一个中断(ADSP给CP发中断)。示意如下图:

上面说过语音通话时系统是靠ADMA中断驱动的。通常10ms一个ADMA中断,这个中断驱动音频系统转起来。在一个loop里,ADSP先从上图中的Play buffer里取CP放进去的要播放的语音数据,然后从ADSP audio buffer里取采集到的语音数据,采集到的语音数据经处理(比如重采样)后送到上图中的Record Buffer里,最后给CP发一个IPC中断。CP收到中断后先从Record buffer里取出采集到的数据并做处理,然后把要播放的数据放进Play buffer里。

2,  audio driver

下图是在上一篇架构(智能手表上的音频(一):架构)中的硬件框图:

从上图看出在用内置codec和BT时音频驱动是有差异的,下面就分两种case来讲。

2.1 内置codec

下图是内置codec下驱动硬件框图:

从上图看出,驱动主要包括两部分,内置codec和ADMA(audio DMA)。先看内置codec相关的。内置codec内部可分成模拟部分(analog, 包括ADC和DAC)和数字部分(digital, 即DFE(digital front-end,数字前端))。内置codec的驱动主要是配置寄存器:包括配置音频时钟、DFE和ADC、DAC等。再看ADMA相关的。既然是DMA,就得有descriptor。下图是其结构体定义:

byte_counts是指ADMA buffer的大小。这个值定好了也就定好了ADMA中断的间隔。 以ADMA中断产生的条件是ADMA buffer为空为例,当ADMA buffer空了就产出一个中断。ADMA buffer的大小通常以毫秒计(这样就可知道ADMA的中断间隔是多少毫秒),知道了采样率和通道数,就可算出字节数。假设ADMA buffer 10ms,48K采样,双声道,则ADMA buffer的大小是1920bytes (10*48*4 = 1920),所以说ADMA buffer的大小定好了也就定好了ADMA中断的间隔。Src_addr是原地址,dst_addr是目的地址,会把音频数据从原地址搬到目的地址。在采集方向上,src_addr是硬件地址, dst_addr是adsp里audio buffer的地址。在播放方向上,src_addr是adsp里audio buffer的地址硬件地址, dst_addr是硬件地址。next_desc指向下一个descriptor,通常在一个方向上有两个descriptor,两个descriptor互指。ADMA的驱动主要包括挂中断、寄存器配置(上面descriptor里内容以及中断产生条件等)以及音频数据的搬运。在采集方向上音频数据主要是从硬件地址上搬到adsp的audio buffer里,在播放方向上音频数据主要是从adsp的audio buffer里搬到硬件地址上。ADSP的audio buffer,两个方向上既可以用不同的两块,也可以用相同的一块。用两块时需要两个ADMA中断(一个方向上一个),相应的就有两个中断服务程序。因为两个方向上不相关,处理相对简单,缺点是浪费memory(ADSP上的memory是非常宝贵的)。用一块时只需要一个ADMA中断,因为两个方向上相关,处理就复杂些(处理不好就造成数据踩踏),优点是节省memory。

2.2 蓝牙

下图是蓝牙下驱动硬件框图:

从上图看出,驱动主要包括两部分,ASSP(audio SSP(Synchronous Serial Port, 同步串行接口))和ADMA。ADMA跟内置codec的一样,这里就不讲了。ASSP说白了就是配置是PCM总线还是I2S总线,配置的内容有音频采样的数据长度、采样方式(上升沿采样还是下降沿采样)、对齐方式(左对齐还是右对齐)、帧同步宽度等。

上面的两种case,不管哪一种,首先要确保的是配好寄存器后ADMA中断要有规律的等时长(比如10ms)来,如不是说明寄存器配置有问题,需要再去仔细看datasheet,理解配置的意思。可以在ISR(中断服务程序)里加个log,如果log等间隔出现,就基本说明ADMA中断等间隔来了。

先调内置codec下的音频驱动。中断正常后先调播放方向上的。往ADSP audio buffer里写正弦波,speaker里放出tone音就说明播放方向上调好了。再调采集方向上的,把ADSP audio buffer里采集到的数据通过IPC发给AP保存成文件,用音频软件(如CoolEdit)去听,跟对着mic说的话一致就说明调好了。由于牵涉到硬件,调试过程中需要ASIC、analog和hardware相关人员的支持,比如寄存器是否配对了、某个PIN输出是否符合期望等,毕竟在硬件上他们更有经验。BT下的驱动只在蓝牙语音通话时才用到,把内置codec下的语音通话调好了再去调BT下的驱动更容易,因为整个链路上只需要把内置codec下的驱动换成BT下的驱动,更方便验证是否调好。BT下的驱动相对内置codec下的,ADMA等做些值的修改,主要调ASSP。ASSP配好寄存器后用示波器量信号(SCLK/SYNC/DIN/DOUOT等),符合预期就可以了。最终用蓝牙打电话,收发方向上声音都正常就说明调好了。

Audio driver相当于地基,地基好了后就开始盖楼(开发具体功能)了。从下篇开始讲具体的功能,首先讲音频文件播放。

猜你喜欢

转载自blog.csdn.net/david_tym/article/details/133883102