一、 前言
近来复刻一个桌面宠物-小呆项目用到了LD3320语音识别模块,简单的分享下使用。
LD3320 是一颗基于非特定人语音识别芯片。芯片上集成了高精度的 A/D 和 D/A 接口,不再必要外接辅助的Flash 和 RAM,即可以实现语音识别/声控/人机对话功能。而且,识别的关键词语列表是可以动态编辑的。
这里先容下什么是非特定人语音识别?
非特定人语音识别是一种不针对特定发音人的语音识别技术。这种技术不分年事、性别,只要发音人说的是相同的语言就可以进行识别。它与特定人语音识别技术形成对比,后者是专门针对一个特定人的声音进行识别,而非特定人语音识别则更加灵活,能够满意不同人的语音识别需求,恰当广泛人群应用。平凡点说,只要是拼音可以拼出的发音都是可以输入芯片进行识别的。
二、硬件
1.原理图
如下图所示,原理图主要由几部分构成
这里的主控IC是STC11L08XE,它是一种STC11系列的51单片机。这里主要接纳的是串口通信的方式。
另外一个IC就是语音芯片LD3320,它内置一个麦克风放大器,它可以对周围的声音进行收罗,并将收罗到的音频信号送入芯片内部的语音信号处理器。
电源输入方面主要是5V输入,颠末AMS1117-3.3进行降压为3.3V,3.3V再给两个IC供电。
这里主要讲一个点,如果要控制喇叭音量,可以通过外部电路的电阻来实现,比方下图中电阻 R9 和 R8 的阻值分别为
33K 和 10K,那么 33/10=3.3,声音被放大了约 3 倍。而如果给 R9 接入可变电阻,就可以手动调治音量了。
管脚 12(MBS)是麦克风偏置,保证能输出一个浮动电压给麦克风。
2.产物参数
产物参数
规格:43*29.7MM
供电电压:DC5V
IO口输出:高电平3.3V
通信方式:串口通信(3.3V TTL电平,不支持max232,RS485)
单片机:STC11L08XE、flash–>8K、SRAM–>1280、eeprom–>32K
三、软件
1.语音识别原理
把通过MIC输入的声音进行频谱分析->提取语音特征->和关键词语列表中的关键词语进行对比匹配->找出得分最高的关键词语作为识别结果输出。
语音识别芯片能在两种情况下给出识别结果:
- 外部送入预定时间的语音数据后(好比5秒钟的语音数据),芯片对这些语音数据运算分析后,给出识别结果
- 外部送入语音数据流,语音识别芯片通过端点检测VAD(voice activity detection)检测出用户制止语言,把用户开始语言到制止语言之间的语音数据进行运算分析后,给出识别结果
这里简单先容下VAD的工作原理:VAD(Voice Activity Detection) 技术是在一段语音数据流中,判断出哪个时间点是人声的开始,哪个时间点是人声的结束。判断的依据是,在背景声音的基础上有了语音发音,则视为声音的开始。而后,检测到一段一连时间的背景音(好比600毫秒),则视为人声语言结束。
2.用户使用模式
用户可以通过编程,设置两种不同的用户使用模式:“触发识别模式"和"循环识别模式”。
2.1 触发识别模式
系统的主控MCU在接受到外界一个触发后(好比用户按动某个按键),启动LD3320芯片的一个定时识别过程(好比5秒钟),要求用户在这个定时过程中说出要识别的语音关键词语。过了这个过程后,必要用户再次触发才能再次启动一个识别过程。
2.2 循环识别模式
系统的主控MCU反复启动识别过程。如果没有人语言没有识别结果,则每次识别过程的定时到时后再启动一个识别过程;如果有识别结果,则根据识别作相应处理后(好比播放某个声音作为答复)再启动一个识别过程。
一样平常来说,触发识别恰当于识别精度要求比力高的场所。外界触发后,产物可以播放提示音或者其他方式来提示用户在接下来的几秒钟内说出要识别的内容,这样来引导用户在规定的时间内只说出要识别的内容,从而保证比力高的识别率。
而循环识别比力恰当于必要始终进行语音监控的场所,或者没有按键等其他装备控制识别开始的场所。而这种状态,识别正确度会有肯定下降,在循环识别的过程中,用户的其他语言声音,或者外界的其他声音,都有可能被识别引擎误识别堕落误的结果,必要产物的控制逻辑都作相应的处理。
3.语音识别程序
3.1 并行方式读写
LD 芯片的四种读写方式,分别是串行 SPI 的软、硬方式和并行 8 位总线的软、硬方式。这里主要讲下用到的并行方式–直接读写 (硬件实现并行读写方式 硬件实现并行读写方式),其他三种方式感兴趣的小伙伴可以自行去相识下。
控制串行/并行的管脚:ICR_MODE 连接 LD3320 芯片的 MD ,高电平为 SPI 方式,低电平为并行方式。
- LD_MODE = 0; // 设置MD管脚为低,并行模式读写
复制代码 由于设计硬件电路板时,思量到了双方芯片读写的时序特征,那么在合理连接的基础上,通过 2 条语句就可实现对 LD 芯片的操纵。这种方式代码简洁,执行速度最快。这是因为 STC 的单片机 STC10L08XE 自身带有硬件的并口方式,STC10L08XE 有单独的 WR和 RD 端口,可以在读写并行总线时,自动产生 WR 和 RD 信号。
- #define LD_INDEX_PORT (*((volatile unsigned char xdata*)(0x8100)))
- #define LD_DATA_PORT (*((volatile unsigned char xdata*)(0x8000)))
- void LD_WriteReg( unsigned char address, unsigned char dataout )
- {
- LD_INDEX_PORT = address;
- LD_DATA_PORT = dataout;
- }
- unsigned char LD_ReadReg( unsigned char address )
- {
- LD_INDEX_PORT = address;
- return (unsigned char)LD_DATA_PORT;
- }
复制代码 3.2 初始化
语音识别用初始化(包括通用初始化)→写入识别列表→开始识别,并准备好制止相应函数,打开制止允许位。这里必要说明一下,如果不用制止方式,也可以通过查询方式工作。在“开始识别”后,读取寄存器 B2H 的值,如果为 21H 就表现有识别结果产生。在此之后读取候选项等操纵与制止方式相同。
- /************************************************************************
- 功能描述: LD模块命令初始化
- 入口参数: none
- 返 回 值: none
- 其他说明: 该函数为出厂配置,一般不需要修改; 有兴趣的可对照开发手册根据需要自行修改。
- **************************************************************************/
- void LD_Init_Common()
- {
- LD_ReadReg(0x06);
- LD_WriteReg(0x17, 0x35);
- delay(10);
- LD_ReadReg(0x06);
- LD_WriteReg(0x89, 0x03);
- delay(5);
- LD_WriteReg(0xCF, 0x43);
- delay(5);
- LD_WriteReg(0xCB, 0x02);
- /*PLL setting*/
- LD_WriteReg(0x11, LD_PLL_11);
- LD_WriteReg(0x1E, 0x00);
- LD_WriteReg(0x19, LD_PLL_ASR_19);
- LD_WriteReg(0x1B, LD_PLL_ASR_1B);
- LD_WriteReg(0x1D, LD_PLL_ASR_1D);
- delay(10);
- LD_WriteReg(0xCD, 0x04);
- // LD_WriteReg(0x17, 0x4c);
- delay(5);
- LD_WriteReg(0xB9, 0x00);
- LD_WriteReg(0xCF, 0x4F);
- LD_WriteReg(0x6F, 0xFF);
- }
- /************************************************************************
- 功能描述: LD模块 ASR功能初始化
- 入口参数: none
- 返 回 值: none
- 其他说明: 该函数为出厂配置,一般不需要修改;有兴趣的可对照开发手册根据需要自行修改。
- **************************************************************************/
- void LD_Init_ASR()
- {
- LD_Init_Common();
- LD_WriteReg(0xBD, 0x00);
- LD_WriteReg(0x17, 0x48);
- delay( 10 );
- LD_WriteReg(0x3C, 0x80);
- LD_WriteReg(0x3E, 0x07);
- LD_WriteReg(0x38, 0xff);
- LD_WriteReg(0x3A, 0x07);
- LD_WriteReg(0x40, 0);
- LD_WriteReg(0x42, 8);
- LD_WriteReg(0x44, 0);
- LD_WriteReg(0x46, 8);
- delay( 1 );
- }
复制代码 这里留意,下面三个寄存器,会随晶振频率变化而设置不同,请根据使用的晶振频率修改参考程序中的 CLK_IN。
- #define CLK_IN 22.1184 /* 用户注意修改输入的晶振时钟大小 */
- #define LD_PLL_ASR_19 (uint8)(CLK_IN*32.0/(LD_PLL_11+1) - 0.51)
- #define LD_PLL_ASR_1B 0x48
- #define LD_PLL_ASR_1D 0x1f
-
- LD_WriteReg(0x19, LD_PLL_ASR_19);
- LD_WriteReg(0x1B, LD_PLL_ASR_1B);
- LD_WriteReg(0x1D, LD_PLL_ASR_1D);
复制代码 3.3 写入识别列表
列表的规则是,每个识别条目对应一个特定的编号(1 个字节),不同的识别条目标编号可以相同,而且不用一连。本芯片最多支持 50个识别条目,每个识别条目是标准平凡话的汉语拼音(小写),每 2个字(汉语拼音)之间用一个空格间隔。编号可以相同,可以不一连,但是数值要小于 256(00H~FFH)。

