I2S(Inter-IC Sound)總線,又稱集成電路內(nèi)置音頻總線,是飛利浦公司為數(shù)字音頻設備之間的音頻數(shù)據(jù)傳輸而制定的一種總線標準。采用了獨立的導線傳輸時鐘與數(shù)據(jù)信號的設計,通過將數(shù)據(jù)和時鐘信號分離,避免了因時差誘發(fā)的失真。
主操作或從操作
9 位可配置線性預分頻器,以達到精確的音頻采樣頻率(8KHz~192KHz)
數(shù)據(jù)幀格式可配置為 16 位、24 位或 32 位
數(shù)據(jù)包幀固定為 16 位(16 位有效數(shù)據(jù))或 32 位(16 位、24 位、32 位有效數(shù)據(jù))
可配置時鐘極性(穩(wěn)定狀態(tài))
發(fā)送模式下具有下溢標志(僅從機),接收模式下具有上溢標志(主/從機)和發(fā)送/接收模式下的幀錯誤標志(僅從機)
用于傳輸和接收的 32 位寄存器為兩個聲道分時復用
數(shù)據(jù)方向始終是 MSB 優(yōu)先
支持 I2S 協(xié)議:
飛利浦標準
MSB 對齊標準(MSB 位向左對齊)
LSB 對齊標準(LSB 位向右對齊)
PCM 標準(具有短幀同步模式、長幀同步模式的兩種方式)
利用 DMA 請求傳輸數(shù)據(jù)(32 位寬)
可配置 MCLK 時鐘輸出來驅(qū)動外部音頻組件,其比率固定在 256×Fs(其中 Fs 為音頻采樣頻率)
1
MM32的I2S總線簡述
MM32F0160的I2S總線與SPI總線復用即SPI_I2S串行外設(串行外設接口與集成電路內(nèi)置音頻總線)。
I2S總線接口與SPI總線接口引腳復用關系如下:
SD:串行數(shù)據(jù)(映射在MOSI引腳上),用于發(fā)送或接收兩次多路數(shù)據(jù)通道(僅在半雙工模式下)。
WS:聲道選擇(映射在NSS引腳上),是主模式控制數(shù)據(jù)的輸出信號,或從模式的輸入。
CK:串行時鐘(映射在SCK引腳上),是主模式串行時鐘的輸出,或從模式串行時鐘的輸入。
MCK:可選的串行時鐘(映射在MISO引腳上),用于驅(qū)動外部音頻組件(僅當外部音頻設備需要時鐘輸入時使用,由主模式提供)。
2
SPI_I2S功能框圖簡介
圖1 SPI_I2S功能框圖
如上圖1所示為SPI_I2S外設的功能框圖,SPI_I2S通過“總線接口邏輯”掛載在APB和DMA總線上,TXREG和RXREG寄存器、主模式控制單元和從模式控制單元、主從選擇控制、收發(fā)控制邏輯以及時鐘生成及控制單元,8Byte的發(fā)送緩沖和8Byte的接收緩沖構成,時鐘控制單元由Spbrg和Pclk提供時鐘。
3
SPI_I2S外設的I2S時鐘預分頻器
I2SCLK時鐘由系統(tǒng)APB時鐘提供,I2S模塊的預分頻器電路結構如下圖2所示:
圖2 SPI_I2S時鐘預分頻器電路結構圖
如上圖2所示,當MCKOE位為‘0’時芯片不需要輸出MCK時鐘,預分頻器直接將I2SCLK分頻到CK;當MCKOE位為‘1’時芯片會輸出MCK時鐘,預分頻器將I2SCLK分頻后得到MCK,然后再經(jīng)過分頻處理才得到CK(分頻倍數(shù)由CHLEN選擇為 4或8)。
音頻采樣率一般常用 192KHz,96 KHz,48 KHz,44.1 KHz,32 KHz,22.05 KHz,16 KHz,11.025KHz,8 KHz。因此可根據(jù) I2S 時鐘分頻器的電路功能式樣,配置寄存器 I2SCFGR中的I2SDIV[8:0]、MCKOE和CHLEN 位來得到期望的音頻采樣率。
I2S 傳輸數(shù)據(jù)時,比特率計算公式如下表1所示(CK 輸出一個時鐘周期對應傳輸 1 比特數(shù)據(jù),因此比特率 = CK頻率FCK)。
表1
音頻采樣率(Fs)和 I2S 比特率的關系由如下的公式定義:
Fs = I2S 比特率/(通道長度×通道數(shù))= FCK /(通道長度×通道數(shù))
注:通道長度,即數(shù)據(jù)包幀長度,可配置為16位或32位;通道數(shù)為左右聲道,值固定為2。
綜上所述,根據(jù)I2SDIV[8: 0]、MCKOE和CHLEN位的配置情況, 得到音頻采樣率與FI2SCLK(APB時鐘頻率)的關系如下表2所示:
表2
4
SPI_I2S外設的I2S接口的音樂播放器
工作原理介紹
基于I2S接口的音樂播放器工作原理框圖如下圖3所示:
圖3 I2S接口的音樂播放器原理框圖
如上圖3所示為I2S接口的音樂播放器工作原理框圖:
MM32F0160作為Host MCU其SPI1接口用于驅(qū)動25WQ80存儲器用于寫入和讀取存儲的音頻文件。
MCU端I2S2(SPI2_I2S2)接口工作在從機模式,MCLK不輸出時鐘。通過PWM輸出12MHz的REF_Clock給NAU88C22音頻編解碼芯片MCLK腳,NAU88C22內(nèi)部PLL合成穩(wěn)定的12.288MHz作為內(nèi)部IMLCK主時鐘。NAU88C22 BCLK輸出bit clock時鐘到MCU端I2S2_CK作為音頻采樣時鐘。I2S2_WS接口即FS用于分時切換左右聲道。I2S2_SD接口即DACIN輸出從25WQ80存儲器讀取的音頻信號流DAC Stream傳輸給NAU88C22音頻編解碼芯片。
MCU端I2C_SDA和I2C_SCL接口用于設置NAU88C22工作模式和參數(shù)。NAU88C22 DAC輸出可以選擇從Speaker PA輸出推喇叭或者從Earphone PA耳機接口輸出推動耳機。
5
I2S接口的GPIO初始化
I2S接口GPIO的初始化代碼如下所示:
voidI2S2_GPIO_Init(void) { GPIO_InitTypeDefGPIO_InitStruct; RCC_GPIO_ClockCmd(GPIOB,ENABLE); GPIO_StructInit(&GPIO_InitStruct); /*PB12I2S2_WS*/ GPIO_InitStruct.GPIO_Pin=GPIO_Pin_12; GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP; GPIO_Init(GPIOB,&GPIO_InitStruct); /*PB13I2S2_CK*/ GPIO_InitStruct.GPIO_Pin=GPIO_Pin_13; GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP; GPIO_Init(GPIOB,&GPIO_InitStruct); /*PB14I2S2_MCLK*/ GPIO_InitStruct.GPIO_Pin=GPIO_Pin_14; GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU; GPIO_Init(GPIOB,&GPIO_InitStruct); /*PB15I2S2_SD*/ GPIO_InitStruct.GPIO_Pin=GPIO_Pin_15; GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP; GPIO_Init(GPIOB,&GPIO_InitStruct); /*PB12AFI2S2_WS*/ GPIO_PinAFConfig(GPIOB,GPIO_PinSource12,GPIO_AF_0); /*PB12AFI2S2_CK*/ GPIO_PinAFConfig(GPIOB,GPIO_PinSource13,GPIO_AF_0); /*PB12AFI2S2_MCLK*/ GPIO_PinAFConfig(GPIOB,GPIO_PinSource14,GPIO_AF_0); /*PB12AFI2S2_SD*/ GPIO_PinAFConfig(GPIOB,GPIO_PinSource15,GPIO_AF_0); }
6
I2S從機的初始化
I2S從機的初始化步驟如下所示:
1
配置SPI_I2S_GCTL.SPIEN位為‘1’,開啟模塊使能;
2
配置SPI_I2S_GCTL.MODE位為‘0’,使模塊功能為從模式;
3
配置寄存器SPI_I2S_I2SCFGR中的 I2SDIV[8:0]、 DATLEN 和 CHLEN 位,以符合希望得到的音頻采樣頻率及數(shù)據(jù)包幀格式;
4
配置SPI_I2S_I2SCFGR.SPI_I2S位為‘1’,使能 I2S 傳輸功能;
5
配置寄存器SPI_I2S_I2SCFGR中的I2SSTD[1: 0]、 PCMSYNC 位,選擇I2S傳輸時使用的通信標準;
6
配置SPI_I2S_GCTL.DMAMODE 位為‘1’,以啟用 DMA 傳輸;
7
開啟半雙工傳輸許可, 即配置寄存器SPI_I2S_GCTL 中的 TXEN 或 RXEN 位為‘1’ (TXEN、 RXEN不可同時配置為‘1’)。
注意:從模式下發(fā)送時,在檢測到WS的邊沿之前,需要對寄存器SPI_I2S_TXREG進行1次數(shù)據(jù)寫入操作;而且,從模式下接收時,在配置RXEN位為‘1’之前,需要一直維持WS輸入信號在高電平。
I2S從機的初始化代碼如下所示:
voidI2S2_Slave_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1ENR_SPI2,ENABLE);/*EnableSPI2_I2SClock*/ SPI2->CCTL&=~SPI_CCTL_LSBFE;/*MSBfirstenable*/ SPI2->CCTL&=~SPI_CCTL_CPHA;/*Clockphaseselectstartsecondclock*/ SPI2->CCTL|=SPI_CCTL_CPHASEL;/*CPHApolarityselectstartsecondclock*/ SPI2->CCTL|=SPI_CCTL_SPILEN;/*SPIcharacterlength8bitdata*/ SPI2->CCTL|=SPI_CCTL_CPOL;/*Clockpolarityselecthigh*/ SPI2->CCTL|=SPI_CCTL_TXEDGE;/*Transmitdataedgefori2sbus*/ SPI2->I2SCFGR&=~SPI_I2SCFGR_MCKOE;/*I2Smasterclockoutputdisable*/ SPI2->I2SCFGR&=~SPI_I2SCFGR_CHLEN;/*Vocaltractlength16bit*/ SPI2->I2SCFGR|=SPI_I2SCFGR_DATLEN_32;/*Audiodatawidth32*/ SPI2->I2SCFGR|=SPI_I2SCFGR_I2SSTD_Philips;/*I2SSTDPhilips*/ SPI2->I2SCFGR|=SPI_I2SCFGR_SPI_I2S;/*SPI/I2Smodulefunctionselection*/ SPI2->GCTL&=~SPI_GCTL_MODE;/*I2SSlavemode*/ SPI2->GCTL|=SPI_GCTL_DW_8_32;/*double-worddataselectsignal*/ SPI2->GCTL|=SPI_GCTL_DMAMODE;/*DMAaccessmodeenable*/ SPI2->GCTL|=SPI_GCTL_TXEN;/*I2STransmitenable*/ SPI2->GCTL|=SPI_GCTL_INTEN;/*SPI_I2Sinterruptenable*/ SPI2->GCTL|=SPI_GCTL_SPIEN;/*EnableI2S*/ }
7
I2S發(fā)送DAC音頻信號流
MM32F0163D7P的I2S2發(fā)送DAC音頻信號流使用DMA中斷發(fā)送,代碼接口如下所示:
voidI2S2_TxData_DMA_Interrupt(uint8_t*Buffer,uint8_tdatasize) { DMA_InitTypeDefDMA_InitStruct; NVIC_InitTypeDefNVIC_InitStruct; RCC_DMA_ClockCmd(DMA1,ENABLE);/*EnableDMA1Clock*/ DMA_DeInit(DMA1_Channel5); DMA_StructInit(&DMA_InitStruct); DMA_InitStruct.DMA_PeripheralBaseAddr=(uint32_t)&(SPI2->TXREG);/*SPI2_I2S2BaseAddr*/ DMA_InitStruct.DMA_MemoryBaseAddr=(uint32_t)(Buffer);/*Memorybufferformusicdata*/ DMA_InitStruct.DMA_DIR=DMA_DIR_PeripheralDST; DMA_InitStruct.DMA_BufferSize=datasize;/*LeftandRightchannelaudiobuffersize*/ DMA_InitStruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable; DMA_InitStruct.DMA_MemoryInc=DMA_MemoryInc_Enable;/*memoryincrement*/ DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;/*halfwordtransfer*/ DMA_InitStruct.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;/*halfwordtransfer*/ DMA_InitStruct.DMA_Mode=DMA_Mode_Normal;/*Normalmode*/ DMA_InitStruct.DMA_Priority=DMA_Priority_Medium;/*DMAPriorityMedium*/ DMA_InitStruct.DMA_M2M=DMA_M2M_Disable;/*Disablememorytomemorytransfer*/ DMA_InitStruct.DMA_Auto_reload=DMA_Auto_Reload_Enable;/*Enableautoreload*/ DMA_Init(DMA1_Channel5,&DMA_InitStruct); DMA_ClearFlag(DMA1_FLAG_TC5);/*Cleartransfercompleteflag*/ DMA_ITConfig(DMA1_Channel5,DMA_IT_TC,ENABLE);/*EnableDMAChannel5SPI2_I2S2DMAtransfercompleteinterrupt*/ NVIC_InitStruct.NVIC_IRQChannel=DMA1_Channel4_7_IRQn;/*SetSPI2_I2S2DMAChannelNVICintterruptpriority*/ NVIC_InitStruct.NVIC_IRQChannelPriority=0x01; NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;/*EnableNVICIRQChannel*/ NVIC_Init(&NVIC_InitStruct); DMA_Cmd(DMA1_Channel5,ENABLE);/*EnableSPI2_I2S2DMAChannel5*/ SPI_DMACmd(SPI1,ENABLE);/*EnableSPI_I2S*/ while(0==I2S2_TX_DMA_InterruptFlag) { } }
I2S的DMA中斷處理函數(shù):
voidDMA1_Channel4_7_IRQHandler(void) { if(SET==DMA_GetITStatus(DMA1_IT_TC5)) { /*I2S2DMAinterrupttransferflag*/ I2S2_TX_DMA_InterruptFlag=1; /*Cleartransfercompleteinterruptflag*/ DMA_ClearITPendingBit(DMA1_IT_TC5); /*EnableSPI2_I2S2DMAChannel5*/ DMA_Cmd(DMA1_Channel5,ENABLE); } }
8
MCU輸出12MHz的PWM
給NAU88C22音頻編解碼芯片
配置MM32F0163D7P TIM1輸出12MHz的PWM給NAU88C22合成12.228MHz時鐘給NAU88C22的IMCLK和MCU的I2S,可參考MM32F0160_Samples中的TIM1 PWM輸出例程。
9
SPI1接口的GPIO驅(qū)動25WQ80存儲器
SPI1接口的GPIO初始化代碼如下所示:
voidSPI1_GPIO_Init(void) { GPIO_InitTypeDefGPIO_InitStruct; RCC_GPIO_ClockCmd(GPIOA,ENABLE); RCC_GPIO_ClockCmd(GPIOB,ENABLE); GPIO_PinAFConfig(GPIOA,GPIO_PinSource15,GPIO_AF_0);/*PA15AFSPI1_CS*/ GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_0);/*PB3AFSPI1_SCK*/ GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_0);/*PB5AFSPI1_MOSI*/ GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_0);/*PB4AFSPI1_MISO*/ GPIO_StructInit(&GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin=GPIO_Pin_15;/*PA15SPI1_CS*/ GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP; GPIO_Init(GPIOA,&GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin=GPIO_Pin_3;/*PB3SPI1_CS*/ GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP; GPIO_Init(GPIOB,&GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;/*PB5SPI1_CS*/ GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP; GPIO_Init(GPIOB,&GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin=GPIO_Pin_4;/*PB4SPI1_CS*/ GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU; GPIO_Init(GPIOB,&GPIO_InitStruct); }
SPI1接口驅(qū)動25WQ80存儲器,存儲待讀取的音頻信號,經(jīng)過SPI1讀取后通過I2S2接口傳輸音頻信號流到NAU88C22音頻編解碼芯片解碼播放音樂,其初始化代碼如下所示:
voidSPI1_NVIC_Config(uint16_tspi_baud_div) { SPI_InitTypeDefSPI_InitStruct; NVIC_InitTypeDefNVIC_InitStruct; /*SPI1NVICPriorityConfig*/ NVIC_InitStruct.NVIC_IRQChannel=SPI1_IRQn; NVIC_InitStruct.NVIC_IRQChannelPriority=0x01; NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStruct); RCC_SPI_ClockCmd(SPI1,ENABLE);/*EnableSPI1Clock*/ SPI_DeInit(SPI1); SPI_StructInit(&SPI_InitStruct); SPI_InitStruct.SPI_Mode=SPI_Mode_Master;/*SPImastermode*/ SPI_InitStruct.SPI_DataSize=SPI_DataSize_8b; SPI_InitStruct.SPI_DataWidth=8; SPI_InitStruct.SPI_CPOL=SPI_CPOL_Low;/*Theclockislowinidlestate.*/ SPI_InitStruct.SPI_CPHA=SPI_CPHA_1Edge;/*Datasamplingstartsfromthefirstclockedge*/ SPI_InitStruct.SPI_NSS=SPI_NSS_Soft; /*SPIdataedgeadjustinfastspeedmode*/ SPI_InitStruct.SPI_BaudRatePrescaler=(SPI_BaudRatePrescaler_TypeDef)spi_baud_div ; SPI_InitStruct.SPI_FirstBit=SPI_FirstBit_MSB;/*DatatransfersstartfromMSB*/ SPI_Init(SPI1,&SPI_InitStruct); if(SPI_InitStruct.SPI_BaudRatePrescaler<=?8) ????{ ????????exSPI_DataEdgeAdjust(SPI1,?SPI_DataEdgeAdjust_FAST); ????} ????/*?Enable?Receive?available?data?interrupt?and?transmitter?empty?interrupt?*/ ????SPI_ITConfig(SPI1,?SPI_IT_RX?|?SPI_IT_TXEPT,?ENABLE); ????SPI_BiDirectionalLineConfig(SPI1,?SPI_Direction_Rx);????/*?Receive?enable?*/ ????SPI_BiDirectionalLineConfig(SPI1,?SPI_Direction_Tx);????/*?Transmit?enable?*/ ????SPI_Cmd(SPI1,?ENABLE);?????????????????????????????????????/*?Enable?SPI1?*/ }
SPI1讀寫25WQ80存儲器函數(shù)接口請參考MM32F0160_Samples中的SPI_FLASH_Interrupt例程。
10
I2C接口的初始化
I2C1主機模式驅(qū)動NAU88C22音頻編解碼芯片用于發(fā)送指令控制NAU88C22音頻編解碼芯片工作。I2C1接口的GPIO初始化代碼如下所示:
voidI2C1_GPIO_Init(void) { GPIO_InitTypeDefGPIO_InitStruct; RCC_GPIO_ClockCmd(GPIOB,ENABLE); GPIO_PinAFConfig(GPIOB,GPIO_PinSource10,GPIO_AF_1);/*PB10AFI2C1_SCL*/ GPIO_PinAFConfig(GPIOB,GPIO_PinSource11,GPIO_AF_1);/*PB10AFI2C1_SDA*/ GPIO_StructInit(&GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;/*PB10I2C1_SCL*/ GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz; /*KeepthebusfreewhichmeansSCK&SDAishigh*/ GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_OD; GPIO_Init(GPIOB,&GPIO_InitStruct); GPIO_InitStruct.GPIO_Pin=GPIO_Pin_11;/*PB11I2C1_SDA*/ GPIO_Init(GPIOB,&GPIO_InitStruct); }
I2C1接口作為主機驅(qū)動NAU88C22音頻編解碼芯片工作,其初始化代碼如下所示:
voidI2C_Master_Mode_Init(I2C_TypeDef*I2Cx,uint32_tI2C_speed) { I2C_InitTypeDefI2C_InitStruct; RCC_I2C_ClockCmd(I2C1,ENABLE);/*EnableI2Cclock*/ I2C_StructInit(&I2C_InitStruct); I2C_InitStruct.Mode=I2C_CR_MASTER;/*ConfigureI2Casmastermode*/ I2C_InitStruct.OwnAddress=0; if(I2C_speed>200000)/*morethan200K*/ { I2C_InitStruct.Speed=I2C_CR_SPEED_FAST;/*I2Cfastspeedmode*/ } else { I2C_InitStruct.Speed=I2C_CR_SPEED_STD;/*I2Cstandardspeedmode*/ } I2C_InitStruct.ClockSpeed=I2C_speed;/*I2CSpeed*/ I2C_Init(I2Cx,&I2C_InitStruct); I2C_Cmd(I2Cx,ENABLE); }
I2C1設置從機地址代碼如下所示:
voidI2C_Set_DeviceAddr(I2C_TypeDef*I2Cx,uint8_tdeviceaddr) { /*DisableI2C*/ I2C_Cmd(I2Cx,DISABLE); /*Setthedeviceaddress*/ I2C_Send7bitAddress(I2Cx,deviceaddr,I2C_Direction_Transmitter); /*EnableI2C*/ I2C_Cmd(I2Cx,ENABLE); }
I2C1的讀寫數(shù)據(jù)和命令的操作參考LibSamples_MM32F0160 I2C例程。
11
編譯I2S程序燒錄燒錄到開發(fā)板中播放音樂
編譯MM32F0163D7P的I2S2程序并燒錄到開發(fā)板中實現(xiàn)音樂播放功能。
實驗1:播放三角波測試文件實測輸出波形
實驗2:播放48KHz/24BIT立體聲WAVE格式音樂文件實測輸出波形
-
接口
+關注
關注
33文章
8598瀏覽量
151164 -
數(shù)據(jù)
+關注
關注
8文章
7030瀏覽量
89038 -
音樂播放器
+關注
關注
0文章
68瀏覽量
15774
原文標題:靈動微課堂 (第262講)|基于MM32F0163D7P的I2S接口的音樂播放器實驗
文章出處:【微信號:MindMotion-MMCU,微信公眾號:靈動MM32MCU】歡迎添加關注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論