基于STM32的智慧物联网系统板---离线语音模块使用
1.海凌科离线语音模块百科
HLK-V20-SUIT是海凌科电子推出的一款高性能纯离线语音识别模块。它专为智能家居、智能小家电和物联网设备而设计。凭借其高性价比、灵活定制、低功耗等特点,成为语音控制领域的热门选择。该模块采用32位RSIC架构核心,集成DSP指令集、FPU浮点运算单元和FFT加速器。它通过神经网络算法对音频信号进行深度训练,显着提高语音识别的准确性。安静环境下识别率超过95%。即使在办公室或音乐播放等嘈杂场景下,仍能保持75%以上的识别率。误唤醒率小于1次/24小时,保证设备稳定响应。支持150条本地命令存储,用户可以通过海灵客语音定制后端系统自由配置唤醒词、命令词和响应词,无需复杂编程即可快速生成专属SDK,满足个性化需求。模块提供UART、GPIO、PWM等丰富接口,可轻松连接动圈扬声器、驻极体麦克风等外围设备。只需要少量的外围元件即可实现语音交互功能。开发周期短,成本可控。其工作电压为5V,平均待机功耗仅为63mA,工作电流约为60mA。适用于电池供电场景,如智能门锁、无线音箱等设备。
模块尺寸紧凑(封装模块设计),易于集成到各种产品中,特别适合空间有限的设备。其应用场景广泛,涵盖智能家居(如语音控制风扇、插座、灯泡、空调)、智能小家电(如智能茶壶、故事机、扫地机)、工业控制(如设备语音操作)、医疗监控(如语音呼叫系统)等领域。例如,用户可以通过语音命令“打开空调”或“调暗灯光”,无需手动操作,提高了生活的便利性;在工业场景中,工作人员可以通过语音控制启动或停止设备,提高工作效率。支持离线识别,无需联网即可使用,避免因网络延迟或断线导致的功能故障,同时保护用户隐私数据。另外,该模块兼容轻量级RTOS系统,资源占用低,运行稳定,适合资源有限的嵌入式设备。对于开发者来说,海灵科提供详细的技术文档和开发工具,降低开发门槛,加速产品上线。
总而言之,HLK-V20-SUIT以其高性能、低功耗、易于定制、广泛适应性的核心优势,为语音控制设备提供了可靠的解决方案。无论是追求性价比的智能家居厂商,还是需要快速迭代的物联网开发者,该模块都能满足需求,帮助产品实现智能化升级。
实际模块如下:
2.HLK-V20-SUIT模组集成
HLK-V20-SUIT 模块采用串口通信。语音录入响应成功通过串口返回响应信息。其硬件接口设计如下:
该模块采用5V供电,自带功放电路。可直接连接扬声器(81W)输出。麦克风输入不需要外部处理电路,可以直接连接。灵敏度为-38DB。实际硬件如图所示:
3.HLK-V20-SUIT自制语音词条
3.1 创建产品
HLK-V20-SUIT 模块支持自定义语音条目。登录海灵客官网并注册账号。海灵科官网地址:【海灵科官网地址】(https://www.hlktech.com/)
滑动到底部可以看到语音定制系统。
注册账号并登录平台。
选择产品访问权限以完成产品创建。
选择产品访问权限以完成产品创建。
3.2 创建SDK
3.3 设置语言词条信息
1. 设置前端信号处理。
2. 设置串口通讯参数
3.设置语音唤醒入口
4.设置离线录入信息
5.设置响应返回数据格式
6. 发音和音量设置
7.其他设置
8.发布SDK。如果发布成功,您可以下载SDK包。
4.SDK离线包固件烧写
1. 下载固件编程工具。下载地址:【固件烧录工具】(https://h.hlktech.com/Mobile/download/FDetail/93.html)
2.本主板预留了固件升级接口,接口电路如下:
硬件方面,将C_RX连接到PA9,TX连接到PA10,用于对STM32系统板进行编程;将C_RX 连接到H_RX,将TX 连接到H_TX 用于对Hilink 模块进行固件编程。因此,此时需要使用跳线帽将C_RX连接到H_RX,TX连接到H_TX。
下载的SDK包中“uni_hb_m_solution-xxxx-xxxx”目录下的uni_app_debug_update.bin和uni_app_release_update.bin是USB升级的固件。 USB升级固件不能有中文路径。
4.2 驱动安装
使用Type-C数据线连接开发板。如果识别成功,会自动识别COM口。
选择要烧录的uni_app_release_update.bin文件。
点击刻录按钮,如图,提示栏显示等待设备。 (注意此时不要给:设备上电)。
下图为Hailink模块的电源引脚。单击刻录之前拔下跳线帽。等待设备出现后,插上跳线帽。
下载完成如下:
至此,固件编程完成。
5.模块驱动
HLK-V20 模块使用串口2 驱动程序。串行通信波特率为115200。数据帧格式为1bit 起始信号+8bit 数据位+1bit 停止信号,无奇偶校验位。驱动程序如下:
/*串口初始化参数: USARTx --要初始化的串口(USART1、USART3、USART2) buad --要设置的波特率*/void USARTx_Init(USART_TypeDef *USARTx,u32 buad){ if(USARTx==USART1) { //1.打开时钟RCC-APB2ENR|=12;//PA RCC-APB2ENR|=114;//usart1 RCC-APB2RSTR|=114;//复位时钟RCC-APB2RSTR=~(114);//取消复位//2.配置GPIO GPIOA-CRH=0xFFFFF00F; GPIOA-CRH|=0x000008B0; USART1-BRR=72000000/buad;//设置波特率#ifdef USART1_IRQ USART1-CR1|=14;//IDLE 空闲帧中断USART1-CR1|=15;//使能串口接收中断STM32_SetNVICPrity(1,1,USART1_IRQn);//设置优先级并使能中断线#endif } else if(USARTx==USART2) { /*1.打开时钟*/RCC-APB2ENR|=12;//PA时钟RCC-APB1ENR|=117;//USART2时钟RCC-APB1RSTR|=117;//打开复位时钟RCC-APB1RSTR=~(117);//取消复位/*2.配置GPIO口*/GPIOA-CRL=0xFFFF00FF;//清除该值原寄存器中GPIOA-CRL|=0x00008B00; //3.配置串口3核心功能USART2-BRR=36000000/buad;//波特率#ifdef USART2_IRQ USART2-CR1|=15;//串口2接收中断USART2-CR1|=14;//空闲帧中断STM32_SetNVICPrity(1,1,USART2_IRQn);//设置优先级#endif } else if(USARTx==USART3) { //1.打开时钟RCC-APB2ENR|=13;//PB RCC-APB1ENR|=118;//USART3 RCC-APB1RSTR|=118;//复位时钟RCC-APB1RSTR=~(118);//取消复位//2.配置GPIO GPIOB-CRH=0xFFFF00FF; GPIOB-CRH|=0x00008B00; //3.配置串口3核心功能USART3-BRR=36000000/buad;//波特率#ifdef USART3_IRQ USART3-CR1|=14;//IDLE空闲帧中断USART3-CR1|=15;//使能串口接收中断STM32_SetNVICPrity(1,1,USART3_IRQn);//设置优先级并使能中断线#endif } USARTx-CR1|=13;//使能发送USARTx-CR1|=12;//使能接收USARTx-CR1|=113;//使能串口}u8 usart2_buffer[1024];//串口1接收数据缓冲区u16 usart2_cnt=0;//保存数组并下班u8 usart2_flag;//接收完成标志void USART2_IRQHandler(void){u8 c;if(USART2-SR15)//判断是否接收中断触发{c=USART2-DR;//USART1-DR=c;if(usart2_flag==0)//判断上次数据处理是否完成{if(usart2_cnt1024){usart2_buffer[usart2_cnt++]=c;}else usart2_flag=1;}} if(USART2-SR14) { c=USART2-DR; c=c; usart2_flag=1; }}主要功能:
#include 'stm32f10x.h' #include 'delay.h' #include 'led.h' #include 'key.h' #include 'usart1.h' #include 'timer.h' #include 'esp8266.h' #include 'oled.h' #include 'rtc.h' #include stdio.h #include string.h #include stdlib.hint main(){ LED_Init();//LED Key_Init(); USARTx_Init(USART1,115200);//一个字符的发送和接收时间:1s/(115200/10)=86us USARTx_Init(USART2,115200); USARTx_Init(USART3,115200); OLED_Init(); OLED_DispalyFont(32,0,16,font_16[0]); OLED_DispalyFont(32+16,0,16,font_16[1]); OLED_DispalyFont(32+16*2,0,16,font_16[2]); OLED_DispalyFont(32+16*3,0,16,font_16[3]); OLED_Refresh(); RTC_Init(); u16键=0; while(1) { if(usart2_flag) { if(usart2_cnt==3) { if(usart2_buffer[0]==0x77 usart2_buffer[2]==0x0a) { switch(usart2_buffer[1]) { case 1://开灯LED1=0;休息; case 7://关灯LED1=1;休息; } } } //printf('rx2=%s,%dn',usart2_buffer,usart2_cnt); usart2_flag=0; usart2_cnt=0; } }}