先先容一个读取 0xB2 寄存器的函数,如果在以后的 ASR 命令函数前不能够读取到正确 Idle 状态,说明芯片内部可能堕落了。经拷机测试,当使用的电源电压/电流出现不稳定有较大波动时,有小概率会出现这种情况。出现这种情况时,建议 Reset LD3320 芯片,重新启动设置芯片。
- /************************************************************************
- 功能描述: 检测LD模块是否空闲
- 入口参数: none
- 返 回 值: flag:1-> 空闲
- 其他说明: none
- **************************************************************************/
- uint8 LD_Check_ASRBusyFlag_b2()
- {
- uint8 j;
- uint8 flag = 0;
- for (j = 0; j < 10; j++)
- {
- if (LD_ReadReg(0xb2) == 0x21)
- {
- flag = 1;
- break;
- }
- delay(10);
- }
- return flag;
- }
- /************************************************************************
- 功能描述: 复位LD模块
- 入口参数: none
- 返 回 值: none
- 其他说明: none
- **************************************************************************/
- void LD_Reset()
- {
- RSTB = 1;
- delay(5);
- RSTB = 0;
- delay(5);
- RSTB = 1;
- delay(5);
- CSB = 0;
- delay(5);
- CSB = 1;
- delay(5);
- }
- /************************************************************************
- 功能描述: 向LD模块添加关键词
- 入口参数: none
- 返 回 值: flag:1->添加成功
- 其他说明: 用户修改.
- 1、根据如下格式添加拼音关键词,同时注意修改sRecog 和pCode 数组的长度
- 和对应变了k的循环置。拼音串和识别码是一一对应的。
- 2、开发者可以学习"语音识别芯片LD3320高阶秘籍.pdf"中
- 关于垃圾词语吸收错误的用法,来提供识别效果。
- 3、”xiao dai “ 为口令,故在每次识别时,必须先发一级口令“小呆”
- **************************************************************************/
- uint8 LD_AsrAddFixed()
- {
- uint8 k, flag;
- uint8 nAsrAddLength;
- #define DATE_A 50 /*数组二维数值*/
- #define DATE_B 20 /*数组一维数值*/
- uint8 code sRecog[DATE_A][DATE_B] =
- {
- "xiao dai", \
- "li zheng", \
- "qi li", \
- "qi shen", \
- "zhan qi lai", \
- "qian jin", \
- "zou", \
- "hou tui", \
- "zuo zhuan", \
- "you zhuan", \
- "pa xia", \
- "wo xia",\
- "zuo xia",\
- "ca lian",\
- "zuo xia ca lian",\
- "shen lan yao",\
- "tai tou",\
- "da zhao hu",\
- "da ge zhao hu",\
- "ha lou",\
- "hai",\
- "yao bai",\
- "tai shou",\
- "tiao wu",\
- "shui jiao",\
- "nan shou",\
- "biao yan bu kai xin",\
- "biao bai",\
- "yuan su zhou qi biao",\
- "xiao xun",\
- "wei shen me",\
- "ni kai xin ma",\
- "cha kan kai xin zhi",\
- "lei bu lei",\
- "cha kan ti li zhi",\
- "cha kan zhi shu",\
- }; /*添加关键词,用户修改*/
- uint8 code pCode[DATE_A] =
- {
- CODE_CMD, \
- CODE_1, \
- CODE_2, \
- CODE_2, \
- CODE_2, \
- CODE_3, \
- CODE_3, \
- CODE_4, \
- CODE_5, \
- CODE_6, \
- CODE_7, \
- CODE_8, \
- CODE_9, \
- CODE_9, \
- CODE_9, \
- CODE_10, \
- CODE_11, \
- CODE_12, \
- CODE_12, \
- CODE_12, \
- CODE_12, \
- CODE_13, \
- CODE_14, \
- CODE_16, \
- CODE_17, \
- CODE_17, \
- CODE_18, \
- CODE_19, \
- CODE_20, \
- CODE_21, \
- CODE_22, \
- CODE_23, \
- CODE_23, \
- CODE_24, \
- CODE_24, \
- CODE_25, \
- CODE_26, \
- CODE_27, \
- CODE_28, \
- CODE_29, \
- CODE_30, \
- CODE_31, \
- CODE_32, \
- CODE_33, \
- CODE_34, \
- CODE_35, \
- }; /*添加识别码,用户修改*/
- flag = 1;
- for (k = 0; k < DATE_A; k++)
- {
- if(LD_Check_ASRBusyFlag_b2() == 0)
- {
- flag = 0;
- break;
- }
- LD_WriteReg(0xc1, pCode[k] );
- LD_WriteReg(0xc3, 0 );
- LD_WriteReg(0x08, 0x04);
- delay(1);
- LD_WriteReg(0x08, 0x00);
- delay(1);
- for (nAsrAddLength = 0; nAsrAddLength < DATE_B; nAsrAddLength++)
- {
- if (sRecog[k][nAsrAddLength] == 0)
- break;
- LD_WriteReg(0x5, sRecog[k][nAsrAddLength]);
- }
- LD_WriteReg(0xb9, nAsrAddLength);
- LD_WriteReg(0xb2, 0xff);
- LD_WriteReg(0x37, 0x04);
- }
- return flag;
- }
复制代码 3.4 开始识别
设置几个相关的寄存器,就可以控制 LD3320 芯片开始语音识别。值得留意:单片机程序中,一样平常会用一个全局变量控制当前状态,(比方:LD_ASR_RUNING 状态或者 LD_ASR_FOUNDOK 状态),在编程时肯定要把对该状态的设置放在正式 LD3320 芯片开始识别以前。
- /************************************************************************************/
- // nAsrStatus 用来在main主程序中表示程序运行的状态,不是LD3320芯片内部的状态寄存器
- // LD_ASR_NONE: 表示没有在作ASR识别
- // LD_ASR_RUNING: 表示LD3320正在作ASR识别中
- // LD_ASR_FOUNDOK: 表示一次识别流程结束后,有一个识别结果
- // LD_ASR_FOUNDZERO: 表示一次识别流程结束后,没有识别结果
- // LD_ASR_ERROR: 表示一次识别流程中LD3320芯片内部出现不正确的状态
- /***********************************************************************************/
- uint8 idata nAsrStatus = 0;
- nAsrStatus = LD_ASR_NONE; // 初始状态:没有在作ASR
- //
- while(1)
- {
- switch(nAsrStatus)
- {
- case LD_ASR_RUNING:
- case LD_ASR_ERROR:
- break;
- case LD_ASR_NONE:
- {
- nAsrStatus = LD_ASR_RUNING;
- if (RunASR() == 0) /* 启动一次ASR识别流程:ASR初始化,ASR添加关键词语,启动ASR运算*/
- {
- nAsrStatus = LD_ASR_ERROR;
- }
- break;
- }
- case LD_ASR_FOUNDOK: /* 一次ASR识别流程结束,去取ASR识别结果*/
- {
- nAsrRes = LD_GetResult(); /*获取结果*/
- User_handle(nAsrRes);//用户执行函数
- nAsrStatus = LD_ASR_NONE;
- break;
- }
- case LD_ASR_FOUNDZERO:
- default:
- {
- nAsrStatus = LD_ASR_NONE;
- break;
- }
- }// switch
- }// while
复制代码 ASR识别流程的代码如下所示:
- /************************************************************************
- 功能描述: 运行ASR识别流程
- 入口参数: none
- 返 回 值: asrflag:1->启动成功, 0—>启动失败
- 其他说明: 识别顺序如下:
- 1、RunASR()函数实现了一次完整的ASR语音识别流程
- 2、LD_AsrStart() 函数实现了ASR初始化
- 3、LD_AsrAddFixed() 函数实现了添加关键词语到LD3320芯片中
- 4、LD_AsrRun() 函数启动了一次ASR语音识别流程
- 任何一次ASR识别流程,都需要按照这个顺序,从初始化开始
- **************************************************************************/
- uint8 RunASR(void)
- {
- uint8 i = 0;
- uint8 asrflag = 0;
- for (i = 0; i < 5; i++) // 防止由于硬件原因导致LD3320芯片工作不正常,所以一共尝试5次启动ASR识别流程
- {
- LD_AsrStart();
- delay(50);
- if (LD_AsrAddFixed() == 0)
- {
- LD_Reset(); // LD3320芯片内部出现不正常,立即重启LD3320芯片
- delay(50); // 并从初始化开始重新ASR识别流程
- continue;
- }
- delay(10);
- if (LD_AsrRun() == 0)
- {
- LD_Reset(); // LD3320芯片内部出现不正常,立即重启LD3320芯片
- delay(50); // 并从初始化开始重新ASR识别流程
- continue;
- }
- asrflag = 1;
- break; // ASR流程启动成功,退出当前for循环。开始等待LD3320送出的中断信号
- }
- return asrflag;
- }
- /************************************************************************
- 功能描述: 启动ASR
- 入口参数: none
- 返 回 值: none
- 其他说明: none
- **************************************************************************/
- void LD_AsrStart()
- {
- LD_Init_ASR();
- }
- /************************************************************************
- 功能描述: 运行ASR
- 入口参数: none
- 返 回 值: 1:启动成功
- 其他说明: none
- **************************************************************************/
- uint8 LD_AsrRun()
- {
- EX0 = 0;
- LD_WriteReg(0x35, MIC_VOL);
- LD_WriteReg(0x1C, 0x09);
- LD_WriteReg(0xBD, 0x20);
- LD_WriteReg(0x08, 0x01);
- delay( 1 );
- LD_WriteReg(0x08, 0x00);
- delay( 1 );
- if(LD_Check_ASRBusyFlag_b2() == 0)
- {
- return 0;
- }
- // LD_WriteReg(0xB6, 0xa); //识别时间 1S
- // LD_WriteReg(0xB5, 0x1E); //背景音段时间 300ms
- // LD_WriteReg(0xB8, 10); //结束时间
- // LD_WriteReg(0x1C, 0x07); //配置双通道音频信号做为输入信号
- LD_WriteReg(0x1C, 0x0b); //配置麦克风做为输入信号
- LD_WriteReg(0xB2, 0xff);
- delay( 1);
- LD_WriteReg(0x37, 0x06);
- delay( 1 );
- LD_WriteReg(0x37, 0x06);
- delay( 5 );
- LD_WriteReg(0x29, 0x10);
- LD_WriteReg(0xBD, 0x00);
- EX0 = 1;
- return 1;
- }
复制代码 3.5 相应制止
如果麦克风收罗到声音,不管是否识别出正常结果,都会产生一个制止信号。而制止程序要根据寄存器的值分析结果。读取 BA 寄存器的值,可以知道有几个候选答案,而 C5 寄存器里的答案是得分最高、最可能正确的答案。

