WM8978是什么?

跳过专业的说法,其实它就是一个可以将文件解码成音频的模块,我们之前学习的SD卡读取和文件系统,也就是说,我们可以读取到任何文件,如果读到的是音频文件,发送给它,它就能帮我们解码成音频,然后使用它自带的喇叭就可以进行播放了。


原理图


移植过程:

将关于内存管理的文件、移植到文件系统中、进行空间开辟、文件类型注册等

在工程中新建一个文件夹存放音乐解码文件

将对应文件添加到工程中、并添加.h文件(其中fattester.c文件不用添加)

延时函数名称不对自行修改、如果在audioplay.c中dir_sdi找不到自行在ff.h中进行声明、声明之前先把ff.c源文件中的static进行注释

然后在主函数进行头文件声明和函数调用

并在key.h中添加音乐头文件

在main.c中添加初始化

WM8978_Init();				//初始化WM8978
	WM8978_HPvol_Set(0, 0);		//耳机音量设置
	WM8978_SPKvol_Set(0);		//喇叭音量设置
	WM8978_ADDA_Cfg(1, 0);		//开启DAC
	WM8978_Input_Cfg(0, 0, 0);	//关闭输入通道
	WM8978_Output_Cfg(1, 0);	//开启DAC输出   
	WM8978_I2S_Cfg(2, 0);		//飞利浦标准,16位数据长度
	I2S2_Init(I2S_Standard_Phillips, I2S_Mode_MasterTx, I2S_CPOL_Low, I2S_DataFormat_16bextended);	//飞利浦标准,主机发送,时钟低电平有效,16位扩展帧长度
	I2S2_SampleRate_Set(44100);	//设置采样率
	I2S2_TX_DMA_Init(NULL, NULL, WAV_I2S_TX_DMA_BUFSIZE/2); 			//配置TX DMA
	status_dev.volume = 63;	//初始保存音量 0~63
	WM8978_SPKvol_Set(status_dev.volume);

在主函数中调用音乐播放函数进行音乐播放

Audio_MusicPlay();//循环播放 按键切换歌曲


hlk v2.0        语音识别模块

工作原理:串口通信,通过串口接收语音识别传来的三个字节的的数据来判断语音的功能,

                  主要为中间字节


串口初始化及中断代码:

/*************************
函数名称:Usart3_Init(u32 baud)
函数功能:Usart3初始化函数
返回值:无
形参:u32 baud
作者:me
版本:1.0 
		U3 TX - PD8		U3	RX - PD9				APB1
*************************/
void Usart3_Init(u32 baud)
{
	//打开串口/IO时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
	//初始化IO口 复用模式	
	//初始化IO口 复用模式	
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStruct.GPIO_Speed = GPIO_Low_Speed;
	GPIO_Init(GPIOD, &GPIO_InitStruct);
	
	GPIO_PinAFConfig(GPIOD,GPIO_PinSource8,GPIO_AF_USART3);//PD8服用到USART3
	GPIO_PinAFConfig(GPIOD,GPIO_PinSource9,GPIO_AF_USART3);//PD9服用到USART3
	
	//初始化USART3控制器
	USART_InitTypeDef USART_InitStruct;
	USART_InitStruct.USART_BaudRate = baud;
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;// 无硬件流控制	
	USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	USART_InitStruct.USART_Parity = USART_Parity_No;		//无校验
	USART_InitStruct.USART_StopBits = USART_StopBits_1;		//一位停止位
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;		//8位数据位
	USART_Init(USART3, &USART_InitStruct);
	
	//中断使能
	USART_ITConfig(USART3,USART_IT_RXNE,ENABLE);//接收中断,接收数据断,接受到一个字节触发
	USART_ITConfig(USART3,USART_IT_IDLE,ENABLE);//空闲中断,接收结束后经过1个帧还没有数据的时候为1
	
	//NVIC
	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel = USART3_IRQn;//要配置的中断通道
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3;//抢占优先级(值越小优先级越高)
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;//次级优先级(值越小优先级越高)
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;//使能,失能中断
	NVIC_Init( &NVIC_InitStruct);
	
	//U3 使能
	USART_Cmd(USART3,ENABLE);
}

