最近打算做个语音的项目,找到了深圳雷龙发展的LSY201B这款语音模块,写出来安利一下
程序源码:SuiXinSc/Speech-Module (github.com)
或者进入Q群找我获取
目录
一,简要介绍:
LSYT201B是深圳雷龙发展推出的一款蓝牙离线语音模块 ,某宝上价格为20元,性价比很高。 详情页
硬件参数:
1,处理器:
基于YT2228芯片 ,32位 处理器,240MHz 的频率,支持FPU ,内部FLASH为2M ,拥有64个中断向量 以及4级别中断优先级。
2,外设:
全速USB 设备,支持USB1.1
四个多功能16位定时器 ,支持捕获和PWM模式
三个16位PWM发生器 用于电机驱动
三个全双工基本UART ,UARTO 和 UART1支持DMA模式
两个SPI接口 支持主机模式和设备模式
一个硬件IIC 接口,支持主机模式和设备模式
内置Cap Sense Key控制器
10位ADC 模拟采样
所有GPIO支持外部唤醒/中断
3,蓝牙:
支持蓝牙V5.3 +BR+EDR +BLE 规范
满足CLASS1,CLASS2 和 CLASS3 输送功率的要求
支持GFSK 和 π/4 DQPSK 所有数据包类型
提供最大发射功率+6 DBM
接收器最小灵敏度-90 DBM
快速ADC增强动态范围
支持A2DP1.3.2\AVCTP1.4\AVDTP1.3\AVRCP1.6.2\HFP 1.8\SPP 1.2\RFCOMM 1.1\PNP1.3\HID 1.1.1\SDP CORE5.3\L2CAP CORE 5.3
4,音频:
两通道16-bit DAC ,SNR>=95dB
一通道16-bit ADC ,SNR>=90dB
采样率支持:8KHz/11.025KHz/16KHz/22.05KHz/24KHz/32KHz/44.1KHz/48KHz
一个模拟 MIC 放大器 ,内置MIC偏置发生器
在 DAC 路径上支持无输出电容模式,单端和差分模式
软件参数:
支持远场拾音,环境降噪,蓝牙控制,小语种识别, 支持15个唤醒+免唤醒, 最大支持150个关键词,USB音频输出,UART通信 ,在Windows/Linux下,还支持USB录音功能 。支持智云译芯平台,有配套的小程序, 在智能家居方面拥有完善的生态。
使用体验:
LSYT201B这款模块确实很好用 ,拾音非常灵敏 ,在客厅中用比较小的声音也能捕获到,而且应用非常简单 ,只需要配置串口即可使用,大大缩短了开发周期。
二,模块定制:
确定好模块词条后,发送给官方定制。词条模板如下:
这个模板可以找官方要,官方有技术人员负责
三,测试:
拿到定制的模块后,先连接好电源(5v),然后接上串口,选择以16进制显示和以16进制发送,接收自动断帧,开始测试
说出唤醒词,看到串口返回了我们定义的数据
再说一下关键词试一下,发现也能返回对应的数据
然后等待10s,发现语音模块正常播放了结束语,串口也接收到了结束数据,模块进入待机模式
四,应用:
LSYT201B的使用非常简单,完全不需要了解任何语音识别有关的技术或是算法,只需一个串口便可以使用,所以先配置一个有两个串口的项目(一个与模块通信,一个与PC通信),由于我要用到舵机,所以加了一个PWM,这两个我前面都讲过了,要注意的是与语音模块通信的那个串口波特率要设置为9600,以及串口中断的优先级要设置一下
另外一点,要想让舵机有较高精度,尽量将预分频减小,自动重装值调大,使得占空比的调节尽量平滑,我这里主频为72MHz,预分频为 36-1,要想得到 50Hz的频率,自动重装值就需要 40000-1
CubeMX配置的过程我直接跳过,直接进入Keil
串口重定向,这里把串口一作为与主机通信的端口
cpp
//发送的重定向,重定向以后可以使用printf等函数
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
//接收的重定向,重定向以后可以使用scanf等函数
int fgetc(FILE *f)
{
int ch;
HAL_UART_Receive(&huart1, (uint8_t *)&ch, 1, 1000);
return (ch);
}
接着写一个操纵舵机的程序,关于舵机的操纵我在讲PWM那一篇也讲过了,有兴趣的可以看看
cpp
//线性映射
int Linear_Mapping(int now,int as,int ae,int bs,int be){
return now*(be-bs)/(ae-as)+bs;
}
void Servo_Control(int angle){
if(angle>180){angle=180;}else if(angle<0){angle=0;} //约束角度数据
__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,Linear_Mapping(angle,0,180,40000*0.025,40000*0.125)); //使用映射法计算计数值
}
函数声明:
cpp
int Linear_Mapping(int now,int as,int ae,int bs,int be);
void Servo_Control(int angle);
在主程序中添加测试程序(别忘了勾选微库)
观察到电脑上串口调试助手在每次循环发送一个Hello PC,同时舵机在 0~180度之间旋转,接下来就是对语音模块的操作了
分别在根目录下Code文件夹中Src和Inc文件夹建立lsyt201b.c 和 lsyt201b.h 两个文件,并将 lsyt201b.c 添加到Keil中并打开,添加如下代码:
lsyt201b.c:
cpp
#include "lsyt201b.h"
#include "string.h"
#include "stdlib.h"
#include "stdio.h"
#include "usart.h"
//定义数据数组
uint8_t Host_RXbuffur[Host_DataSize]={0};
uint8_t LSYT_RXbuffur[LSYT_DataSize]={0};
//定义解析标志位
int A_flag = 0;
int DataSize = LSYT_DataSize;
//串口的空闲接收中断
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart,uint16_t Size){
__HAL_UNLOCK(huart); //解锁串口状态
if(huart == &huart1){ //如果是主机发送的数据
printf("%s",Host_RXbuffur); //数据回显
HAL_UARTEx_ReceiveToIdle_IT(&huart1,Host_RXbuffur,Host_DataSize-1); //再次开启空闲中断
}else if(huart == & huart2){ //如果是模块发来的数据
printf("LSYT_Data:");
printf("%X %X %X %X %X %X\r\n",
LSYT_RXbuffur[0],LSYT_RXbuffur[1],LSYT_RXbuffur[2],LSYT_RXbuffur[3],LSYT_RXbuffur[4],LSYT_RXbuffur[5]);
//转发给电脑
A_flag = 1;
HAL_UARTEx_ReceiveToIdle_IT(&huart2,LSYT_RXbuffur,LSYT_DataSize-1); //再次开启空闲中断
}
}
uint8_t LSYT_CMD[][6]={
{0x0A,0x02,0x00,0x00,0x00,0xED}, //语音:已执行
{0x0A,0x02,0x00,0x00,0x01,0xED}, //语音:执行失败
{0x0A,0x02,0x00,0x00,0x02,0xED}, //语音:已完成
};
//向模块发送命令
int LSYT201B_SendCMD(uint8_t Data){
uint8_t* tmp0 = malloc(sizeof(int)*6);
switch(Data){
case Executed:
memcpy(tmp0,LSYT_CMD[0],6);
break;
case Failed:
memcpy(tmp0,LSYT_CMD[1],6);
break;
case Completed:
memcpy(tmp0,LSYT_CMD[2],6);
break;
default:
return ERROR;
}
int tmp1 = HAL_UART_Transmit(&huart2,tmp0,6,10);
free(tmp0);
return tmp1;
}
//解析模块数据
int LSYT201B_Analysis(uint8_t* result){
if(A_flag == 0){
return ERROR;
}
A_flag = 0;
if(LSYT_RXbuffur[0] != 0x0A){ //判断数据是否有效
return ERROR;
}
switch(LSYT_RXbuffur[1]){ //依据数据格式处理数据
/*System:*/
case 0x43:
result[0] = SystemCMD;
result[1] = Close;
break;
case 0x04:
result[0] = SystemCMD;
result[1] = Start;
break;
/*CMD:*/
case 0x0F:
result[0] = NormalCMD;
result[1] = LSYT_RXbuffur[4];
break;
case 0x0B:
result[0] = ExtendCMD;
result[1] = LSYT_RXbuffur[4];
break;
default:
return ERROR;
}
return SUCCESS;
}
lsyt201b.h:
cpp
#ifndef __LSYT201B_H_
#define __LSYT201B_H_
#include "main.h"
//宏定义
#define Host_DataSize 512
#define LSYT_DataSize 8
#define Start 0x0F
#define Close 0xF0
#define Executed 0xA0
#define Failed 0xA1
#define Completed 0xA2
#define SystemCMD 0x00
#define NormalCMD 0x01
#define ExtendCMD 0x02
//枚举定义命令
enum Normal_CMD{
Crotate = 0x00,
Urotate,
Forward,
FallBack,
Lshift,
Rshift,
Rise,
Down,
Add,
Reduce,
Origin = 0x10,
Reset
};
enum Extend_CMD{
CMD0 = 0x00,
CMD1,
CMD2,
CMD3,
CMD4,
CMD5,
};
extern uint8_t Host_RXbuffur[Host_DataSize];
extern uint8_t LSYT_RXbuffur[LSYT_DataSize];
int LSYT201B_SendCMD(uint8_t Data);
int LSYT201B_Analysis(uint8_t* result);
#endif
然后在主程序中编写程序:
循环中代码:
cpp
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(LSYT201B_Analysis(Data) == SUCCESS){
switch(Data[0]){
case SystemCMD:
break;
case NormalCMD:
switch(Data[1]){
case Origin: last = angle; angle = 90;
break;
case Reset: angle = last;
break;
case Crotate: angle -= step;
break;
case Urotate: angle += step;
break;
case Add:
step += 10;
LSYT201B_SendCMD(Completed);
break;
case Reduce:
step -= 10;
LSYT201B_SendCMD(Completed);
break;
}
break;
case ExtendCMD:
switch(Data[1]){
case CMD0: LSYT201B_SendCMD(Completed);
break;
/*............*/
}
break;
}
if(angle > 180){
angle = 180;
}else if(angle < 0){
angle = 0;
}
printf("%d\r\n",angle);
}
Servo_Control(angle);
}
/* USER CODE END 3 */
整个 main.c:
cpp
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "lsyt201b.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM2_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);
Servo_Control(0);
printf("Hello PC\r\n");
HAL_UARTEx_ReceiveToIdle_IT(&huart1,Host_RXbuffur,Host_DataSize-1);
HAL_UARTEx_ReceiveToIdle_IT(&huart2,LSYT_RXbuffur,LSYT_DataSize-1);
int step = 10;
int angle = 90;
int last = 0;
uint16_t Data[2]={0};
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(LSYT201B_Analysis(Data) == SUCCESS){
switch(Data[0]){
case SystemCMD:
break;
case NormalCMD:
switch(Data[1]){
case Origin: last = angle; angle = 90;
break;
case Reset: angle = last;
break;
case Crotate: angle -= step;
break;
case Urotate: angle += step;
break;
case Add:
step += 10;
LSYT201B_SendCMD(Completed);
break;
case Reduce:
step -= 10;
LSYT201B_SendCMD(Completed);
break;
}
break;
case ExtendCMD:
switch(Data[1]){
case CMD0: LSYT201B_SendCMD(Completed);
break;
/*............*/
}
break;
}
if(angle > 180){
angle = 180;
}else if(angle < 0){
angle = 0;
}
printf("%d\r\n",angle);
}
Servo_Control(angle);
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
看效果:
LSYT201B语音模块效果展示
五,总结:
LSYT201B模块具有价格低廉,性能优异,应用简单 等优点,更多参数可以在深圳雷龙发展官网自行搜索查看,大家可以入手一个试试。