当前位置:   article > 正文

RT-Thread实战笔记|LD3320非限定词条语音控制器使用详解_ld3320语音识别算法

ld3320语音识别算法

视频演示:

【Rt-thread平台使用LD3320语音识别控制器-哔哩哔哩】https://b23.tv/5m9OXt

前言

物联网设计这么火了,这么能离得开语音控制,搭载上国产物联网操作系统RT-Thread,本期跟小飞哥一起学习RT-Thread SPI设备驱动的使用,如何在RT-Thread系统上操作LD3320。

SPI通讯协议介绍

注:此部分摘自rt-thread官网

SPI(Serial Peripheral Interface,串行外设接口)是一种高速、全双工、同步通信总线,常用于短距离通讯,主要应用于 EEPROM、FLASH、实时时钟、AD 转换器、还有数字信号处理器和数字信号解码器之间。SPI 一般使用 4 根线通信,如下图所示:

  • MOSI –主机输出 / 从机输入数据线(SPI Bus Master Output/Slave Input)。

  • MISO –主机输入 / 从机输出数据线(SPI Bus Master Input/Slave Output)。

  • SCLK –串行时钟线(Serial Clock),主设备输出时钟信号至从设备。

  • CS –从设备选择线 (Chip select)。也叫 SS、CSB、CSN、EN 等,主设备输出片选信号至从设备。

SPI 以主从方式工作,通常有一个主设备和一个或多个从设备。通信由主设备发起,主设备通过 CS 选择要通信的从设备,然后通过 SCLK 给从设备提供时钟信号,数据通过 MOSI 输出给从设备,同时通过 MISO 接收从设备发送的数据。

如下图所示芯片有 2 个 SPI 控制器,SPI 控制器对应 SPI 主设备,每个 SPI 控制器可以连接多个 SPI 从设备。挂载在同一个 SPI 控制器上的从设备共享 3 个信号引脚:SCK、MISO、MOSI,但每个从设备的 CS 引脚是独立的。

从设备的时钟由主设备通过 SCLK 提供,MOSI、MISO 则基于此脉冲完成数据传输。SPI 的工作时序模式由 CPOL(Clock Polarity,时钟极性)和 CPHA(Clock Phase,时钟相位)之间的相位关系决定,CPOL 表示时钟信号的初始电平的状态,CPOL 为 0 表示时钟信号初始状态为低电平,为 1 表示时钟信号的初始电平是高电平。CPHA 表示在哪个时钟沿采样数据,CPHA 为 0 表示在首个时钟变化沿采样数据,而 CPHA 为 1 则表示在第二个时钟变化沿采样数据。

根据 CPOL 和 CPHA 的不同组合共有 4 种工作时序模式:

  • CPOL=0,CPHA=0

  • CPOL=0,CPHA=1

  • CPOL=1,CPHA=0

  • CPOL=1,CPHA=1。如下图所示:

RT-Thread IIC设备驱动使用

1、挂载SPI设备

函数原型:

  1. rt_err_t rt_spi_bus_attach_device(struct rt_spi_device *device,
  2.                                   const char           *name,
  3.                                   const char           *bus_name,
  4.                                   void                 *user_data)
参数描述
deviceSPI设备名称
nameSPI设备名称
bus_nameSPI总线名称
user_data用户数据指针
返回--
busRET_EOK_name成功
其他错误码失败

此函数主要是用来挂载一个 SPI 设备到指定的 SPI 总线,并向内核注册 SPI 设备,并将 user_data 保存到 SPI 设备的控制块里。

一般 SPI 总线命名原则为 spix, SPI 设备命名原则为 spixy ,如 spi10 表示挂载在 spi1 总线上的 0 号设备。user_data 一般为 SPI 设备的 CS 引脚指针,进行数据传输时 SPI 控制器会操作此引脚进行片选。

光说不练假把式,结合我们本次要操作的LD3320驱动来看看这个函数如何使用

我们以SPI4为例,CS引脚选择PE4

配置SPI

主要涉及SPI通讯模式,最大通讯速率,数据模式等参数,具体怎么配置看从机是什么模式,讲究个主从匹配。

挂载 SPI 设备到 SPI 总线后需要配置 SPI 设备的传输参数。

