文章目录

  • 前言
  • Hi3861的UART与PWM简介
    • UART简介
    • PWM
  • ASR-01离线语音识别
    • 天问官方介绍
  • 硬件连接
  • 软件部分
    • ASR-01代码
    • Hi3861端代码
      • 初始化资源
      • 串口指令识别
      • 效果一览
  • 总结
  • 目录

前言

本文将介绍Hi3861的UART通信以及PWM控制,并与天问ASR01的离线语音识别模块组成一个离线语音助手,实现语音识别控制的功能。

Hi3861的UART与PWM简介

UART简介

串口通信是一种常用的异步通信方式,其简介笔者在之前的树莓派的UART中有过简介,有需要的可以去查看,这里给大家截个图:

前面提到过,Hi3861内部是有丰富的接口的,其中UART就有0、1、2三组,本次使用的是UART1,也就是对应GPIO5与GPIO6,在小熊派的底层代码中,有一个wifiiot_uart.h,里面封装了UART相关的API函数接口,有一点类似STM32的HAL库中的接口,在使用过程中不需要像之前的I2C一样首先还要配置GPIO,直接调用UartInit()即可初始化好对应的IO,同样,接收和发送数据也是直接调用接口即可,详细的代码后面会介绍,这里预览一下接口函数名及其功能。

PWM

PWM控制技术,在日常生活中的使用频率也是很高的,最近的手机产商们在屏幕调光上也运用到了此技术,其实就是一个调整固定时间单元内高低电平的比率的过程。它的简介,笔者也在之前的博客中有过介绍,想要仔细了解可以去笔者智能车浅谈的方向篇查看,大致内容先截个图:
Hi3861的PWM接口函数一览:

ASR-01离线语音识别

天问官方介绍

ASR-01是一颗专用于语音处理的人工智能芯片,可广泛应用于家电、家居、照明、玩具等产品领域,实现语音交
互及控制。
ASR-ONE内置自主研发的脑神经网络处理器BNPU,支持200条命令词以内的本地语音识别,内置CPU核和高性能低功耗Audio Codec模块,集成多路UART、IIC、PWM、GPIO等外围控制接口,可以开发各类高性价比单芯片智能语音产品方案。


通过芯片手册介绍可以发现,该语音识别模块的接口也是非常丰富,其实本文的语音识别助手完全可以有这一个模块单独完成,其内部采用的是FreeRTOS系统框架,对于开发者而言也是很友好的;除此之外,该模块还支持使用天问Block的图形化编程,而且可以一键生成语音模型,再也不用找第三方的字符转MP3的插件了,如果有使用语音识别的需求,笔者建议可以试试这款,比LD3320更便宜,而且更好用;相对于启英泰伦的离线语音,此款有着集成的开发环境,可以直接一键生成语音,加上图像化的编程可以节约很多写BUG的时间,笔者觉着这款模块是离线语音的不二之选。

硬件连接

通过上面的模块介绍,想必大家也猜到了笔者的思路,使用UART实现ASR-01与Hi3861的通信,利用ASR-01实现语音识别以及交互的工作,HI3861实现控制。
笔者本意是想做一个语音识别的分类识别垃圾桶的,后来发现,Hi3861的API接口只支持最高65535的分频,而Hi3861的时钟频率在160MHz,想要通过直接使用API产生50HZ的舵机控制PWM貌似不太现实,如果想要实现,只能用定时器来实现一个粗糙的效果,细细一想,感觉工作量是有的,所以暂时鸽了,这个方案笔者后面会试试,当然也期待大佬们提供解决方案。本着点灯大师的信仰,笔者觉定将原本的舵机换成LED和无源蜂鸣器。于是就有了下图“基于Hi3861的二号电子垃圾”。

上图中的红色PCB是一个光耦隔离模块,本来是打算用来隔离驱动舵机来着,由于上面提到的原因,改成了驱动LED和无源蜂鸣器。
接线表:

Hi3861 ASR01 其他模块
GPIO5 TX
DHT DHT11数据脚
GPIO7 无源蜂鸣器I/O
GPIO12 LED
GPIO13 LED

软件部分

ASR-01代码

硬件连接完毕后,就可以开始喜闻乐见的写BUG时光了,这次先来个简单的吧,换个平台写写ASR-01语音识别端的代码,笔者使用的是图形,直接CV,
有兴趣的同学可以用字符编程模式哈,感觉整个代码的结构和Arduino的有点像。
程序思路是:通过识别语音指令,进行语音播报,然后通过串口发送一个标志数据,笔者这里是“G*”来表示,为的是方便后面的Hi3861解析出指令内容;然后是需要一个DHT11来实现温湿度的采集和播报,这个在图形化的模块里面有,可以直接拖过来用。
这里的代码如下图:

到此已经完成了AR-01的代码,点击生成模型,然后一键下载,下载完成后,就可以进行语音识别了,这里可以用USB-TTL模块测试一下串口是否输出了对应的指令,这一过程笔者不做介绍,需要的同学自己去倒腾。

Hi3861端代码

Hi3861端的代码思路很简单,就是接收语音识别的指令,然后解析出来,实现对应的操作即可,至于具体的操作,我这里是用的PWM来实现控制的。然后代码框架还是使用新建任务的方式来。
理清思路,接下来开干。

初始化资源

这里需要根据需要初始化UART1以及三路PWM,这里笔者选用了PWM0、PWM2、PWM3。串口初始化不涉及GPIO的那个套娃操作,但是PWM还是需要使用之前的套娃操作,首先初始化GPIO,然后指定复用功能,再然后配置为输出模式,最后初始化对应的PWM接口即可。
具体代码如下:

//初始化GPIO,复用为PWM,注意笔者一开始是想用四路PWM控制四个舵机做分类垃圾桶来着,所以此处初始化了四组PWM。
void Garbage_GPIO_Init(void)
{//初始化GPIOGpioInit();//设置GPIO_7引脚复用功能为PWMIoSetFunc(WIFI_IOT_IO_NAME_GPIO_7, WIFI_IOT_IO_FUNC_GPIO_7_PWM0_OUT);//设置GPIO_8引脚复用功能为PWMIoSetFunc(WIFI_IOT_IO_NAME_GPIO_8, WIFI_IOT_IO_FUNC_GPIO_8_PWM1_OUT);//设置GPIO_12引脚复用功能为PWMIoSetFunc(WIFI_IOT_IO_NAME_GPIO_12, WIFI_IOT_IO_FUNC_GPIO_12_PWM3_OUT);//设置GPIO_13引脚复用功能为PWMIoSetFunc(WIFI_IOT_IO_NAME_GPIO_13, WIFI_IOT_IO_FUNC_GPIO_13_PWM4_OUT);//设置GPIO_7引脚为输出模式GpioSetDir(WIFI_IOT_IO_NAME_GPIO_7, WIFI_IOT_GPIO_DIR_OUT);//设置GPIO_8引脚为输出模式GpioSetDir(WIFI_IOT_IO_NAME_GPIO_8, WIFI_IOT_GPIO_DIR_OUT);//设置GPIO_12引脚为输出模式GpioSetDir(WIFI_IOT_IO_NAME_GPIO_12, WIFI_IOT_GPIO_DIR_OUT);//设置GPIO_13引脚为输出模式GpioSetDir(WIFI_IOT_IO_NAME_GPIO_13, WIFI_IOT_GPIO_DIR_OUT);//初始化PWM0端口PwmInit(WIFI_IOT_PWM_PORT_PWM0);//初始化PWM1端口PwmInit(WIFI_IOT_PWM_PORT_PWM1);//初始化PWM2端口PwmInit(WIFI_IOT_PWM_PORT_PWM3);//初始化PWM3端口PwmInit(WIFI_IOT_PWM_PORT_PWM4);//Initialize uart driver
}// static const char *data = "Hello, BearPi!\r\n";static void UART_Task(void)
{uint32_t ret;
//以下代码是配UART的参数,波特率、数据位、停止位WifiIotUartAttribute uart_attr = {//baud_rate: 9600.baudRate = 9600,//data_bits: 8bits.dataBits = 8,.stopBits = 1,.parity = 0,};Garbage_GPIO_Init();//初始化PWM接口ret = UartInit(WIFI_IOT_UART_IDX_1, &uart_attr, NULL);if (ret != WIFI_IOT_SUCCESS){printf("Failed to init uart! Err code = %d\n", ret);return;}printf("UART Test Start\n");while (1){printf("=======================================\r\n");printf("*************UART_example**************\r\n");printf("=======================================\r\n");//通过串口1接收数据UartRead(WIFI_IOT_UART_IDX_1, uart_buff_ptr, UART_BUFF_SIZE);Garbage_Uart_Cmd((char *)uart_buff_ptr);//串口指令识别usleep(500000);}
}static void UART_ExampleEntry(void)
{osThreadAttr_t attr;attr.name = "UART_Task";attr.attr_bits = 0U;attr.cb_mem = NULL;attr.cb_size = 0U;attr.stack_mem = NULL;attr.stack_size = UART_TASK_STACK_SIZE;attr.priority = UART_TASK_PRIO;if (osThreadNew((osThreadFunc_t)UART_Task, NULL, &attr) == NULL){printf("[ADCExample] Falied to create UART_Task!\n");}
}APP_FEATURE_INIT(UART_ExampleEntry);

串口指令识别

再完成所需资源初始化后,可以先验证一下,资源是否初始化成功,有条件的当然是可以直接用示波器查看了,但没有示波器的也可以用这种廉价的逻辑分析仪试试,30不到,大多数的调试都可以解决。

再确保资源被正常初始化后,就可以开始写逻辑代码了,这里需要先初始化一个缓存数组用来存放接收到数据内容,然后就是使用string.h和stdlib.h的字符串处理函数来对指令操作,提取出对应的指令,再根据指令执行操作即可。
代码如下:

//这个枚举在整个过程中没起到啥作用,可以忽略。
enum{WAIT,Open_Alarm,Close_Alarm,Kitchen_LED_Open,Kitchen_LED_Close,Living_LED_Open,Living_LED_Close,LED_Add,LED_Reduce
};//检测串口指令
void Garbage_Uart_Cmd(char *str)
{char  *Str;unsigned char ID=255;Str=&str[1];//定位到指令的数字部分“G1”ID=atoi(Str);//将G后面的字符转换成十进制数据。if(strstr((const char *)str,"G")!=NULL)           //如果字符串str中包含有“G”{switch(ID){case 1:    // 开蜂鸣器step = Open_Alarm;printf("Open_Alarm\r\n");PwmStart(WIFI_IOT_PWM_PORT_PWM0, 10000, 50000);break;case 2:   // 关蜂鸣器step = Close_Alarm;printf("Close_Alarm\r\n");PwmStop(WIFI_IOT_PWM_PORT_PWM0);break;case 3:    // 打开厨房灯step = Kitchen_LED_Open;printf("Kitchen_LED_Open\r\n");PwmStart(WIFI_IOT_PWM_PORT_PWM4, 30000, 40000);break;case 4:  //  关闭厨房灯step = Kitchen_LED_Close;printf("Kitchen_LED_Close\r\n");PwmStop(WIFI_IOT_PWM_PORT_PWM4);break;case 5:  // 打开客厅灯step = Living_LED_Open;printf("Living_LED_Open\r\n");PwmStart(WIFI_IOT_PWM_PORT_PWM3, Livling_LED_Duty, 60000);break;case 6: // 关闭客厅灯step = Living_LED_Close;printf("Living_LED_Close\r\n");PwmStop(WIFI_IOT_PWM_PORT_PWM3);break;case 7: // 客厅灯变亮step = LED_Add;PwmStop(WIFI_IOT_PWM_PORT_PWM3);printf("LED_Add\r\n");if(Livling_LED_Duty<55000)Livling_LED_Duty+=10000;PwmStart(WIFI_IOT_PWM_PORT_PWM3, Livling_LED_Duty, 60000);break;case 8:  // 客厅灯变暗step = LED_Reduce;PwmStop(WIFI_IOT_PWM_PORT_PWM3);printf("LED_Reduce\r\n");if(Livling_LED_Duty>=10000)Livling_LED_Duty-=10000;PwmStart(WIFI_IOT_PWM_PORT_PWM3, Livling_LED_Duty, 60000);break;default:printf("%s ERROR",str);step =  WAIT;break;}}memset(uart_buff,0,sizeof(uart_buff));//清空缓存数据,避免出错。
}

然后编译下载,就可以实现功能了。

效果一览

emmm,CSDN上传视频也太麻烦了,移步去立创EDA查看吧——【毕设】Hi3861语音识别小助手。

总结

至此,一个简易的语音识别助手就完工了,文中如有不妥之处,欢迎匹配指正,有关源码,笔者后面会整理上传至我的资源,或者直接私聊获取。

目录

OpenHarmony学习笔记——南向开发环境搭建
OpenHarmony学习笔记——编辑器访问Linux服务器进行编译
OpenHarmony学习笔记——点亮你的LED
OpenHarmony学习笔记——多线程的创建
OpenHarmony学习笔记——I2C驱动0.96OLED屏幕
OpenHarmony学习笔记——Hi3861使用DHT11获取温湿度
OpenHarmony学习笔记——Hi3861接入OneNET
手把手教你OneNET数据可视化
OpenHarmony学习笔记——Hi386+ASR-01的语音识别助手

OpenHarmony学习笔记——Hi3861+ASR-01的语音识别助手相关推荐

  1. OpenHarmony学习笔记——Hi3861使用DHT11获取温湿度

    文章目录 前言 DHT11简介 通信流程 硬件连接 编程实现 GPIO API简介 复位总线 DHT11应答 数据读取 效果一览 总结 目录 前言 此文主要是使用Hi3861的GPIO口,模拟1-Wi ...

  2. OpenHarmony学习笔记——I2C驱动0.96OLED屏幕

    文章目录 前言 I2C简介 硬件连接 编程实现 创建代码框架 初始化并复用GPIO 初始化I2C0 初始化OLED 从机地址 OLED初始化配置 功能代码 总结 目录 前言 前面介绍了一些关于在Hi3 ...

  3. 深度学习笔记 | 第16讲:语音识别——一份简短的技术综述

    原标题:深度学习笔记 | 第16讲:语音识别--一份简短的技术综述 大家好!又到了每周一狗熊会的深度学习时间了.在上一讲中,小编给大家介绍了经典的 seq2seq,以及著名的注意力模型,并且小编在这些 ...

  4. GAMES101-现代计算机图形学学习笔记(作业01)

    GAMES101-现代计算机图形学学习笔记(作业01) Assignment 01 GAMES101-现代计算机图形学学习笔记(作业01) 作业 作业描述 需要补充的函数 思路 结果 原课程视频链接以 ...

  5. arduino 学习笔记及课件01基础入门

    arduino 学习笔记及课件01基础入门 以下学习笔记中图片部分由太极创客视频截图所得 概论 一. 引脚及信号 1.1引脚模式 INPUT:当Arduino没有使用上拉电阻或下拉电阻而直接与开放的开 ...

  6. 《Python自然语言处理(第二版)-Steven Bird等》学习笔记:第01章 语言处理与Python

    第01章 语言处理与Python 1.1 语言计算:文本和单词 Python入门 NLTK 入门 搜索文本 计数词汇 1.2 近观Python:将文本当做词链表 链表(list,也叫列表) 索引列表 ...

  7. 【学习笔记】Docker - 01. Docker是啥

    我只是把之前的学习笔记整理一下,贴到这里,可能会显得比较凌乱... 1.1 啥是Docker? Docker 是一个开源项目,它被用来做构建.打包和运行程序.它是一个命令行程序,一个后台进程,也是一组 ...

  8. vue3小兔鲜商城项目学习笔记+资料分享01

    最近正在学习vue3小兔鲜,需要相关学习资料的可以点链接 https://docs.qq.com/doc/DUmhUVERtUHpLaG1a 下面试学习笔记 项目起步 项目预览地址 小兔鲜儿商城:ht ...

  9. linux 指定库名 登录mysql_数据库学习笔记之MySQL(01)

    目录 01. 什么是数据库? 02. 常见数据库 03. MySQL简介 04. 数据库系统(Database System)的组成 05. SQL语言 06. SQL语言组成 07. MySQL的常 ...

最新文章

  1. 【Design pattern】简单工厂过渡策略模式
  2. 国产搜索引擎首超Google 中文搜索瓦解霸权?
  3. python足球联赛赛程_python爬取足球比赛赛程笔记
  4. 微软任务管理软件 Planner,Office 365 用户免费用
  5. Tomcat 7.0 servlet @WebServlet
  6. informix11.7界面入门工具
  7. Strus2中关于ValueStack详解
  8. WZ132源代码有的在运果子
  9. ECCV 2020 | 微软亚洲研究院精选论文摘录
  10. easypoi 语法_知识点总结及语法学习资料及视频
  11. 批处理保存windows10开机壁纸
  12. java分布式框架_5个强大的Java分布式缓存框架推荐
  13. 怎么用虚拟服务器多开yy号,YY盒子多开器怎么用?歪歪语音多开器使用教程图文详解(图文)...
  14. ASP.NET AES加密
  15. 数据用什么挖?数据挖掘常用工具分享
  16. 后台界面设计之表格设计
  17. deepin驱动精灵_深度用户展望Deepin 15.11或Deepin 16版本应该有的新功能
  18. 5G提速 高通助推标准走向现实
  19. 不可忽视的 C 语言陷阱!
  20. 单片机音乐盒c语言,基于单片机的音乐盒设计与实现

热门文章

  1. 做玫瑰花的方法 用纸_史上最简单的纸玫瑰花的折法教程教你用纸编法做玫瑰花...
  2. 没有core文件时定位segfault at 0 ip (null) 的问题(三):艰难定位,多种原因
  3. 陈乔恩版东方不败出炉 全方位对比各版教主
  4. 不积跬步,无以致千里
  5. 通过时间管理充分利用一天24小时
  6. 80端口、8080端口和443端口是什么用的
  7. 欢迎进入杰杰的博客导航一站式搜索(所有博客的汇总帖)
  8. 医疗智能BI助你建立智慧医院
  9. 【Dart】一门为跨端开发而生的编程语言
  10. 华山论剑之iOS中文转化为拼音的正确姿势