I2S(Inter-IC Sound)總線,又稱集成電路內置音頻總線,是飛利浦公司為數字音頻設備之間的音頻數據傳輸而制定的一種總線標準。采用了獨立的導線傳輸時鐘與數據信號的設計,通過將數據和時鐘信號分離,避免了因時差誘發的失真。
- 半雙工通信(僅發送器或接收器)
- 主操作或從操作
- 9 位可配置線性預分頻器,以達到精確的音頻采樣頻率(8KHz~192KHz)
- 數據幀格式可配置為 16 位、24 位或 32 位
- 數據包幀固定為 16 位(16 位有效數據)或 32 位(16 位、24 位、32 位有效數據)
- 可配置時鐘極性(穩定狀態)
- 發送模式下具有下溢標志(僅從機),接收模式下具有上溢標志(主/從機)和發送/接收模式下的幀錯誤標志(僅從機)
- 用于傳輸和接收的 32 位寄存器為兩個聲道分時復用
- 數據方向始終是 MSB 優先
- 支持 I2S 協議:
- 飛利浦標準
- MSB 對齊標準(MSB 位向左對齊)
- LSB 對齊標準(LSB 位向右對齊)
- PCM 標準(具有短幀同步模式、長幀同步模式的兩種方式)
- 利用 DMA 請求傳輸數據(32 位寬)
- 可配置 MCLK 時鐘輸出來驅動外部音頻組件,其比率固定在 256×Fs(其中 Fs 為音頻采樣頻率)
1. MM32的I2S總線簡述
MM32F0160的I2S總線與SPI總線復用即SPI_I2S串行外設(串行外設接口與集成電路內置音頻總線)。
I2S總線接口與SPI總線接口引腳復用關系如下:
SD: 串行數據(映射在MOSI引腳上),用于發送或接收兩次多路數據通道(僅在半雙工模式下)。
WS: 聲道選擇(映射在NSS引腳上),是主模式控制數據的輸出信號,或從模式的輸入。
CK: 串行時鐘(映射在SCK引腳上),是主模式串行時鐘的輸出,或從模式串行時鐘的輸入。
MCK: 可選的串行時鐘(映射在MISO引腳上),用于驅動外部音頻組件(僅當外部音頻設備需要時鐘輸入時使用,由主模式提供)。
2. SPI_I2S功能框圖簡介
圖1 SPI_I2S功能框圖
如上圖1所示為SPI_I2S外設的功能框圖,SPI_I2S通過“總線接口邏輯”掛載在APB和DMA總線上,TXREG和RXREG寄存器、主模式控制單元和從模式控制單元、主從選擇控制、收發控制邏輯以及時鐘生成及控制單元,8Byte的發送緩沖和8Byte的接收緩沖構成,時鐘控制單元由Spbrg和Pclk提供時鐘。
3. SPI_I2S外設的I2S時鐘預分頻器
I2SCLK時鐘由系統APB時鐘提供,I2S模塊的預分頻器電路結構如下圖2所示:
圖2 SPI_I2S時鐘預分頻器電路結構圖
如上圖2所示,當MCKOE位為‘0’時芯片不需要輸出MCK時鐘,預分頻器直接將I2SCLK分頻到CK;當MCKOE位為‘1’時芯片會輸出MCK時鐘,預分頻器將I2SCLK分頻后得到MCK,然后再經過分頻處理才得到CK(分頻倍數由CHLEN選擇為 4或8)。
音頻采樣率一般常用 192KHz,96 KHz,48 KHz,44.1 KHz,32 KHz,22.05 KHz,16 KHz,11.025KHz,8 KHz。因此可根據 I2S 時鐘分頻器的電路功能式樣,配置寄存器 I2SCFGR中的I2SDIV[8:0]、MCKOE和CHLEN 位來得到期望的音頻采樣率。
I2S 傳輸數據時,比特率計算公式如下表1所示(CK 輸出一個時鐘周期對應傳輸 1 比特數據,因此比特率 = CK頻率FCK)。
表1
音頻采樣率(Fs)和 I2S 比特率的關系由如下的公式定義:
Fs = I2S 比特率/(通道長度×通道數)= FCK /(通道長度×通道數)
注:通道長度,即數據包幀長度,可配置為16位或32位;通道數為左右聲道,值固定為2。
綜上所述,根據I2SDIV[8: 0]、MCKOE和CHLEN位的配置情況, 得到音頻采樣率與FI2SCLK(APB時鐘頻率)的關系如下表2所示:
表2
4. SPI_I2S外設的I2S接口的音樂播放器
工作原理介紹
基于I2S接口的音樂播放器工作原理框圖如下圖3所示:
圖3 I2S接口的音樂播放器原理框圖
如上圖3所示為I2S接口的音樂播放器工作原理框圖:
MM32F0160作為Host MCU其SPI1接口用于驅動25WQ80存儲器用于寫入和讀取存儲的音頻文件。
MCU端I2S2(SPI2_I2S2)接口工作在從機模式,MCLK不輸出時鐘。通過PWM輸出12MHz的REF_Clock給NAU88C22音頻編解碼芯片MCLK腳,NAU88C22內部PLL合成穩定的12.288MHz作為內部IMLCK主時鐘。NAU88C22 BCLK輸出bit clock時鐘到MCU端I2S2_CK作為音頻采樣時鐘。I2S2_WS接口即FS用于分時切換左右聲道。I2S2_SD接口即DACIN輸出從25WQ80存儲器讀取的音頻信號流DAC Stream傳輸給NAU88C22音頻編解碼芯片。
MCU端I2C_SDA和I2C_SCL接口用于設置NAU88C22工作模式和參數。NAU88C22 DAC輸出可以選擇從Speaker PA輸出推喇叭或者從Earphone PA耳機接口輸出推動耳機。
5. I2S接口的GPIO初始化
I2S接口GPIO的初始化代碼如下所示:
void I2S2_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_GPIO_ClockCmd(GPIOB, ENABLE);
GPIO_StructInit(&GPIO_InitStruct);
/* PB12 I2S2_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);
/* PB13 I2S2_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);
/* PB14 I2S2_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);
/* PB15 I2S2_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);
/* PB12 AF I2S2_WS */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource12, GPIO_AF_0);
/* PB12 AF I2S2_CK */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_0);
/* PB12 AF I2S2_MCLK */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_0);
/* PB12 AF I2S2_SD */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_0);
}
6. I2S從機的初始化
I2S從機的初始化步驟如下所示:
- 配置SPI_I2S_GCTL.SPIEN位為‘1’,開啟模塊使能;
- 配置SPI_I2S_GCTL.MODE位為‘0’,使模塊功能為從模式;
- 配置寄存器SPI_I2S_I2SCFGR中的 I2SDIV[8:0]、 DATLEN 和 CHLEN 位,以符合希望得到的音頻采樣頻率及數據包幀格式;
- 配置SPI_I2S_I2SCFGR.SPI_I2S位為‘1’,使能 I2S 傳輸功能;
- 配置寄存器SPI_I2S_I2SCFGR中的I2SSTD[1: 0]、 PCMSYNC 位,選擇I2S傳輸時使用的通信標準;
- 配置SPI_I2S_GCTL.DMAMODE 位為‘1’,以啟用 DMA 傳輸;
- 開啟半雙工傳輸許可, 即配置寄存器SPI_I2S_GCTL 中的 TXEN 或 RXEN 位為‘1’ (TXEN、 RXEN不可同時配置為‘1’)。
注意:從模式下發送時,在檢測到WS的邊沿之前,需要對寄存器SPI_I2S_TXREG進行1次數據寫入操作;而且,從模式下接收時,在配置RXEN位為‘1’之前,需要一直維持WS輸入信號在高電平。
I2S從機的初始化代碼如下所示:
void I2S2_Slave_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1ENR_SPI2, ENABLE); /* Enable SPI2_I2S Clock */
SPI2- >CCTL &= ~SPI_CCTL_LSBFE; /* MSB first enable */
SPI2- >CCTL &= ~SPI_CCTL_CPHA; /* Clock phase select start second clock */
SPI2- >CCTL |= SPI_CCTL_CPHASEL; /* CPHA polarity select start second clock */
SPI2- >CCTL |= SPI_CCTL_SPILEN; /* SPI character length 8bit data */
SPI2- >CCTL |= SPI_CCTL_CPOL; /* Clock polarity select high */
SPI2- >CCTL |= SPI_CCTL_TXEDGE; /* Transmit data edge for i2s bus */
SPI2- >I2SCFGR &= ~SPI_I2SCFGR_MCKOE; /* I2S master clock output disable */
SPI2- >I2SCFGR &= ~SPI_I2SCFGR_CHLEN; /* Vocal tract length 16bit */
SPI2- >I2SCFGR |= SPI_I2SCFGR_DATLEN_32; /* Audio data width 32 */
SPI2- >I2SCFGR |= SPI_I2SCFGR_I2SSTD_Philips; /* I2S STD Philips */
SPI2- >I2SCFGR |= SPI_I2SCFGR_SPI_I2S; /* SPI/I2S module function selection */
SPI2- >GCTL &= ~SPI_GCTL_MODE; /* I2S Slave mode */
SPI2- >GCTL |= SPI_GCTL_DW_8_32; /* double-word data select signal */
SPI2- >GCTL |= SPI_GCTL_DMAMODE; /* DMA access mode enable */
SPI2- >GCTL |= SPI_GCTL_TXEN; /* I2S Transmit enable */
SPI2- >GCTL |= SPI_GCTL_INTEN; /* SPI_I2S interrupt enable */
SPI2- >GCTL |= SPI_GCTL_SPIEN; /* Enable I2S */
}
7. I2S發送DAC音頻信號流
MM32F0163D7P的I2S2發送DAC音頻信號流使用DMA中斷發送,代碼接口如下所示:
void I2S2_TxData_DMA_Interrupt(uint8_t *Buffer, uint8_t datasize)
{
DMA_InitTypeDef DMA_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_DMA_ClockCmd(DMA1, ENABLE);/* Enable DMA1 Clock */
DMA_DeInit(DMA1_Channel5);
DMA_StructInit(&DMA_InitStruct); DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(SPI2- >TXREG); /* SPI2_I2S2 BaseAddr */
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)(Buffer); /* Memory buffer for music data*/
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStruct.DMA_BufferSize = datasize; /* Left and Right channel audio buffer size */
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; /* memory increment */
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; /* half word transfer */
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; /* half word transfer */
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal; /* Normal mode */
DMA_InitStruct.DMA_Priority = DMA_Priority_Medium; /* DMA Priority Medium */
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; /* Disable memory to memory transfer */
DMA_InitStruct.DMA_Auto_reload = DMA_Auto_Reload_Enable; /* Enable auto reload */
DMA_Init(DMA1_Channel5, &DMA_InitStruct);
DMA_ClearFlag(DMA1_FLAG_TC5); /* Clear transfer complete flag */
DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE); /* Enable DMA Channel5 SPI2_I2S2 DMA transfer complete interrupt */ NVIC_InitStruct.NVIC_IRQChannel = DMA1_Channel4_7_IRQn; /* Set SPI2_I2S2 DMA Channel NVIC intterrupt priority */
NVIC_InitStruct.NVIC_IRQChannelPriority = 0x01; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; /* Enable NVIC IRQChannel */
NVIC_Init(&NVIC_InitStruct);
DMA_Cmd(DMA1_Channel5, ENABLE); /* Enable SPI2_I2S2 DMA Channel5 */
SPI_DMACmd(SPI1, ENABLE); /* Enable SPI_I2S */
while (0 == I2S2_TX_DMA_InterruptFlag)
{
}
}
I2S的DMA中斷處理函數:
void DMA1_Channel4_7_IRQHandler(void)
{
if(SET == DMA_GetITStatus(DMA1_IT_TC5))
{
/* I2S2 DMA interrupt transfer flag */
I2S2_TX_DMA_InterruptFlag = 1;
/* Clear transfer complete interrupt flag */
DMA_ClearITPendingBit(DMA1_IT_TC5);
/* Enable SPI2_I2S2 DMA Channel5 */
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驅動25WQ80存儲器
SPI1接口的GPIO初始化代碼如下所示:
void SPI1_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_GPIO_ClockCmd(GPIOA, ENABLE);
RCC_GPIO_ClockCmd(GPIOB, ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource15, GPIO_AF_0); /* PA15 AF SPI1_CS */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_0); /* PB3 AF SPI1_SCK */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_0); /* PB5 AF SPI1_MOSI */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_0); /* PB4 AF SPI1_MISO */
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15; /* PA15 SPI1_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; /* PB3 SPI1_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; /* PB5 SPI1_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; /* PB4 SPI1_CS */
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOB, &GPIO_InitStruct);
}
SPI1接口驅動25WQ80存儲器,存儲待讀取的音頻信號,經過SPI1讀取后通過I2S2接口傳輸音頻信號流到NAU88C22音頻編解碼芯片解碼播放音樂,其初始化代碼如下所示:
void SPI1_NVIC_Config(uint16_t spi_baud_div)
{
SPI_InitTypeDef SPI_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
/* SPI1 NVIC Priority Config */
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); /* Enable SPI1 Clock */
SPI_DeInit(SPI1);
SPI_StructInit(&SPI_InitStruct);
SPI_InitStruct.SPI_Mode = SPI_Mode_Master; /* SPI master mode */
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStruct.SPI_DataWidth = 8;
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; /* The clock is low in idle state. */
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; /* Data sampling starts from the first clock edge */
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
/* SPI data edge adjust in fast speed mode */
SPI_InitStruct.SPI_BaudRatePrescaler = (SPI_BaudRatePrescaler_TypeDef)spi_baud_div ;
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; /* Data transfers start from MSB */
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存儲器函數接口請參考MM32F0160_Samples中的SPI_FLASH_Interrupt例程。
10. I2C接口的初始化
I2C1主機模式驅動NAU88C22音頻編解碼芯片用于發送指令控制NAU88C22音頻編解碼芯片工作。I2C1接口的GPIO初始化代碼如下所示:
void I2C1_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_GPIO_ClockCmd(GPIOB, ENABLE);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_1); /* PB10 AF I2C1_SCL */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_1); /* PB10 AF I2C1_SDA */
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10; /* PB10 I2C1_SCL */
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
/* Keep the bus free which means SCK & SDA is high */
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11; /* PB11 I2C1_SDA */
GPIO_Init(GPIOB, &GPIO_InitStruct);
}
I2C1接口作為主機驅動NAU88C22音頻編解碼芯片工作,其初始化代碼如下所示:
void I2C_Master_Mode_Init(I2C_TypeDef *I2Cx, uint32_t I2C_speed)
{
I2C_InitTypeDef I2C_InitStruct;
RCC_I2C_ClockCmd(I2C1, ENABLE); /* Enable I2C clock */
I2C_StructInit(&I2C_InitStruct);
I2C_InitStruct.Mode = I2C_CR_MASTER; /* Configure I2C as master mode */
I2C_InitStruct.OwnAddress = 0;
if (I2C_speed > 200000) /* more than 200K */{
I2C_InitStruct.Speed = I2C_CR_SPEED_FAST; /* I2C fast speed mode */
}
else
{
I2C_InitStruct.Speed = I2C_CR_SPEED_STD; /* I2C standard speed mode */
}
I2C_InitStruct.ClockSpeed = I2C_speed; /* I2C Speed */
I2C_Init(I2Cx, &I2C_InitStruct);
I2C_Cmd(I2Cx, ENABLE);
}
I2C1設置從機地址代碼如下所示:
void I2C_Set_DeviceAddr(I2C_TypeDef *I2Cx, uint8_t deviceaddr)
{
/* Disable I2C */
I2C_Cmd(I2Cx, DISABLE);
/* Set the device address */
I2C_Send7bitAddress(I2Cx, deviceaddr, I2C_Direction_Transmitter);
/* Enable I2C */
I2C_Cmd(I2Cx, ENABLE);
}
I2C1的讀寫數據和命令的操作參考LibSamples_MM32F0160 I2C例程。
11. 編譯I2S程序燒錄燒錄到開發板中播放音樂
編譯MM32F0163D7P的I2S2程序并燒錄到開發板中實現音樂播放功能。
實驗1:播放三角波測試文件實測輸出波形
實驗2:播放48KHz/24BIT立體聲WAVE格式音樂文件實測輸出波形
評論
查看更多