- 功能描述: 中断处理函数
- 入口参数: none
- 返 回 值: none
- 其他说明: 当LD模块接收到音频信号时,将进入该函数,
- 判断识别是否有结果,如果没有从新配置寄
- 存器准备下一次的识别。
- **************************************************************************/
- void ProcessInt0(void)
- {
- uint8 nAsrResCount = 0;
- EX0 = 0;
- ucRegVal = LD_ReadReg(0x2B);
- LD_WriteReg(0x29, 0) ;
- LD_WriteReg(0x02, 0) ;
- if((ucRegVal & 0x10) &&
- LD_ReadReg(0xb2) == 0x21 &&
- LD_ReadReg(0xbf) == 0x35) /*识别成功*/
- {
- nAsrResCount = LD_ReadReg(0xba);
- if(nAsrResCount > 0 && nAsrResCount <= 4)
- {
- nAsrStatus = LD_ASR_FOUNDOK;
- }
- else
- {
- nAsrStatus = LD_ASR_FOUNDZERO;
- }
- }/*没有识别结果*/
- else
- {
- nAsrStatus = LD_ASR_FOUNDZERO;
- }
- LD_WriteReg(0x2b, 0);
- LD_WriteReg(0x1C, 0); /*写0:ADC不可用*/
- LD_WriteReg(0x29, 0) ;
- LD_WriteReg(0x02, 0) ;
- LD_WriteReg(0x2B, 0);
- LD_WriteReg(0xBA, 0);
- LD_WriteReg(0xBC, 0);
- LD_WriteReg(0x08, 1); /*清除FIFO_DATA*/
- LD_WriteReg(0x08, 0); /*清除FIFO_DATA后 再次写0*/
- EX0 = 1;
- }
- /************************************************************************
- 功能描述: 获取识别结果
- 入口参数: none
- 返 回 值: LD_ReadReg(0xc5 ); 读取内部寄存器返回识别码。
- 其他说明: none
- **************************************************************************/
- uint8 LD_GetResult()
- {
- return LD_ReadReg(0xc5 );
- }
复制代码 值得留意:获取识别结果LD_ReadReg(0xba); 多少条候选识别结果,值 1~4 说明是有正确的识别结果。
4 个候选结果的读取:根据 0xba 决定读取几个识别结果。
- LD_ReadReg(0xc5);
- LD_ReadReg(0xc7);
- LD_ReadReg(0xc9);
- LD_ReadReg(0xcb);
复制代码 在目前的程序中,只读取了最优候选。在其他使用场所,如果必要读取其他候选,用户可以自己编程实现。
另外LD3320还支持像MP3一样播放的声音,这些声音是事先在PC机上录制好的MP3文件,在PC机上合成到一个文件 voice.dat中
可以把这个voide.dat存储到用户系统中的存储芯片中,诸如 spi-flash中,在必要播放时,用户的主控MCU可以到spi-flash中根据要播放文件的起始地点无读取MP3数据,并送入LD3320进行播放。这里不详细先容,感兴趣的小伙伴可以自己去相识下,最后讲下怎样烧写程序。
4.烧写程序
如果没有烧写软件,可以去这里stc-isp 单片机烧录软件安装与使用下载
4.1 硬件连接
用串口调试工具连接LD3320语音识别模块,连接如下:
串口调试工具LD3320语音识别模块GNDGNDRXDTXDTXDRXD3V33.3V 留意 RXD/TXD 必须交叉连接
4.2 打开STC-ISP软件
4.3 按要求设置软件
- 选择单片机型号
- 串口号一样平常会直接识别,识别不到就根据装备管理器的端口选择串口号
- 打开程序文件,选择程序hex文件
- 点击下载/编程进行烧写程序,这时间必要重新给模块通上电源(拔下 GND 连接线,再重新连接,即可以重新上电,下载程序时冷启动也是一样的操纵)
4.4 烧写成功
四、总结
今天主要讲了LD3320语音识别模块的简单应用。
感谢你的观看!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |