语音识别芯片LD3320介绍再续

语音识别芯片LD3320驱动程序

    1、芯片复位

    复位就是对LD3320芯片的第47腿(RSTB*)发送低电平,然后需要对片选CS做一次拉低→拉  高的操作,以激活内部DSP。按照以下顺序:

    void LD_reset()

    {

     RSTB=1;delay(1);RSTB=0;delay(1);RSTB=1;delay(1);

     CSB=0;delay(1);CSB=1;delay(1);

   }

    delay(1)是为了更稳定地工作。 初始化一般在程序的开始进行,如果有时芯片的反应不太正常,也可用这个方法恢复芯片初始状态。

    (1)通用初始化

    按照以下序列设置寄存器。

    void LD_Init_Common()

    {

     bMp3Play=0;

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

     if(nLD_Mode==LD_MODE_MP3)

     {

      LD_WriteReg(0x1E,0x00);

      //!!注意,下面三个寄存器,会随晶振频率变化而设置不同

      //!!注意,请根据使用的晶振频率修改参考程序中的CLK_IN

      LD_WriteReg(0x19,LD_PLL_MP3_19);

      LD_WriteReg(0x1B,LD_PLL_MP3_1B);

      LD_WriteReg(0x1D,LD_PLL_MP3_1D);

     }

     else

     {

      LD_WriteReg(0x1E,0x00);

      //!!注意,下面三个寄存器,会随晶振频率变化而设置不同

      //!!注意,请根据使用的晶振频率修改参考程序中的CLK_IN

      LD_WriteReg(0x19,LD_PLL_ASR_19);

      LD_WriteReg(0x1B,LD_PLL_ASR_1B);

      LD_WriteReg(0x1D,LD_PLL_ASR_1D);

    }

    LD_WriteReg(0xCD,0x04);LD_WriteReg(0x17,0x4c);delay(5);

    LD_WriteReg(0xB9,0x00);LD_WriteReg(0xCF,0x4f);

    LD_WriteReg(0x6F,0xFF);

  }

   (2)语音识别用初始化

    按照以下序列设置寄存器。

    void LD_Init_ASR()

     {

      nLD_Mode=LD_MODE_ASR_RUN;

      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,0x00);LD_WriteReg(0x42,0x08);

      LD_WriteReg(0x44,0x00);LD_WriteReg(0x46,0x08);delay(1);

     }

    (3)写入识别列表

    列表的规则是:每个识别条目对应一个特定的编号(1个字节),不同的识别条目的编号可以相同,而且不用连续。本芯片最多支持50个识别条目,每个识别条目是标准普通话的汉语拼音(小写),每2个字(汉语拼音)之间用一个空格间隔。下面是一个简单的例子:

语音识别芯片LD3320介绍再续_第1张图片

     编号可以相同,可以不连续,但是数值要小于256(00H~FFH)。例子中的“北京”和“首都”对应同一编号,说这两个词会有相同的结果返回。简单流程图如下:

