高性价比模块:LSYT201B语音模块学习使用

最近打算做个语音的项目,找到了深圳雷龙发展的LSY201B这款语音模块,写出来安利一下

程序源码:SuiXinSc/Speech-Module (github.com)

或者进入Q群找我获取

目录

一,简要介绍:

硬件参数:

1,处理器:

2,外设:

3,蓝牙:

4,音频:

软件参数:

使用体验:

二,模块定制:

三,测试:

四,应用:

五,总结:


一,简要介绍:

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,CLASS2CLASS3 输送功率的要求

支持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,以及串口中断的优先级要设置一下

PWM:传送门;串口:传送门

另外一点,要想让舵机有较高精度,尽量将预分频减小,自动重装值调大,使得占空比的调节尽量平滑,我这里主频为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模块具有价格低廉,性能优异,应用简单 等优点,更多参数可以在深圳雷龙发展官网自行搜索查看,大家可以入手一个试试。

相关推荐
helloKittywz6 小时前
内网学习第6天 liunx定时任务 环境变量和权限配置,以及数据库提权
学习·web安全·网络安全·内网渗透·liunx·权限提升·学习记录
Sui_Network6 小时前
探索Sui的面向对象模型和Move编程语言
大数据·人工智能·学习·区块链·智能合约
远望创客学堂7 小时前
【单片机毕业设计选题24047】-基于阿里云的工地环境监测系统
stm32·单片机·嵌入式硬件·阿里云·云计算·课程设计·arduino
爱我所爱flash7 小时前
计算机专业怎么选择电脑
学习·电脑
极客小张7 小时前
利用 STM32 实现多协议物联网网关:Modbus/Zigbee 到以太网/Wi-Fi 的数据桥接
stm32·单片机·嵌入式硬件·物联网·网络协议·https·硬件工程
逆袭的小羊7 小时前
SR-IOV学习笔记
学习·云计算
FPGAmaster创新者8 小时前
基于AGX ORIN与FPGA K7实现PCIE高速数据通信/Orin与FPGA高速数据传输/XDMA在linux系统使用教程
linux·嵌入式硬件·fpga开发
XD7429716368 小时前
【TB作品】电子琴,ATMEGA16单片机,Proteus仿真
单片机·proteus·电子琴·atmega
maybe_YX9 小时前
51单片机基础8——单片机控制超声波模块
c语言·单片机·嵌入式硬件·51单片机
@一二三四五9 小时前
STM32 看门狗 HAL
stm32·单片机·嵌入式硬件