函数原型:

  1. rt_err_t rt_spi_configure(struct rt_spi_device *device,
  2.                           struct rt_spi_configuration *cfg)
参数描述
deviceSPI 设备句柄
cfgSPI 配置参数指针
返回---
RT_EOK成功

这部分就类似于我们的cubemx中关于SPI的配置,参数在struct rt_spi_configuration 结构体中原型如下:

  1. struct rt_spi_configuration
  2. {
  3.     rt_uint8_t mode;        /* 模式 */
  4.     rt_uint8_t data_width;  /* 数据宽度,可取8位、16位、32位 */
  5.     rt_uint16_t reserved;   /* 保留 */
  6.     rt_uint32_t max_hz;     /* 最大频率 */
  7. };

模式:包含 MSB/LSB、主从模式、 时序模式等,可取宏组合如下:

  1. /* 设置数据传输顺序是MSB位在前还是LSB位在前 */
  2. #define RT_SPI_LSB      (0<<2)                        /* bit[2]: 0-LSB */
  3. #define RT_SPI_MSB      (1<<2)                        /* bit[2]: 1-MSB */
  4. /* 设置SPI的主从模式 */
  5. #define RT_SPI_MASTER   (0<<3)                        /* SPI master device */
  6. #define RT_SPI_SLAVE    (1<<3)                        /* SPI slave device */
  7. /* 设置时钟极性和时钟相位 */
  8. #define RT_SPI_MODE_0   (0 | 0)                       /* CPOL = 0, CPHA = 0 */
  9. #define RT_SPI_MODE_1   (0 | RT_SPI_CPHA)             /* CPOL = 0, CPHA = 1 */
  10. #define RT_SPI_MODE_2   (RT_SPI_CPOL | 0)             /* CPOL = 1, CPHA = 0 */
  11. #define RT_SPI_MODE_3   (RT_SPI_CPOL | RT_SPI_CPHA)   /* CPOL = 1, CPHA = 1 */
  12. #define RT_SPI_CS_HIGH  (1<<4)                        /* Chipselect active high */
  13. #define RT_SPI_NO_CS    (1<<5)                        /* No chipselect */
  14. #define RT_SPI_3WIRE    (1<<6)                        /* SI/SO pin shared */
  15. #define RT_SPI_READY    (1<<7)                        /* Slave pulls low to pause */
  • 数据宽度:根据 SPI 主设备及 SPI 从设备可发送及接收的数据宽度格式设置为8位、16位或者32位。

  • 最大频率:设置数据传输的波特率,同样根据 SPI 主设备及 SPI 从设备工作的波特率范围设置。

配置示例如下所示(具体根据设备要求):

  1.     struct rt_spi_configuration cfg;
  2.     cfg.data_width = 8;
  3.     cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;
  4.     cfg.max_hz = 20 * 1000 *1000;                           /* 20*/
  5.     rt_spi_configure(spi_dev, &cfg);

访问 SPI 设备

一般情况下 MCU 的 SPI 器件都是作为主机和从机通讯,在 RT-Thread 中将 SPI 主机虚拟为 SPI 总线设备,应用程序使用 SPI 设备管理接口来访问 SPI 从机器件,主要接口如下所示:

发送、接收函数有好几个,但基础函数是send和receive两个,就不再占用篇幅,一一介绍了,后面会主要看下rt_spi_transfer_message() 自定义数据传输函数,一定要注意,SPI相关的函数不要再中断中调用

  • 查找 SPI 设备

和前面章节:IIC设备驱动详解 一样,SPI也是要先查找SPI设备:

rt_device_t rt_device_find(const char* name);

参数描述
name设备名称
返回---
设备句柄查找到对应设备将返回相应的设备句柄
RT_NULL没有找到相应的设备对象

结合LD3320,定义SPI名称为SPI4,看代码:

  1. struct rt_spi_device *spi_dev_ld3320
  2.     spi_dev_ld3320 = (struct rt_spi_device *)rt_device_find(name);
  3.     if (!spi_dev_ld3320)
  4.     {
  5.         rt_kprintf("spi sample run failed! can't find %s device!\n", name);
  6.     }
  7.     else
  8.     {
  9.         rt_kprintf("spi sample run success!find %s device!\n", name);
  10.     }

自定义传输数据

