<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > s3c2440的IIS應(yīng)用——放音與錄音

          s3c2440的IIS應(yīng)用——放音與錄音

          作者: 時間:2016-11-19 來源:網(wǎng)絡(luò) 收藏
          IIS(Inter-IC Sound)由飛利浦公司開發(fā),是一種常用的音頻設(shè)備接口,主要用于CD、MD、MP3等設(shè)備。

          s3c2440一共有5個引腳用于IIS:IISDO、IISDI、IISSCLK、IISLRCK和CDCLK。前兩個引腳用于數(shù)字音頻信號的輸出和輸入,另外三個引腳都與音頻信號的頻率有關(guān),可見要用好IIS,就要把信號頻率設(shè)置正確。IISSCLK為串行時鐘,每一個時鐘信號傳送一位音頻信號,因此IISSCLK的頻率=聲道數(shù)×采樣頻率×采樣位數(shù),如采樣頻率fs為44.1kHz,采樣的位數(shù)為16位,聲道數(shù)2個(左、右兩個聲道),則IISSCLK的頻率=32fs=1411.2kHz。IISLRCK為幀時鐘,用于切換左、右聲道,如IISLRCK為高電平表示正在傳輸?shù)氖亲舐暤罃?shù)據(jù),為低電平表示正在傳輸?shù)氖怯衣暤罃?shù)據(jù),因此IISLRCK的頻率應(yīng)該正好等于采樣頻率。由于IIS只負責(zé)數(shù)字音頻信號的傳輸,而要真正實現(xiàn)音頻信號的放、錄,還需要額外的處理芯片(在這里,我們使用的是UDA1341),CDCLK為該芯片提供系統(tǒng)同步時鐘,即編解碼時鐘,主要用于音頻的A/D、D/A采樣時的采樣時鐘,一般CDCLK為256fs或384fs。

          通過以上分析可以發(fā)現(xiàn),采樣頻率fs對頻率的設(shè)置至關(guān)重要。而fs不是任意設(shè)置的,一般基于不同的應(yīng)用場合和聽覺效果,而設(shè)置不同的幾個固定的值,如8kHz、16kHz、22.05kHz、44.1kHz、48kHz、96kHz等。為了使系統(tǒng)得到以fs為基數(shù)的各類時鐘信號,就要重新調(diào)整系統(tǒng)時鐘。s3c2440用于IIS的時鐘源有PCLK和MPLLin,我們這里選擇PCLK作為IIS的時鐘源。PCLK經(jīng)過兩個預(yù)分頻器處理后分別得到IISSCLK、IISLRCK和CDCLK(預(yù)分頻器A得到IISSCLK、IISLRCK,預(yù)分頻器B得到CDCLK)。寄存器IISPSR是IIS預(yù)分頻器寄存器,5~9位是預(yù)分頻器A,0~4位是預(yù)分頻器B,一般來說,這兩個預(yù)分頻器的值N相等,即只要知道一個,另一個也就知道,而這里我們是通過CDCLK來計算預(yù)分頻器B的值N的,即CDCLK=PCLK / (N+1)。PCLK與FCLK有一定的比例關(guān)系,而FCLK又是由輸入頻率Fin得到。在這里,我們?yōu)榱撕喕嬎悖桓淖働CLK與FCLK的比例關(guān)系(即維持在啟動代碼中定義的1:8的關(guān)系),那么由Fin而得到CDCLK一共涉及到四個參數(shù):MDIV、PDIV、SDIV和前面公式中的N,涉及到的寄存器有MPLLCON和IISPSR。因此要得到這四個參數(shù)值,就需要一點耐心地計算,原則是誤差最小,其中需要注意的是,計算的結(jié)果(包括中間過程的結(jié)果)不要溢出,即不要超過32位。例如Fin為12MHz,我們設(shè)置采樣頻率fs=44.1kHz,而CDCLK=384fs=16.9344MHz,那么經(jīng)過計算,最終得到N=3,MDIV=150,PDIV=5,SDIV=0,即IISPSR = (3<<5) | 3;,MPLLCON = (150<<12) | (5<<4) | 0;。

          s3c2440有關(guān)IIS的寄存器除了IISPSR外,還包括IIS控制寄存器IISCON,主要用于控制數(shù)據(jù)傳輸?shù)姆绞?、預(yù)分頻器和IIS接口是否開啟;IIS模式寄存器IISMOD,主要用于設(shè)置IIS的時鐘源、主從方式、接收發(fā)送方式、串行接口方式、每個聲道串行數(shù)據(jù)位數(shù)和各種頻率值;IIS的FIFO接口寄存器IISFCON用于設(shè)置和判斷數(shù)據(jù)傳輸?shù)腇IFO狀態(tài);而寄存器IISFIFO則用于音頻數(shù)據(jù)的傳輸。

          由于s3c2440要實現(xiàn)IIS的錄、放音,還需要UDA1341芯片,因此我們再簡要介紹一下這個芯片的使用。s3c2440與UDA1341之間除了我們前面介紹過的IIS接口相連接外,還有一個稱之為L3總線的連接,用于s3c2440配置UDA1341內(nèi)部的寄存器。由于s3c2440不具備L3總線接口,因此我們是用三個通用IO口來模擬L3,從而實現(xiàn)L3總線的傳輸。UDA1341有兩種模式:地址模式和數(shù)據(jù)傳輸模式。地址模式表示傳輸?shù)氖堑刂沸畔ⅲ母?位永遠是000101,低兩位表示的是傳輸?shù)哪J?,是狀態(tài)模式、數(shù)據(jù)0模式還是數(shù)據(jù)1模式,其中狀態(tài)模式主要用于配置UDA1341的各類初始狀態(tài),數(shù)據(jù)模式主要用于改善音頻輸入、輸出的效果。

          下面我們就給出具體的程序,在這里我們使用的是正常模式來實現(xiàn)數(shù)據(jù)的輸入和輸出的,即不使用DMA模式。首先是實現(xiàn)s3c2440對某一音頻信號數(shù)據(jù)的輸出,即放音。我們事先已知道該音頻信號的各類特性,如采樣頻率、聲道數(shù)、采樣信號的位數(shù)等。

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

          //純音頻信號數(shù)據(jù)數(shù)組
          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總線接口的寫函數(shù)
          //輸入?yún)?shù)data為要寫入的數(shù)據(jù)
          //輸入?yún)?shù)address,為1表示地址模式,為0表示數(shù)據(jù)傳輸模式
          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(數(shù)據(jù)傳輸模式)
          for(i=0;i<10;i++)
          ;//等待一段時間

          //并行數(shù)據(jù)轉(zhuǎn)串行數(shù)據(jù)輸出,以低位在前、高位在后的順序
          for(i=0;i<8;i++)
          {
          if(data & 0x1)// H
          {
          rGPBDAT &= ~L3C;//L3C=L
          rGPBDAT |= L3D;//L3D=H
          for(j=0;j<5;j++)
          ;//等待一段時間
          rGPBDAT |= L3C;//L3C=H
          rGPBDAT |= L3D;//L3D=H
          for(j=0;j<5;j++)
          ;//等待一段時間
          }
          else// L
          {
          rGPBDAT &= ~L3C;//L3C=L
          rGPBDAT &= ~L3D;//L3D=L
          for(j=0;j<5;j++)
          ;//等待一段時間
          rGPBDAT |= L3C;//L3C=H
          rGPBDAT &= ~L3D;//L3D=L
          for(j=0;j<5;j++)
          ;//等待一段時間
          }
          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 + 2,1);//狀態(tài)模式(000101xx+10)
          WriteL3(0x60,0);//0,1,10, 000,0 :狀態(tài)0,復(fù)位

          WriteL3(0x14 + 2,1);//狀態(tài)模式(000101xx+10)
          WriteL3(0x10,0);//0,0,01, 000,0 :狀態(tài)0, 384fs,IIS,no DC-filtering

          WriteL3(0x14 + 2,1);//狀態(tài)模式(000101xx+10)
          WriteL3(0xc1,0);//1,1,0,0, 0,0,01:狀態(tài)1,
          //Gain of DAC 6 dB,Gain of ADC 0dB,ADC non-inverting,
          //DAC non-inverting,Single speed playback,ADC-Off DAC-On

          //配置s3c2440的IIS寄存器
          //預(yù)分頻器為3,所以CDCLK=PCLK/(3+1)=16.928kHz
          rIISPSR = 3<<5|3;
          //無效DMA,輸入空閑,預(yù)分頻器有效
          rIISCON= (0<<5)|(0<<4)|(0<<3)|(1<<2)|(1<<1);
          //PCLK為時鐘源,輸出模式,IIS模式,每個聲道16位,CODECLK=384fs,SCLK=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中的數(shù)據(jù)為16位,深度為32
          //當(dāng)輸出FIFO為空時,一次性向FIFO寫入32個16位數(shù)據(jù)
          for(i=0;i<32;i++)
          {
          rIISFIFO=(buffer[2*i+count])+(buffer[2*i+1+count]<<8);
          }
          count+=64;
          if(count>length)
          flag=0;//音頻數(shù)據(jù)傳輸完,則退出
          }
          }
          rIISCON = 0x0;//關(guān)閉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)
          {
          ;
          }
          }


          上面的程序可以實現(xiàn)簡單的播放內(nèi)存中固有音頻數(shù)據(jù)的功能,下面的程序?qū)崿F(xiàn)了錄制一段音頻數(shù)據(jù),然后再播出的功能。我們用UART來控制錄、放音:當(dāng)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];//用于存放錄制的音頻數(shù)據(jù)
          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;
          }

          …………

          //錄音
          //輸入?yún)?shù)為數(shù)組,輸出參數(shù)為所錄制數(shù)據(jù)的字節(jié)長度
          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 + 2,1);//狀態(tài)模式(000101xx+10)
          WriteL3(0x60,0);//0,1,10, 000,0 :狀態(tài)0,復(fù)位

          WriteL3(0x14 + 2,1);//狀態(tài)模式(000101xx+10)
          WriteL3(0x10,0);//0,0,01, 000,0 :狀態(tài)0, 384fs,IIS,no DC-filtering

          WriteL3(0x14 + 2,1);//狀態(tài)模式(000101xx+10)
          WriteL3(0xa2,0);//1,0,1,0, 0,0,10狀態(tài)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 + 0,1);//DATA0 (000101xx+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 + input 2 X MB

          //配置s3c2440的IIS寄存器
          //預(yù)分頻器為3,所以CDCLK=PCLK/(3+1)=16.928kHz
          rIISPSR = 3<<5|3;
          //無效DMA,輸出空閑,預(yù)分頻器有效
          rIISCON= (0<<5)|(0<<4)|(1<<3)|(0<<2)|(1<<1);
          //PCLK為時鐘源,輸入模式,IIS模式,每個聲道16位,CODECLK=384fs,SCLK=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中的數(shù)據(jù)為16位,深度為32
          //當(dāng)輸入FIFO為滿時,一次性讀取FIFO中的32個16位數(shù)據(jù)
          for(i=0;i<32;i++)
          {
          temp=rIISFIFO;
          record_buffer[count+2*i]=(unsigned char)temp;
          record_buffer[count+2*i+1]=(unsigned char)(temp>>8);
          }
          count+=64;
          if(count>1000000)
          stop=1;//當(dāng)錄制的數(shù)據(jù)超過數(shù)組長度時,退出
          }
          }

          rIISCON=0;//關(guān)閉IIS

          return count;//返回錄制數(shù)據(jù)長度
          }

          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//還沒有錄制音頻數(shù)據(jù)
          {
          while(!(rUTRSTAT0 & 0x2));
          rUTXH0 = 0xff;
          }
          cmd=0;
          break;
          }
          }
          }





          補充:

          應(yīng)大家的要求,我把UDA1341的L3通信協(xié)議詳細介紹一下。

          顧名思義,L3就是line 3(3條線)的意思,它只有L3DATA(數(shù)據(jù)線:用于傳輸數(shù)據(jù))、L3MODE(模式線:用于選擇模式)、L3CLOCK(時鐘線:用于傳輸時鐘)。L3一共有兩個模式:地址模式和數(shù)據(jù)傳輸模式,先傳輸?shù)刂纺J綌?shù)據(jù),再傳輸數(shù)據(jù)模式數(shù)據(jù)。L3MODE為低時是地址模式,L3MODE為高時是數(shù)據(jù)傳輸模式。L3DATA和L3CLOCK相互作用,完成8位數(shù)據(jù)的傳輸,傳輸?shù)捻樞蚴窍鹊臀粩?shù)據(jù),再高位數(shù)據(jù)。

          地址模式是用于選擇設(shè)備和定義目標寄存器,在這種模式下,8位數(shù)據(jù)的含義是:高6位是設(shè)備地址(UDA1341的地址為000101),低兩位是后面數(shù)據(jù)模式下寄存器的類型(00:DATA0,01:DATA1,10:STATUS)。只要沒有再改變地址模式下的數(shù)據(jù),則數(shù)據(jù)模式下的數(shù)據(jù)始終是傳輸?shù)缴弦粋€地址模式所定義的寄存器內(nèi)。

          在傳輸數(shù)據(jù)模式下,STATUS是用于設(shè)置復(fù)位,系統(tǒng)時鐘頻率、數(shù)據(jù)輸入模式、DC濾波等內(nèi)容。DATA0分為直接尋址模式和擴展尋址模式,直接尋址模式是直接進行模式的控制,包括音量、靜音等等,而擴展尋址模式是在直接尋址模式下先設(shè)置3位擴展地址,再在直接尋址模式下設(shè)置5位擴展數(shù)據(jù)。在DATA1下,可以讀取到被檢測峰值。至于具體的DATA0、DATA1、STATUS下,每一位數(shù)據(jù)具體的含義,還請自己查閱手冊。


          評論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();