嵌入式之路,贵在日常点滴
---阿杰在线送代码
目录
一、 LD3320模块介绍(类似于耳朵)
1、简介
2、 有三个工作模式
3、板上管脚资源
二、MP3模块(类似于嘴巴)
1、简介
2、板上管脚资源
3、 MP3模块播放的指令和格式
三、LD3320和MP3之间的对话
四、功能实现
一、 LD3320模块介绍(类似于耳朵)
1、简介
LD3320是一颗基于非特定人语音识别(SI-ASR:Speaker-Independent Automatic Speech Recognition)技术的语音识别芯片,或者说是语音声控芯片。
非特定人语音识别是什么。
通俗点讲就是不用针对指定发音人的识别技术,这种语音识别技术不分年龄、性别,只要发音人说的是相同的语言就可以识别。再说得简单点,就是说只要是拼音可以拼出的发音,都是可以输入芯片进行识别的。
这个语音识别模块它不是智能的,不是说我们问它什么它就可以回答什么,它不像我们手机上的语音助手比如小爱或者Siri那样,能跟我们对答如流。这个模块能识别的内容,是需要我们提前设定好的,我们把要识别的关键词语列好,然后把这些关键词语以字符的形式传送到 LD3320 内部,这样才可以对用户说出的关键词语进行识别,而且在同一时刻,它最多是在50条关键词语中进行识别。
2、 有三个工作模式
1、普通模式:不同的工作模式可以通过程序编程来实现
2、口令模式:模块工作时,我们先要说出一个一级口令,比如“芝麻开门”,模块在识别到正确的一级口令后才进行下一级的识别
3、按键触发模式:模块工作时,我们在进行语音识别之前,需要先按一下按键,接下来才能进行下一步的识别
(不同的工作模式可以通过程序编程来实现)
这三个模式我觉得口令模式比较实用,本实验,我用的就是口令模式。
3、板上管脚资源
如上图中这个语音识别模块的图片,红色框框中的引脚就是我们可以用的IO,竖着那一排由下而上标着P10~P17有八个,横着那一排从左往右是P34、P33,P23~P27,P41,也有8个,这样我们可以用的IO口就有16个啦,这16个IO口可以用来控制继电器,LED,或者接单片机的IO口。
上图中,最左边由下而上标着5V,GND,TX,RX的四个引脚就是用来给模块下载程序的地方,串口就在这里了。我们通过串口给其它模块或者单片机发数据也是从这里接。
二、MP3模块(类似于嘴巴)
1、简介
模块型号名称是MP3-TF-16P,别看它体积不大,功能可是很强大的。简单来说,它就是一个音乐播放器,可以用来播放U盘、TF卡、NOR FLASH里的MP3、WAV、WMA格式的音频文件,同时它也可以作为电脑的声卡,用来播放电脑的声音。它也可以作为读卡器,通过USB连接电脑后就可以更新TF卡里的内容,用来更新文件。
这个小东西能用来放音乐,放广播,它不仅支持通过按键来选曲、调音量,还可以通过单片机来操作,这也就是说,我们可以通过编程,通过串口来发指令,指定它什么时候放哪首歌曲,放多大的音量,中途插不插播广告等,根本不用我们动手,程序上就解决了。
2、板上管脚资源
一共有16个脚,左上角为第一个脚,是模块的电源输入引脚,输入电压范围是3.3-5V,建议用5V供电。
第一个脚是VCC,接5V电
第二个脚是RX,是UART 串行数据输入。
第三个脚是TX,是UART 串行数据输出。
第四个脚是DAC_R,是音频输出右声道,可驱动耳机或者输出音频信号给功放。
第五个脚是DAC_L,是音频输出左声道。
第六个脚是SPK2,是接小喇叭+的,可以驱动小于3W的喇叭。
第七个脚是GND,接电源负极。
第八个脚是SPK1,是接小喇叭-的。
第九个脚是IO1,是触发口,默认上一曲(长按音量-)。
第十个脚也是GND。
第十一个脚是IO2,也是触发口,默认下一曲(长按音量+)。
第十二个脚是ADKEY1,AD口1,当触发时是第一段(长按循环第一段)。
第十三个脚是ADKEY2,AD口2,当触发时是第五段(长按循环第五段)。
第十四个脚是USB+,USB+ DP,接U盘或插电脑的USB口。
第十五个脚是USB-,USB- DM,接U盘或插电脑的USB口。
最后一个是BUSY引脚,它是播放指示,有音频播放时是输出低电平,没有音频播放时是输出高电平。
串口接线电路图:
3、 MP3模块播放的指令和格式
指定播放一个MP3文件的指令是这样的:7E FF 06 03 00 00 01 FE F7 EF
--》03 00 00 01这四个字节,代表的就是指定播放第一首歌曲,不需要信息反馈
从语音模块(LD3320)通过串口给MP3模块发这一串东西,MP3模块收到后就知道这是要播放第几个MP3文件
7E 是起始位,固定不变的,都要有它
FF 这是这个模块的版本信息,也是固定不变的
06 指的是从上一字节开始,到后面的字节个数,也就是说,从FF开始,到06 03 00 00 01为止,一共有六个字节
03 在这里,指的是指定曲目播放,也是固定不变的
00 在这里指的是不需要反馈信息。如果是01,就是要反馈信息,这里的反馈信息指的是MP3播放模块反馈给语音识别模块的信息
00 01 这里指的是歌曲序号,00是数据高字节,01是数据低字节,低字节01在这里表示的就是播放第一首歌曲
FE和F7指的是累加和校验,是用来给数据查错的
最后一位EF是结束位,当接收到这一位,就说明整个数据发送完毕了
-》设置那么复杂的指令是为了提高数据传输时的准确性
三、LD3320和MP3之间的对话
思路:对于命令的传输我们通过串口来发送,当语音识别模块(LD3320)识别到我们的命令后,让它通过串口给这个MP3模块发送一串指令,这MP3模块接收到指令后,就播放相对应的MP3文件,这样不就实现了系统对我们的应答了嘛。
比如我设置这个语音识别模块的一级口令为“阿杰在线送代码”,我叫一声“阿杰在线送代码”,语音识别模块识别成功后就给MP3模块发送一个指令,指定播放一个MP3文件,而这个文件的内容是“主人,我在”,这样就完成了一个交互的过程。
好,先把硬件连接好,来个简单的电路图:
左边这个是语音识别模块,右边的是MP3模块,它们电源正极和地都需要接,这个不用说,然后就是数据的传输了,通过串口来,它们的RX和TX交叉来接,RX接TX,TX接RX,这就完成了,很简单。
四、功能实现
通过语音识别模块(LD3320)识别想要执行的动作,MP3给出回应开始执行动作。
实现:
一级指令:科技之城
MP3:主人,我在
二级指令:转动舵机
MP3:好的,开始转动舵机 (此时STM32读取LD3320给出的高低电平状态来开始执行动作)
一级指令:科技之城
MP3:主人,我在
二级指令:恢复原位
MP3:好的,恢复原位 (此时STM32读取LD3320给出的高低电平状态来开始执行动作)
程序我们直接利用商家给的初始化程序,我们去做修改即可。
需要修改的地方
废话不说上代码
*LDChips.cuint8 LD_AsrAddFixed()
{ ...#define DATE_A 9 /*数组二维数值*/#define DATE_B 24 /*数组一维数值*/uint8 code sRecog[DATE_A][DATE_B] = {"ke ji zhi cheng",\ "kai shi shao shui",\"guan bi shao shuii",\"kai shi pao cha",\"hui shou di yi ge bei zi",\"kai shi xi cha bei",\"zhuan dong duo ji",\"hui fu yuan wei",\"yin lue"}; /*添加关键词,用户修改*/uint8 code pCode[DATE_A] = {CODE_CMD,\CODE_KSSS,\CODE_GBSS,\CODE_KSPC,\CODE_HSDYGBZ,\CODE_KSXCB,\ CODE_ZDDJ,\CODE_HFYW,\CODE_YL}; /*添加识别码,用户修改*/ ....
}
*LDChips.h//识别码客户修改处
#define CODE_CMD 0x00 //该命令码0x00用户不可进行修改。
#define CODE_GBSS 0x01 //开始烧水
#define CODE_KSSS 0x02 //关闭烧水
#define CODE_KSPC 0x04 //开始泡茶
#define CODE_HSDYGBZ 0x05 //回收第一个杯子
#define CODE_KSXCB 0x06 //开始洗茶杯
#define CODE_ZDDJ 0x07 //转动电机
#define CODE_HFYW 0x08 //恢复原位
#define CODE_YL 0x09 //音乐
*usart.c
//直接在MP3的串口调试移植,需要修改一点东西#include "config.h"
#define FOSC 22118400L //System frequency
uint32_t baud=9600; //UART baudrate
unsigned char Send_buf[20]; /*指针溢出了*/
/************************************************************************
函 数 名: 串口初始化
功能描述: STC10L08XE 单片机串口初始化函数
返回函数: none
其他说明: none
**************************************************************************/
void UartIni(void)
{SCON = 0x50; //8-bit variable UARTTMOD = 0x20; //Set Timer1 as 8-bit auto reload modeTH1 = TL1 = -(FOSC/12/32/baud); //Set auto-reload vauleTR1 = 1; //Timer1 start runES = 1; //Enable UART interruptEA = 1; //Open master interrupt switch
}
/***********************************************************
* 名 称:
* 功 能:
* 入口参数: 无
* 出口参数:无
* 说 明:
**********************************************************/
void Uart_Isr() interrupt 4 using 1
{ if(RI){ }}
/************************************************************************
功能描述: 串口发送一字节数据
入口参数: DAT:带发送的数据
返 回 值: none
其他说明: none
**************************************************************************/
void Uart_PutByte(uint8_t DAT)
{ES = 0;TI=0;SBUF = DAT;while(TI==0);TI=0;ES = 1;
}
/************************************************************************
功能描述: 串口发送字符串数据
入口参数: *DAT:字符串指针
返 回 值: none
其他说明: API 供外部使用,直观!
**************************************************************************/
void PrintCom(uint8_t *DAT)
{while(*DAT){Uart_PutByte(*DAT++);}
}void SendCmd(int len)
{ int a = 0;Uart_PutByte(0x7E); //起始for(a=0; a<len; a++)//数据{ Uart_PutByte(Send_buf[a]) ; } Uart_PutByte(0xEF) ;//结束
} void DoSum(unsigned char *Str,unsigned char len)
{ int xorsum = 0; unsigned char a; for(a=0; a<len; a++) { xorsum = xorsum + Str[a]; } xorsum = 0 -xorsum; *(Str+a) = (unsigned char)(xorsum >>8); *(Str+a+1) = (unsigned char)(xorsum & 0x00ff);
}
void Uart_SendCMD(unsigned char CMD ,unsigned char feedback ,int dat)
{ Send_buf[0] = 0xff; //保留字节 Send_buf[1] = 0x06; //长度Send_buf[2] = CMD; //控制指令Send_buf[3] = feedback;//是否需要反馈Send_buf[4] = (unsigned char )((dat >> 8)&0xFF);//datah Send_buf[5] = (unsigned char )(dat&0xFF); //datal DoSum(Send_buf,6); //校验SendCmd(8); //发送此帧数据
}
*main.cvoid User_handle(uint8 dat)
{//UARTSendByte(dat);//串口识别码(十六进制)if(0==dat){G0_flag=ENABLE;
// PrintCom("“主人,我在”命令识别成功\r\n");LED=0;Uart_SendCMD(0x03,0x00,0x01);}else if(ENABLE==G0_flag){ G0_flag=DISABLE;LED=1;switch(dat) /*对结果执行相关操作,客户修改*/{case CODE_KSSS: /*命令“全开”*/
// PrintCom("“好的 开始烧水”命令识别成功\r\n");//串口输出提示信息(可删除)Uart_SendCMD(0x03,0x00,0x02);break;case CODE_GBSS: /*命令“测试”*/
// PrintCom("“好的 关闭烧水”命令识别成功\r\n"); //串口输出提示信息(可删除)Uart_SendCMD(0x03,0x00,0x03);break;case CODE_KSPC: /*命令“复位”*/
// PrintCom("“好的 开始泡茶”命令识别成功\r\n"); //串口输出提示信息(可删除)Uart_SendCMD(0x03,0x00,0x04); PA3=1;//让PA3端口为高电平 break;case CODE_HSDYGBZ: /*命令“复位”*/
// PrintCom("“好的 回收第一个杯子”命令识别成功\r\n"); //串口输出提示信息(可删除)Uart_SendCMD(0x03,0x00,0x05); PA3=0;//让PA3端口为低电平break;case CODE_KSXCB: /*命令“复位”*/
// PrintCom("“好的 开始洗茶杯”命令识别成功\r\n"); //串口输出提示信息(可删除)Uart_SendCMD(0x03,0x00,0x06); PA4=1;//让PA4端口为高电平break;case CODE_ZDDJ:Uart_SendCMD(0x03,0x00,0x07);PA1=1;//让PA2端口为高电平break;case CODE_HFYW:Uart_SendCMD(0x03,0x00,0x08);PA1=0;//让PA1端口为高电平break;case CODE_YL:Uart_SendCMD(0x03,0x00,0x09);break;default:
// PrintCom("请重新识别发口令\r\n"); //串口输出提示信息(可删除)break;} } else {
// PrintCom("请说出一级口令\r\n"); //串口输出提示信息(可删除) }
}
上电后,你会发现MP3不给你回应,你可以尝试给MP3重新复位,即地拔掉再插上
下面是STM32的代码
//用来判断语音识别给的是高电平还是低电平来执行动作#include "duoji.h"void duoji_init(void)
{GPIO_InitTypeDef GPIO_InitStructure;//定义一个结构体变量RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能GPIOA 使能PC端口时钟 rcc.h-693GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;//PA15 设置成下拉输入GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//IO口速度为50MHzGPIO_Init(GPIOA,&GPIO_InitStructure);//根据设定参数初始化GPIOE.5 }
#define DUOJI GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8)//读取 PA8 电平
main.c//舵机调试if(DUOJI == 1) {TIM_SetCompare1(TIM3,75);//舵机90度 delay_ms(500);TIM_SetCompare1(TIM3,100);//舵机135度delay_ms(500); TIM_SetCompare1(TIM3,125);//舵机180度delay_ms(500); TIM_SetCompare1(TIM3,100);//舵机135度delay_ms(500);TIM_SetCompare1(TIM3,75);//舵机90度 delay_ms(500);TIM_SetCompare1(TIM3,50);//舵机45度 delay_ms(500);TIM_SetCompare1(TIM3,20);//舵机45度 delay_ms(500);} else{TIM_SetCompare1(TIM3,75);//舵机90度 delay_ms(500);}