设为首页 收藏本站
查看: 2323|回复: 0

[经验分享] s3c2440的IIS应用——放音与录音

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2015-11-15 10:38:08 | 显示全部楼层 |阅读模式

      IISInter-IC Sound)由飞利浦公司开发,是一种常用的音频设备接口,主要用于CDMDMP3等设备。

      s3c2440一共有5个引脚用于IISIISDOIISDIIISSCLKIISLRCKCDCLK。前两个引脚用于数字音频信号的输出和输入,另外三个引脚都与音频信号的频率有关,可见要用好IIS,就要把信号频率设置正确。IISSCLK为串行时钟,每一个时钟信号传送一位音频信号,因此IISSCLK的频率=声道数×采样频率×采样位数,如采样频率fs44.1kHz,采样的位数为16位,声道数2个(左、右两个声道),则IISSCLK的频率=32fs1411.2kHzIISLRCK为帧时钟,用于切换左、右声道,如IISLRCK为高电平表示正在传输的是左声道数据,为低电平表示正在传输的是右声道数据,因此IISLRCK的频率应该正好等于采样频率。由于IIS只负责数字音频信号的传输,而要真正实现音频信号的放、录,还需要额外的处理芯片(在这里,我们使用的是UDA1341),CDCLK为该芯片提供系统同步时钟,即编解码时钟,主要用于音频的A/DD/A采样时的采样时钟,一般CDCLK256fs384fs

      通过以上分析可以发现,采样频率fs对频率的设置至关重要。而fs不是任意设置的,一般基于不同的应用场合和听觉效果,而设置不同的几个固定的&#20540;,如8kHz16kHz22.05kHz44.1kHz48kHz96kHz等。为了使系统得到以fs为基数的各类时钟信号,就要重新调整系统时钟。s3c2440用于IIS的时钟源有PCLKMPLLin,我们这里选择PCLK作为IIS的时钟源。PCLK经过两个预分频器处理后分别得到IISSCLKIISLRCKCDCLK(预分频器A得到IISSCLKIISLRCK,预分频器B得到CDCLK)。寄存器IISPSRIIS预分频器寄存器,5~9位是预分频器A0~4位是预分频器B,一般来说,这两个预分频器的&#20540;N相等,即只要知道一个,另一个也就知道,而这里我们是通过CDCLK来计算预分频器B的&#20540;N的,即CDCLKPCLK/ (N1)PCLKFCLK有一定的比例关系,而FCLK又是由输入频率Fin得到。在这里,我们为了简化计算,不改变PCLKFCLK的比例关系(即维持在启动代码中定义的1:8的关系),那么由Fin而得到CDCLK一共涉及到四个参数:MDIVPDIVSDIV和前面公式中的N,涉及到的寄存器有MPLLCONIISPSR。因此要得到这四个参数&#20540;,就需要一点耐心地计算,原则是误差最小,其中需要注意的是,计算的结果(包括中间过程的结果)不要溢出,即不要超过32位。例如Fin12MHz,我们设置采样频率fs44.1kHz,而CDCLK384fs16.9344MHz,那么经过计算,最终得到N3MDIV150PDIV5SDIV0,即IISPSR= (3<<5) | 3;MPLLCON = (150<<12) | (5<<4) | 0;

      s3c2440有关IIS的寄存器除了IISPSR外,还包括IIS控制寄存器IISCON,主要用于控制数据传输的方式、预分频器和IIS接口是否开启;IIS模式寄存器IISMOD,主要用于设置IIS的时钟源、主从方式、接收发送方式、串行接口方式、每个声道串行数据位数和各种频率&#20540;;IISFIFO接口寄存器IISFCON用于设置和判断数据传输的FIFO状态;而寄存器IISFIFO则用于音频数据的传输。

      由于s3c2440要实现IIS的录、放音,还需要UDA1341芯片,因此我们再简要介绍一下这个芯片的使用。s3c2440UDA1341之间除了我们前面介绍过的IIS接口相连接外,还有一个称之为L3总线的连接,用于s3c2440配置UDA1341内部的寄存器。由于s3c2440不具备L3总线接口,因此我们是用三个通用IO口来模拟L3,从而实现L3总线的传输。UDA1341有两种模式:地址模式和数据传输模式。地址模式表示传输的是地址信息,它的高6位永远是000101,低两位表示的是传输的模式,是状态模式、数据0模式还是数据1模式,其中状态模式主要用于配置UDA1341的各类初始状态,数据模式主要用于改善音频输入、输出的效果。

      下面我们就给出具体的程序,在这里我们使用的是正常模式来实现数据的输入和输出的,即不使用DMA模式。首先是实现s3c2440对某一音频信号数据的输出,即放音。我们事先已知道该音频信号的各类特性,如采样频率、声道数、采样信号的位数等。

……  ……
//L3接口
#define L3C (1<<4)             //GPB4 = L3CLOCK
#define L3D (1<<3)            //GPB3 = L3DATA
#define L3M (1<<2)            //GPB2 = L3MODE

//纯音频信号数据数组
unsigned char music[ ] = {
0xB8, 0xFF, 0xBA, 0xFF, 0xBF, 0xFF, 0xC0, 0xFF, 0xD4, 0xFF, 0xD3, 0xFF, 0xF2, 0xFF, 0xED, 0xFF,
0x0E, 0x00, 0x05, 0x00, 0x1C, 0x00, 0x0F, 0x00, 0x15, 0x00, 0x06, 0x00, 0xFC, 0xFF, 0xEC, 0xFF,
……  ……
}

//L3总线接口的写函数
//输入参数data为要写入的数据
//输入参数address,为1表示地址模式,为0表示数据传输模式
static void WriteL3(U8 data,U8 address)
{
      int i,j;
      if(address == 1)
             rGPBDAT = rGPBDAT & ~(L3D | L3M | L3C) | L3C;        //L3D=L, L3M=L(地址模式), L3C=H
      else
             rGPBDAT = rGPBDAT & ~(L3D | L3M | L3C) | (L3C | L3M);         //L3M=H(数据传输模式)
      for(i=0;i<10;i&#43;&#43;)
             ;             //等待一段时间
      
      //并行数据转串行数据输出,以低位在前、高位在后的顺序
      for(i=0;i<8;i&#43;&#43;)     
      {
             if(data & 0x1)                      // H
             {
                    rGPBDAT &= ~L3C;            //L3C=L
                    rGPBDAT |= L3D;                //L3D=H            
                    for(j=0;j<5;j&#43;&#43;)
;                   //等待一段时间
                    rGPBDAT |= L3C;                //L3C=H
                    rGPBDAT |= L3D;                //L3D=H
                    for(j=0;j<5;j&#43;&#43;)
;                   //等待一段时间
              }
             else                       // L
             {
                    rGPBDAT &= ~L3C;            //L3C=L
                    rGPBDAT &= ~L3D;            //L3D=L
                    for(j=0;j<5;j&#43;&#43;)
;                   //等待一段时间
                    rGPBDAT |= L3C;                //L3C=H
                    rGPBDAT &= ~L3D;            //L3D=L
                    for(j=0;j<5;j&#43;&#43;)
;                   //等待一段时间         
             }
             data >>= 1;
      }
      rGPBDAT = rGPBDAT & ~(L3D | L3M | L3C) | (L3C | L3M);         //L3M=H,L3C=H
}

//放音
void playsound(unsigned char *buffer, int length)
{
      int count,i;
      char flag;
      
      rGPBDAT = rGPBDAT & ~(L3M|L3C|L3D) |(L3M|L3C); //L3开始传输:L3M=H, L3C=H

      //配置UDA1341
      WriteL3(0x14 &#43; 2,1);            //状态模式(000101xx&#43;10)
      WriteL3(0x60,0);          //0,1,10, 000,0 :状态0,复位
      
      WriteL3(0x14 &#43; 2,1);            //状态模式 (000101xx&#43;10)
      WriteL3(0x10,0);          //0,0,01, 000,0 :状态0, 384fs,IIS,no DC-filtering
      
      WriteL3(0x14 &#43; 2,1);     //状态模式 (000101xx&#43;10)
      WriteL3(0xc1,0);           //1,1,0,0, 0,0,01:状态1,
//Gain of DAC 6 dB,Gain of ADC 0dB,ADC non-inverting,
//DAC non-inverting,Single speed playback,ADC-Off DAC-On
      
      //配置s3c2440IIS寄存器
//预分频器为3,所以CDCLK=PCLK/(3&#43;1)=16.928kHz
rIISPSR = 3<<5|3;
      //无效DMA,输入空闲,预分频器有效
rIISCON = (0<<5)|(0<<4)|(0<<3)|(1<<2)|(1<<1);   
      //PCLK为时钟源,输出模式,IIS模式,每个声道16位,CODECLK=384fsSCLK=32fs
      rIISMOD  = (0<<9)|(0<<8)|(2<<6)|(0<<5)|(0<<4)|(1<<3)|(1<<2)|(1<<0);     
      rIISFCON = (0<<15)|(1<<13);           //输出FIFO正常模式,输出FIFO使能
      
      flag=1;
      count=0;
      //开启IIS
      rIISCON |= 0x1;
      while(flag)
      {
      
             if((rIISCON & (1<<7))==0)               //检查输出FIFO是否为空
             {     
                    //FIFO中的数据为16位,深度为32
                    //当输出FIFO为空时,一次性向FIFO写入3216位数据
for(i=0;i<32;i&#43;&#43;)
                    {
                           rIISFIFO=(buffer[2*i&#43;count])&#43;(buffer[2*i&#43;1&#43;count]<<8);
                    }                  
count&#43;=64;
                    if(count>length)
                           flag=0;                  //音频数据传输完,则退出
             }
      }
      rIISCON = 0x0;            //关闭IIS
}

void Main(void)
{
      
      //配置MPLL
      //fs=44.1kHz,CODECLK=384fs=16.9344MHz
      //不改变CLKDIVN,所以PCLK=FCLK/8
      //MPLLCON:MDIV=150,PDIV=5,SDIV=0,所以FCLK=541.7143MHz,PCLK=67.714MHz
      rMPLLCON = (150<<12) | (5<<4) | 0;
      
//配置L3接口总线,GPB2:L3MODE,GPB3:L3DATA, GPB4:L3CLOCK
rGPBCON = 0x015550;              //输出
rGPBUP = 0x7ff;              //上拉无效
rGPBDAT = 0x1e4;
   
//配置IIS接口
rGPEUP = rGPEUP & ~(0x1f) | 0x1f;        //上拉无效,GPE[4:0] 1 1111
      rGPECON = rGPECON & ~(0x3ff) | 0x2aa;

playsound(music,sizeof(music));
      
      while(1)
      {
             ;
}
}


      上面的程序可以实现简单的播放内存中固有音频数据的功能,下面的程序实现了录制一段音频数据,然后再播出的功能。我们用UART来控制录、放音:当s3c2440接收到0x51时录音,接收到0x55时停止录音,接收到0x66时放音。

……  ……
#define L3C (1<<4)             //GPB4 = L3CLOCK
#define L3D (1<<3)             //GPB3 = L3DATA
#define L3M (1<<2)             //GPB2 = L3MODE

unsigned char record_buffer[1000000];                    //用于存放录制的音频数据
char stop,cmd;

//UART中断
void __irq uartISR(void)
{
      char ch;
      rSUBSRCPND |= 0x1;
      rSRCPND |= 0x1<<28;
      rINTPND |= 0x1<<28;
      ch=rURXH0;
      
      switch(ch)
      {
      case 0x51:                    //开始录音
             cmd=0x01;
             break;
      case 0x55:                    //停止录音
             stop=1;                  //置退出录音标志
             break;
      case 0x66:                    //放音
             cmd=0x03;
             break;
      }
      rUTXH0=ch;
}

……  ……

//录音
//输入参数为数组,输出参数为所录制数据的字节长度
int record(unsigned char * buffer)
{
      int count,i;
      unsigned short temp;
      
      rGPBDAT = rGPBDAT & ~(L3M|L3C|L3D) |(L3M|L3C); //L3开始传输: L3M=H, L3C=H

      //配置UDA1341
      WriteL3(0x14 &#43; 2,1);            //状态模式(000101xx&#43;10)
      WriteL3(0x60,0);          //0,1,10, 000,0 :状态0,复位
      
      WriteL3(0x14 &#43; 2,1);            //状态模式 (000101xx&#43;10)
      WriteL3(0x10,0);          //0,0,01, 000,0 :状态0, 384fs,IIS,no DC-filtering
      
      WriteL3(0x14 &#43; 2,1);     //状态模式 (000101xx&#43;10)
      WriteL3(0xa2,0);           //1,0,1,0, 0,0,10 状态1
                                         //Gain of DAC 0 dB,Gain of ADC 6dB,ADC non-inverting,
//DAC non-inverting,Single speed playback,ADC-On DAC-Off
     
      WriteL3(0x14 &#43; 0,1);     //DATA0 (000101xx&#43;00)
      WriteL3(0x7b,0);          //01,11 10,11 : Data0, Bass Boost 18~24dB, Treble 6dB

      WriteL3(0xc4,0);           //1100 0,100 : Extended addr(3bits), 100
      WriteL3(0xf0,0);           //111,1 00,00 : DATA0, Enable AGC, 00, input amplifier gain channel 2 (2bits)

      WriteL3(0xc0,0);           //1100 0,000 : Extended addr(3bits), 000         
      WriteL3(0xe0,0);           //111, 00000 : MA = 0dB
      WriteL3(0xc1,0);           //1100 0,001 : Extended addr(3bits), 001
      WriteL3(0xe0,0);           //111, 00000 : MB = 0dB
            
      WriteL3(0xc2,0);           //1100 0,010 : Extended addr(3bits), 010
      WriteL3(0xf9,0);           //111,1 10,11 : DATA0, MIC Amplifier Gain 27dB, input 1 X MA &#43; input 2 X MB

      //配置s3c2440IIS寄存器
//预分频器为3,所以CDCLK=PCLK/(3&#43;1)=16.928kHz
      rIISPSR = 3<<5|3;
      //无效DMA,输出空闲,预分频器有效
rIISCON = (0<<5)|(0<<4)|(1<<3)|(0<<2)|(1<<1);   
      //PCLK为时钟源,输入模式,IIS模式,每个声道16位,CODECLK=384fsSCLK=32fs
rIISMOD = (0<<9)|(0<<8)|(1<<6)|(0<<5)|(0<<4)|(1<<3)|(1<<2)|(1<<0);
      rIISFCON = (0<<14)|(1<<12);           //输入FIFO正常模式,输入FIFO使能
      
      count=0;

      //开启IIS
      rIISCON |= 0x1;
      while(stop==0)
      {
      
             if((rIISCON & (1<<6))==0)               //检查输入FIFO是否为满
             {     
                    //FIFO中的数据为16位,深度为32
                    //当输入FIFO为满时,一次性读取FIFO中的3216位数据
                    for(i=0;i<32;i&#43;&#43;)
                    {
                           temp=rIISFIFO;
                           record_buffer[count&#43;2*i]=(unsigned char)temp;
                           record_buffer[count&#43;2*i&#43;1]=(unsigned char)(temp>>8);   
                    }
                    count&#43;=64;
                    if(count>1000000)
                           stop=1;                  //当录制的数据超过数组长度时,退出
             }  
      }
      
      rIISCON=0;                 //关闭IIS

      return count;                 //返回录制数据长度
}

void Main(void)
{
      
      char play;
      int bufferlength;

……  ……

      //由于改变了PCLK,所以需要重新计算UART波特率因子
rUBRDIV0 = 36;
   
……  ……

      stop=0;
      cmd=0;
      play=0;
      
      while(1)
      {
             switch(cmd)
             {
                    case 0x01:                    //录音
                           bufferlength=record(record_buffer);
                           play=1;          //置录音标志
                           cmd=0;
                           break;
                    case 0x03:                    //放音
                           if(play)
                                  playsound(record_buffer,bufferlength);
                           else                //还没有录制音频数据
                           {
                                  while(!(rUTRSTAT0 & 0x2));
                                  rUTXH0 = 0xff;
                           }
                           cmd=0;
                           break;
             }
}
}


  


补充:

应大家的要求,我把UDA1341L3通信协议详细介绍一下。

      顾名思义,L3就是line 33条线)的意思,它只有L3DATA(数据线:用于传输数据)、L3MODE(模式线:用于选择模式)、L3CLOCK(时钟线:用于传输时钟)。L3一共有两个模式:地址模式和数据传输模式,先传输地址模式数据,再传输数据模式数据。L3MODE为低时是地址模式,L3MODE为高时是数据传输模式。L3DATAL3CLOCK相互作用,完成8位数据的传输,传输的顺序是先低位数据,再高位数据。

地址模式是用于选择设备和定义目标寄存器,在这种模式下,8位数据的含义是:高6位是设备地址(UDA1341的地址为000101),低两位是后面数据模式下寄存器的类型(00DATA001DATA110STATUS)。只要没有再改变地址模式下的数据,则数据模式下的数据始终是传输到上一个地址模式所定义的寄存器内。

在传输数据模式下,STATUS是用于设置复位,系统时钟频率、数据输入模式、DC滤波等内容。DATA0分为直接寻址模式和扩展寻址模式,直接寻址模式是直接进行模式的控制,包括音量、静音等等,而扩展寻址模式是在直接寻址模式下先设置3位扩展地址,再在直接寻址模式下设置5位扩展数据。在DATA1下,可以读取到被检测峰&#20540;。至于具体的DATA0DATA1STATUS下,每一位数据具体的含义,还请自己查阅手册。


         版权声明:本文为博主原创文章,未经博主允许不得转载。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-139438-1-1.html 上篇帖子: IIS发布程序 下篇帖子: win7安装IIS7(加简单配置)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表