U3 u3;
/*************************
函数名称:USART3_IRQHandler(void)
函数功能:USART3接收中断服务函数
返回值:无
形参:无
作者:me
版本:1.0 
函数说明:这里结构体由于是全局的变量,则初始值为0	
*************************/
void USART3_IRQHandler(void)
{
	if(USART_GetITStatus(USART3, USART_IT_RXNE))//如果接收到了数据	0中断未触发 1触发
	{
		USART_ClearITPendingBit(USART3,USART_IT_RXNE);//清除接收中断标志位
		u3.buff[u3.len++] = USART_ReceiveData(USART3);
		
	}
	
	if(USART_GetITStatus(USART3, USART_IT_IDLE))//进入空闲中断
	{
		USART3->SR;//清标志位
		USART3->DR;//清标志位
		
		printf("%s", u3.buff);  // 打印接收到的数据
		u3.len = 0;
		u3.flag = 1;
		for(u8 i=0;i<3;i++)
		{
			printf("0x%02x  ", u3.buff[i]);
			
		}
		printf("\r\n");
		
	}
}

USART3SPRE.H

#ifndef _USART3SPRE_H
#define _USART3SPRE_H

#include "main.h"

typedef struct{
	
	u8 buff[10];//保存接收的字符串
	u16 len;//记录接收数据长度的大小
	u8 flag;//记录数据是否完成	0:未接收到新数据		1:接收数据完成
	
}U3;

extern U3 u3;

void Usart3_Init(u32 baud);
void Voice_Control(void);

#endif

功能函数的实现

/*************************
函数名称:Voice_Control(void)
函数功能:语言控制函数
返回值:无
形参:无
作者:me
版本:1.0 
*************************/
void Voice_Control(void)
{
	//判断U3是否接收数据完成
	if(u3.flag == 1)
	{
		u3.flag = 0;
		switch(u3.buff[1])
		{
			case 0x35:
					//唤醒词	小凌小凌 	需要做什么
					cst.current_page = 0;  // 切换到电机页面显示
					Touch_Range(0,0,240,320);
					//播放语音反馈
					Audio_PlaySong((u8 *)"0:/voice/迎.wav");
			break;
		
			case 0x01:
					//打开风扇		需要做什么
					cst.motor_state = 1;
					cst.current_page = 2;  // 切换到电机页面显示
					TIM_SetCompare3(TIM3,1000);
					Touch_Range(0,0,240,320);
					//语言反馈
					Audio_PlaySong((u8 *)"0:/voice/开风扇.wav");
					
			break;
			
			case 0x02:
					//关闭风扇
					cst.motor_state = 0;
					cst.current_page = 2;  // 切换到电机页面显示
					TIM_SetCompare3(TIM3,0);
					Touch_Range(0,0,240,320);
					//语言反馈
					Audio_PlaySong((u8 *)"0:/voice/关风扇.wav");
					break;
			case 0x10:
					//开灯
					cst.led_state = 1;
					cst.current_page = 1;  // 切换到LED页面显示
					LED1_ON; LED2_ON; LED3_ON;
					Rgb_Control(0xff,0xff,0xff);
					Rgb_Control(0xff,0xff,0xff);
					Rgb_Control(0xff,0xff,0xff);
					Rgb_Control(0xff,0xff,0xff);
					Touch_Range(0,0,240,320);
					//语言反馈
					Audio_PlaySong((u8 *)"0:/voice/开灯.wav");
			break;
			
			case 0x11:
					//关灯
					cst.led_state = 0;
					cst.current_page = 1;  // 切换到LED页面显示
					LED1_OFF; LED2_OFF; LED3_OFF;
					Rgb_Control(0x00,0x00,0x00);
					Rgb_Control(0x00,0x00,0x00);
					Rgb_Control(0x00,0x00,0x00);
					Rgb_Control(0x00,0x00,0x00);
					Touch_Range(0,0,240,320);
					//语言反馈
					Audio_PlaySong((u8 *)"0:/voice/关灯.wav");
			break;
			
			case 0x07:
					//打开空调		开舵机
					cst.servo_state = 1;
					cst.current_page = 3;  // 切换到舵机页面显示
					Servo_motor_Control(30);
					Touch_Range(0,0,240,320);
					//语言反馈
					Audio_PlaySong((u8 *)"0:/voice/开舵机.wav");
			break;
						
			case 0x08:
					//关闭空调		关闭舵机
					cst.servo_state = 0;
					cst.current_page = 3;  // 切换到舵机页面显示
					Servo_motor_Control(120);
					Touch_Range(0,0,240,320);
					//语言反馈
					Audio_PlaySong((u8 *)"0:/voice/关舵机.wav");
			break;
		
		
		
		}
	}
	
}

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