struct rt_spi_message *rt_spi_transfer_message(struct rt_spi_device  *device,struct rt_spi_message *message);

此函数可以传输一连串消息,用户可以自定义每个待传输的 message 结构体各参数的数值,从而可以很方便的控制数据传输方式。struct rt_spi_message 原型如下:

  1. struct rt_spi_message
  2. {
  3.     const void *send_buf;           /* 发送缓冲区指针 */
  4.     void *recv_buf;                 /* 接收缓冲区指针 */
  5.     rt_size_t length;               /* 发送 / 接收 数据字节数 */
  6.     struct rt_spi_message *next;    /* 指向继续发送的下一条消息的指针 */
  7.     unsigned cs_take    : 1;        /* 片选选中 */
  8.     unsigned cs_release : 1;        /* 释放片选 */
  9. };

sendbuf 为发送缓冲区指针,其值为 RT_NULL 时,表示本次传输为只接收状态,不需要发送数据。

recvbuf 为接收缓冲区指针,其值为 RT_NULL 时,表示本次传输为只发送状态,不需要保存接收到的数据,所以收到的数据直接丢弃。

length 的单位为 word,即数据长度为 8 位时,每个 length 占用 1 个字节;当数据长度为 16 位时,每个 length 占用 2 个字节。

参数 next 是指向继续发送的下一条消息的指针,若只发送一条消息,则此指针值为 RT_NULL。多个待传输的消息通过 next 指针以单向链表的形式连接在一起。

cs_take 值为 1 时,表示在传输数据前,设置对应的 CS 为有效状态。cs_release 值为 1 时,表示在数据传输结束后,释放对应的 CS。

注:* 当 send_buf 或 recv_buf 不为空时,两者的可用空间都不得小于 length。* 若使用此函数传输消息,传输的第一条消息 cs_take 需置为 1,设置片选为有效,最后一条消息的 cs_release 需置 1,释放片选。

具体如何使用,在后面详细介绍

SPI的原理以及一些基本的函数、参数介绍就这样了,小伙伴们也可以去rt-thread官网查看

LD3320简介

某宝买的,价格挺贵,功能也挺多,语音播放、录放音、语音识别等等,关于LD3320的资料介绍实在是太多了,下面主要介绍几点:

芯片介绍

主要特色功能

主要技术参数

模块原理图

串行驱动时序

标准的SPI协议,看一下即可...

写时序:

读时序:

代码编写

硬件连接:

rt studio配置:

先来对WR、RST、IRQ引脚进行定义

  1. #define LD3320_WR GET_PIN(H, 2)
  2. #define LD3320_RST GET_PIN(H, 3)
  3. #define LD3320_IRQ GET_PIN(B, 0)
  4. #define LD3320_CS GET_PIN(E, 4)
  5. #define LD3320_WR_Func(x) x ? rt_pin_write(LD3320_WR, PIN_HIGH) : rt_pin_write(LD3320_WR, PIN_LOW)
  6. #define LD3320_RST_Func(x) x ? rt_pin_write(LD3320_RST, PIN_HIGH) : rt_pin_write(LD3320_RST, PIN_LOW)
  7. #define LD3320_CS_Func(x) x ? rt_pin_write(LD3320_CS, PIN_HIGH) : rt_pin_write(LD3320_CS, PIN_LOW)

IRQ需要注册中断:

  1. void rt_ld3320_irq_pin_init()
  2. {
  3.     rt_pin_mode(LD3320_IRQ, PIN_MODE_INPUT_PULLUP);
  4.     rt_pin_attach_irq(LD3320_IRQ, PIN_IRQ_MODE_FALLING, rt_ld3320_isr, RT_NULL);
  5.     rt_pin_irq_enable(LD3320_IRQ, PIN_IRQ_ENABLE);
  6. }

SPI设备注册初始化:

  1. void rt_ld3320_init(int argc, char *argv[])
  2. {
  3.     char name[RT_NAME_MAX];
  4.     if (argc == 2)
  5.     {
  6.         rt_strncpy(name, argv[1], RT_NAME_MAX);
  7.     }
  8.     else
  9.     {
  10.         rt_strncpy(name, LD3320_SPI_DEVICE_NAME, RT_NAME_MAX);
  11.     }
  12.     rt_hw_ld3320_init();
  13.     spi_dev_ld3320 = (struct rt_spi_device *)rt_device_find(name);
  14.     if (!spi_dev_ld3320)
  15.     {
  16.         rt_kprintf("spi sample run failed! can't find %s device!\n", name);
  17.     }
  18.     else
  19.     {
  20.         rt_kprintf("spi sample run success!find %s device!\n", name);
  21.     }
  22.     struct rt_spi_configuration cfg;
  23.     cfg.data_width = 8;
  24.     cfg.mode = RT_SPI_MSB | RT_SPI_MASTER | RT_SPI_MODE_2;
  25.     cfg.max_hz = 6 * 1000 * 100/* 6*/
  26.     if (rt_spi_configure(spi_dev_ld3320&cfg) == RT_ERROR)
  27.     {
  28.         rt_kprintf("spi4 config error\r\n");
  29.     }
  30.     rt_kprintf("spi4 config success\r\n");
  31.     rt_LD_init();
  32. }

老规矩。不出意外的话,SPI初始化OK的,来看:

接下来编写读写寄存器的基本函数:

写寄存器:结合上面对自定义数据传输函数的介绍,使用自定义数据实现SPI数据传输

  1. /********************************************************************************
  2. function:
  3.                 Write data to LD3320 reg
  4. ********************************************************************************/
  5. static void rt_LD_WriteReg(rt_uint8_t data1, rt_uint8_t data2)
  6. {
  7.     rt_uint8_t buf[3];
  8.     struct rt_spi_message msg;
  9.     LD3320_WR_Func(0);
  10.     buf[0= 0x04;
  11.     buf[1= data1;
  12.     buf[2= data2;
  13.     msg.send_buf = buf;
  14.     msg.recv_buf = RT_NULL;
  15.     msg.length = 3;
  16.     msg.cs_take = 1;
  17.     msg.cs_release = 1;
  18.     msg.next = RT_NULL;
  19.     rt_spi_transfer_message(spi_dev_ld3320&msg);
  20. }

读取寄存器:

  1. /********************************************************************************
  2. function:
  3.                 Read data from LD3320 reg
  4. ********************************************************************************/
  5. static rt_uint8_t rt_LD_ReadReg(rt_uint8_t reg_addr)
  6. {
  7.     rt_uint8_t ret_byte;
  8.     LD3320_WR_Func(0);
  9.     struct rt_spi_message msg1, msg2;
  10.     rt_uint8_t buf[3];
  11.     buf[0= 0x05;
  12.     buf[1= reg_addr;
  13.     buf[2= 0x00;
  14.     msg1.send_buf = buf;
  15.     msg1.recv_buf = RT_NULL;
  16.     msg1.length = 2;
  17.     msg1.cs_take = 1;
  18.     msg1.cs_release = 0;
  19.     msg1.next = &msg2;
  20.     msg2.send_buf = RT_NULL;
  21.     msg2.recv_buf = &ret_byte;
  22.     msg2.length = 1;
  23.     msg2.cs_take = 0;
  24.     msg2.cs_release = 1;
  25.     msg2.next = RT_NULL;
  26.     rt_spi_transfer_message(spi_dev_ld3320&msg1);
  27.     return (ret_byte);
  28. }

关于语音播放,录放音的本章先不做介绍,重点看看语音识别如何实现:

模块在开始工作之前,要先对其进行复位操作,就是对芯片的第 47 腿( RSTB*)发送低电平,然后需要对片选 CS 做一次 拉低→拉高的操作,以激活内部 DSP。可按照以下顺序:

  1. /********************************************************************************
  2. function:
  3.                 LD3320 hardware init
  4. ********************************************************************************/
  5. void rt_LD_init(void)
  6. {
  7.     LD3320_RST_Func(1);
  8.     rt_thread_delay(100);
  9.     LD3320_RST_Func(0);
  10.     rt_thread_delay(100);
  11.     LD3320_RST_Func(1);
  12.     rt_thread_delay(100);
  13.     LD3320_CS_Func(0);
  14.     rt_thread_delay(100);
  15.     LD3320_CS_Func(1);
  16.     rt_thread_delay(100);
  17. }

语音识别的操作顺序是:

语音识别用初始化(包括通用初始化)→写入识别列表→开始识别, 并准备好中断响应函数,打开中断允许位。

这里需要说明一下,如果不用中断方式,也可以通过查询方式工作。在“开 始识别”后,读取寄存器 B2H 的值,如果为 21H 就表示有识别结果产生。

在此之后读取候选项等操作与中断方式相同。

  • 通用初始化

  1. /********************************************************************************
  2. function:
  3.                 Common init
  4. ********************************************************************************/
  5. static void rt_LD_Init_Common(void)
  6. {
  7.     bMp3Play = 0;
  8.     rt_LD_ReadReg(0x06);
  9.     rt_LD_WriteReg(0x170x35);
  10.     rt_thread_delay(20);
  11.     rt_LD_ReadReg(0x06);
  12.     rt_LD_WriteReg(0x890x03);
  13.     rt_thread_delay(20);
  14.     rt_LD_WriteReg(0xCF, 0x43);
  15.     rt_thread_delay(20);
  16.     rt_LD_WriteReg(0xCB, 0x02);
  17.     /*PLL setting*/
  18.     rt_LD_WriteReg(0x11, LD_PLL_11);
  19.     if (nLD_Mode == LD_MODE_MP3)
  20.     {
  21.         rt_LD_WriteReg(0x1E, 0x00);
  22.         rt_LD_WriteReg(0x19, LD_PLL_MP3_19);
  23.         rt_LD_WriteReg(0x1B, LD_PLL_MP3_1B);
  24.         rt_LD_WriteReg(0x1D, LD_PLL_MP3_1D);
  25.     }
  26.     else
  27.     {
  28.         rt_LD_WriteReg(0x1E, 0x00);
  29.         rt_LD_WriteReg(0x19, LD_PLL_ASR_19);
  30.         rt_LD_WriteReg(0x1B, LD_PLL_ASR_1B);
  31.         rt_LD_WriteReg(0x1D, LD_PLL_ASR_1D);
  32.     }
  33.     rt_thread_delay(20);
  34.     rt_LD_WriteReg(0xCD, 0x04);
  35.     rt_LD_WriteReg(0x170x4c);
  36.     rt_thread_delay(20);
  37.     rt_LD_WriteReg(0xB90x00);
  38.     rt_LD_WriteReg(0xCF, 0x4F);
  39.     rt_LD_WriteReg(0x6F, 0xFF);
  40. }
  • 语音识别初始化

  1. /********************************************************************************
  2. function:
  3.                 ASR init
  4. ********************************************************************************/
  5. static void rt_LD_Init_ASR(void)
  6. {
  7.     nLD_Mode = LD_MODE_ASR_RUN;
  8.     rt_LD_Init_Common();
  9.     rt_LD_WriteReg(0xBD, 0x00);
  10.     rt_LD_WriteReg(0x170x48);
  11.     rt_thread_delay(20);
  12.     rt_LD_WriteReg(0x3C, 0x80);
  13.     rt_LD_WriteReg(0x3E, 0x07);
  14.     rt_LD_WriteReg(0x380xff);
  15.     rt_LD_WriteReg(0x3A, 0x07);
  16.     rt_thread_delay(20);
  17.     rt_LD_WriteReg(0x400);
  18.     rt_LD_WriteReg(0x428);
  19.     rt_LD_WriteReg(0x440);
  20.     rt_LD_WriteReg(0x468);
  21.     rt_thread_delay(20);
  22. }
  • 写入识别表

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

  1. /********************************************************************************
  2. function:
  3.                 Check ASR state
  4. ********************************************************************************/
  5. static rt_uint8_t rt_LD_Check_ASRBusyFlag(void)
  6. {
  7.     rt_uint8_t j;
  8.     rt_uint8_t flag = 0;
  9.     for (j = 0; j < 10; j++)
  10.     {
  11.         if (rt_LD_ReadReg(0xb2== 0x21)
  12.         {
  13.             flag = 1;
  14.             //rt_kprintf("success!!! ASR OK\r\n");
  15.             break;
  16.         }
  17.         rt_thread_delay(10);
  18.         //printf("ERROR!!! ASR Busy\r\n");
  19.     }
  20.     return flag;
  21. }
  22. /********************************************************************************
  23. function:
  24.                 Add ASR Keyword
  25. ********************************************************************************/
  26. static rt_uint8_t rt_LD_AsrAddKey(void)
  27. {
  28.     rt_uint8_t k, flag;
  29.     rt_uint8_t nAsrAddLength;
  30. #define DATE_A 5
  31. #define DATE_B 20
  32.     rt_uint8_t sRecog[DATE_A][DATE_B] = {
  33.         //add commond,use pinying
  34.         "qian jin",
  35.         "hou tui",
  36.         "zuo zhuan",
  37.         "you zhuan"
  38.     };
  39.     rt_uint8_t pCode[DATE_A] = {
  40.         //add commond code to do the commond
  41.         CODE_RUN,
  42.         CODE_KEY,
  43.         CODE_FLASH,
  44.         CODE_PLAY,
  45.     };
  46.     flag = 1;
  47.     for (k = 0; k < DATE_A; k++)
  48.     { //write data to LD3320
  49.         if (rt_LD_Check_ASRBusyFlag() == 0)
  50.         {
  51.             flag = 0;
  52.             break;
  53.         }
  54.         rt_LD_WriteReg(0xc1, pCode[k]);
  55.         rt_LD_WriteReg(0xc30);
  56.         rt_LD_WriteReg(0x080x04);
  57.         rt_thread_delay(1);
  58.         rt_LD_WriteReg(0x080x00);
  59.         rt_thread_delay(1);
  60.         for (nAsrAddLength = 0; nAsrAddLength < DATE_B; nAsrAddLength++)
  61.         {
  62.             if (sRecog[k][nAsrAddLength] == 0)
  63.                 break;
  64.             rt_LD_WriteReg(0x5, sRecog[k][nAsrAddLength]);
  65.         }
  66.         rt_LD_WriteReg(0xb9, nAsrAddLength);
  67.         rt_LD_WriteReg(0xb20xff);
  68.         rt_LD_WriteReg(0x370x04);
  69.     }
  70.     return flag;
  71. }
  • 开始识别

  1. /********************************************************************************
  2. function:
  3.                 Begin to ASR
  4. ********************************************************************************/
  5. static rt_uint8_t rt_LD_AsrRun(void)
  6. {
  7.     rt_LD_WriteReg(0x35, MIC_VOL);
  8.     rt_LD_WriteReg(0x1C, 0x09);
  9.     rt_LD_WriteReg(0xBD, 0x20);
  10.     rt_LD_WriteReg(0x080x01);
  11.     rt_thread_delay(20);
  12.     rt_LD_WriteReg(0x080x00);
  13.     rt_thread_delay(20);
  14.     if (rt_LD_Check_ASRBusyFlag() == 0)
  15.         return 0;
  16.     rt_LD_WriteReg(0xB20xff);
  17.     rt_LD_WriteReg(0x370x06);
  18.     rt_LD_WriteReg(0x370x06);
  19.     rt_thread_delay(20);
  20.     rt_LD_WriteReg(0x1C, 0x0b);
  21.     rt_LD_WriteReg(0x290x10);
  22.     rt_LD_WriteReg(0xBD, 0x00);
  23.     return 1;
  24. }
  25. /********************************************************************************
  26. function:
  27.                 Run ASR
  28. ********************************************************************************/
  29. rt_uint8_t rt_LD_ASR(void)
  30. {
  31.     rt_uint8_t i = 0;
  32.     rt_uint8_t asrflag = 0;
  33.     for (i = 0; i < 5; i++)
  34.     {                     //run ASR try 5 times
  35.         rt_LD_Init_ASR(); //init ASR
  36.         rt_thread_delay(100);
  37.         if (rt_LD_AsrAddKey() == 0)
  38.         { //Add fixed to LD3320
  39.             rt_kprintf("ERROR!!! LD_AsrAddKey\r\n");
  40.             rt_LD_init(); //ERROR,Reset LD3320
  41.             rt_thread_delay(50);
  42.             continue;
  43.         }
  44.         rt_thread_delay(10);
  45.         if (rt_LD_AsrRun() == 0)
  46.         { //start ASR
  47.             rt_kprintf("ERROR!!! LD_AsrRun\r\n");
  48.             rt_LD_init(); //ERROR,Reset LD3320
  49.             rt_thread_delay(50);
  50.             continue;
  51.         }
  52.         asrflag = 1;
  53.         break;
  54.     }
  55.     //rt_kprintf("RunASR\r\n");
  56.     return asrflag;
  57. }
  • 响应中断 如果麦克风采集到声音,不管是否识别出正常结果,都会产生一个 中断信号。而中断程序要根据寄存器的值分析结果。读取 BA 寄存器的值,可以知道有几个候选答案,而 C5 寄存器里的 答案是得分最高、最可能正确的答案。例如发音为“上海”并被成功识别(无其他候选),那么 BA 寄存器 里的数值是 1,而 C5 寄存器里的值是对应的编码 3。

中断到来我们置一标志即可,逻辑处理函数放在main中执行

  • 根据识别内容执行对应用户代码

  1. void rt_ProcessInt()
  2. {
  3.     rt_uint8_t nAsrResCount = 0;
  4.     ucRegVal = rt_LD_ReadReg(0x2B);
  5.     ucHighInt = rt_LD_ReadReg(0x29); // interrupt enable flag
  6.     ucLowInt = rt_LD_ReadReg(0x02); // interrupt enable flag
  7.     rt_LD_WriteReg(0x290);// interrupt disenable
  8.     rt_LD_WriteReg(0x020);// interrupt disenable
  9.     if(nLD_Mode == LD_MODE_ASR_RUN) {
  10.         //rt_kprintf("---------------ASR---------------\r\n");
  11.         //The interruption caused by speech recognition
  12.         //(There is sound inputand there is interruption whether the recognition is successful or failed)
  13.         if((ucRegVal & 0x10&& rt_LD_ReadReg(0xb2)==0x21 && rt_LD_ReadReg(0xbf)==0x35) {
  14.             nAsrResCount = rt_LD_ReadReg(0xba);
  15.             if(nAsrResCount>0 && nAsrResCount<=4) {
  16.                 rt_kprintf("ASR SUCCESSFUL \r\n");
  17.                 nAsrStatus = LD_ASR_FOUNDOK;
  18.             }
  19.             else {
  20.                 rt_kprintf("ASR UNSUCCESSFUL \r\n");
  21.                 nAsrStatus = LD_ASR_FOUNDZERO;
  22.             }
  23.         }
  24.         else {
  25.             //rt_kprintf("No ASR \r\n");
  26.             nAsrStatus = LD_ASR_FOUNDZERO;
  27.         }
  28.         rt_LD_WriteReg(0x2b, 0);
  29.         rt_LD_WriteReg(0x1c, 0);
  30.         return;
  31.     }
  32.     rt_kprintf("--------------PLAY MP3--------------\r\n");
  33.     // Play MP3 to produce 3 kinkd of intterupt
  34.     // A. play over
  35.     // B. data send over
  36.     // C. Data will be used up and sent
  37.     if(rt_LD_ReadReg(0xBA) & CAUSE_MP3_SONG_END) {
  38.         // A. play over
  39.         rt_LD_WriteReg(0x2B, 0);
  40.         rt_LD_WriteReg(0xBA, 0);
  41.         rt_LD_WriteReg(0xBC, 0);
  42.         rt_LD_WriteReg(0x081);
  43.         rt_LD_WriteReg(0x080);
  44.         rt_LD_WriteReg(0x330);
  45.         rt_kprintf("play over \r\n");
  46.         bMp3Play = 0;                   // play status
  47.         return;
  48.     }
  49.     if(nMp3Pos >= nMp3Size) {
  50.         // B. data send over
  51.         rt_LD_WriteReg(0xBC, 0x01);//data voer
  52.         rt_LD_WriteReg(0x290x10);
  53.         return;
  54.     }
  55.     // C. Data will be used up and sent
  56.     rt_LD_ReloadMp3Data();
  57.     rt_LD_WriteReg(0x29, ucHighInt);
  58.     rt_LD_WriteReg(0x02, ucLowInt);
  59. }

至此,语音识别流程就算完了,来看看如何调用:

创建两个线程,关于如何创建,紧跟小飞哥步伐,小飞哥会在后面的实战中详细介绍

  1. int ld3320_sample(void)
  2. {
  3.     static rt_thread_t tid1 = RT_NULL,tid2 = RT_NULL;
  4.     rt_ld3320_irq_pin_init();
  5.     /* 创建线程  */
  6.     tid1=rt_thread_create(
  7.                    "thread1",
  8.                    thread1_entry,
  9.                    RT_NULL,
  10.                    THREAD_STACK_SIZE,
  11.                    THREAD_PRIORITY, THREAD_TIMESLICE);
  12.     /* 如果获得线程控制块,启动这个线程 */
  13.     if (tid1 != RT_NULL)
  14.         rt_thread_startup(tid1);
  15.     /* 创建线程  */
  16.     tid2=rt_thread_create(
  17.                    "thread2",
  18.                    thread2_entry,
  19.                    RT_NULL,
  20.                    THREAD_STACK_SIZE,
  21.                    THREAD_PRIORITY, THREAD_TIMESLICE);
  22.     /* 如果获得线程控制块,启动这个线程 */
  23.     if (tid2 != RT_NULL)
  24.         rt_thread_startup(tid2);
  25.     return 0;
  26. }
  27. /* 导出到 msh 命令列表中 */
  28. //MSH_CMD_EXPORT(ld3320_sample, ld3320 sample);
  29. INIT_COMPONENT_EXPORT(ld3320_sample);

任务函数:

  1. /* 线程入口 */
  2. static void thread1_entry(void* parameter)
  3. {
  4.     int i;
  5.     while(1)
  6.     {
  7. /*      rt_kprintf("success!!! thread1_entry OK\r\n");
  8.         rt_thread_delay(500);*/
  9.         if(ld3320_flag)
  10.         {
  11.             ld3320_flag=0;
  12.             rt_ProcessInt();
  13.         }
  14.         else {
  15.             ld3320_flag=0;
  16.             i++;
  17.             if(i%500==0){
  18.                 rt_kprintf("\r\n\rvoice checking,please speak...\r\n");
  19.                 i=0;
  20.             }
  21.             rt_thread_delay(1);
  22.         }
  23.     }
  24. }
  25. /* 线程入口 */
  26. static void thread2_entry(void* parameter)
  27. {
  28.    rt_uint8_t nAsrRes = 0;
  29.   while (1)
  30.   {
  31.       if (bMp3Play)
  32.       {
  33.           rt_kprintf("*********playing*********\r\n");
  34.           continue;
  35.       }
  36.       switch (nAsrStatus)
  37.       {
  38.       case LD_ASR_RUNING:
  39.       case LD_ASR_ERROR:
  40.           break;
  41.       case LD_ASR_NONE:
  42.           nAsrStatus = LD_ASR_RUNING;
  43.           if (rt_LD_ASR() == 0//Start the ASR process once
  44.               nAsrStatus = LD_ASR_ERROR;
  45.           break;
  46.       case LD_ASR_FOUNDOK:
  47.           nAsrRes = rt_LD_GetResult(); //once ASR process endget the result
  48.           switch (nAsrRes)
  49.           { //show the commond
  50.           case CODE_RUN:
  51.               rt_kprintf("voice control system is runing.....\r\n");
  52.               break;
  53.           case CODE_KEY:
  54.               rt_kprintf("voice control system is runing.....\r\n");
  55.               break;
  56.           case CODE_FLASH:
  57.               rt_kprintf("voice control system is runing.....\r\n");
  58.               break;
  59.           case CODE_PLAY:
  60.               rt_kprintf("voice control system is runing.....\r\n");
  61.               break;
  62.           default:
  63.               break;
  64.           }
  65.           nAsrStatus = LD_ASR_NONE;
  66.           break;
  67.       case LD_ASR_FOUNDZERO:
  68.       default:
  69.           nAsrStatus = LD_ASR_NONE;
  70.           break;
  71.       }
  72.       rt_Board_text(nAsrRes); //do the commond
  73.       nAsrRes = 0;
  74.   }
  75. /*      rt_kprintf("success!!! thread2_entry OK\r\n");
  76.         rt_thread_delay(500);*/
  77. }

至此,完成,ending

效果演示

既然能够语音识别了,来,咱们一起和它对对话,分别对他说:你好,你来自哪里,背一首诗,世界这么大 识别之后分别对应输出相应的内容:

资料获取

关注公众号,后台回复“LD3320”即可获取源码资料,欢迎添加小飞哥好友,进群交流,欢迎点击阅读原文

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小舞很执着/article/detail/835967
推荐阅读
  

闽ICP备14008679号