由于需要给WM8978的接收发送音频数据增加延迟,所以将音频数据先通过WM8978的ADC发送给STM32F103,再让STM32F103发送音频数据到WM8978的DAC,进而实现延迟效果,但是由于STM32F103的I2S2是半双工,所以配置STM32F103的I2S2接收音频数据,I2S3发送音频数据,音频数据的收发采用DMA以及双缓冲。
首先是I2S2和I2S3的GPIO初始化。
void I2S_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC |RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStructure.GPIO_Pin = I2S2_WS_PIN | I2S2_CK_PIN | I2S2_SD_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIO_I2S2_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = I2S2_MCLK_PIN;
GPIO_Init(GPIO_I2S2_MCLK_PORT, &GPIO_InitStructure);
}
void I2S3_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC |RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStructure.GPIO_Pin = I2S3_CK_PIN | I2S3_SD_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIO_I2S3_PORT2, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = I2S3_WS_PIN;
GPIO_Init(GPIO_I2S3_PORT1, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = I2S3_MCLK_PIN;
GPIO_Init(GPIO_I2S3_MCLK_PORT, &GPIO_InitStructure);
}
然后是I2S2和I2S3的配置
void I2S_Mode_Config(uint16_t _usStandard, uint16_t _usWordLen, uint16_t _usAudioFreq)
{
I2S_InitTypeDef I2S_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
/* 复位 SPI2 外设到缺省状态 */
SPI_I2S_DeInit(SPI2);
/* I2S2 外设配置 */
I2S_InitStructure.I2S_Mode = I2S_Mode_MasterRx; /* 配置I2S工作模式 */
I2S_InitStructure.I2S_Standard = _usStandard; /* 接口标准 */
I2S_InitStructure.I2S_DataFormat = _usWordLen; /* 数据格式,16bit */
I2S_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Enable; /* 主时钟模式 */
I2S_InitStructure.I2S_AudioFreq = _usAudioFreq; /* 音频采样频率 */
I2S_InitStructure.I2S_CPOL = I2S_CPOL_Low;
I2S_Init(SPI2, &I2S_InitStructure);
/* 禁止I2S2 TXE中断(发送缓冲区空),需要时再打开 */
SPI_I2S_ITConfig(SPI2, SPI_I2S_IT_TXE, DISABLE);
/* 禁止I2S2 RXNE中断(接收不空),需要时再打开 */
SPI_I2S_ITConfig(SPI2, SPI_I2S_IT_RXNE, DISABLE);
/* 使能 SPI2/I2S2 外设 */
I2S_Cmd(SPI2, ENABLE);
}
void I2S3_Mode_Config(uint16_t _usStandard, uint16_t _usWordLen, uint16_t _usAudioFreq)
{
I2S_InitTypeDef I2S_InitStructure;
/* 打开 I2S3 APB1 时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);
/* 复位 SPI3 外设到缺省状态 */
SPI_I2S_DeInit(SPI3);
/* I2S3 外设配置 */
I2S_InitStructure.I2S_Mode = I2S_Mode_MasterTx; /* 配置I2S工作模式 */
I2S_InitStructure.I2S_Standard = _usStandard; /* 接口标准 */
I2S_InitStructure.I2S_DataFormat = _usWordLen; /* 数据格式,16bit */
I2S_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Enable; /* 主时钟模式 */
I2S_InitStructure.I2S_AudioFreq = _usAudioFreq; /* 音频采样频率 */
I2S_InitStructure.I2S_CPOL = I2S_CPOL_Low;
I2S_Init(SPI3, &I2S_InitStructure);
/* 禁止I2S3 TXE中断(发送缓冲区空),需要时再打开 */
SPI_I2S_ITConfig(SPI3, SPI_I2S_IT_TXE, DISABLE);
/* 禁止I2S3 RXNE中断(接收不空),需要时再打开 */
SPI_I2S_ITConfig(SPI3, SPI_I2S_IT_RXNE, DISABLE);
/* 使能 SPI3/I2S3 外设 */
I2S_Cmd(SPI3, ENABLE);
}
然后配置DMA通道
DMA1_Channel4 ----- I2S2_RX
DMA2_Channel2 ----- I2S3_TX
void DMA_RX_InitConfig(uint16_t *rxBuffer, uint32_t bufferSize)
{
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// 配置接收DMA SPI2_RX
DMA_DeInit(DMA1_Channel4);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)rxBuffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = bufferSize;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel4, &DMA_InitStructure);
DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE); // 使能传输完成中断
// 配置NVIC
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Rx,ENABLE);//SPI2 RX DMA请求使能
DMA_Cmd(DMA1_Channel4, ENABLE); // 使能DMA1 Channel4
}
void DMA_TX_InitConfig(uint16_t *txBuffer, uint32_t bufferSize)
{
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
// 配置发送DMA SPI3_TX
DMA_DeInit(DMA2_Channel2);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI3->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)txBuffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = bufferSize;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA2_Channel2, &DMA_InitStructure);
DMA_ITConfig(DMA2_Channel2, DMA_IT_TC, ENABLE); // 使能传输完成中断
// 配置NVIC
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Channel2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
SPI_I2S_DMACmd(SPI3,SPI_I2S_DMAReq_Tx,ENABLE);//SPI3 TX DMA请求使能
DMA_Cmd(DMA2_Channel2, ENABLE); // 使能DMA2 Channel2
}
DMA中断处理,设置接收完成标志位和发送完成标志位
extern volatile uint8_t bufferReady;
extern volatile uint8_t bufferReady01;
// 接收DMA传输完成中断处理函数
void DMA1_Channel4_IRQHandler(void)
{
if (DMA_GetITStatus(DMA1_IT_TC4))
{
DMA_ClearITPendingBit(DMA1_IT_TC4);
bufferReady = 1;
}
}
// 发送DMA传输完成中断处理函数
void DMA2_Channel2_IRQHandler(void)
{
if (DMA_GetITStatus(DMA2_IT_TC2))
{
DMA_ClearITPendingBit(DMA2_IT_TC2);
bufferReady01 = 1;
}
}
主函数,audioBuffer1和audioBuffer2分别承担接收发送任务,任务都完成后交换彼此的接收发送功能。
#define BUFFER_SIZE 256
uint16_t audioBuffer1[BUFFER_SIZE];
uint16_t audioBuffer2[BUFFER_SIZE];
volatile uint8_t bufferReady = 0;
volatile uint8_t bufferReady01 = 0;
volatile uint8_t currentBuffer = 0;
int main(void)
{
System_Modle_Init(); //芯片及外设初始化,参数初始化
DMA_RX_InitConfig(audioBuffer1, BUFFER_SIZE);
DMA_TX_InitConfig(audioBuffer2, BUFFER_SIZE);
while(1)
{
if (bufferReady && bufferReady01)
{
// 交换缓冲区
if (currentBuffer == 0)
{
DMA_TX_InitConfig(audioBuffer2, BUFFER_SIZE);
DMA_RX_InitConfig(audioBuffer1, BUFFER_SIZE);
currentBuffer = 1;
bufferReady = 0;
bufferReady01 = 0;
} else
{
DMA_TX_InitConfig(audioBuffer1, BUFFER_SIZE);
DMA_RX_InitConfig(audioBuffer2, BUFFER_SIZE);
currentBuffer = 0;
bufferReady = 0;
bufferReady01 = 0;
}
}
}
}
这样声音的延时时长由双缓冲数组的大小即BUFFER_SIZE 所决定,数组越大声音的延迟时长越大。