语音识别芯片LD3320介绍再续_第2张图片

 

    代码如下:

    #define CODE_DEFAULT 0

    #define CODE_BEIJING 1

    #define CODE_SHANGHAI 3

    #define CODE_TIANJIN 7

    #define CODE_CHONGQING 8

    先介绍一个读取0xB2寄存器的函数,如果在以后的ASR命令函数前不能够读取到正确Idle状态,说明芯片内部可能出错了。当使用的电源电压/电流出现不稳定有较大波动时,有小概率会出现这种情况。出现这种情况时,建议Reset LD3320芯片,重新启动设置芯片。

    // Return 1: success.

    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;

   }

    // Return 1: success.

    uint8 LD_AsrAddFixed()

    {

     uint8 k, flag;

     uint8 nAsrAddLength;

     const char sRecog[5][13]={"bei jing","shou du","shang hai","tian jin", "chong qing"};

     const uint8 pCode[5]={CODE_BEIJING,CODE_BEIJING,CODE_SHANGHAI,                                                                   CODE_TIANJIN,CODE_CHONGQING};

     flag=1;

     for(k=0;k<5;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<20;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;

   }

(4)开始识别

    设置几个相关的寄存器,就可以控制LD3320芯片开始语音识别。 值得注意:单片机程序中,一般会用一个全局变量控制当前状态,(例如:LD_ASR_RUNING状态或者LD_ASR_FOUNDOK状态),在编程时一定要把对该状态的设置放在正式LD3320芯片开始识别以前,例如下面例程中的语句nAsrStatus=LD_ASR_RUNING;便是设置控制变量。需要把这句语句放置在LD3320正式开始识别之前调用:

    {

     。。。

     nAsrStatus=LD_ASR_RUNING;LD_AsrRun();

    }

    参考代码如下:

    // Return 1: success.

    uint8 LD_AsrRun()

    {

     nAsrStatus=LD_ASR_RUNING;

     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(0xB2,0xff);

     LD_WriteReg(0x37,0x06);delay(5);

     LD_WriteReg(0x1C,0x0b);

     LD_WriteReg(0x29,0x10);

     LD_WriteReg(0xBD,0x00);

     EX0=1;

     return 1;

    }

    综上所述:语音识别的流程可以总结成如下的参考代码,可以以此为参考,根据使用流程进行合理的改动。

    uint8 RunASR()

    {

     uint8 i=0;uint8 asrflag=0;

     for(i=0;i<5;i++)

     {

      LD_Init_ASR();delay(100);

      if(LD_AsrAddFixed()==0)

      {LD_reset();delay(100);continue;}

      delay(10);

      if(LD_AsrRun()==0)

      {LD_reset();delay(100);continue;}

      asrflag=1;

      break;

     }

     return asrflag;

   }

    对上述函数的调用参考代码:

    文件main.c 函数main()

    case LD_ASR_NONE:

    {

     nAsrStatus=LD_ASR_RUNING;

     if (RunASR()==0)

     {nAsrStatus=LD_ASR_ERROR;LED1=0;LED2=0;}

     break;

    }

    (5)响应中断

    如果麦克风采集到声音,不管是否识别出正常结果,都会产生一个中断信号。而中断程序要根据寄存器的值分析结果。

    读取BA寄存器的值,可以知道有几个候选答案,而C5寄存器里的答案是得分最高、最可能正确的答案。

    例如发音为“上海”并被成功识别(无其他候选),那么BA寄存器里的数值是1,而C5寄存器里的值是对应的编码3。

    以下为简单流程图:

语音识别芯片LD3320介绍再续_第3张图片

    程序代码如下:

    void ExtInt0Handler(void) interrupt 0 {ProcessInt0();}

    void ProcessInt0()

    {

     uint8 nAsrResCount=0;EX0=0;ET0=0;

     ucRegVal=LD_ReadReg(0x2B);

     ucHighInt=LD_ReadReg(0x29);

     LD_WriteReg(0x29,0);

     ucLowInt=LD_ReadReg(0x02);

     LD_WriteReg(0x02,0);

     if(nLD_Mode==LD_MODE_ASR_RUN)

     {

      if((ucRegVal&0x10)&&LD_ReadReg(0xbf)==0x35&&LD_ReadReg(0xb2)==0x21)

      {

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

      ET0=1;

      return;

     }

   }

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

    在目前的Demo程序中,只读取了最优候选。在其他使用场合,如果需要读取其他候选,用户可以自己编程实现。

    3、声音播放

    播放声音的操作顺序是:

    MP3播放用初始化(包括通用初始化)→调节播放音量→开始播放声音, 并准备好中断响应函数,打开中断允许位。

    (1)通用初始化

     和语音识别部分一样,按指定序列设置寄存器。

    (2)声音播放用初始化 请参照源代码设置寄存器。

    void LD_Init_MP3()

    {

     nLD_Mode = LD_MODE_MP3;

     LD_Init_Common();

     LD_WriteReg(0xBD,0x02);

     LD_WriteReg(0x17, 0x48); delay(10);

     LD_WriteReg(0x85,0x52);

     LD_WriteReg(0x8F,0x00);

     LD_WriteReg(0x81,0x00);

     LD_WriteReg(0x83,0x00);

     LD_WriteReg(0x8E,0xff);

     LD_WriteReg(0x8D,0xff); delay(1);

     LD_WriteReg(0x87,0xff);

     LD_WriteReg(0x89,0xff); delay(1);

     LD_WriteReg(0x22,0x00);

     LD_WriteReg(0x23,0x00);

     LD_WriteReg(0x20,0xef);

     LD_WriteReg(0x21,0x07);

     LD_WriteReg(0x24, 0x77);

     LD_WriteReg(0x25,0x03);

     LD_WriteReg(0x26, 0xbb);

     LD_WriteReg(0x27,0x01);

    }

    (3)调节播放音量

    这里需要修改寄存器8E。 音量分为16级,用4位二进制表示,范围是0-15。 设置音量时,将(15-音量值) 设给寄存器8E的第2-5位。

    源代码如下:

    void LD_AdjustMIX2SPVolume(uint8 val)

    {

     uint8 ucTmp;

     ucSPVol=val;

     val=((15-val)&0x0f)<<2;

     LD_WriteReg(0x8E,val|0xc3);

    }

    这个函数只调节喇叭(Speaker)的音量,和耳机等其他输出无关。而且实验板上只有喇叭连接了输出。

    耳机音量是可以调节的,使用寄存器81(左耳音量)和83(右耳音量)。 2个寄存器都是只有第1-5位有效,共32级,而且这5位都为0(00000B)的时候是音量最大的,都为1(11111B)的时候音量最小。

    (4)开始播放声音

    开始播放位置清零(自定义变量Mp3Pos=0)

    寄存器1B的第3位设为1(按位或0x08)

        循环执行:

        while(【播放条件】=true)

        {

         顺序将MP3数据放入寄存器01(每次一个字节);

         Mp3Pos增加1

        }

  【播放条件】为下面条件都成立,有一个不满足就跳出循环:

        读取寄存器06,第3位=0

        Mp3Pos < MP3文件的总长度。

   修改BA 、17等寄存器。(参照源代码)

   开放中断允许。例如,EX0=1。

   开始播放的简单流程图如下:

语音识别芯片LD3320介绍再续_第4张图片

 

    源代码如下:

    void LD_play()

    {

     nMp3Pos=0;bMp3Play=1;

     if(nMp3Pos>=nMp3Size)return ;

     LD_ReloadMp3Data();

     LD_WriteReg(0xBA,0x00);

     LD_WriteReg(0x17,0x48);

     LD_WriteReg(0x33,0x01);

     LD_WriteReg(0x29,0x04);

     LD_WriteReg(0x02,0x01);

     LD_WriteReg(0x85,0x5A);

     EX0=1;

    }

    void LD_ReloadMp3Data()

    {

     uint32 nCurMp3Pos;uint8 val;uint8 k;

     nCurMp3Pos=nMp3StartPos+nMp3Pos;

     FLASH_CS=1;FLASH_CLK=0;FLASH_CS=0;

     IO_Send_Byte(W25P_FastReadData);                   /* read command */

     IO_Send_Byte(((nCurMp3Pos&0xFFFFFF)>>16)); /* send 3 address bytes */

     IO_Send_Byte(((nCurMp3Pos&0xFFFF)>>8));

     IO_Send_Byte(nCurMp3Pos&0xFF);

     IO_Send_Byte(0xFF);

     ucStatus=LD_ReadReg(0x06);

     while(!(ucStatus&MASK_FIFO_STATUS_AFULL)&&(nMp3Pos

     {

      val=0;

      for(k=0;k<8;k++){FLASH_CLK=0;val<<=1;FLASH_CLK=1;val|=FLASH_DO;}     

      LD_WriteReg(0x01,val);nMp3Pos++;ucStatus=LD_ReadReg(0x06);

     }

    FLASH_CS=1;FLASH_CLK=0;

   }

    LD_ReloadMp3Data()函数的功能是送入数据,不同的硬件结构可能需要改写这一部分。例如有的系统可能使用大容量的RAM,取数据就会很方便。这里是根据串行FLASH存储器的接口写的函数,使用的是SPI协议。

     (5)中断响应。

    开始播放可以把声音数据的最初部分送入芯片,等到芯片播放这一段后会发出中断请求。而中断函数里会不断的送入数据,直到FIFO_DATA装满或声音数据结束。这一段程序和开始播放比较类似,都是通过LD_ReloadMp3Data()函数送入数据。

    由于LD3320芯片只有一只管脚负责中断请求输出,所以一般情况下用一个中断响应函数处理2种中断。这里为了简明,将中断函数分开书写。

    中断处理函数里,播放声音部分流程图如下:

语音识别芯片LD3320介绍再续_第5张图片

    要注意的是,寄存器操作2)和寄存器操作3) 并不会恢复寄存器02和29的内容。因为这时播放已经结束了,应该让02,29清零。 而数据重载结束后(达到了继续播放的条件),需要恢复寄存器02,29。 以便于继续出中断,连续播放。 

    源代码是:

    void ExtInt0Handler(void) interrupt 0{ProcessInt0();}

    void ProcessInt0()

    {

     uint8 nAsrResCount=0;

     EX0=0;ET0=0;

     ucRegVal=LD_ReadReg(0x2B);

     ucHighInt=LD_ReadReg(0x29);

     ucLowInt=LD_ReadReg(0x02);

     LD_WriteReg(0x29,0);

     LD_WriteReg(0x02,0);

     if(nLD_Mode==LD_MODE_MP3)

     {

      if(LD_ReadReg(0xBA)&CAUSE_MP3_SONG_END)

      {

       LD_WriteReg(0x2B,0);

       LD_WriteReg(0xBA,0x00);

       LD_WriteReg(0xBC,0x00);

       bMp3Play=0;

       LD_WriteReg(0x08,0x01);

       LD_WriteReg(0x08,0x00);

       LD_WriteReg(0x33,0x00);

       return ;

     }

    if(nMp3Pos>=nMp3Size)

    {

     LD_WriteReg(0xBC,0x01);

     LD_WriteReg(0x29,0x10);

     EX0=1;

     return;

    }

    LD_ReloadMp3Data();

    LD_WriteReg(0x29,ucHighInt);

    LD_WriteReg(0x02,ucLowInt) ;

    EX0=1;

    }

  }

    补充说明

    1、此芯片的特色是兼有语音识别和MP3播放的两项功能,但是由于这两项功能会使用一些公用的资源,所以为了使芯片稳定地工作,在功能切换的时候,必须从最“通用初始化”开始,对芯片进行一系列的设置。

    2、当芯片长时间没有响应时,可能是应用程序的设置不合理或者是电源的电压/电流有比较大的波动造成。这时应使用芯片复位的功能(对芯片的RTSB*发送低电平),使芯片重新开始工作。

你可能感兴趣的:(语音识别,人工智能)