基于ThreadX系统的I2S音频总线驱动设计方案
2018-01-15
(重庆邮电大学 通信与信息工程学院,重庆 400065)
引 言
ThreadX是Express Logic公司专为嵌入式应用而设计的一款高性能的实时操作系统,支持大量的处理器和SoC,也因此广泛应用于消费电子、汽车电子、工业自动化、军事及航空航天等领域。其中随着信息化、智能化的发展,数字音频技术音频系统是嵌入式系统应用中的一个重要组成部分。
I2S总线是工业和嵌入式领域常采用的一种音频总线,其驱动程序的实现主要依赖于音频总线接口、相应硬件的工作原理以及驱动的体系结构。基于具体的项目需求背景下,本文主要阐述了音频接口I2S驱动程序设计方法与流程,为I2S总线在ThreadX下的应用提供了一定的研究方法和技术参考。
1 基本原理
1.1 ThreadX概述
ThreadX是Express Logic公司专为嵌入式应用而设计的一种高性能实时内核,具有体积小、速度快、高可靠性和实时性等特点,同时拥有高稳定性和低成本。ThreadX占用空间小,适合先进的终端系统级芯片的设计,且凭借其多任务的特性,ThreadX可以满足应用系统实时性的要求,有助于提高整个系统的稳定性和可靠性。
ThreadX包含了实时操作系统应该具备的所有服务和组件,包括线程、消息队列、计数信号量、互斥量、事件标志、内存块池、内存字节池、应用定时器、时钟计数器和中断控制。其中,通过图1介绍了ThreadX的几个功能模块:线程管理和调度、同步与通信、内存管理、时钟和定时器以及中断处理。
图1 ThreadX总体架构图
1.2 ThreadX启动过程
当系统通电之后,首先是进行系统复位以及开发工具初始化,当初始化完成之后,控制转移到用户提供的main主函数上,在主函数中调用tx_kernel_enter进入ThreadX系统内核。入口函数tx_kernel_enter负责调用处理器指定的低级初始化和C语言的高级初始化,配合ThreadX内部不同数据结构的初始化。
图2 ThreadX启动流程
当tx_kernel_enter返回时,控制转移到线程调度循环,同时也标志着初始化的完成。紧接着在入口函数中调用程序定义函数tx_application_define。tx_application_define函数定义了所有初始的应用线程、队列、消息、事件标志、存储池和定时器,同时可以在正常的应用程序执行过程中创建或者删除系统资源,最后会进入调度循环开始线程调度。ThreadX的启动过程如图2所示。
1.3 ThreadX下的I2S总线驱动结构
基于上文所述,ThreadX启动并经过初始化之后开始进入tx_application_define(),并根据具体的应用做一些特定的操作。对于音频系统,在程序定义函数tx_application_define()中首先为codec线程分配堆栈,并通过动态地调用tx_thread_create(&thread_codec,"thread_codec",thread_codec_entry,NULL,pointer,STACK_SIZE*2,6,6,TX_NO_TIME_SLICE, TX_AUTO_START)函数创建codec线程,然后再通过调用入口函数thread_codec_entry()进入I2S音频系统的操作。
在应用I2S音频系统初时需先将I2S总线复位到原始状态,再调用arm_i2s()配置I2S音频系统。
void thread_codec_entry(ULONG thread_input) {
i2s_reset();
arm_i2s(main_mes);
}
I2S音频系统的功能是通过I2S总线接口与音频编解码芯片进行交互来实现,其中I2S接口负责传输音频数据,音频编解码芯片在I2C总线的控制下完成功能配置后负责接收音频数据并进行编解码,其具体功能调用实现图如3所示。由于在实际项目中,ThreadX提供的驱动源代码中已经包含了I2C接口驱动模块与CODEC驱动模块,故本次驱动开发只需对I2S接口驱动模块进行配置,测试验证后方可移植到ThreadX系统中。
图3 ThreadX下的I2S调用模块
2 I2S总线接口
I2S(Inter-IC Sound)总线是一种总线标准,即飞利浦公司为数字音频设备之间的音频数据传输而制定的。且I2S总线与音频设备之间进行数据传输,广泛应用于各种多媒体系统。
I2S有3个主要信号:串行时钟SCLK(continuous serial clock)、帧时钟 LRCK(word select )、串行数据 SDATA(serial data)。在I2S音频系统中,产生SCLK和LRCK信号的一方可称为主设备,I2S总线控制器与音频芯片均可作为发送端与接收端,且发送端和接收端需要同步时钟信号来控制数据的传输,传输模式之一如图4所示。如果有多个接收端和发送端,就需要一个控制端作为主导装置来产生SCLK和LRCK的信号。
图4 I2S数据传输模式
2.1 I2S的总线协议
(1)帧时钟LRCK
音频数据由左右声道组成,使用帧时钟选择可以区分左右声道,即LRCK为低电平时表示正在传输的是左声道的数据,为高电平时表示正在传输的是右声道的数据。LRCK可以在串行时钟的上升沿或者下降沿发生改变,且总是在最高位传输前的一个时钟周期发生改变,这样可以从设备得到与被传输的串行数据同步的时间。
(2)串行时钟SCLK
串行时钟,即表示每一个时钟信号传送一位音频数据,因此,I2S串行时钟的频率计算公式如下:
I2S SCLK的频率=声道数×采样频率(fs)×采样位数
其中声道数通道数为2,即左、右声道。采样频率可为系统主时钟,并且I2S LRCK的频率值可等值于采样频率。采样位数可根据寄存器配置数据宽度决定。
由于I2S总线只负责音频数据的传输,而要真正实现音频数据的录入和播放,还需要同外接的音频芯片同步采样时钟与采样位数来处理音频数据。在I2S总线中,I2S接口端和音频芯片(CODEC)端分别可以作为主设备,其中只要当某一端作为产生时钟信号LRCK和SCLK的器件,则就能作为主设备。
(3)串行数据SDATA
串行数据,其用二进制补码表示音频数据,在串行时钟SCLK脉冲下,数据一位一位地传输在SDATA线上。当音频数据被音频芯片模数转化器处理成二进制数据流后,将数据分成8位或16位传输,且每个字节的数据传输从左边的二进制位MSB(MostSignificantBit)开始。当发送方和接收方的数据宽度不一样时,发送方不考虑接收方的数据宽度,如果发送方的数据宽度小于系统字段宽度,可以在低位补0;反之超过LSB(Least Significant Bit)的部分被截断。
2.2 TLV320AIC3106音频芯片
基于实际项目,I2S接口模块集成在芯片中,其目的是为了适应移动音频设备越来越高的要求,且内置的I2S总线接口也可以方便和其他音频编解码芯片配合使用。而实际项目中采用的音频编解码芯片为TLV320AIC3106芯片。
TLV320AIC3106是TI公司生产的具有立体声耳机放大器的低功耗立体声音频编解码器,记录路径包含集成的麦克风偏置、数字控制的立体声麦克风前置放大器和自动增益控制(AGC),内部A/D转换器和D/A转换器可用于实现模拟音频信号的采集和数字音频信号的模拟输出。
虽仅支持I2S硬件接口和I2S总线数据格式,但字节长度最高可达到32位,且其还包括高度可编程的PLL,用于灵活的时钟生成,并支持来自各种可用MCLK(从512 kHz~50 MHz)的所有标准音频速率,能有效降低使用功耗,可在多种需要进行声音录入或输出的嵌入式媒体设备中进行实际应用。
2.3 I2S总线驱动程序的通信原理
从实际项目出发,处理器需要通过I2S总线才能完成和音频芯片之间的数据传输。图5为I2S音频系统逻辑框图。
一般而言,处理器与外部芯片进行数据传输的方式有两种:传统的中断方式和直接存储器存取DMA方式。音频数据的传输可以通过设置寄存器先入先出FIFO队列来完成。若处理器采用中断的方式往FIFO队列录入数据,其不仅很难保证音频播放的连续性,而且还会因处理器频繁的响应中断而导致系统执行效率和性能的降低。为了避免录入与播放的断续,且FIFO空间有限,需要将录入的音频数据存储到其他内存缓冲区内,录入数据与播放数据可存放在不同的内存缓冲中,但需要快速切换使用的缓存空间,所以根据实际项目的需求,在为音频系统开发I2S总线接口驱动过程中,设计采用DMA方式进行音频数据传输。
音频系统最终是为了实现录音与播音的异时或同时处理效果。项目的系统DMA支持8个通道,为了实现音频数据全双工传输,开发过程中采用两个DMA通道,通过设置使能I2S接口控制器中的寄存器I2S_TX_DMA_DISABLE和I2S_RX_DMA_DISABLE来使I2S接口与处理器之间音频数据传输工作在DMA模式下。
此模式下使用I2S接口的FIFO寄存器组I2S_TX_DMA_FIFO_INPUT_DATA、I2S_TX_DMA_FIFO_STATUS、I2S_RX_DMA_OUTPUT_DATA和I2S_RX_FIFO_STATUS来存放音频数据,设置FIFO的状态来触发DMA控制器,将数据与内存区域之间进行传输,最终以实现录音和播音。
图5 I2S音频系统逻辑框图
如图5所示,在音频数据传输过程中使用了两个DMA通道以实现全双工工作方式。当录制声音时则先由音频芯片采集音频数据,然后由音频数据传输专用总线将数据发送到I2S总线控制器的rx_fifo中,最后由内部总线将采集到的音频数据通过DMA使用其中1个DMA通道搬送,写入到内存缓冲区中。
当播放声音时,首先将音频数据通过另一个DMA通道经由DMA控制器将音频数据写入I2S总线控制器的tx_fifo中,最后由音频数据传输专用总线传送到音频芯片中进行播放。
3 ThreadX下I2S音频总线的驱动程序开发
3.1 I2S音频接口驱动程序模块开发
图6 全双工模式(TX+RX)
音频系统中I2S接口需经过正确的配置方可实现录音和播音等功能。本文主要是根据实际项目的需求来设计一组固定的I2S音频接口驱动程序,控制音频数据在硬件中流动,以实现音频数据的正确接收和发送。设计的音频系统采样规格是系统主时钟为44.1 kHz,采样位数为16位。
基于实际项目需求来实现音频系统的全双工模式,主要是对I2S总线的寄存器进行设置。其工作流程如图6所示。
而具体开发I2S音频接口驱动程序完成的工作有:
(1)配置音频录入RX_ONLY模式与音频播放TX_ONLY模式
音频数据的录入和播放都必须先配置好I2S_tx与I2S_rx的相关参数,如音频数据的采样率与采样位数等。若要实现音频数据的播放,则调用开发函数I2S_tx_master()将I2S总线初始化为发送模式;若要实现音频数据的录入,则调用开发函数I2S_rx_master()将I2S总线初始化为发送模式。根据实际项目需求,将I2S接口的数据发送和接收模式分别通过寄存器I2S_TX_WORK_MODE和I2S_RX_SLAVE_EN设置为Master模式,并对发送和接收Master模式下的高低占空比、串行位时钟占空比进行配置,然后再依次分别对I2S接口与音频芯片之间的数据接收与发送进行配置,如采样宽度、音频芯片接口模式以及音量调节等参数。部分代码如下:
voidI2S_tx_master(){
REG32(I2S_TX_WORK_MODE) = 0;//设置I2S传输方式:主设备
REG32(I2S_TX_SRC_INFO) = ((0x0<<5) |(0x10<<0));//传输数据格式与宽度:立体声,16位
REG32(I2S_SRC_SR_CYCLE) = 0x177;//外部编解码器采样时钟占空比: 32KFS
REG32(I2S_TX_BCLK_DUTY_CYCLE) = 0x5;
//传输串行位时钟占空比:32KFS
REG32(I2S_TX_BAL) = ((1<<4) |(1<<0));
//平衡音量控制
REG32(I2S_TX_VLD_DW) = 0x10;
//传输到编解码器的有效数据宽度:16位
REG32(I2S_TX_TYPE) = ((1<<5)|(0<<4) |(0<<0));//传输类型为I2S标准模式
REG32(I2S_TX_CLK_PHASE) = ((0<<4) |(0<<0));//传输LRCK相位设置和传输位串行相位设置
REG32(I2S_TX_ANTI_POP_LENTH) = 0x8;
REG32(I2S_TX_VOL) = 0x1;//主音量控制
观察组患者的LVEDD为(56.34±4.78)mm,LVESD为(41.01±6.67)mm,LVEF为(52.43±5.32)%,血清BNP(309.77±104.44)pg/mL,6MWT为(389.32±70.32)m;对照组的LVEDD为(58.54±3.98)mm,LVESD为(46.32±4.01)mm,LVEF为(45.78±4.78)%,血清BNP(695.33±321.34)pg/mL,6MWT为(312.45±72.12)m,组间比较,差异有统计学意义(P<0.05)。
}
(2)全双工模式配置模块
通过调用开发函数I2S_loop_back_set()来设置I2S_LOOP_BACK_ENABLE的第0位为0,即以REG32(I2S_LOOP_BACK_ENABLE) &= ~0x1,将I2S总线初始化为全双工模式。寄存器I2S_LOOP_BACK_ENABLE具体信息如下所示:
I2S_LOOP_BACK_ENABLEWidthBitDefaultDescription1[0]0TX-->RXLoopdebugcontrol:0:disable1:enable,internallyconnectTXSDTOtoRXSDTI
(3)DMA配置模块
为实现音频数据在CPU与外部芯片之间的存储与播放,需进行DMA通道DMA_CHn(0≤n≤7)配置,其中DMA_CH0作为将数据从内存缓冲区传送到I2S接口控制器的tx_fifo中的通道,调用i2s_dma_tx_configure()实现;DMA_CH1作为将音频设备传送过来的音频数据从I2S接口控制器的rx_fifo传送到内存缓冲去中的通道,调用i2s_dma_rx_configure()实现。DMA功能的实现主要是配置各通道的源地址(DMAC_CHn_BASE + DMAC_SAR)与目的地址(DMAC_CH0_BASE + DMAC_DAR),配置完DMA_CH0与DMA_CH1之后再通过配置寄存器DMAC_DMACFGREG和DMAC_CHENREG使能DMA传输功能。部分代码如下:
//配置DMA_CH0
REG32(DMAMAC_BASE_ADDR+DMAC_CH0_BASE + DMAC_SAR) =0xc0000000;//设置源地址 REG32(DMAMAC_BASE_ADDR+DMAC_CH0_BASE+DMAC_DAR)=I2S_TX_DMA_FIFO_INPUT_DATA ;//设置目的地址
REG32(DMAMAC_BASE_ADDR + DMAC_CH0_BASE + DMAC_DSTATAR) = I2S_TX_DMA_FIFO_STATUS;
REG32(DMAMAC_BASE_ADDR + DMAC_CH0_BASE + DMAC_CTL) = 0x00109125;
REG32(DMAMAC_BASE_ADDR + DMAC_CH0_BASE + DMAC_CTL + 4) = 0x00000380;
……
//使能DMA_CH0
REG32(DMAMAC_BASE_ADDR + DMAC_DMACFGREG) =0x00000001;
REG32(DMAMAC_BASE_ADDR + DMAC_CHENREG) = 0x00000101; REG32(DMAMAC_BASE_ADDR + DMAC_MASKTFR) = 0x0101; REG32(DMAMAC_BASE_ADDR + DMAC_MASKBLOCK) = 0x0101;while(REG32(I2S_TX_DMA_FIFO_STATUS) & 0x4);
3.2 I2S音频接口驱动程序的测试
综上所述,完成I2S全双工模式相关配置后,则需要对驱动程序进行测试。首先将I2S接口驱动程序置于Makefile工程环境中进行编译,编译通过,然后再将其进行ASIC仿真测试。全双工模式下的音频录入测试结果与播放测试结果分析如下。
音频录入测试:图7为音频芯片采样第一个数据的波形图,根据ws_in对应的波形图,来读取sdi对应的波形图可得采样的第一个数据,其左声道数据为“b510”,其右声道数据为“bd10”,再观察图8中rx_fifo_din对应的紫色光标处得知rx_fifo中存储的第一个数据为“32’hbd10_b510”,此表明了音频录入数据无误。
图7 音频芯片采样数据波形图
图8 I2S总线rx_fifo数据图
音频播放测试:同理如上。图9中tx_fifo_dout对应的数据是通过DMA从内存缓冲区搬到tx_fifo中的数据,其值为“bd1f_b51f”。再观察图10中ws_out对应的波形
图来读取sdo对应的波形图的数据,其左声道数据为“b51f”,其右声道数据为“bd1f”,继而表明了音频播放数据无误。
图9 I2S总线tx_fifo数据图
图10 音频芯片采样数据波形图
综上测试结果分析可得,本文设计的I2S接口驱动程序能实现CPU与外部芯片之间音频数据的正确录入与播放,可移植到ThreadX系统中并与外接芯片完成音频数据的格式转换,最终实现录音与播音功能。
结 语
[1] 潘应进,朱子元. 基于ThreadX实时操作系统的USB设备驱动开发[J]. 工业控制计算机,2016,29(2):30-32.
[2] 宋鑫梦,郭改枝,王秀丽. 基于嵌入式的I2S音频系统设计与实现[J]. 内蒙古师范大学学报:自然科学汉文版,2015,44(2):245-248.
[3] 霍燃,高丽萍,陈庆奎. 嵌入式Linux系统下基于UDA1341芯片的音频驱动程序设计[J]. 计算机应用与软件,2012,29(4):16-19.
[4] 李文正. 基于I2S总线的嵌入式音频系统的设计[J]. 软件,2010,31(12):55-60.
[5] 赵鹏. 嵌入式Linux音频驱动及简单播放器的设计与实现[D].长春:吉林大学,2009.
[6] 丁德红,刘亚波. 嵌入式系统中的I2S音频接口技术[J]. 单片机与嵌入式系统应用,2009(2):25-28.
[7] 房国志,马传龙. Linux中I2S总线声卡驱动的研究[J]. 科技创新导报,2009(2):5-7.
[8] 庄海军. 基于S3C2410的I2S音频总线研究及其驱动实现[J]. 淮阴工学院学报,2008,17(5):72-76.
刘伍洋(研究生),主要研究内容为嵌入式底层软件开发。