(轉)Cortex-M3 (NXP LPC1788)之IIS應用--UDA1380進行音頻數據播放


LPC1788發送到I2S總線上的音頻數據要通過音頻解碼芯片才能輸出模擬音頻信號。開發板上使用的是UDA1380,對它的寄存器的配置可以通過L3總線或者I2C總線進行,這里使用I2C總線進行控制,對於I2C總線的操作可以參考之前I2C的介紹。UDA1380的寄存器主要分成3類,系統控制、插值濾波(interpolation filter)、抽取濾波(decimator filter)。插值濾波和DAC轉換有關,用於控制控制聲音的輸出參數。抽取濾波和ADC有關,用於控制對音頻的采樣。寄存器的地址和功能如圖1所示。

圖1:UDA1380寄存器地址和功能

        根據圖1的紅色標記中的內容,可以知道兩個濾波器的正常使用需要一個128fs的clock,這個時鍾可以通過SYSCLK引腳或者WSI的信號獲得。在硬件連接上,通過將LPC1788的MCLK輸出的時鍾,連接到UDA1380的SYSCLK引腳。因此,我們需要配置I2S的發送模式控制寄存器I2STXMODE,使能TX_REF在MCLK輸出,使UDA1380內部產生一個濾波器需要的時鍾。

        程序中我們通過I2S發送一段音頻數據,該數據是我從WAV格式的文件中去掉WAV頭格式后得到的一個純音頻數據數組。該WAV音頻為16位雙通道 采樣頻率為44.1KHZ。LPC1788將該數組發送到I2S總線,UDA1380讀取該數據進行聲音的輸出。程序如下

 

  1. #include "i2c.h" 
  2. #include "audio.h" 
  3.  
  4. #define rI2SDAO         (*(volatile unsigned *)(0x400A8000)) 
  5. #define rI2STXFIFO      (*(volatile unsigned *)(0x400A8008)) 
  6. #define rI2STXRATE      (*(volatile unsigned *)(0x400A8020)) 
  7. #define rI2STXBITRATE   (*(volatile unsigned *)(0x400A8028)) 
  8. #define rI2STXMODE      (*(volatile unsigned *)(0x400A8030)) 
  9.  
  10. #define rI2SDMA1        (*(volatile unsigned *)(0x400A8014)) 
  11. #define rI2SDMA2        (*(volatile unsigned *)(0x400A8018)) 
  12. #define rI2SSTATE       (*(volatile unsigned *)(0x400A8010)) 
  13. #define rI2SIRQ         (*(volatile unsigned *)(0x400A801C)) 
  14.  
  15. #define rI2SDAI         (*(volatile unsigned *)(0x400A8004)) 
  16. #define rI2SRXFIFO      (*(volatile unsigned *)(0x400A800C)) 
  17. #define rI2SRXRATE      (*(volatile unsigned *)(0x400A8024)) 
  18. #define rI2SRXBITRATE   (*(volatile unsigned *)(0x400A802C)) 
  19. #define rI2SRXMODE      (*(volatile unsigned *)(0x400A8034)) 
  20.  
  21. #define rIOCON_P0_07    (*(volatile unsigned *)(0x4002C01C)) 
  22. #define rIOCON_P0_08    (*(volatile unsigned *)(0x4002C020)) 
  23. #define rIOCON_P0_09    (*(volatile unsigned *)(0x4002C024)) 
  24. #define rIOCON_P1_16    (*(volatile unsigned *)(0x4002C0C0)) 
  25.  
  26. #define UDA1380_ADDRESS 0x1A 
  27.  
  28. void Uda1380_WriteData(unsigned char reg, unsigned shortint data) 
  29.     unsigned char config[3]; 
  30.      
  31.     config[0] = reg; 
  32.     config[1] = (data >> 8) & 0xFF;    //MS 
  33.     config[2] = data&0xFF;             //LS 
  34.      
  35.      
  36.     I2C0_MasterTransfer(UDA1380_ADDRESS, config, sizeof(config), 0, 0); 
  37.      
  38.     I2C0_MasterTransfer(UDA1380_ADDRESS, config, 1, &config[1], 2);     //校驗寫入的數據是否正確 
  39.     if((config[1]<<8|config[2]) != data) 
  40.     { 
  41.         while(1);   //寫入和讀出的數據不一致 
  42.     } 
  43.  
  44. void Uda1380_config() 
  45.     I2C0_Init(); 
  46.      
  47.     Uda1380_WriteData(0x7F, 0x0);         //restore L3-default values 
  48.     Uda1380_WriteData(0x01, 0x0);         //數據格式為標准的I2S格式 
  49.      
  50.     Uda1380_WriteData(0x13, 0x0);         //配置音頻的輸出 
  51.     Uda1380_WriteData(0x14, 0x0);            
  52.  
  53.     Uda1380_WriteData(0x00, 0x2|0x1<<8|0x1<<9);     //使能DAC的時鍾,選擇使用SYSCLK產生128fs的時鍾 
  54.     Uda1380_WriteData(0x02,0x1<<15|0x1<<13|0x1<<10|0x1<<8); //使能DAC 電源 
  55.      
  56.  
  57. int main(void
  58. {    
  59.     unsigned int count=0, i; 
  60.     unsigned char flag=1; 
  61.      
  62.     rIOCON_P0_07 = (rIOCON_P0_07&(~0x3))|0x1;   //I2S_TX_SCK 
  63.     rIOCON_P0_08 = (rIOCON_P0_08&(~0x3))|0x1;   //I2S_TX_WS 
  64.     rIOCON_P0_09 = (rIOCON_P0_09&(~0x3))|0x1;   //I2S_TX_SDA 
  65.     rIOCON_P1_16 = (rIOCON_P1_16&(~0x3))|0x2;   //I2SMCLK 
  66.     rPCONP |= 0x1<<27; 
  67.      
  68.     rI2SDAO = (16 - 1)<<6 | 0x1<<4 | 0x1<<3  |0x1;  //16位, 立體音, 禁止發送 
  69.  
  70.     rI2STXMODE |= 0x1<<3;       //使能MCLK輸出,使TX_REF輸出到UDA1380的SYSCLK引腳 
  71.      
  72.     rI2STXRATE = 0x1<<8|0x1;    //配置分數速率寄存器 得到TX_REF=CCLK/(1/1)/2 
  73.     rI2STXBITRATE = CCLK/2/(44100*2*16) - 1;  //44.1KHZ采樣16位 
  74.      
  75.     for(i = 0; i <0x1000000; i++);  //延時 等待UDA1380內部通過SYSCLK產生穩定的128fs提供插值濾波和抽取濾波使用 
  76.      
  77.     Uda1380_config(); 
  78.      
  79.     rI2SDAO &= ~ (1<<4); 
  80.     rI2SDAO &= ~ (1<<3); 
  81.     rI2SDAO &= ~ (1<<15);   //啟動I2S數據傳輸 
  82.      
  83.     while(flag) 
  84.     { 
  85.         if(((rI2SSTATE>>16)&0xFF)<=4)       //如果發送FIFO中的數據小於或等於4個字 
  86.         { 
  87.             for(i=0; i<8-(((rI2SSTATE>>16)&0xFF)); i++)     //將FIFO填充到8個字 
  88.             { 
  89.                 rI2STXFIFO = *(unsigned int *)(audio + count);  //轉換成int類型的指針,從指針指向的位置讀取32位數據 
  90.                  
  91.                 count+=4;               //讀取一個字,相當於讀取char類型數組中的4個元素 
  92.                 
  93.                 if(count>=sizeof(audio)) //數組中的數據發送完 
  94.                 { 
  95.                     flag = 0; 
  96.                     break
  97.                 } 
  98.             } 
  99.         } 
  100.     } 
  101.      
  102.     rI2SDAO |= 0x1<<3|0x1<<4;   //停止I2S傳輸 
  103.      
  104.     return 0; 
#include "i2c.h"
#include "audio.h"

#define rI2SDAO         (*(volatile unsigned *)(0x400A8000))
#define rI2STXFIFO      (*(volatile unsigned *)(0x400A8008))
#define rI2STXRATE      (*(volatile unsigned *)(0x400A8020))
#define rI2STXBITRATE   (*(volatile unsigned *)(0x400A8028))
#define rI2STXMODE      (*(volatile unsigned *)(0x400A8030))

#define rI2SDMA1        (*(volatile unsigned *)(0x400A8014))
#define rI2SDMA2        (*(volatile unsigned *)(0x400A8018))
#define rI2SSTATE       (*(volatile unsigned *)(0x400A8010))
#define rI2SIRQ         (*(volatile unsigned *)(0x400A801C))

#define rI2SDAI         (*(volatile unsigned *)(0x400A8004))
#define rI2SRXFIFO      (*(volatile unsigned *)(0x400A800C))
#define rI2SRXRATE      (*(volatile unsigned *)(0x400A8024))
#define rI2SRXBITRATE   (*(volatile unsigned *)(0x400A802C))
#define rI2SRXMODE      (*(volatile unsigned *)(0x400A8034))

#define rIOCON_P0_07	(*(volatile unsigned *)(0x4002C01C))
#define rIOCON_P0_08	(*(volatile unsigned *)(0x4002C020))
#define rIOCON_P0_09	(*(volatile unsigned *)(0x4002C024))
#define rIOCON_P1_16	(*(volatile unsigned *)(0x4002C0C0))

#define UDA1380_ADDRESS 0x1A

void Uda1380_WriteData(unsigned char reg, unsigned short int data)
{
    unsigned char config[3];
    
    config[0] = reg;
    config[1] = (data >> 8) & 0xFF;    //MS
    config[2] = data&0xFF;             //LS
    
    
    I2C0_MasterTransfer(UDA1380_ADDRESS, config, sizeof(config), 0, 0);
    
    I2C0_MasterTransfer(UDA1380_ADDRESS, config, 1, &config[1], 2);     //校驗寫入的數據是否正確
    if((config[1]<<8|config[2]) != data)
    {
        while(1);   //寫入和讀出的數據不一致
    }
}

void Uda1380_config()
{
    I2C0_Init();
    
    Uda1380_WriteData(0x7F, 0x0);         //restore L3-default values
    Uda1380_WriteData(0x01, 0x0);         //數據格式為標准的I2S格式
    
    Uda1380_WriteData(0x13, 0x0);         //配置音頻的輸出
    Uda1380_WriteData(0x14, 0x0);           

    Uda1380_WriteData(0x00, 0x2|0x1<<8|0x1<<9);     //使能DAC的時鍾,選擇使用SYSCLK產生128fs的時鍾
    Uda1380_WriteData(0x02,0x1<<15|0x1<<13|0x1<<10|0x1<<8); //使能DAC 電源
    
}

int main(void)
{   
    unsigned int count=0, i;
    unsigned char flag=1;
    
    rIOCON_P0_07 = (rIOCON_P0_07&(~0x3))|0x1;   //I2S_TX_SCK
    rIOCON_P0_08 = (rIOCON_P0_08&(~0x3))|0x1;   //I2S_TX_WS
    rIOCON_P0_09 = (rIOCON_P0_09&(~0x3))|0x1;   //I2S_TX_SDA
    rIOCON_P1_16 = (rIOCON_P1_16&(~0x3))|0x2;   //I2SMCLK
    rPCONP |= 0x1<<27;
    
    rI2SDAO = (16 - 1)<<6 | 0x1<<4 | 0x1<<3  |0x1;  //16位, 立體音, 禁止發送

    rI2STXMODE |= 0x1<<3;       //使能MCLK輸出,使TX_REF輸出到UDA1380的SYSCLK引腳
    
    rI2STXRATE = 0x1<<8|0x1;    //配置分數速率寄存器 得到TX_REF=CCLK/(1/1)/2
    rI2STXBITRATE = CCLK/2/(44100*2*16) - 1;  //44.1KHZ采樣16位
    
    for(i = 0; i <0x1000000; i++);  //延時 等待UDA1380內部通過SYSCLK產生穩定的128fs提供插值濾波和抽取濾波使用
    
    Uda1380_config();
    
    rI2SDAO &= ~ (1<<4);
    rI2SDAO &= ~ (1<<3);
    rI2SDAO &= ~ (1<<15);   //啟動I2S數據傳輸
    
    while(flag)
    {
        if(((rI2SSTATE>>16)&0xFF)<=4)       //如果發送FIFO中的數據小於或等於4個字
        {
            for(i=0; i<8-(((rI2SSTATE>>16)&0xFF)); i++)     //將FIFO填充到8個字
            {
                rI2STXFIFO = *(unsigned int *)(audio + count);  //轉換成int類型的指針,從指針指向的位置讀取32位數據
                
                count+=4;               //讀取一個字,相當於讀取char類型數組中的4個元素
               
                if(count>=sizeof(audio)) //數組中的數據發送完
                {
                    flag = 0;
                    break;
                }
            }
        }
    }
    
    rI2SDAO |= 0x1<<3|0x1<<4;   //停止I2S傳輸
    
    return 0;
}

下面對程序需要注意的做下說明:

 

1,i2c.h中是上一篇介紹I2C總線中所用的函數,aduio.h中存放的是音頻數據的數組,const unsigned char audio[]={0,0,0,0,0,......................

2,I2C總線每次發送的數據為1個字節,而UDA1380的寄存器為16為,因此我們先發送高字節然后再發送低字節,具體的時序可以參考UDA1380的數據手冊。

3,程序中配置發送控制寄存器I2STXMODE使能了MCLK輸出TX_REF的時鍾到UDA1380的SYSCLK引腳,而UDA1380中配置成使用該時鍾產生內部濾波器需要的128fs的時鍾。在圖1中標志中說明run at ....因此在程序中配置UDA1380之前,使用了一個for延時,用於等待UDA1380內部產生穩定的128fs時鍾。只有這樣才能正確的配置0x10之后的濾波器相關寄存器。否則對0x10之后的濾波器相關寄存器操作會失敗。這點沒有驗證,但是在debug調試的時候可以正常的有聲音輸出,但是下載到板子上運行,則沒有效果。如果去掉for循環延時效果也不正常發音。如果不使能MCLK輸出,則寫0x13寄存器的值不會成功,讀取該寄存器的值永遠都是其默認值。因此推測和UDA1380的SYSCLK產生內部濾波器使用的128fs時鍾有關。

      參考了linux內核里面的uda1380的驅動,其中也提到了配置0x10以后的濾波器相關寄存器要滿足條件

  1. 107         /* the interpolator & decimator regs must only be written when the 
  2. 108          * codec DAI is active. 
  3. 109          */ 
107         /* the interpolator & decimator regs must only be written when the
108          * codec DAI is active.
109          */

 

 

誰有這方面的經驗,希望多指教!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM