资讯详情

基于Arduino IDE开发的LD3320语音识别模块

基于Arduino的LD3320语音识别模块设计详细说明

文章目录

    • 基于Arduino的LD3320语音识别模块设计详细说明
  • 前言
  • 一、LD3320驱动编写
    • step 1.0 使用Arduino的SPI库,通过硬件SPI和LD3320通信,读写寄存器
    • setp 1.1 访问LD检查硬件连接的可靠性
    • setp 1.2 编写驱动程序,编写驱动程序LD3320进行语音识别
  • 二、二部分 用模拟SPI编写LD3320的驱动程序
    • 模拟SPI通信程序例程
  • 三、IIC修改命令词

前言

本文的最终目的是设计一个来记录我的学习过程IIC语音识别模块的主要功能是识别程序中设置的指令词,并返回识别结果。指令词和相应的返回号可以在程序中任意修改,无需为语音识别模块烧录固件,支持命令词的动态编辑。 模块硬件包括单片机芯片,一块LD3320芯片和外围电路。PCB该项目已开源,网上随处可见。 基础功能: 1.动态编辑命令词,不需要烧录模块的固件 2.识别成功后返回相应结果 文章将会记录我从零开始的调试步骤以及遇到的问题,还有解决问题的详细思路。 让我们开始吧。 工程已上传(PCB工程 模块固件 Demo),如有必要,可以先下载,链接挂在最后 PCB如上所示

一、LD3320驱动编写

根据ICroute官方文件写的是驱动LD3320语音识别可以通过软硬并口通信进行LD读写3320寄存器驱动LD3320也可以使用软硬SPI串行通信是对的LD读写3320寄存器也可以驱动LD3320。需要注意的是,官方不推荐模拟并口驱动,不仅速度慢,而且通信不稳定。硬件推荐SPI如果MCU没有SPI也可以使用普通的硬件接口I/O口模拟SPI通信相对稳定。 回到正题,我手头碰巧有一块网上买的LD3320模块是没有单片机的模块,板上只有LD3320的那种。

直接先拿Arduino UNO开发板和这个LD3320原理测试,Let’s go。 直接用杜邦线,按以下方式连接:

5V – VCC GND – GND MISO – D12 MOSI – D11 SCK – D13 NSS – D4 RST – D9 IRQ – D2 WR – GND

官方文件说,LD3320的电压为3V3.通信引脚的耐压性也是3V3,超过3V模块使用不稳定,但在使用中Arduino在实际测试中,无电压转换模块,反应良好。不要向我学习,最好用3V三是转压还是得转请注意,引脚电平可超过3v但是!电源只能是3v3 这部分我分三小步完成:

step 1.0 使用Arduino的SPI库,通过硬件SPI和LD3320通信,读写寄存器 setp 1.1 访问LD检查硬件连接的可靠性 setp 1.2 编写驱动程序,编写驱动程序LD3320进行语音识别

step 1.0 使用Arduino的SPI库,通过硬件SPI和LD3320通信,读写寄存器

这一步的主要目的是确定和LD众所周知,3320能否正常通信,SPI这是一种串行通信方式,需要至少连接五条线,为什么是五条而不是四条,因为它需要公开。 【SCK】 》》同步主机产生的数据传输时钟脉冲。 【MOSI】 》》用于向外设发送数据的线,主机从引脚发送,从引脚接收。 【MISO】用于向主设备发送数据的线路,主机从引脚接收,从引脚发送。 【CS】 》》片选信号,主机控制引脚打开和禁用从机,低电平使能。 【GND】 祖传公地。 好了,SPI我们知道这么多相关知识。我们只需要知道他是全双工串行通信,这些引脚是干什么的。继续下一步。

我们要和LD3320通信,首先要让LD3320工作,需要先复位芯片,激活内部DSP。 只需要将LD3320芯片的47脚【RSTB*】低电平可以复位LD3320到初始状态。然后需要激活DSP,需要对片选择【CS】做一个拉低再拉高的操作。

复位LD3320激活内部dsp的子函数

void LD_reset()//对LD3320复位 低电平发送到47脚 然后选择一次反转片 激活DSP: { 
           digitalWrite(RSTB, HIGH);   delay(1);   digitalWrite(RSTB, LOW);   delay(1);   digitalWrite(RSTB, HIGH);   delay(1);   cSLow();   delay(1);
  cSHigh();
  delay(1);
  //writeReg(0xb9, 0x00); //寄存器 0xB9 当前添加识别语句的字符串长度 初始化时写入0x00
}

激活DSP之后,就可以进行LD3320的寄存器读写了,我们使用的是SPI串行方式,根据官方的文档描述,SPI的通讯参数需要设置如下: SPI没有官方标准,所以要好好看芯片的datasheet

在Arduino的setup里对SPI参数进行设置

void setup() { 
        
	......
  SPI.setClockDivider(SPI_CLOCK_DIV16);  //16分频
  SPI.setDataMode(SPI_MODE2);  //时钟极性CPOL 1 时钟相位CPHA 0
  SPI.setBitOrder(MSBFIRST);  //高位在前
	......
}

接下来写一个用于读寄存器的程序,根据官方文档描述,要读寄存器,需要先发送0x05,然后再发送十六进制的寄存器地址,然后再读取芯片返回过来的十六进制数据。

读寄存器的子函数

unsigned char LD_readReg(unsigned char address) 
{ 
        
  unsigned char result;//局部变量 保存读取的数据
  cSLow();   //片选使能 低电平有效
  delay(10);
  SPI.transfer(0x05);
  SPI.transfer(address);  //发送 寄存器地址
  result = SPI.transfer(0x00);  //读取数据
  cSHigh();  //片选去使能
  return (result);   //返回数据
}

接下来写一个用于写寄存器的程序,根据官方文档描述,要写取寄存器,需要先发送0x04,然后再发送十六进制的寄存器地址,最后发送十六进制数据。

写寄存器的子函数

void LD_writeReg(unsigned char address, unsigned char value) 
{ 
        
  cSLow();//片选使能
  delay(10);
  SPI.transfer(0x04);//发送0x04 是写入模式
  SPI.transfer(address);  //发送 寄存器地址
  SPI.transfer(value);  //发送 数据
  cSHigh();//片选去使能
}

接下来,进行对LD3320寄存器的读写测试。 如果,我们先向可读写的寄存器写入某个数值,再读出来,用来检查寄存器读写是否正常。 每次先向一个寄存器写,再读出来,内容是完全正确,但是在接下来的语音识别操作中,发现LD3320芯片并不会鸟你,这是为什么呢,因为可能你的数据存在SPI的总线上,但是没有Touch到寄存器里面 所以我建议读写寄存器的序列如下:

对寄存器写入再读取的操作顺序

void setup() { 
        
......
  LD_reset();//先复位激活LD3320
  delay(1);
  LD_readReg(0x06);//读取一次0X06寄存器
  delay(1);
  LD_writeReg(0x35, 0x33);//对寄存器0x35写入0x33
  LD_writeReg(0x1b, 0x55);//对寄存器0x1b写入0x55
  LD_writeReg(0xb3, 0xaa);//对寄存器0xb3写入0xaa
  Serial.print(LD_readReg(0x35),HEX); Serial.print(" ");//读取并打印寄存器0x35的值
  Serial.print(LD_readReg(0x1b),HEX); Serial.print(" ");//读取并打印寄存器0x1b的值
  Serial.print(LD_readReg(0xb3),HEX); Serial.println(" ");//读取并打印寄存器0xb3的值
}

就是向 3 个寄存器先依次写数据,再依次读数据出来。作为比较。 这样可以有效地验证读写寄存器是否正常。 如果结果是 33 55 aa 那么恭喜你,读写寄存器正常,可以进行下一步操作了。 这一步基本不会出错,如果出错了,请详细比对SPI的参数设置 step1.0 完整代码,在工程文档中,有需要的直接下载即可

setp 1.1 访问LD3320的三个指定寄存器,检查硬件连接的可靠性

既然已经可以正常读写LD3320的寄存器了,那为了保证LD3320稳定工作,再来检查一下硬件之间连接的可靠性。 我们在复位LD3320之后,读取寄存器0x06两次,再读取寄存器0x35和0xb3,将值打印出来。

访问三个指定寄存器的操作

void setup() { 
        
......
  LD_reset();//先复位激活LD3320
  delay(1);
  Serial.print(LD_readReg(0x06),HEX); Serial.print(" "); //读取并打印寄存器0x06的值
  Serial.print(LD_readReg(0x06),HEX); Serial.print(" "); //读取并打印寄存器0x06的值
  Serial.print(LD_readReg(0x35),HEX); Serial.print(" "); //读取并打印寄存器0x35的值
  Serial.print(LD_readReg(0xb3),HEX); Serial.println(" ");//读取并打印寄存器0xb3的值
  }

如果打印出来的值,是 87 87 80 FF 或者 00 87 80 FF 则正常。不要把LD3320断电,复位Arduino UNO ,多读取几次,看看值是否稳定,如果稳定,则连接非常稳定。就可以进行语音识别的操作了。

setp 1.2 进行驱动程序的编写,驱动LD3320进行语音识别

现在我们进行完整驱动的编写。首先,先介绍驱动LD3320进行语音识别的流程。 ASR初始化→写入识别列表→开始识别→准备好中断函数→打开中断 然后每次识别到语音,无论是否识别到命令词,就会触发中断,运行你准备好的中断函数,正常情况是只运行一次就结束了,但是你可以在中断函数中添加ASR初始化→开始识别,继续进行下一轮识别,达到循环识别的目的。 接下来,我们按照流程来编写驱动程序。先贴两张寄存器地址的图表。

通用初始化函数,直接抄作业就可以了,都是官方例程,唯一需要注意的,就是晶振频率相关的四个变量

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);
  LD_writeReg(0x11, PLL_11);//和晶振频率有关,注意调整
  LD_writeReg(0x1e, 0x00);
  LD_writeReg(0x19, PLL_ASR_19);//和晶振频率有关,注意调整
  LD_writeReg(0x1b, PLL_ASR_1B);//和晶振频率有关,注意调整
  LD_writeReg(0x1d, 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);
}

ASR初始化函数,一样的,抄作业就完事了,没什么需要注意的

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

接下来,写入识别列表,也就是把自定义的命令词写入到LD3320里面。需要注意的是,我们在写入识别列表之前,要先读取0xB2寄存器的值,查看芯片的状态是否正常,如果不正常,则需要先复位一次芯片,再重新初始化,再写入。为什么会出现不正常的情况,主要原因,是电压和电流不稳定造成的。

检查0xB2寄存器的函数,返回1则正常,返回0失败

int LD_Check_ASRbusyFlag_b2()/**检查芯片状态**/
{ 
        
  for (int j = 0; j < 10; j++)
  { 
        
    if (LD_readReg(0xb2) == 0x21)//寄存器值0x21为空闲
    { 
        
      return 1;
    }
    delay(10);
  }
  return 0;
}

如果检查寄存器结果是正常,那么就可以写入识别词了。

写入识别列表的函数,char *pass也可以用string pass代替

int LD_ASRAddFixed(char *pass, int num) /**添加命令词**/
{ 
        
  int i; //局部变量 用来循环写入识别字的数据,并记录字符的长度
  int flag;//返回添加成功或者失败
  flag = 1;
  for (int j = 0; j < 5; j++) { 
        //这个循环没有任何意义
    if (LD_Check_ASRbusyFlag_b2() == 0) { 
        
      flag = 0;
      break;//如果检查到不支持,则退出,并返回添加失败
    }
    LD_writeReg(0xc1, num);//添加命令的编号
    LD_writeReg(0xc3, 0);//识别字添加时写入0x00
    LD_writeReg(0x08, 0x04);//清除数据缓存器内容
    delay(1);
    LD_writeReg(0x08, 0x00);//清除后再次写入0x00
    delay(1);
    for (i = 0; i <80; i++)//为什么i<80,因为单个命令词最多支持80字节,最好只写入79字节
    { 
        
      if (pass[i] == 0)break;//如果读取到空字符则退出循环写入
      LD_writeReg(0x5, pass[i]);///将识别字写入寄存器0x05
    }
    LD_writeReg(0xb9, i);//在0xb9寄存器写入当前字符长度
    LD_writeReg(0xb2, 0xff);
    LD_writeReg(0x37, 0x04);//在0x37寄存器写入 0x04通知DSP 我要写入一条识别词
    break;//写入完成,退出
  }
  return flag;//返回添加成功或者失败
}

接下来,就要启动识别了。

启动识别函数,照抄就行,这些变量都在程序头部进行宏定义

int LD_AsrRun()/**启动ASR**/
{ 
        
  LD_writeReg(0x35, MIC_VOL);//在0x35寄存器 写入识别灵敏度
  LD_writeReg(0xb3, speech_endpoint);//在0xb3寄存器 写入语音端点检测功能的灵敏度 0关闭 数值越小越灵敏
  LD_writeReg(0xb4, speech_start_time);//在0xb4寄存器 写入判断语句开始的时间
  LD_writeReg(0xb5, speech_end_time);//在0xb5寄存器 写入判断语句结束的时间
  LD_writeReg(0xb6, voice_max_length);//在0xb6寄存器 写入识别语句的最长长度
  LD_writeReg(0xb7, noise_time);//在0xb7寄存器 写入初始忽略掉的底噪时间
  LD_writeReg(0x1c, 0x09);//保留命令字
  LD_writeReg(0xbd, 0x20);//保留命令字
  LD_writeReg(0x08, 0x01);//清除缓存器
  delay( 1);
  LD_writeReg(0x08, 0x00);//再次写入00
  delay( 1);
  if (LD_Check_ASRbusyFlag_b2() == 0) //查看寄存器0xb2的值是否正常
  { 
        
    return 0;//不正常返回0 ,启动失败
  }
  LD_writeReg(0xb2, 0xff);//更新寄存器0xb2的值
  LD_writeReg(0x37, 0x06);//通知dsp开始语音识别
  delay( 5 );
  LD_writeReg(0x1c, 0x0b);//选择声音输入方式,因为我用双极电容麦,直接写入0x0B,
  LD_writeReg(0x29, 0x10);//中断开启
  LD_writeReg(0xbd, 0x00);//启动ASR模块
  return 1;//成功
}

启动之后,我们准备好中断函数,用来响应LD3320发送的中断信号,查询返回值并启动下一轮识别。

中断响应函数

void LD_ASRget()/**中断执行程序**/
{ 
        
  /****以下程序对LD3320的运算结果进行分析,取其运算结果****/
  uint8_t Asr_Count = 0;
  LD_writeReg(0x29, 0) ;
  LD_writeReg(0x02, 0) ;
  if ((LD_readReg(0x2b) & 0x10) && LD_readReg(0xb2) == 0x21 && LD_readReg(0xbf) == 0x35)
  { 
        

    Asr_Count = LD_readReg(0xba); //读取有几个候选值
    if (Asr_Count > 0 && Asr_Count < 4)
    { 
        
      readnum = LD_readReg(0xc5); //得到最佳选项 如果你需要其他选项,可以去另外三个寄存器掏
      readflag = 1;
    }
    else { 
        
      Serial.println("运算结果是无法识别");
    }
    LD_writeReg(0x2b, 0);
    LD_writeReg(0x1C, 0);
  }
  else { 
        
    Serial.println("运算结果是无法识别");
  }
  /****以下程序对LD3320进行初始化,预备进行下一次识别****/
    LD_readReg(0x06);
    delay(10);
    LD_readReg(0x06);
    LD_writeReg(0x89, 0x03);
    delay(5);
    LD_writeReg(0xcf, 0x43);
    delay(5);
    LD_writeReg(0xcb, 0x02);
    LD_writeReg(0x11, PLL_11);
    LD_writeReg(0x1e, 0x00);
    LD_writeReg(0x19, PLL_ASR_19);
    LD_writeReg(0x1b, PLL_ASR_1B);
    LD_writeReg(0x1d, PLL_ASR_1D);
    delay(10);
    LD_writeReg(0xcd, 0x04);
    LD_writeReg(0x17, 0x4c);
    delay(5);
    LD_writeReg(0xcf, 0x4f);
    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 

标签: 双极系列电源连接器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台