新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > Cortex-M3 (NXP LPC1788)之IIS應(yīng)用--UDA1380進(jìn)行音頻數(shù)據(jù)播放

Cortex-M3 (NXP LPC1788)之IIS應(yīng)用--UDA1380進(jìn)行音頻數(shù)據(jù)播放

作者: 時間:2016-11-19 來源:網(wǎng)絡(luò) 收藏
LPC1788發(fā)送到I2S總線上的音頻數(shù)據(jù)要通過音頻解碼芯片才能輸出模擬音頻信號。開發(fā)板上使用的是UDA1380,對它的寄存器的配置可以通過L3總線或者I2C總線進(jìn)行,這里使用I2C總線進(jìn)行控制,對于I2C總線的操作可以參考之前I2C的介紹。UDA1380的寄存器主要分成3類,系統(tǒng)控制、插值濾波(interpolation filter)、抽取濾波(decimator filter)。插值濾波和DAC轉(zhuǎn)換有關(guān),用于控制控制聲音的輸出參數(shù)。抽取濾波和ADC有關(guān),用于控制對音頻的采樣。寄存器的地址和功能如圖1所示。

本文引用地址:http://butianyuan.cn/article/201611/318458.htm

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

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

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

  1. #include"i2c.h"
  2. #include"audio.h"
  3. #definerI2SDAO(*(volatileunsigned*)(0x400A8000))
  4. #definerI2STXFIFO(*(volatileunsigned*)(0x400A8008))
  5. #definerI2STXRATE(*(volatileunsigned*)(0x400A8020))
  6. #definerI2STXBITRATE(*(volatileunsigned*)(0x400A8028))
  7. #definerI2STXMODE(*(volatileunsigned*)(0x400A8030))
  8. #definerI2SDMA1(*(volatileunsigned*)(0x400A8014))
  9. #definerI2SDMA2(*(volatileunsigned*)(0x400A8018))
  10. #definerI2SSTATE(*(volatileunsigned*)(0x400A8010))
  11. #definerI2SIRQ(*(volatileunsigned*)(0x400A801C))
  12. #definerI2SDAI(*(volatileunsigned*)(0x400A8004))
  13. #definerI2SRXFIFO(*(volatileunsigned*)(0x400A800C))
  14. #definerI2SRXRATE(*(volatileunsigned*)(0x400A8024))
  15. #definerI2SRXBITRATE(*(volatileunsigned*)(0x400A802C))
  16. #definerI2SRXMODE(*(volatileunsigned*)(0x400A8034))
  17. #definerIOCON_P0_07(*(volatileunsigned*)(0x4002C01C))
  18. #definerIOCON_P0_08(*(volatileunsigned*)(0x4002C020))
  19. #definerIOCON_P0_09(*(volatileunsigned*)(0x4002C024))
  20. #definerIOCON_P1_16(*(volatileunsigned*)(0x4002C0C0))
  21. #defineUDA1380_ADDRESS0x1A
  22. voidUda1380_WriteData(unsignedcharreg,unsignedshortintdata)
  23. {
  24. unsignedcharconfig[3];
  25. config[0]=reg;
  26. config[1]=(data>>8)&0xFF;//MS
  27. config[2]=data&0xFF;//LS
  28. I2C0_MasterTransfer(UDA1380_ADDRESS,config,sizeof(config),0,0);
  29. I2C0_MasterTransfer(UDA1380_ADDRESS,config,1,&config[1],2);//校驗寫入的數(shù)據(jù)是否正確
  30. if((config[1]<<8|config[2])!=data)
  31. {
  32. while(1);//寫入和讀出的數(shù)據(jù)不一致
  33. }
  34. }
  35. voidUda1380_config()
  36. {
  37. I2C0_Init();
  38. Uda1380_WriteData(0x7F,0x0);//restoreL3-defaultvalues
  39. Uda1380_WriteData(0x01,0x0);//數(shù)據(jù)格式為標(biāo)準(zhǔn)的I2S格式
  40. Uda1380_WriteData(0x13,0x0);//配置音頻的輸出
  41. Uda1380_WriteData(0x14,0x0);
  42. Uda1380_WriteData(0x00,0x2|0x1<<8|0x1<<9);//使能DAC的時鐘,選擇使用SYSCLK產(chǎn)生128fs的時鐘
  43. Uda1380_WriteData(0x02,0x1<<15|0x1<<13|0x1<<10|0x1<<8);//使能DAC電源
  44. }
  45. intmain(void)
  46. {
  47. unsignedintcount=0,i;
  48. unsignedcharflag=1;
  49. rIOCON_P0_07=(rIOCON_P0_07&(~0x3))|0x1;//I2S_TX_SCK
  50. rIOCON_P0_08=(rIOCON_P0_08&(~0x3))|0x1;//I2S_TX_WS
  51. rIOCON_P0_09=(rIOCON_P0_09&(~0x3))|0x1;//I2S_TX_SDA
  52. rIOCON_P1_16=(rIOCON_P1_16&(~0x3))|0x2;//I2SMCLK
  53. rPCONP|=0x1<<27;
  54. rI2SDAO=(16-1)<<6|0x1<<4|0x1<<3|0x1;//16位,立體音,禁止發(fā)送
  55. rI2STXMODE|=0x1<<3;//使能MCLK輸出,使TX_REF輸出到UDA1380的SYSCLK引腳
  56. rI2STXRATE=0x1<<8|0x1;//配置分?jǐn)?shù)速率寄存器得到TX_REF=CCLK/(1/1)/2
  57. rI2STXBITRATE=CCLK/2/(44100*2*16)-1;//44.1KHZ采樣16位
  58. for(i=0;i<0x1000000;i++);//延時等待UDA1380內(nèi)部通過SYSCLK產(chǎn)生穩(wěn)定的128fs提供插值濾波和抽取濾波使用
  59. Uda1380_config();
  60. rI2SDAO&=~(1<<4);
  61. rI2SDAO&=~(1<<3);
  62. rI2SDAO&=~(1<<15);//啟動I2S數(shù)據(jù)傳輸
  63. while(flag)
  64. {
  65. if(((rI2SSTATE>>16)&0xFF)<=4)//如果發(fā)送FIFO中的數(shù)據(jù)小于或等于4個字
  66. {
  67. for(i=0;i<8-(((rI2SSTATE>>16)&0xFF));i++)//將FIFO填充到8個字
  68. {
  69. rI2STXFIFO=*(unsignedint*)(audio+count);//轉(zhuǎn)換成int類型的指針,從指針指向的位置讀取32位數(shù)據(jù)
  70. count+=4;//讀取一個字,相當(dāng)于讀取char類型數(shù)組中的4個元素
  71. if(count>=sizeof(audio))//數(shù)組中的數(shù)據(jù)發(fā)送完
  72. {
  73. flag=0;
  74. break;
  75. }
  76. }
  77. }
  78. }
  79. rI2SDAO|=0x1<<3|0x1<<4;//停止I2S傳輸
  80. return0;
  81. }
下面對程序需要注意的做下說明:

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

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

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

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


  1. 107/*theinterpolator&decimatorregsmustonlybewrittenwhenthe
  2. 108*codecDAIisactive.
  3. 109*/

誰有這方面的經(jīng)驗,希望多指教!




評論


相關(guān)推薦

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

關(guān)閉