STM32F103C8T6 · HAL库 · UART串口通信 · 离线语音识别
SU-03T 离线语音识别模块工作原理、核心功能及引脚定义
SU-03T 是机芯智能(AimaChip)基于 US516P6(蜂鸟M SOC)芯片开发的纯离线语音识别模块。其核心工作原理如下:
支持最多 150 条本地指令离线识别,每条命令词 2~10 个字符,响应速度 100~300ms,识别距离 1~3 米(安静环境最远 5 米)
通过零代码平台配置唤醒词、命令词、回复语和行为动作,无需编程即可实现个性化语音控制方案
内置 3W 单声道 AB 类功放,支持语音唤醒回复、命令确认、开机播报等语音反馈,总回复字符数可达 500 字
通过 UART 接口与 MCU 通信,3.3V TTL 电平,最高支持 3Mbps,本项目使用 9600bps 8N1 格式输出识别结果
本模块由底板 + SU-03T 模组组成,底板引出 6 个引脚,并带有扬声器与麦克风接口:
| 引脚号 | 名称 | 功能说明 | STM32 连接 |
|---|---|---|---|
| 1 | NC(悬空) | 预留引脚,不连接 | — |
| 2 | +5V | 模块电源输入(3.6V~5.5V,推荐 5V) | 5V 供电 |
| 3 | GND | 电源地 | GND |
| 4 | TXD | 模块串口发送(UART1_TXD,B7 脚) | PB11(USART3_RX) |
| 5 | RXD | 模块串口接收(UART1_RXD,B6 脚) | PB10(USART3_TX) |
| 6 | NC(悬空) | 预留引脚,不连接 | — |
底板上还集成了扬声器接口(SPK+/SPK-)和驻极体麦克风接口(MIC+/MIC-),可直接连接外设。
SU-03T 语音模块正面与背面实物展示
SU-03T 语音模块电路原理图与 PCB 封装图
STM32 HAL 库工程源代码逐模块解析
主程序完成系统初始化、OLED 显示初始化、定时器中断启动和 SU-03T 串口初始化,在 while(1) 主循环中处理语音指令并每 50ms 刷新 OLED 屏幕显示灯光状态。
/* Private includes */ #include "main.h" #include "tim.h" #include "usart.h" #include "gpio.h" #include "./HAL/OLED/OLED.h" #include "./HAL/SU_03T/su_03t.h" uint32_t Time_Now; // 时间戳 int main(void) { HAL_Init(); SystemClock_Config(); /* 初始化外设 */ MX_GPIO_Init(); MX_USART3_UART_Init(); MX_TIM1_Init(); /* OLED 显示初始化 */ OLED_Init(); /* 启动定时器1中断(1ms周期) */ HAL_TIM_Base_Start_IT(&htim1); /* SU-03T 串口初始化 */ SU_03T_USART_Init(); while (1) { // 语音指令处理线程 SU_03T_USART_LogicLoop(); // 50ms 刷新一次屏幕,显示灯光状态 if(HAL_GetTick() - Time_Now >= 50) { OLED_Clear(); if(HAL_GPIO_ReadPin(LED_GPIO_Port, LED_Pin)) OLED_CenterPrintf(24, "灯光:关闭"); else OLED_CenterPrintf(24, "灯光:开启"); OLED_Update(); Time_Now = HAL_GetTick(); } } } // 定时器1中断回调(1ms)——SU-03T帧超时检测 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == htim1.Instance) SU_03T_TIM_MS(); } // 串口接收中断回调——SU-03T数据接收 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { SU_03T_USART_RxCallback(huart); }
配置 USART3,波特率 9600,8 位数据位,无校验,1 位停止位(8N1),收发模式。USART3_TX 使用 PB10,USART3_RX 使用 PB11,开启 USART3 全局中断。
UART_HandleTypeDef huart3; void MX_USART3_UART_Init(void) { huart3.Instance = USART3; huart3.Init.BaudRate = 9600; huart3.Init.WordLength = UART_WORDLENGTH_8B; huart3.Init.StopBits = UART_STOPBITS_1; huart3.Init.Parity = UART_PARITY_NONE; huart3.Init.Mode = UART_MODE_TX_RX; huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart3.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart3) != HAL_OK) Error_Handler(); } /* USART3 GPIO: PB10=TX, PB11=RX */ void HAL_UART_MspInit(UART_HandleTypeDef *uartHandle) { if(uartHandle->Instance == USART3) { __HAL_RCC_USART3_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct = {0}; // PB10 = USART3_TX (复用推挽) GPIO_InitStruct.Pin = GPIO_PIN_10; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); // PB11 = USART3_RX (浮空输入) GPIO_InitStruct.Pin = GPIO_PIN_11; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); HAL_NVIC_SetPriority(USART3_IRQn, 0, 0); HAL_NVIC_EnableIRQ(USART3_IRQn); } }
配置 LED 引脚 PC13 为推挽输出模式,初始电平为高(LED 熊灭,因为板载 LED 为低电平点亮)。同时使能 GPIOC/GPIOD/GPIOB/GPIOA 时钟。
/* main.h 中定义: */ #define LED_Pin GPIO_PIN_13 #define LED_GPIO_Port GPIOC void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); // LED 初始为高电平(关闭状态) HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET); // PC13 = 推挽输出,低速,无上下拉 GPIO_InitStruct.Pin = LED_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(LED_GPIO_Port, &GPIO_InitStruct); }
配置 TIM1 产生 1ms 周期中断。预分频器 71(72MHz/(71+1)=1MHz),自动重装载值 999(1MHz/(999+1)=1kHz=1ms)。用于 SU-03T 串口接收帧超时检测。
TIM_HandleTypeDef htim1; void MX_TIM1_Init(void) { htim1.Instance = TIM1; htim1.Init.Prescaler = 71; // 72MHz/(71+1) = 1MHz htim1.Init.CounterMode = TIM_COUNTERMODE_UP; htim1.Init.Period = 999; // 1MHz/(999+1) = 1kHz = 1ms htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_Base_Init(&htim1) != HAL_OK) Error_Handler(); HAL_NVIC_SetPriority(TIM1_UP_IRQn, 0, 0); HAL_NVIC_EnableIRQ(TIM1_UP_IRQn); }
SU-03T 驱动采用环形缓冲区 + 超时帧检测机制,实现非阻塞式串口异步收发。核心配置:串口 huart3,缓冲区 128 字节,帧超时 10ms。数据帧格式为 [0xF4, 0x06, 命令字节, 0xFF]。
#define SU_03T_USART huart3 #define SU_03T_USART_RX_BUF_SIZE 128 // 环形缓冲区大小 #define SU_03T_FRAME_TIMEOUT 10 // 帧超时阈值 10ms
uint8_t SU_03T_RxBuffer[SU_03T_USART_RX_BUF_SIZE]; uint16_t SU_03T_RxWrPtr = 0; // 写指针(中断中写入) uint16_t SU_03T_RxRdPtr = 0; // 读指针(主循环中读取) uint16_t SU_03T_RxTimeout = 0; uint8_t SU_03T_RxFrameFlag = 0; // 初始化:清空缓冲区,启动中断接收 void SU_03T_USART_Init(void) { memset(SU_03T_RxBuffer, 0, SU_03T_USART_RX_BUF_SIZE); SU_03T_RxWrPtr = 0; SU_03T_RxRdPtr = 0; SU_03T_RxTimeout = 0; SU_03T_RxFrameFlag = 0; HAL_UART_Receive_IT(&SU_03T_USART, &SU_03T_RxBuffer[0], 1); } // 串口中断回调:写入环形缓冲区并重启接收 void SU_03T_USART_RxCallback(UART_HandleTypeDef *huart) { if(huart->Instance == SU_03T_USART.Instance) { // 写入当前字节到环形缓冲区 SU_03T_RxBuffer[SU_03T_RxWrPtr] = huart->Instance->DR; SU_03T_RxWrPtr = (SU_03T_RxWrPtr + 1) % SU_03T_USART_RX_BUF_SIZE; // 有新数据,重置超时计时 SU_03T_RxTimeout = 0; // 重启串口中断接收 HAL_UART_Receive_IT(&SU_03T_USART, &SU_03T_RxBuffer[SU_03T_RxWrPtr], 1); } }
// 1ms定时器回调:超时判定帧完成 void SU_03T_TIM_MS(void) { if(SU_03T_RxWrPtr != 0) // 有未读取数据 { SU_03T_RxTimeout++; if(SU_03T_RxTimeout >= SU_03T_FRAME_TIMEOUT) // 10ms无新数据 { SU_03T_RxTimeout = 0; SU_03T_RxWrPtr = 0; SU_03T_RxFrameFlag = 1; // 置位帧完成标志 } } } // 主循环逻辑:解析数据帧并执行命令 void SU_03T_USART_LogicLoop(void) { if(SU_03T_RxFrameFlag == 1) { SU_03T_RxFrameFlag = 0; // 校验帧头0xF4 + 类型0x06 + 帧尾0xFF if(SU_03T_RxBuffer[0]==0xF4 && SU_03T_RxBuffer[1]==0x06 && SU_03T_RxBuffer[3]==0xFF) { switch (SU_03T_RxBuffer[2]) { case 0x01: // "打开灯光" HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET); break; case 0x02: // "关闭灯光" HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET); break; default: break; } } } }
SU-03T 模块通过 UART 串口以固定格式数据帧输出识别结果。每帧 4 字节,采用帧头 + 类型 + 命令 + 帧尾结构。STM32 通过 10ms 超时检测帧边界,校验帧头帧尾后提取命令字节执行对应操作。
| 命令字节 | 语音指令 | 执行动作 | LED 状态 |
|---|---|---|---|
| 0x01 | "打开灯光" | HAL_GPIO_WritePin(LED, GPIO_PIN_RESET) | 开启(低电平点亮) |
| 0x02 | "关闭灯光" | HAL_GPIO_WritePin(LED, GPIO_PIN_SET) | 关闭(高电平熄灭) |
OLED 采用软件 I2C 通信,SCL=PB15,SDA=PB14。主循环每 50ms 调用 OLED_CenterPrintf 在 Y=24 位置居中显示灯光状态文字,调用 OLED_Update 将显存刷新到屏幕。
OLED_Init() — 初始化 OLED 显示参数OLED_Clear() — 清空显存OLED_CenterPrintf(24, fmt, ...) — 在 Y=24 处居中打印格式化字符串OLED_Update() — 将显存数据通过 I2C 写入 OLED 屏STM32 主程序执行流程,含 while(1) 主循环
虚线框为 while(1) 主循环体;UART 中断和 TIM1 中断为异步事件,分别在串口数据到达和 1ms 定时器溢出时触发
模拟 SU-03T 语音模块识别命令、UART 传输、LED 控制与 OLED 显示的完整流程
麦克风:模拟 SU-03T 模块上的 ECM 驻极体麦克风,接收语音信号后变为高亮状态并产生声波动画。
UART 数据流:语音识别成功后,模块通过 UART(9600bps 8N1)发送 4 字节数据帧 [0xF4, 0x06, 命令, 0xFF] 到 STM32。
STM32 处理:STM32 通过 USART3 接收数据帧,校验帧头帧尾后提取命令字节,控制 PC13 引脚电平驱动 LED。
OLED 显示:STM32 每 50ms 刷新 OLED 屏幕,居中显示当前灯光开关状态。