STM32F103 硬件SPI+WM8978单声道语音回环

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;
		}
	}
}

https://download.csdn.net/download/qq_44065318/86245927

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值