IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> RT-Thread实战笔记|LD3320非限定词条语音控制器使用详解 -> 正文阅读

[人工智能]RT-Thread实战笔记|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设备

函数原型:

rt_err_t?rt_spi_bus_attach_device(struct?rt_spi_device?*device,
??????????????????????????????????const?char???????????*name,
??????????????????????????????????const?char???????????*bus_name,
??????????????????????????????????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 设备的传输参数。

函数原型:

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

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

struct?rt_spi_configuration
{
????rt_uint8_t?mode;????????/*?模式?*/
????rt_uint8_t?data_width;??/*?数据宽度,可取8位、16位、32位?*/
????rt_uint16_t?reserved;???/*?保留?*/
????rt_uint32_t?max_hz;?????/*?最大频率?*/
};

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

/*?设置数据传输顺序是MSB位在前还是LSB位在前?*/
#define?RT_SPI_LSB??????(0<<2)????????????????????????/*?bit[2]:?0-LSB?*/
#define?RT_SPI_MSB??????(1<<2)????????????????????????/*?bit[2]:?1-MSB?*/

/*?设置SPI的主从模式?*/
#define?RT_SPI_MASTER???(0<<3)????????????????????????/*?SPI?master?device?*/
#define?RT_SPI_SLAVE????(1<<3)????????????????????????/*?SPI?slave?device?*/

/*?设置时钟极性和时钟相位?*/
#define?RT_SPI_MODE_0???(0?|?0)???????????????????????/*?CPOL?=?0,?CPHA?=?0?*/
#define?RT_SPI_MODE_1???(0?|?RT_SPI_CPHA)?????????????/*?CPOL?=?0,?CPHA?=?1?*/
#define?RT_SPI_MODE_2???(RT_SPI_CPOL?|?0)?????????????/*?CPOL?=?1,?CPHA?=?0?*/
#define?RT_SPI_MODE_3???(RT_SPI_CPOL?|?RT_SPI_CPHA)???/*?CPOL?=?1,?CPHA?=?1?*/

#define?RT_SPI_CS_HIGH??(1<<4)????????????????????????/*?Chipselect?active?high?*/
#define?RT_SPI_NO_CS????(1<<5)????????????????????????/*?No?chipselect?*/
#define?RT_SPI_3WIRE????(1<<6)????????????????????????/*?SI/SO?pin?shared?*/
#define?RT_SPI_READY????(1<<7)????????????????????????/*?Slave?pulls?low?to?pause?*/
  • 数据宽度:根据 SPI 主设备及 SPI 从设备可发送及接收的数据宽度格式设置为8位、16位或者32位。

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

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

????struct?rt_spi_configuration?cfg;
????cfg.data_width?=?8;
????cfg.mode?=?RT_SPI_MASTER?|?RT_SPI_MODE_0?|?RT_SPI_MSB;
????cfg.max_hz?=?20?*?1000?*1000;???????????????????????????/*?20M?*/

????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,看代码:

struct?rt_spi_device?*spi_dev_ld3320;?
????spi_dev_ld3320?=?(struct?rt_spi_device?*)rt_device_find(name);
????if?(!spi_dev_ld3320)
????{
????????rt_kprintf("spi?sample?run?failed!?can't?find?%s?device!\n",?name);
????}
????else
????{
????????rt_kprintf("spi?sample?run?success!find?%s?device!\n",?name);
????}

自定义传输数据

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

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

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

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引脚进行定义

#define?LD3320_WR?GET_PIN(H,?2)
#define?LD3320_RST?GET_PIN(H,?3)
#define?LD3320_IRQ?GET_PIN(B,?0)
#define?LD3320_CS?GET_PIN(E,?4)

#define?LD3320_WR_Func(x)?x???rt_pin_write(LD3320_WR,?PIN_HIGH)?:?rt_pin_write(LD3320_WR,?PIN_LOW)
#define?LD3320_RST_Func(x)?x???rt_pin_write(LD3320_RST,?PIN_HIGH)?:?rt_pin_write(LD3320_RST,?PIN_LOW)
#define?LD3320_CS_Func(x)?x???rt_pin_write(LD3320_CS,?PIN_HIGH)?:?rt_pin_write(LD3320_CS,?PIN_LOW)

IRQ需要注册中断:

void?rt_ld3320_irq_pin_init()
{
????rt_pin_mode(LD3320_IRQ,?PIN_MODE_INPUT_PULLUP);

????rt_pin_attach_irq(LD3320_IRQ,?PIN_IRQ_MODE_FALLING,?rt_ld3320_isr,?RT_NULL);
????rt_pin_irq_enable(LD3320_IRQ,?PIN_IRQ_ENABLE);
}

SPI设备注册初始化:

void?rt_ld3320_init(int?argc,?char?*argv[])
{
????char?name[RT_NAME_MAX];

????if?(argc?==?2)
????{
????????rt_strncpy(name,?argv[1],?RT_NAME_MAX);
????}
????else
????{
????????rt_strncpy(name,?LD3320_SPI_DEVICE_NAME,?RT_NAME_MAX);
????}
????rt_hw_ld3320_init();

????spi_dev_ld3320?=?(struct?rt_spi_device?*)rt_device_find(name);
????if?(!spi_dev_ld3320)
????{
????????rt_kprintf("spi?sample?run?failed!?can't?find?%s?device!\n",?name);
????}
????else
????{
????????rt_kprintf("spi?sample?run?success!find?%s?device!\n",?name);
????}

????struct?rt_spi_configuration?cfg;
????cfg.data_width?=?8;
????cfg.mode?=?RT_SPI_MSB?|?RT_SPI_MASTER?|?RT_SPI_MODE_2;
????cfg.max_hz?=?6?*?1000?*?100;?/*?6M?*/

????if?(rt_spi_configure(spi_dev_ld3320,?&cfg)?==?RT_ERROR)
????{
????????rt_kprintf("spi4?config?error\r\n");
????}
????rt_kprintf("spi4?config?success\r\n");

????rt_LD_init();
}

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

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

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

/********************************************************************************
function:
????????????????Write?data?to?LD3320?reg
********************************************************************************/
static?void?rt_LD_WriteReg(rt_uint8_t?data1,?rt_uint8_t?data2)
{
????rt_uint8_t?buf[3];

????struct?rt_spi_message?msg;

????LD3320_WR_Func(0);

????buf[0]?=?0x04;
????buf[1]?=?data1;
????buf[2]?=?data2;

????msg.send_buf?=?buf;
????msg.recv_buf?=?RT_NULL;
????msg.length?=?3;
????msg.cs_take?=?1;
????msg.cs_release?=?1;
????msg.next?=?RT_NULL;

????rt_spi_transfer_message(spi_dev_ld3320,?&msg);
}

读取寄存器:

/********************************************************************************
function:
????????????????Read?data?from?LD3320?reg
********************************************************************************/
static?rt_uint8_t?rt_LD_ReadReg(rt_uint8_t?reg_addr)
{
????rt_uint8_t?ret_byte;

????LD3320_WR_Func(0);

????struct?rt_spi_message?msg1,?msg2;

????rt_uint8_t?buf[3];

????buf[0]?=?0x05;
????buf[1]?=?reg_addr;
????buf[2]?=?0x00;

????msg1.send_buf?=?buf;
????msg1.recv_buf?=?RT_NULL;
????msg1.length?=?2;
????msg1.cs_take?=?1;
????msg1.cs_release?=?0;
????msg1.next?=?&msg2;

????msg2.send_buf?=?RT_NULL;
????msg2.recv_buf?=?&ret_byte;
????msg2.length?=?1;
????msg2.cs_take?=?0;
????msg2.cs_release?=?1;
????msg2.next?=?RT_NULL;

????rt_spi_transfer_message(spi_dev_ld3320,?&msg1);

????return?(ret_byte);
}

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

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

/********************************************************************************
function:
????????????????LD3320?hardware?init
********************************************************************************/
void?rt_LD_init(void)
{
????LD3320_RST_Func(1);
????rt_thread_delay(100);
????LD3320_RST_Func(0);
????rt_thread_delay(100);
????LD3320_RST_Func(1);

????rt_thread_delay(100);
????LD3320_CS_Func(0);
????rt_thread_delay(100);
????LD3320_CS_Func(1);
????rt_thread_delay(100);
}

语音识别的操作顺序是:

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

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

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

  • 通用初始化

/********************************************************************************
function:
????????????????Common?init
********************************************************************************/
static?void?rt_LD_Init_Common(void)
{
????bMp3Play?=?0;

????rt_LD_ReadReg(0x06);
????rt_LD_WriteReg(0x17,?0x35);
????rt_thread_delay(20);
????rt_LD_ReadReg(0x06);

????rt_LD_WriteReg(0x89,?0x03);
????rt_thread_delay(20);
????rt_LD_WriteReg(0xCF,?0x43);
????rt_thread_delay(20);
????rt_LD_WriteReg(0xCB,?0x02);

????/*PLL?setting*/
????rt_LD_WriteReg(0x11,?LD_PLL_11);
????if?(nLD_Mode?==?LD_MODE_MP3)
????{
????????rt_LD_WriteReg(0x1E,?0x00);
????????rt_LD_WriteReg(0x19,?LD_PLL_MP3_19);
????????rt_LD_WriteReg(0x1B,?LD_PLL_MP3_1B);
????????rt_LD_WriteReg(0x1D,?LD_PLL_MP3_1D);
????}
????else
????{
????????rt_LD_WriteReg(0x1E,?0x00);
????????rt_LD_WriteReg(0x19,?LD_PLL_ASR_19);
????????rt_LD_WriteReg(0x1B,?LD_PLL_ASR_1B);
????????rt_LD_WriteReg(0x1D,?LD_PLL_ASR_1D);
????}
????rt_thread_delay(20);
????rt_LD_WriteReg(0xCD,?0x04);
????rt_LD_WriteReg(0x17,?0x4c);
????rt_thread_delay(20);
????rt_LD_WriteReg(0xB9,?0x00);
????rt_LD_WriteReg(0xCF,?0x4F);
????rt_LD_WriteReg(0x6F,?0xFF);
}
  • 语音识别初始化

/********************************************************************************
function:
????????????????ASR?init
********************************************************************************/
static?void?rt_LD_Init_ASR(void)
{
????nLD_Mode?=?LD_MODE_ASR_RUN;
????rt_LD_Init_Common();

????rt_LD_WriteReg(0xBD,?0x00);
????rt_LD_WriteReg(0x17,?0x48);
????rt_thread_delay(20);

????rt_LD_WriteReg(0x3C,?0x80);
????rt_LD_WriteReg(0x3E,?0x07);
????rt_LD_WriteReg(0x38,?0xff);
????rt_LD_WriteReg(0x3A,?0x07);
????rt_thread_delay(20);

????rt_LD_WriteReg(0x40,?0);
????rt_LD_WriteReg(0x42,?8);
????rt_LD_WriteReg(0x44,?0);
????rt_LD_WriteReg(0x46,?8);
????rt_thread_delay(20);
}
  • 写入识别表

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

/********************************************************************************
function:
????????????????Check?ASR?state
********************************************************************************/
static?rt_uint8_t?rt_LD_Check_ASRBusyFlag(void)
{
????rt_uint8_t?j;
????rt_uint8_t?flag?=?0;
????for?(j?=?0;?j?<?10;?j++)
????{
????????if?(rt_LD_ReadReg(0xb2)?==?0x21)
????????{
????????????flag?=?1;
????????????//rt_kprintf("success!!!?ASR?OK\r\n");
????????????break;
????????}
????????rt_thread_delay(10);
????????//printf("ERROR!!!?ASR?Busy\r\n");
????}
????return?flag;
}

/********************************************************************************
function:
????????????????Add?ASR?Keyword
********************************************************************************/
static?rt_uint8_t?rt_LD_AsrAddKey(void)
{
????rt_uint8_t?k,?flag;
????rt_uint8_t?nAsrAddLength;
#define?DATE_A?5
#define?DATE_B?20

????rt_uint8_t?sRecog[DATE_A][DATE_B]?=?{
????????//add?commond,use?pinying
????????"qian?jin",
????????"hou?tui",
????????"zuo?zhuan",
????????"you?zhuan"

????};
????rt_uint8_t?pCode[DATE_A]?=?{
????????//add?commond?code?to?do?the?commond
????????CODE_RUN,
????????CODE_KEY,
????????CODE_FLASH,
????????CODE_PLAY,

????};
????flag?=?1;
????for?(k?=?0;?k?<?DATE_A;?k++)
????{?//write?data?to?LD3320
????????if?(rt_LD_Check_ASRBusyFlag()?==?0)
????????{
????????????flag?=?0;
????????????break;
????????}
????????rt_LD_WriteReg(0xc1,?pCode[k]);
????????rt_LD_WriteReg(0xc3,?0);
????????rt_LD_WriteReg(0x08,?0x04);
????????rt_thread_delay(1);
????????rt_LD_WriteReg(0x08,?0x00);
????????rt_thread_delay(1);
????????for?(nAsrAddLength?=?0;?nAsrAddLength?<?DATE_B;?nAsrAddLength++)
????????{
????????????if?(sRecog[k][nAsrAddLength]?==?0)
????????????????break;
????????????rt_LD_WriteReg(0x5,?sRecog[k][nAsrAddLength]);
????????}
????????rt_LD_WriteReg(0xb9,?nAsrAddLength);
????????rt_LD_WriteReg(0xb2,?0xff);
????????rt_LD_WriteReg(0x37,?0x04);
????}
????return?flag;
}

  • 开始识别

/********************************************************************************
function:
????????????????Begin?to?ASR
********************************************************************************/
static?rt_uint8_t?rt_LD_AsrRun(void)
{
????rt_LD_WriteReg(0x35,?MIC_VOL);
????rt_LD_WriteReg(0x1C,?0x09);
????rt_LD_WriteReg(0xBD,?0x20);
????rt_LD_WriteReg(0x08,?0x01);
????rt_thread_delay(20);
????rt_LD_WriteReg(0x08,?0x00);
????rt_thread_delay(20);

????if?(rt_LD_Check_ASRBusyFlag()?==?0)
????????return?0;

????rt_LD_WriteReg(0xB2,?0xff);
????rt_LD_WriteReg(0x37,?0x06);
????rt_LD_WriteReg(0x37,?0x06);
????rt_thread_delay(20);
????rt_LD_WriteReg(0x1C,?0x0b);
????rt_LD_WriteReg(0x29,?0x10);
????rt_LD_WriteReg(0xBD,?0x00);
????return?1;
}

/********************************************************************************
function:
????????????????Run?ASR
********************************************************************************/
rt_uint8_t?rt_LD_ASR(void)
{
????rt_uint8_t?i?=?0;
????rt_uint8_t?asrflag?=?0;
????for?(i?=?0;?i?<?5;?i++)
????{?????????????????????//run?ASR?try?5?times
????????rt_LD_Init_ASR();?//init?ASR
????????rt_thread_delay(100);
????????if?(rt_LD_AsrAddKey()?==?0)
????????{?//Add?fixed?to?LD3320
????????????rt_kprintf("ERROR!!!?LD_AsrAddKey\r\n");
????????????rt_LD_init();?//ERROR,Reset?LD3320
????????????rt_thread_delay(50);
????????????continue;
????????}
????????rt_thread_delay(10);
????????if?(rt_LD_AsrRun()?==?0)
????????{?//start?ASR
????????????rt_kprintf("ERROR!!!?LD_AsrRun\r\n");
????????????rt_LD_init();?//ERROR,Reset?LD3320
????????????rt_thread_delay(50);
????????????continue;
????????}
????????asrflag?=?1;
????????break;
????}
????//rt_kprintf("RunASR\r\n");
????return?asrflag;
}

  • 响应中断 如果麦克风采集到声音,不管是否识别出正常结果,都会产生一个 中断信号。而中断程序要根据寄存器的值分析结果。读取 BA 寄存器的值,可以知道有几个候选答案,而 C5 寄存器里的 答案是得分最高、最可能正确的答案。例如发音为“上海”并被成功识别(无其他候选),那么 BA 寄存器 里的数值是 1,而 C5 寄存器里的值是对应的编码 3。

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

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

void?rt_ProcessInt()
{
????rt_uint8_t?nAsrResCount?=?0;
????ucRegVal?=?rt_LD_ReadReg(0x2B);
????ucHighInt?=?rt_LD_ReadReg(0x29);?//?interrupt?enable?flag
????ucLowInt?=?rt_LD_ReadReg(0x02);?//?interrupt?enable?flag
????rt_LD_WriteReg(0x29,?0);//?interrupt?disenable
????rt_LD_WriteReg(0x02,?0);//?interrupt?disenable

????if(nLD_Mode?==?LD_MODE_ASR_RUN)?{
????????//rt_kprintf("---------------ASR---------------\r\n");
????????//The?interruption?caused?by?speech?recognition
????????//(There?is?sound?input,?and?there?is?interruption?whether?the?recognition?is?successful?or?failed)
????????if((ucRegVal?&?0x10)?&&?rt_LD_ReadReg(0xb2)==0x21?&&?rt_LD_ReadReg(0xbf)==0x35)?{
????????????nAsrResCount?=?rt_LD_ReadReg(0xba);
????????????if(nAsrResCount>0?&&?nAsrResCount<=4)?{
????????????????rt_kprintf("ASR?SUCCESSFUL?\r\n");
????????????????nAsrStatus?=?LD_ASR_FOUNDOK;
????????????}
????????????else?{
????????????????rt_kprintf("ASR?UNSUCCESSFUL?\r\n");
????????????????nAsrStatus?=?LD_ASR_FOUNDZERO;
????????????}
????????}
????????else?{
????????????//rt_kprintf("No?ASR?\r\n");
????????????nAsrStatus?=?LD_ASR_FOUNDZERO;
????????}
????????rt_LD_WriteReg(0x2b,?0);
????????rt_LD_WriteReg(0x1c,?0);
????????return;
????}
????rt_kprintf("--------------PLAY?MP3--------------\r\n");
????//?Play?MP3?to?produce?3?kinkd?of?intterupt
????//?A.?play?over
????//?B.?data?send?over
????//?C.?Data?will?be?used?up?and?sent
????if(rt_LD_ReadReg(0xBA)?&?CAUSE_MP3_SONG_END)?{
????????//?A.?play?over
????????rt_LD_WriteReg(0x2B,?0);
????????rt_LD_WriteReg(0xBA,?0);
????????rt_LD_WriteReg(0xBC,?0);
????????rt_LD_WriteReg(0x08,?1);
????????rt_LD_WriteReg(0x08,?0);
????????rt_LD_WriteReg(0x33,?0);
????????rt_kprintf("play?over?\r\n");
????????bMp3Play?=?0;???????????????????//?play?status
????????return;
????}
????if(nMp3Pos?>=?nMp3Size)?{
????????//?B.?data?send?over
????????rt_LD_WriteReg(0xBC,?0x01);//data?voer
????????rt_LD_WriteReg(0x29,?0x10);
????????return;
????}
????//?C.?Data?will?be?used?up?and?sent
????rt_LD_ReloadMp3Data();
????rt_LD_WriteReg(0x29,?ucHighInt);
????rt_LD_WriteReg(0x02,?ucLowInt);
}

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

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

int?ld3320_sample(void)
{

????static?rt_thread_t?tid1?=?RT_NULL,tid2?=?RT_NULL;

????rt_ld3320_irq_pin_init();
????/*?创建线程??*/
????tid1=rt_thread_create(
???????????????????"thread1",
???????????????????thread1_entry,
???????????????????RT_NULL,
???????????????????THREAD_STACK_SIZE,
???????????????????THREAD_PRIORITY,?THREAD_TIMESLICE);
????/*?如果获得线程控制块,启动这个线程?*/
????if?(tid1?!=?RT_NULL)
????????rt_thread_startup(tid1);

????/*?创建线程??*/
????tid2=rt_thread_create(
???????????????????"thread2",
???????????????????thread2_entry,
???????????????????RT_NULL,
???????????????????THREAD_STACK_SIZE,
???????????????????THREAD_PRIORITY,?THREAD_TIMESLICE);
????/*?如果获得线程控制块,启动这个线程?*/
????if?(tid2?!=?RT_NULL)
????????rt_thread_startup(tid2);

????return?0;
}
/*?导出到?msh?命令列表中?*/
//MSH_CMD_EXPORT(ld3320_sample,?ld3320?sample);
INIT_COMPONENT_EXPORT(ld3320_sample);

任务函数:

/*?线程入口?*/
static?void?thread1_entry(void*?parameter)
{

????int?i;
????while(1)
????{
/*??????rt_kprintf("success!!!?thread1_entry?OK\r\n");
????????rt_thread_delay(500);*/
????????if(ld3320_flag)
????????{
????????????ld3320_flag=0;
????????????rt_ProcessInt();
????????}
????????else?{
????????????ld3320_flag=0;
????????????i++;
????????????if(i%500==0){

????????????????rt_kprintf("\r\n\rvoice?checking,please?speak...\r\n");
????????????????i=0;
????????????}
????????????rt_thread_delay(1);
????????}
????}
}

/*?线程入口?*/
static?void?thread2_entry(void*?parameter)
{
???rt_uint8_t?nAsrRes?=?0;

??while?(1)
??{
??????if?(bMp3Play)
??????{
??????????rt_kprintf("*********playing*********\r\n");
??????????continue;
??????}
??????switch?(nAsrStatus)
??????{
??????case?LD_ASR_RUNING:
??????case?LD_ASR_ERROR:
??????????break;
??????case?LD_ASR_NONE:
??????????nAsrStatus?=?LD_ASR_RUNING;
??????????if?(rt_LD_ASR()?==?0)?//Start?the?ASR?process?once
??????????????nAsrStatus?=?LD_ASR_ERROR;
??????????break;
??????case?LD_ASR_FOUNDOK:
??????????nAsrRes?=?rt_LD_GetResult();?//once?ASR?process?end,?get?the?result
??????????switch?(nAsrRes)
??????????{?//show?the?commond
??????????case?CODE_RUN:
??????????????rt_kprintf("voice?control?system?is?runing.....\r\n");
??????????????break;
??????????case?CODE_KEY:
??????????????rt_kprintf("voice?control?system?is?runing.....\r\n");
??????????????break;
??????????case?CODE_FLASH:
??????????????rt_kprintf("voice?control?system?is?runing.....\r\n");
??????????????break;
??????????case?CODE_PLAY:
??????????????rt_kprintf("voice?control?system?is?runing.....\r\n");
??????????????break;
??????????default:
??????????????break;
??????????}
??????????nAsrStatus?=?LD_ASR_NONE;
??????????break;
??????case?LD_ASR_FOUNDZERO:
??????default:
??????????nAsrStatus?=?LD_ASR_NONE;
??????????break;
??????}
??????rt_Board_text(nAsrRes);?//do?the?commond
??????nAsrRes?=?0;

??}
/*??????rt_kprintf("success!!!?thread2_entry?OK\r\n");
????????rt_thread_delay(500);*/
}

至此,完成,ending

效果演示

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

资料获取

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

?

?

  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2021-08-17 15:24:00  更:2021-08-17 15:26:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 19:54:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码