《嵌入式 - 语音识别TWen-ASR-ONE开发笔记》第3章 TWen-ASR-ONE 多线程和消息队列

3.1 Scratch简介

在开始本章之前,需要介绍下TWen-ASR-ONE的用户开发语言-Scratch,Scratch是麻省理工学院开发的一款简易图形化编程工具主要是为青少年开发的一种编程工具,它图形化编程工具当中最广为人知的一种形式,所有人都可以在软件中创作自己的程序。

TWen-ASR-ON的开发者通过Scratch编程的方式将软件逻辑构建出来后,天问Block IDE再将其转换为C语言而已,只是不对开发者开放而已,Scratch很简单,你只有创意,只需在IDE上拉动模块,像搭积木的方式构建一个个逻辑块即可。对于笔者来说,我是不屑玩这个的,无奈官方不开发底层接口,关于Scratch我就不去细讲了,只要有编程基础的,一看就会,我觉得细讲是对大家的侮辱,因此,关于语言本身我是不会讲了。

官网地址

3.2多线程基础

TWen-ASR-ON的内部是基于FreeRTOS,因此,TWen-ASR-ON是可以使用多线程,关于线程,笔者本不想多讲的,毕竟能看到我的文章的人都是有一定的编程基础的,但笔者正在这里还是简单讲解吧。
我们想要做多件事情的时候,就需要创建多任务来并发执行,但需要注意的是,对于单核CPU而言:多线程就是一个CPU在来回的切换,在交替执行。对于多核CPU而言:多线程就是同时有多条执行路径在同时(并行)执行,每个核执行一个线程,多个核就有可能是一块同时执行的,对于TWen-ASR-ON来说,是一个单核,因此当你创建多个任务后,看着好像是多个任务在一起执行,其实是CPU在不断地切换任务,每个线程有自己的堆栈和局部变量,线程由CPU独立调度执行,所以说线程是程序执行时的最小单位

FreeRTOS 是一个抢占式的实时多任务系统, 那么其任务调度器也是抢占式的,运行过程如图所示:

《嵌入式 - 语音识别TWen-ASR-ONE开发笔记》第3章 TWen-ASR-ONE 多线程和消息队列

在上图中,高优先级的任务可以打断低优先级任务的运行而取得 CPU 的使用权,这样就保证了那些紧急任务的运行。 这样我们就可以为那些对实时性要求高的任务设置一个很高的优先级,比如自动驾驶中的障碍物检测任务等。 高优先级的任务执行完成以后重新把 CPU 的使用权归还给低优先级的任务,这个就是抢占式多任务系统的基本原理。

3.3 TWen-ASR-ONE多线程实现

TWen-ASR-ON多线程实现很简单,通过天问Block IDE组合出相应的逻辑块即可。

《嵌入式 - 语音识别TWen-ASR-ONE开发笔记》第3章 TWen-ASR-ONE 多线程和消息队列

可以看到多线程模块创建线程的逻辑块很少,主要是消息相关的,新建线程很简单,主需要将‘新建线程’代码块拖到工作区即可。

《嵌入式 - 语音识别TWen-ASR-ONE开发笔记》第3章 TWen-ASR-ONE 多线程和消息队列

新建线程块之后,可根据实际需要修改线程名、优先级、内存小大和延时时间,接下来就是实现线程逻辑代码。

为了方便,笔者接下来就是用官方的范例。

《嵌入式 - 语音识别TWen-ASR-ONE开发笔记》第3章 TWen-ASR-ONE 多线程和消息队列

该范例新建了两个线程,分别是蓝灯和绿灯每个一定时间不停地闪烁。其余的代码和上一章的一样。
最后编译下载运行效果如下:

《嵌入式 - 语音识别TWen-ASR-ONE开发笔记》第3章 TWen-ASR-ONE 多线程和消息队列

字符代码如下:

#include "asr.h"
#include "setup.h"

uint32_t snid;
void ASR_CODE();

//{ID:250,keyword:"命令词",ASR:"最大音量",ASRTO:"音量调整到最大"}
//{ID:251,keyword:"命令词",ASR:"中等音量",ASRTO:"音量调整到中等"}
//{ID:252,keyword:"命令词",ASR:"最小音量",ASRTO:"音量调整到最小"}
void LED_1_app(){
  while (1) {
    digitalWrite(12,1);
    delay(700);
    digitalWrite(12,0);
    delay(700);
  }
vTaskDelete(NULL);
}
void LED_2_app(){
  while (1) {
    digitalWrite(14,1);
    delay(300);
    digitalWrite(14,0);
    delay(300);
  }
vTaskDelete(NULL);
}
/*描述该功能...
*/
void ASR_CODE(){
  switch (snid) {
   case 4:
    digitalWrite(13,1);
    break;
   case 6:
    digitalWrite(13,0);
    break;
  }
}

void setup()
{
  //{speak:小蝶-清新女声,vol:10,speed:10}
  //{playid:10001,voice:欢迎使用智能管家,用智能管家唤醒我。}
  //{playid:10002,voice:我退下了,用智能管家唤醒我}
  //{ID:2,keyword:"唤醒词",ASR:"智能管家",ASRTO:"我在"}
  //{ID:4,keyword:"命令词",ASR:"打开红灯",ASRTO:"好的,马上打开红灯"}
  //{ID:6,keyword:"命令词",ASR:"关闭红灯",ASRTO:"好的,马上关闭红灯"}
  xTaskCreate(LED_1_app,"LED_1_app",128,NULL,4,NULL);
  xTaskCreate(LED_2_app,"LED_2_app",128,NULL,4,NULL);
}

字符代码可能看起来更清切写,是不是很简单。

3.4 TWen-ASR-ONE消息队列的使用

消息队列放到了线程的模块中,那么肯定是和线程有关的,所谓消息队列,其实就是一种常用于线程(任务)间通信的数据结构,TWEN-ASR ONE 与消息队列相关的逻辑块如下:

《嵌入式 - 语音识别TWen-ASR-ONE开发笔记》第3章 TWen-ASR-ONE 多线程和消息队列

既然是一种数据结构,肯定就有新建,而消息队列是一种通信手段,那么就会有接收,有发送。

至于中断你唤醒消息,是在CPU休眠后,可通过该方式将其唤醒。

接下来还是通过实例讲解吧,还是使用官方的例子。

《嵌入式 - 语音识别TWen-ASR-ONE开发笔记》第3章 TWen-ASR-ONE 多线程和消息队列

这里创建了两个消息队列和两个线程,当语言识别ID为4时,将ID值写入消息队列1中,如果消息队列中有消息,则将消息通串口输出,另外一个同理,关于串口在后面的内容讲解。

最后编译下载。效果如下:

我:智能管家
TWen-ASR-ONE:我在
我:打开红灯
TWen-ASR-ONE:好的,马上打开红灯

《嵌入式 - 语音识别TWen-ASR-ONE开发笔记》第3章 TWen-ASR-ONE 多线程和消息队列

我:关闭红灯
TWen-ASR-ONE:好的,马上关闭红灯

《嵌入式 - 语音识别TWen-ASR-ONE开发笔记》第3章 TWen-ASR-ONE 多线程和消息队列




欢迎访问我的网站

BruceOu的哔哩哔哩
BruceOu的主页
BruceOu的博客
BruceOu的CSDN博客
BruceOu的简书
BruceOu的知乎


欢迎订阅我的微信公众号

《嵌入式 - 语音识别TWen-ASR-ONE开发笔记》第3章 TWen-ASR-ONE 多线程和消息队列

上一篇:初探语音识别ASR算法


下一篇:ASR项目实战-产品分析