STM32F3/F4系列芯片拥有全双工I2S接口可以与Codec通信,但是目前F1系列芯片或有些国产芯片没有全双工的I2S,要想使用Codec传输音频只能使用两个半双工合为一个全双工I2S,这不仅硬件上需要额外引脚,软件配置上也会少一个可使用的SPI接口。针对这一问题,我们可以使用SPI接口与Codec的“左对齐”接口模式进行通信(不是用SPI模拟I2S接口),单片机作为从器件,Codec作为主器件。接下来介绍使用DMA+乒乓buffer双缓存形式来进行语音回环处理。
/************WM8978配置**********/
uint8_t WM8978_Write_Reg(uint16_t nRegAddr,uint16_t nRegValue)
{
IIC_Start();
IIC_Send_Byte(0x34);
if(IIC_Wait_Ack())
return 1;
IIC_Send_Byte((nRegAddr<<1)|((nRegValue>>8)&0x01));
if(IIC_Wait_Ack())
return 2;
IIC_Send_Byte(nRegValue&0xFF);
if(IIC_Wait_Ack())
return 3;
IIC_Stop();
return 0;
}
void WM8978_Init(void)
{
IIC_Init();
WM8978_Write_Reg(0,0x0000);
WM8978_Write_Reg(1,0x013F);
WM8978_Write_Reg(2,0x01BF);
WM8978_Write_Reg(3,0x006F);
WM8978_Write_Reg(4,0x0008); //左对齐模式
WM8978_Write_Reg(6,0x01AD); //MCLK=12MHz
WM8978_Write_Reg(7,0x000A); //8K
WM8978_Write_Reg(10,0x0089); //ADC高通滤波器打开
WM8978_Write_Reg(14,0x0108); //DAC高通滤波器打开
WM8978_Write_Reg(36,0x0018);
WM8978_Write_Reg(43,0x0010);
WM8978_Write_Reg(44,0x0177);
WM8978_Write_Reg(45,0x0139);
WM8978_Write_Reg(46,0x0139);
WM8978_Write_Reg(47,0x0050);
WM8978_Write_Reg(48,0x0050);
WM8978_Write_Reg(49,0x0002);
WM8978_Write_Reg(52,0x012F);
WM8978_Write_Reg(53,0x012F);
WM8978_Write_Reg(54,0x0129);
WM8978_Write_Reg(55,0x0129);
}
SPI2_CS ---> WM8978_LRC
SPI2_SCK ---> WM8978_BCLK
SPI2_MISO ---> WM8978_ADC
SPI2_MOSI ---> WM8978_DAC
PA8--->WM8978_MCLK
void PWM_Init(uint16_t psc,uint16_t arr)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //使能定时器1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA外设时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period = arr-1; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc-1; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
//初始化PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OCInitStructure.TIM_Pulse=arr>>1;
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM1,ENABLE);
TIM_CtrlPWMOutputs(TIM1, ENABLE);
TIM_Cmd(TIM1, ENABLE);
}
int16_t *pADC,*pDAC;
int16_t SPI2_RxBuf[320],SPI2_TxBuf[320];
__IO uint32_t SPI2_Flag=0;
void SPI2_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
pADC=&SPI2_RxBuf[160];
pDAC=&SPI2_TxBuf[160];
NVIC_InitStructure.NVIC_IRQChannel=DMA1_Channel4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_15;
GPIO_Init(GPIOB,&GPIO_InitStructure);
DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)&SPI2->DR; //外设地址
DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)SPI2_RxBuf; //DMA1内存地址
DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC; //数据传输方向:外设到内存
DMA_InitStructure.DMA_BufferSize=320; //DMA1缓存大小
DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable; //外设地址不变
DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable; //内存地址递增
DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord; //数据宽度16位
DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord; //数据宽度16位
DMA_InitStructure.DMA_Mode=DMA_Mode_Circular; //DMA1工作循环模式
DMA_InitStructure.DMA_Priority=DMA_Priority_Low; //DMA1通道4中优先级低
DMA_InitStructure.DMA_M2M=DMA_M2M_Disable; //DMA1非内存到内存传输
DMA_Init(DMA1_Channel4,&DMA_InitStructure);
DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)&SPI2->DR; //外设地址
DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)SPI2_TxBuf; //DMA1内存地址
DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralDST; //数据传输方向:内存到外设
DMA_InitStructure.DMA_BufferSize=320; //DMA1缓存大小
DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable; //外设地址不变
DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable; //内存地址递增
DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord; //数据宽度16位
DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord; //数据宽度16位
DMA_InitStructure.DMA_Mode=DMA_Mode_Circular; //DMA1工作循环模式
DMA_InitStructure.DMA_Priority=DMA_Priority_Low; //DMA1通道5中优先级低
DMA_InitStructure.DMA_M2M=DMA_M2M_Disable; //DMA1非内存到内存传输
DMA_Init(DMA1_Channel5,&DMA_InitStructure);
DMA_ITConfig(DMA1_Channel4,DMA_IT_HT,ENABLE);
DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //SPI设置为双线双向全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Slave; //SPI从机
SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b; //发送接收16位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //时钟悬空低
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //数据捕获于第1个时钟沿
SPI_InitStructure.SPI_NSS = SPI_NSS_Hard; //CS引脚硬件配置
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; //定义波特率预分频的值:波特率预分频值为16
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从MSB位开始
SPI_Init(SPI2, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Tx,ENABLE);
SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Rx,ENABLE);
}
void SPI2_Start(void)
{
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12)==RESET);
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12)==SET);
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12)==RESET);
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12)==SET);
DMA_Cmd(DMA1_Channel4,ENABLE);
DMA_Cmd(DMA1_Channel5,ENABLE);
SPI_Cmd(SPI2,ENABLE);
}
void SPI2_Stop(void)
{
DMA_Cmd(DMA1_Channel4,DISABLE);
DMA_Cmd(DMA1_Channel5,DISABLE);
SPI_Cmd(SPI2,DISABLE);
}
void DMA1_Channel4_IRQHandler(void)
{
if(DMA_GetITStatus(DMA1_IT_HT4) == SET)
{
DMA_ClearITPendingBit(DMA1_IT_HT4);
pADC=&SPI2RxBuf[0];
pDAC=&SPI2_TxBuf[0];
SPI2_Flag=1;
}
if(DMA_GetITStatus(DMA1_IT_TC4) == SET)
{
DMA_ClearITPendingBit(DMA1_IT_TC4);
pADC=&SPI2_RxBuf[160];
pDAC=&SPI2_TxBuf[160];
SPI2_Flag=1;
}
}
int main()
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_init(SYSCLK_FREQ_72MHz);
WM8978_Init();
PWM_Init(3,2); //PA08 PWM产生12MHz频率给Codec
SPI2_Init();
Led_Init();
SPI2_Start();
while(1)
{
if(SPI2_Flag == 1)
{
SPI2_Flag=0;
for(uint32_t i=0;i<160;i++) //声音回环
{
pDAC[i]=pADC[i];
}
GPIOC->ODR^=GPIO_Pin_13;
}
}
}