LD3320语音识别(基于ESP32)
- (PS:本文章只写识别)
-
一、关于LD3320的简介
LD3320 芯片是一款“语音识别”专用芯片。该芯片集成了语音识别处理器和一些外部电路,包括 AD、DA 转换器、麦克风接口、声音输出接口等。本芯片不需要外接任何的辅助芯片如 Flash、RAM 等,直接集成在现有的产品中即可以实现语音识别/声控/人机对话功能。并且,识别的关键词语列表是可以任意动态编辑的(关键词为中文拼音,比较银杏化)。
-
二、功能介绍
特征:
- 工作供电为 3.3V
- 不需要训练,只需要指定关键词,识别正确率高不需要外接任何辅助的 Flash 芯片,RAM 芯片和 AD 芯片,就可以完成语
-
音识别功能。真正提供了单芯片语音识别解决方案每次识别最多可以设置 50 项候选识别句,每个识别句可以是单字,词组或短句,长度为不超过 10 个汉字或者 79 个字节的拼音串。识别句内容可以编辑修改。
-
芯片内部已经准备了 16 位 A/D 转换器、16 位 D/A 转换器和功放电路,
麦克风、立体声耳机和单声道喇叭可以很方便地和芯片管脚连接。立体
声耳机接口的输出功率为 20mW,而喇叭接口的输出功率为 550mW,能产
生清晰响亮的声音 - 支持并行和串行接口,串行方式可以简化与其他模块的连接
- 可设置为休眠状态,而且可以方便地激活
-
支持 MP3 播放功能,无需外围辅助器件,主控 MCU 将 MP3 数据依次送入
LD3320 芯片内部就可以从芯片的相应 PIN 输出声音。产品设计可以选
择 从 立 体 声 的 耳 机 或 者 单 声 道 喇 叭 来 获 得 声 音 输 出 。 支 持
MPEG1(ISO/IEC11172-3), MPEG2(ISO/IEC13818-3) 和 MPEG 2.5 layer
3 等格式 -
芯片必须连接外部时钟,可接受的频率范围是 4—48MHz;而芯片内部还
有 PLL 频率合成器,可产生特定的频率供内部模块使用 - 通信是通过串行接口SPI协议与外部主控板连接
-
三、IO接线
下面这个是中断触发,连IRQ引脚(我这里写的是ESP32的GPIO4)
-
四、ESP32的LD3320驱动码源
#include "ld3320.h" #include <SPI.h> uint8_t g_Mic; uint8_t MIC_VOL = 0x55; //ADC增益初始值 uint8_t speech_endpoint = 0x10; //语音端点检测初始值 uint8_t speech_start_time = 0x08; //语音端点检测开始时间初始值 uint8_t speech_end_time = 0x10; //语音端点检测结束时间初始值 uint8_t voice_max_length = 0xC3; //最长语音段时间,默认20秒 uint8_t noise_time = 0x02; //忽略上电噪声时间 //uint8_t ASR_time最长时间 int readflag = 0; int readnum = 0; byte transfer(byte _data); VoiceRecognition::VoiceRecognition() {} int VoiceRecognition::read() //识别结果读取 { if (readflag == 1) { readflag = 0; return readnum; } return -1; } void update() //中断服务函数 { uint8_t Asr_Count = 0; Serial.print("into interrupt..."); Serial.print("\r\n"); if ((readReg(0x2b) & 0x10) && readReg(0xb2) == 0x21 && readReg(0xbf) == 0x35) //如果有语音识别中断、DSP闲、ASR正常结束 { writeReg(0x29, 0); ///////////关中断 writeReg(0x02, 0); /////////////关FIFO中断 Asr_Count = readReg(0xba); //读中断辅助信息 if (Asr_Count > 0 && Asr_Count < 4) //////如果有识别结果 { readnum = readReg(0xc5); readflag = 1; } writeReg(0x2b, 0); writeReg(0x1C, 0); } readReg(0x06); delay(10); readReg(0x06); writeReg(0x89, 0x03); delay(5); writeReg(0xcf, 0x43); delay(5); writeReg(0xcb, 0x02); writeReg(0x11, PLL_11); writeReg(0x1e, 0x00); writeReg(0x19, PLL_ASR_19); writeReg(0x1b, PLL_ASR_1B); writeReg(0x1d, PLL_ASR_1D); delay(10); writeReg(0xcd, 0x04); writeReg(0x17, 0x4c); delay(5); writeReg(0xcf, 0x4f); writeReg(0xbd, 0x00); writeReg(0x17, 0x48); delay(10); writeReg(0x3c, 0x80); writeReg(0x3e, 0x07); writeReg(0x38, 0xff); writeReg(0x3a, 0x07); writeReg(0x40, 0); writeReg(0x42, 8); writeReg(0x44, 0); writeReg(0x46, 8); delay(1); writeReg(0x1c, 0x09); ////////麦克风设置保留 writeReg(0xbd, 0x20); /////////保留设置 writeReg(0x08, 0x01); ///////////→清除FIFO_DATA delay(1); writeReg(0x08, 0x00); ////////////清除指定FIFO后再写入一次00H delay(1); writeReg(0xb2, 0xff); ////////给0xB2写FF writeReg(0x37, 0x06); ////////开始识别 delay(5); writeReg(0x1c, g_Mic); ////////选择麦克风 writeReg(0x29, 0x10); ////////开同步中断 writeReg(0xbd, 0x00); /////////启动为语音识别 } void cSHigh() { //CS拉高 digitalWrite(CS, HIGH); } void cSLow() { //CS脚拉低 digitalWrite(CS, LOW); } void writeReg(unsigned char address, unsigned char value) ////////写寄存器,参数(寄存器地址,数据) { cSLow(); ////拉低CS delay(10); transfer(0x04); ////////////写指令 transfer(address); transfer(value); cSHigh(); ////拉高CS } unsigned char readReg(unsigned char address) ///读寄存器,参数(寄存器地址) { unsigned char result; cSLow(); ////拉低CS delay(10); transfer(0x05); ///////////读指令 transfer(address); result = transfer(0x00); cSHigh(); ///拉高CS return (result); } byte transfer(byte _data) /////////////////SPI sent_beyt { int32_t i = 0; uint8_t data = 0; cSLow(); ////拉低CS for (i = 7; i >= 0; i--) { //MSB传输 if (_data & (1 << i)) { digitalWrite(SPI_MOSI_PIN, HIGH); //bit位为1 就置高电平 } else { digitalWrite(SPI_MOSI_PIN, LOW); //bit位为0 就置低电平 } if (digitalRead(SPI_MISO_PIN)) { //发送完 马上接收 data |= (1 << i); } /*一定要看手册和时序图 下降沿有效*/ /*SPI模式3 空闲CS=1,第一个跳边沿触发*/ digitalWrite(SPI_SCK_PIN, LOW); delay(1); digitalWrite(SPI_SCK_PIN, HIGH); delay(1); } return data; //是否接收返回看需求 } void VoiceRecognition::init(uint8_t mic) ////////模块启用,参数为麦克风选择(MIC/MONO)与丝印对照,在SETUP中调用 { if (mic == MIC) { g_Mic = MIC; } else if (mic == MONO) { g_Mic = MONO; } pinMode(RSTB, OUTPUT); pinMode(CS, OUTPUT); cSHigh(); pinMode(4, INPUT_PULLUP); pinMode(SPI_MISO_PIN, INPUT); pinMode(SPI_MOSI_PIN, OUTPUT); pinMode(SPI_SCK_PIN, OUTPUT); // #ifndef SOFTWARE_SPI // // SS must be in output mode even it is not chip select // pinMode(LD_CHIP_SELECT_PIN, OUTPUT); // digitalWrite(LD_CHIP_SELECT_PIN, HIGH); // disable any SPI device using hardware SS 拉高ss // // Enable SPI, Master, clock rate f_osc/128 // //SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0);/////初始化SPI寄存器 // // clear double speed // //SPSR &= ~(1 << SPI2X);//2倍速 // #endif // SOFTWARE_SPI // SPCR = (SPCR & ~SPI_MODE_MASK) | 0x08;//设置SCK常态电平与取样时间,0x08为SCK常态为高电平,下降沿有效 reset(); //LD3320复位操作 // #if defined(__AVR_ATmega32U4__) // attachInterrupt(1,update,LOW);//开中断 // #else // attachInterrupt(0,update,LOW);//开中断 // #endif attachInterrupt(4, update, FALLING); //开中断 ASR_init(); ///语音识别初始化函数 } void VoiceRecognition::reset() //LD3320复位操作 { digitalWrite(RSTB, HIGH); delay(1); digitalWrite(RSTB, LOW); delay(1); digitalWrite(RSTB, HIGH); delay(1); cSLow(); delay(1); cSHigh(); delay(1); writeReg(0xb9, 0x00); } void VoiceRecognition::ASR_init() ////////////初始化语音识别模式、 { //添加状态标记 readReg(0x06); // writeReg(0x17, 0x35); delay(10); readReg(0x06); writeReg(0x89, 0x03); delay(5); writeReg(0xcf, 0x43); delay(5); writeReg(0xcb, 0x02); writeReg(0x11, PLL_11); writeReg(0x1e, 0x00); writeReg(0x19, PLL_ASR_19); writeReg(0x1b, PLL_ASR_1B); writeReg(0x1d, PLL_ASR_1D); delay(10); writeReg(0xcd, 0x04); writeReg(0x17, 0x4c); delay(5); // writeReg(0xb9, 0x00); writeReg(0xcf, 0x4f); writeReg(0xbd, 0x00); writeReg(0x17, 0x48); delay(10); writeReg(0x3c, 0x80); writeReg(0x3e, 0x07); writeReg(0x38, 0xff); writeReg(0x3a, 0x07); writeReg(0x40, 0); writeReg(0x42, 8); writeReg(0x44, 0); writeReg(0x46, 8); delay(1); } void VoiceRecognition::addCommand(char *pass, int num) { int i; writeReg(0xc1, num); //字符编号 writeReg(0xc3, 0); //添加时输入00 writeReg(0x08, 0x04); //不清除 delay(1); writeReg(0x08, 0x00); // delay(1); for (i = 0; i <= 80; i++) { if (pass[i] == 0) break; writeReg(0x5, pass[i]); ///写入FIFO_EXT } writeReg(0xb9, i); //写入当前添加字符串长度 writeReg(0xb2, 0xff); //////////B2全写ff writeReg(0x37, 0x04); //添加语句 } unsigned char VoiceRecognition::start() //////开始识别 { writeReg(0x35, MIC_VOL); ////adc增益;会影响识别范围即噪声 writeReg(0xb3, speech_endpoint); //语音端点检测控制 writeReg(0xb4, speech_start_time); //语音端点起始时间 writeReg(0xb5, speech_end_time); //语音结束时间 writeReg(0xb6, voice_max_length); //语音结束时间 writeReg(0xb7, noise_time); //噪声时间 writeReg(0x1c, 0x09); ////////麦克风设置保留 writeReg(0xbd, 0x20); /////////保留设置 writeReg(0x08, 0x01); ///////////→清除FIFO_DATA delay(1); writeReg(0x08, 0x00); ////////////清除指定FIFO后再写入一次00H delay(1); if (check_b2() == 0) ////////读取0xB2寄存器函数如果DSP没在闲状态则RETURN 0 { return 0; } writeReg(0xb2, 0xff); ////////给0xB2写FF writeReg(0x37, 0x06); ////////开始识别 delay(5); writeReg(0x1c, g_Mic); ////////选择麦克风 writeReg(0x29, 0x10); ////////开同步中断 writeReg(0xbd, 0x00); /////////启动为语音识别 return 1; ////返回1 } int check_b2() ////////用作检测芯片工作是否正常,或者DSP是否忙,不需用户操作,正常/闲返回1 { for (int j = 0; j < 10; j++) { if (readReg(0xb2) == 0x21) { return 1; } delay(10); } return 0; } void VoiceRecognition::micVol(uint8_t vol) //调整ADC增益,参数(0x00~0xFF,建议10-60); { MIC_VOL = vol; writeReg(0x35, MIC_VOL); ////adc增益;会影响识别范围即噪声 } void VoiceRecognition::speechEndpoint(uint8_t speech_endpoint_) //调整语音端点检测,参数(0x00~0xFF,建议10-40); { speech_endpoint = speech_endpoint_; writeReg(0xb3, speech_endpoint); //语音端点检测控制 } void VoiceRecognition::speechStartTime(uint8_t speech_start_time_) //调整语音端点起始时间,参数(0x00~0x30,单位10MS); { speech_start_time = speech_start_time_; writeReg(0xb4, speech_start_time); //语音端点起始时间 } void VoiceRecognition::speechEndTime(uint8_t speech_end_time_) //调整语音端点结束时间(吐字间隔时间),参数(0x00~0xC3,单位10MS); { speech_end_time = speech_end_time_; writeReg(0xb5, speech_end_time); //语音结束时间 } void VoiceRecognition::voiceMaxLength(uint8_t voice_max_length_) //最长语音段时间,参数(0x00~0xC3,单位100MS); { voice_max_length = voice_max_length_; writeReg(0xb6, voice_max_length); //语音 } void VoiceRecognition::noiseTime(uint8_t noise_time_) //上电噪声略过,参数(0x00~0xff,单位20MS); { noise_time = noise_time_; writeReg(0xb7, noise_time); //噪声时间 }
#ifndef LD3320_H #define LD3320_H #include <Arduino.h> #include <Wire.h> #include "SPI.h" #define uint8 unsigned char //#define SPI_MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR #define MIC 0x0b #define MONO 0x23 #define uint8 unsigned char #define CLK_IN 24///频率 #define PLL_11 (uint8)((CLK_IN/2.0)-1) #define PLL_ASR_19 (uint8)(CLK_IN*32.0/(PLL_11+1) - 0.51) #define PLL_ASR_1B 0x48 #define PLL_ASR_1D 0x1f /** SPI Master Out Slave In pin */ #define SPI_MOSI_PIN 15 /** SPI Master In Slave Out pin */ #define SPI_MISO_PIN 14 /** SPI Clock pin */ #define SPI_SCK_PIN 27 #define RSTB 13 //RSTB引脚定义 #define CS 32 //RSTB引脚定义 class VoiceRecognition { public: VoiceRecognition(); void reset(); void init(uint8_t mic=MIC); void ASR_init(); unsigned char start(); void addCommand(char *pass,int num); int read(); void micVol(uint8_t vol); void speechEndpoint(uint8_t speech_endpoint_); void speechStartTime(uint8_t speech_start_time_); void speechEndTime(uint8_t speech_end_time_); void voiceMaxLength(uint8_t voice_max_length_); void noiseTime(uint8_t noise_time_); private: }; void writeReg(unsigned char address,unsigned char value); unsigned char readReg(unsigned char address); byte transfer(byte _data); void cSHigh(void); void cSLow(void); void update(); int check_b2(); #endif
#include <Arduino.h> #include <LD3320.h> VoiceRecognition Voice; //声明一个语音识别对象 void Vioce_INIT(); void Recognize_Voice(); void setup() { Serial.begin(115200); SPI.begin(SPI_SCK_PIN, SPI_MISO_PIN, SPI_MOSI_PIN, CS); Vioce_INIT(); /*LD3320语音识别初始化*/ } void loop() { Recognize_Voice(); /*测试LD3320*/ } void Vioce_INIT() { /**********LD3320Init*****************/ Voice.init(MIC); //初始化VoiceRecognition模块 Voice.addCommand("kai deng", 0); //添加指令,参数(指令内容,指令标签(可重复)) Voice.addCommand("wang ba dan", 1); //添加指令,参数(指令内容,指令标签(可重复)) Voice.addCommand("wei wei", 2); //添加指令,参数(指令内容,指令标签(可重复)) Voice.addCommand("ni hao", 3); //添加指令,参数(指令内容,指令标签(可重复)) Voice.start(); //开始识别 /*************************************/ /*************LD3320调试code**************/ // Voice.reset(); // readReg(0x6); // writeReg(0x35, 0x33); // writeReg(0x1b, 0x55); // writeReg(0xb3, 0xaa); // Serial.print(readReg(0x06) ); // Serial.print(readReg(0x06) ); // Serial.print(readReg(0x35) ); // Voice.addCommand("kai deng",0); // Serial.print(readReg(0xBF) ); // Serial.print("\r\n"); //delay(2000); } void Recognize_Voice() { /******************************************/ switch (Voice.read()) //判断识别 { case 0: //若是指令“kai deng” Serial.print("000\r\n"); break; case 1: //若是指令“guan deng” Serial.print("111\r\n"); break; default: //Serial.print("no\r\n"); break; } /*****************************************/ }
-
五、调试寄存器步骤
(建议先做这个以排除通信问题,就是先写后读看看对错与否)
1、先运行LD_reset();然后依次读取并打印0x06 0x06 0x35 0xb3寄存器的值
调试代码如下:
LD_reset(); LD_ReadReg(0x6); LD_WriteReg(0x35, 0x33); LD_WriteReg(0x1b, 0x55); LD_WriteReg(0xb3, 0xaa); Serial.println(LD_ReadReg(0x35)); Serial.println(LD_ReadReg(0x1b)); Serial.println(LD_ReadReg(0xb3));
如果依次打印的值是 (00 87 80 FF)或者(87 87 80 FF)那就正常,第一次读0x06是激活了芯片
2、运行过程中检查寄存器状态
如果第一步的测试正常还是不能正常的话就需要检查工作中的寄存器状态了。
(1) 在完成 LD_AsrAddFixed() 函数后,检测 0xBF 寄存器的数值,看是否是 0x31;
(2) 在 LD_AsrRun()函数中的LD_WriteReg(0x37, 0x06);delay( 5 );后读取 0xBF 寄存器的数值并通过串口打印看看是否不再是 0x31,而是 0x32~0x3a 之间的某一个数值;
(3)如果这两个步骤检查不正确,可以先适当延长 初始化函数中 和 LD_AsrRun()函数中各个 delay 的时间,看是否可以调整好;
(4)如果以上调试过还是不行那就可以从初始化函数里面具体的每一步去读取寄存器的值,看看那步出现问题。
(PS:0x17,0x87,0x89这三个寄存器是在向其写入数据后,再去读取回来的数值并不是写入的数据)
参考资料来自:ICRoute