cpp
#include "stm32f10x.h"
#include "usart.h"
#include "motor.h"
#include "delay.h"
#include "gpio.h"
//合作加V :Niumajiuhao
// 定义窗帘状态
typedef enum {
CURTAIN_CLOSED,
CURTAIN_OPEN,
CURTAIN_OPENING,
CURTAIN_CLOSING
} CurtainState;
// 全局变量
CurtainState currentState = CURTAIN_CLOSED;
uint8_t voiceCommand = 0; // 存储语音指令
// 指令定义
#define CMD_OPEN 0x01 // 打开窗帘
#define CMD_CLOSE 0x02 // 关闭窗帘
#define CMD_STOP 0x03 // 停止
#define CMD_HALF 0x04 // 半开
// 函数声明
void System_Init(void);
void CheckVoiceCommand(void);
void UpdateCurtainState(void);
int main(void) {
// 系统初始化
System_Init();
// 发送启动信息
USART_SendString(USART1, "Voice controlled curtain system initialized\r\n");
while (1) {
// 检查语音指令
CheckVoiceCommand();
// 更新窗帘状态
UpdateCurtainState();
// 延时一小段时间
delay_ms(100);
}
}
// 系统初始化
void System_Init(void) {
RCC_Configuration(); // 配置系统时钟
GPIO_Configuration(); // 配置GPIO
USART_Configuration(); // 配置串口
Motor_Init(); // 初始化电机
Delay_Init(); // 初始化延时函数
NVIC_Configuration(); // 配置中断向量
}
// 检查语音指令
void CheckVoiceCommand(void) {
if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != RESET) {
voiceCommand = USART_ReceiveData(USART1);
// 打印接收到的指令
USART_SendString(USART1, "Received command: ");
USART_SendData(USART1, voiceCommand);
USART_SendString(USART1, "\r\n");
}
}
// 更新窗帘状态
void UpdateCurtainState(void) {
// 检测限位开关状态
uint8_t openLimit = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0); // 全开限位
uint8_t closeLimit = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1); // 全关限位
switch (currentState) {
case CURTAIN_CLOSED:
if (voiceCommand == CMD_OPEN) {
USART_SendString(USART1, "Opening curtain...\r\n");
Motor_Forward();
currentState = CURTAIN_OPENING;
voiceCommand = 0;
} else if (voiceCommand == CMD_HALF) {
USART_SendString(USART1, "Opening curtain to half...\r\n");
Motor_Forward();
currentState = CURTAIN_OPENING;
voiceCommand = 0;
// 设置定时器,半开位置约为全开的一半时间
// 此处省略定时器设置代码
}
break;
case CURTAIN_OPEN:
if (voiceCommand == CMD_CLOSE) {
USART_SendString(USART1, "Closing curtain...\r\n");
Motor_Backward();
currentState = CURTAIN_CLOSING;
voiceCommand = 0;
} else if (voiceCommand == CMD_HALF) {
USART_SendString(USART1, "Closing curtain to half...\r\n");
Motor_Backward();
currentState = CURTAIN_CLOSING;
voiceCommand = 0;
// 设置定时器,半开位置约为全关的一半时间
// 此处省略定时器设置代码
}
break;
case CURTAIN_OPENING:
if (voiceCommand == CMD_STOP) {
USART_SendString(USART1, "Stopping curtain...\r\n");
Motor_Stop();
currentState = (openLimit ? CURTAIN_OPEN : CURTAIN_CLOSED);
voiceCommand = 0;
} else if (openLimit) {
// 达到全开位置
Motor_Stop();
USART_SendString(USART1, "Curtain fully opened\r\n");
currentState = CURTAIN_OPEN;
}
// 半开逻辑判断...
break;
case CURTAIN_CLOSING:
if (voiceCommand == CMD_STOP) {
USART_SendString(USART1, "Stopping curtain...\r\n");
Motor_Stop();
currentState = (closeLimit ? CURTAIN_CLOSED : CURTAIN_OPEN);
voiceCommand = 0;
} else if (closeLimit) {
// 达到全关位置
Motor_Stop();
USART_SendString(USART1, "Curtain fully closed\r\n");
currentState = CURTAIN_CLOSED;
}
// 半开逻辑判断...
break;
default:
break;
}
}
cpp
#include "motor.h"
#include "stm32f10x.h"
//合作加V 牛马9号 :Niumajiuhao
// 步进电机引脚定义
#define MOTOR_PORT GPIOB
#define IN1_PIN GPIO_Pin_0
#define IN2_PIN GPIO_Pin_1
#define IN3_PIN GPIO_Pin_2
#define IN4_PIN GPIO_Pin_3
// 步进电机相序表 (四相八拍)
const uint8_t stepTable[8][4] = {
{1, 0, 0, 0},
{1, 1, 0, 0},
{0, 1, 0, 0},
{0, 1, 1, 0},
{0, 0, 1, 0},
{0, 0, 1, 1},
{0, 0, 0, 1},
{1, 0, 0, 1}
};
uint8_t currentStep = 0;
uint8_t motorRunning = 0;
uint8_t motorDirection = 1; // 1: 正转(开), 0: 反转(关)
// 初始化电机控制引脚
void Motor_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
// 使能GPIOB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// 配置电机控制引脚为推挽输出
GPIO_InitStructure.GPIO_Pin = IN1_PIN | IN2_PIN | IN3_PIN | IN4_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(MOTOR_PORT, &GPIO_InitStructure);
// 初始状态全部为低电平
GPIO_ResetBits(MOTOR_PORT, IN1_PIN | IN2_PIN | IN3_PIN | IN4_PIN);
// 初始化定时器用于电机脉冲
Motor_Timer_Init();
}
// 初始化定时器用于控制电机转速
void Motor_Timer_Init(void) {
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 使能定时器3时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
// 定时器配置,控制电机转速
TIM_TimeBaseStructure.TIM_Period = 100; // 周期值,控制转速
TIM_TimeBaseStructure.TIM_Prescaler = 7199; // 预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// 使能定时器更新中断
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
// 配置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
// 电机正转(打开窗帘)
void Motor_Forward(void) {
motorDirection = 1;
motorRunning = 1;
TIM_Cmd(TIM3, ENABLE); // 启动定时器
}
// 电机反转(关闭窗帘)
void Motor_Backward(void) {
motorDirection = 0;
motorRunning = 1;
TIM_Cmd(TIM3, ENABLE); // 启动定时器
}
// 停止电机
void Motor_Stop(void) {
motorRunning = 0;
TIM_Cmd(TIM3, DISABLE); // 停止定时器
// 所有引脚置低
GPIO_ResetBits(MOTOR_PORT, IN1_PIN | IN2_PIN | IN3_PIN | IN4_PIN);
}
// 定时器3中断服务函数,用于控制电机步进
void TIM3_IRQHandler(void) {
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) {
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
if (motorRunning) {
// 根据方向更新当前步数
if (motorDirection) {
currentStep = (currentStep + 1) % 8;
} else {
currentStep = (currentStep - 1 + 8) % 8;
}
// 设置电机引脚状态
if (stepTable[currentStep][0]) {
GPIO_SetBits(MOTOR_PORT, IN1_PIN);
} else {
GPIO_ResetBits(MOTOR_PORT, IN1_PIN);
}
if (stepTable[currentStep][1]) {
GPIO_SetBits(MOTOR_PORT, IN2_PIN);
} else {
GPIO_ResetBits(MOTOR_PORT, IN2_PIN);
}
if (stepTable[currentStep][2]) {
GPIO_SetBits(MOTOR_PORT, IN3_PIN);
} else {
GPIO_ResetBits(MOTOR_PORT, IN3_PIN);
}
if (stepTable[currentStep][3]) {
GPIO_SetBits(MOTOR_PORT, IN4_PIN);
} else {
GPIO_ResetBits(MOTOR_PORT, IN4_PIN);
}
}
}
}
cpp
#include "usart.h"
#include "stm32f10x.h"
#include <stdio.h>
//合作加V :Niumajiuhao
// 配置USART1用于与SU-03T语音模块通信
void USART_Configuration(void) {
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 使能USART1和GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
// 配置USART1_TX (PA.09)为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置USART1_RX (PA.10)为浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置USART1参数
USART_InitStructure.USART_BaudRate = 9600; // SU-03T默认波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
// 配置USART1中断
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 使能USART1接收中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
// 使能USART1
USART_Cmd(USART1, ENABLE);
}
// 发送一个字节数据
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data) {
// 等待发送缓冲区为空
while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
// 发送数据
USARTx->DR = (Data & (uint16_t)0x01FF);
}
// 发送字符串
void USART_SendString(USART_TypeDef* USARTx, char* str) {
while (*str) {
USART_SendData(USARTx, *str++);
// 等待发送完成
while (USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
}
}
// 重定向fputc函数,支持printf输出到串口
int fputc(int ch, FILE *f) {
USART_SendData(USART1, (uint8_t)ch);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
return ch;
}
// USART1中断服务函数
void USART1_IRQHandler(void) {
// 这里可以处理接收到的数据
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
// 清除中断标志
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
// 读取数据的操作在主函数中处理
}
}
基于 STM32 单片机与 SU-03T 语音模块的智能声控窗帘系统设计与实现
一、引言
1.1 研究背景与意义
随着科技的飞速发展,智能家居已成为当今社会发展的重要趋势,它旨在为人们提供更便捷、舒适、高效的生活体验。在智能家居体系中,窗帘作为家居环境的重要组成部分,其智能化升级具有重要意义。传统窗帘需要手动操作,在很多场景下带来不便,比如当人们手中持有物品时,或者在寒冷的冬季不想离开温暖的沙发时。
智能声控窗帘通过语音指令实现窗帘的自动开合,极大地提升了操作的便捷性,尤其对于老人、儿童以及行动不便的人群更为友好。此外,智能声控窗帘还能与其他智能家居设备联动,构建更加智能化的家居生态系统。本研究设计的基于 STM32 单片机与 SU-03T 语音模块的智能声控窗帘系统,成本较低、性能稳定,具有较强的市场潜力,对推动智能家居的普及和发展具有积极的推动作用。
1.2 国内外研究现状
国内外在智能窗帘领域已经开展了较多的研究。国外一些知名企业推出的智能窗帘产品,通常采用较为先进的无线通信技术和传感器技术,实现了远程控制、自动感应等功能,但这些产品价格较高,难以在普通家庭中普及。
国内的研究则更注重性价比,许多研究采用单片机作为控制核心,结合各类传感器和执行机构实现智能控制。在语音控制方面,早期多采用简单的语音识别模块,识别率较低,指令集也较为有限。近年来,随着语音识别技术的发展,出现了如 SU-03T 等性能更优、成本更低的语音模块,为智能声控窗帘的研究提供了更好的技术支持。
与现有研究相比,本设计选用 STM32 单片机作为主控制器,其处理能力强、接口丰富,能够满足系统复杂控制的需求;采用 SU-03T 语音模块,提高了语音识别的准确率和响应速度;选用步进电机作为执行机构,能够实现窗帘的精确控制。整体方案在性能和成本之间取得了较好的平衡,具有一定的独特性和实用性。
1.3 研究目标与内容
本研究的目标是设计并实现一个基于 STM32 单片机与 SU-03T 语音模块的智能声控窗帘系统,该系统能够准确识别用户的语音指令(如 "打开窗帘""关闭窗帘""停止""半开" 等),并通过控制步进电机实现窗帘的相应动作,同时具备较高的稳定性和可靠性。
研究内容主要包括以下几个方面:
- 系统硬件设计:包括 STM32 最小系统的设计、SU-03T 语音模块与 STM32 的接口电路设计、步进电机驱动电路设计以及限位传感器等辅助电路的设计。
- 系统软件设计:搭建软件开发环境,设计主程序流程,实现语音识别程序、步进电机控制程序以及数据处理与交互程序等。
- 系统测试与优化:对系统进行功能测试和性能测试,根据测试结果进行分析和优化,确保系统满足设计要求。
二、系统总体设计方案
2.1 需求分析
用户对智能声控窗帘的功能需求主要包括以下几个方面:
- 语音控制功能:能够通过特定的语音指令控制窗帘的打开、关闭、停止和半开等动作,语音识别应具有较高的准确率,能够在一定的环境噪声下正常工作。
- 自动定位功能:窗帘在打开和关闭过程中,能够准确到达指定位置,避免过度运动或运动不足。
- 状态反馈功能:系统能够对窗帘的当前状态(如全开、全关、半开等)进行检测,并在必要时通过语音或其他方式反馈给用户。
- 手动控制备用:在语音控制出现故障时,能够通过手动方式控制窗帘的运动,提高系统的可靠性。
- 低功耗设计:系统在待机和工作过程中应尽量降低功耗,延长设备的使用寿命。
基于以上需求,确定系统设计方向为:以 STM32 单片机为核心,结合 SU-03T 语音模块实现语音指令的识别与解析,通过步进电机驱动电路控制步进电机的运动,利用限位传感器实现窗帘的精确定位,同时设计手动控制接口作为备用。
2.2 系统架构设计
系统整体架构主要由 STM32 单片机、SU-03T 语音模块、步进电机及驱动模块、限位传感器和电源模块等组成。
STM32 单片机作为主控制器,负责协调各个模块的工作。它通过串口与 SU-03T 语音模块进行通信,接收语音模块发送的指令;通过 GPIO 接口连接步进电机驱动器,控制步进电机的转动方向和转速;通过 GPIO 接口连接限位开关,检测窗帘的位置状态。
SU-03T 语音模块负责采集用户的语音指令,并对其进行识别和解析,将解析后的指令通过串口发送给 STM32 单片机。
步进电机及驱动模块在 STM32 单片机的控制下,带动窗帘进行开合运动。步进电机能够实现精确的角度控制,保证窗帘运动的准确性。
限位传感器安装在窗帘的全开和全关位置,当窗帘运动到这些位置时,传感器会向 STM32 单片机发送信号,单片机接收到信号后控制电机停止运动,实现窗帘的精确定位。
电源模块为系统中的各个组件提供合适的工作电压,确保系统的稳定运行。
2.3 技术路线选择
- 单片机选型:选择 STM32 单片机作为主控制器,主要原因是其具有强大的处理能力,能够快速处理各类数据和指令;丰富的外设接口,便于与语音模块、电机驱动器、传感器等进行连接;同时,STM32 系列单片机在市场上应用广泛,技术资料丰富,开发工具成熟,有利于系统的开发和调试。与其他单片机相比,如 51 单片机,STM32 的性能更优,能够满足系统复杂控制的需求;与更高端的处理器相比,STM32 成本较低,性价比更高。
- 语音模块选型:选用 SU-03T 语音模块,该模块具有识别准确率高、响应速度快、成本低等优点。它支持自定义唤醒词和指令集,能够满足本系统对语音控制的需求。与其他语音模块相比,如 LD3320,SU-03T 的集成度更高,使用更加方便,不需要复杂的外围电路设计。
- 电机选型:选择步进电机作为执行机构,因为步进电机具有控制精度高、运行稳定、响应速度快等特点,能够精确控制窗帘的运动距离和位置。与直流电机相比,步进电机不需要位置传感器就能实现精确定位,简化了系统设计;与伺服电机相比,步进电机成本较低,适合本系统的成本预算。
三、系统硬件设计
3.1 STM32 最小系统设计
本系统选用 STM32F103 系列单片机作为主控制器。STM32 最小系统主要包括电源电路、时钟电路和复位电路等。
电源电路:STM32 单片机需要 3.3V 的工作电压,系统采用外部 5V 电源供电,通过低压差线性稳压器(LDO)将 5V 电压转换为 3.3V,为单片机及其他需要 3.3V 电压的模块供电。电源电路中还加入了滤波电容,以减少电源噪声对系统的影响。
时钟电路:STM32 单片机具有内部时钟和外部时钟两种时钟源。为了保证系统时钟的准确性和稳定性,本设计采用外部 8MHz 的晶体振荡器作为时钟源,通过锁相环(PLL)倍频后为单片机提供 72MHz 的系统时钟。时钟电路中加入了合适的负载电容,确保振荡器能够稳定起振。
复位电路:采用手动复位和上电复位相结合的方式。上电复位通过电容和电阻组成的 RC 电路实现,当系统上电时,电容充电,产生复位信号;手动复位通过复位按钮实现,当按下按钮时,产生复位信号。复位电路能够确保单片机在系统上电或出现异常时能够正确复位,保证系统的正常工作。
3.2 SU-03T 语音模块电路设计
SU-03T 语音模块是一款集成了语音识别、语音合成和麦克风输入的模块,其工作电压为 3.3V。该模块通过串口与 STM32 单片机进行通信,实现指令的传输。
SU-03T 语音模块的接口电路设计较为简单,主要包括电源接口、串口接口和麦克风接口。电源接口直接连接到系统的 3.3V 电源;串口接口的 TX 引脚连接到 STM32 单片机的 USART_RX 引脚,RX 引脚连接到 STM32 单片机的 USART_TX 引脚,实现双向通信;麦克风接口连接外部麦克风,用于采集用户的语音指令。
在电路设计中,为了保证串口通信的稳定性,在模块的 TX 和 RX 引脚上分别串联了一个小电阻,以减少信号干扰。同时,在模块的电源引脚附近加入了滤波电容,提高模块工作的稳定性。
SU-03T 语音模块的工作原理是:首先通过麦克风采集用户的语音信号,经过内部的语音识别算法对信号进行处理和识别,将识别结果转换为相应的指令代码,然后通过串口将指令代码发送给 STM32 单片机。单片机接收到指令代码后,进行解析并执行相应的操作。
3.3 步进电机驱动电路设计
步进电机的工作特性是需要通过脉冲信号来控制其转动,每个脉冲信号对应电机的一个固定角度转动。本系统选用的步进电机为四相八拍步进电机,步距角为 1.8 度,需要通过驱动电路来控制其工作。
驱动芯片选用 ULN2003,它是一款高电压、大电流的达林顿晶体管阵列,能够为步进电机提供足够的驱动电流。ULN2003 具有 7 个通道的驱动能力,适合驱动四相步进电机。
步进电机驱动电路的设计如下:STM32 单片机的四个 GPIO 引脚分别连接到 ULN2003 的四个输入引脚,ULN2003 的四个输出引脚分别连接到步进电机的四个相绕组。通过单片机输出不同时序的脉冲信号,控制 ULN2003 内部晶体管的导通和截止,从而控制步进电机各相绕组的通电顺序,实现电机的正反转和调速。
在电路设计中,为了保护驱动芯片和步进电机,在电机的各相绕组上分别并联了续流二极管,以吸收电机绕组产生的反向电动势。同时,在 ULN2003 的电源引脚附近加入了滤波电容,减少电源噪声的影响。
电机正反转控制是通过改变各相绕组的通电顺序来实现的。例如,正转时的通电顺序为 A→AB→B→BC→C→CD→D→DA,反转时的通电顺序则为 A→AD→D→DC→C→CB→B→BA。调速则是通过改变脉冲信号的频率来实现的,频率越高,电机转速越快,反之则越慢。
3.4 其他辅助电路设计
为了增强系统的智能化程度和可靠性,本系统还设计了限位传感器电路和手动控制电路等辅助电路。
限位传感器电路:采用机械触点式限位开关作为传感器,分别安装在窗帘的全开和全关位置。限位开关的一端连接到 STM32 单片机的 GPIO 引脚,另一端接地。当窗帘运动到全开或全关位置时,会触发相应的限位开关,使其触点闭合,单片机的 GPIO 引脚检测到低电平信号,从而判断窗帘已到达极限位置,控制电机停止运动。
手动控制电路:设计了两个按钮,分别用于手动控制窗帘的打开和关闭。按钮的一端连接到 STM32 单片机的 GPIO 引脚,另一端接地,同时通过上拉电阻将 GPIO 引脚拉到高电平。当按下按钮时,GPIO 引脚检测到低电平信号,单片机接收到信号后控制电机相应地转动,实现窗帘的手动控制。
这些辅助电路的设计,为系统提供了更全面的功能和更高的可靠性,使得系统在语音控制出现故障时,仍然能够正常工作。
四、系统软件设计
4.1 软件开发环境搭建
本系统的软件开发环境采用 Keil MDK(Microcontroller Development Kit),它是一款针对 ARM 处理器的集成开发环境,支持 STM32 系列单片机的开发。
搭建开发环境的步骤如下:
- 安装 Keil MDK 软件:从官方网站下载适合的 Keil MDK 版本,按照安装向导进行安装,安装过程中需要输入许可证信息。
- 安装 STM32 芯片支持包:打开 Keil MDK 软件,通过 Pack Installer 安装 STM32F103 系列芯片的支持包,以便在开发过程中能够正确识别和配置单片机。
- 配置编译器:在 Keil MDK 的工程设置中,选择合适的编译器,本系统选用 ARMCC 编译器。
- 配置调试器:根据所使用的调试工具(如 J-Link、ST-Link 等),在工程设置中配置调试器的相关参数,确保能够正常进行程序的下载和调试。
通过以上步骤,完成软件开发环境的搭建,为后续的程序设计和调试做好准备。
4.2 主程序流程设计
主程序是系统的核心,负责协调各个模块的工作,其流程图如下:
系统上电后,首先进行初始化操作,包括 STM32 单片机的初始化(如时钟初始化、GPIO 初始化、USART 初始化等)、SU-03T 语音模块的初始化、步进电机驱动模块的初始化以及限位传感器的初始化等。初始化完成后,系统进入待机状态,等待用户的语音指令或手动控制信号。
当接收到 SU-03T 语音模块发送的指令时,主程序对指令进行解析,判断是 "打开窗帘""关闭窗帘""停止" 还是 "半开" 等指令,然后根据指令控制步进电机进行相应的动作。在电机运动过程中,主程序不断检测限位传感器的信号,如果检测到窗帘已到达极限位置,或者接收到 "停止" 指令,则控制电机停止运动。
当接收到手动控制信号时,主程序根据信号的类型(打开或关闭),控制步进电机进行相应的动作,同时也会检测限位传感器的信号,确保窗帘运动到合适的位置后停止。
主程序在执行过程中,还会不断监测系统的工作状态,如电源电压、模块连接状态等,一旦发现异常,会进行相应的处理,如发出报警信号或进入保护状态。
4.3 语音识别程序设计
SU-03T 语音模块的编程主要通过串口通信来实现。首先需要对 SU-03T 语音模块进行配置,设置唤醒词和指令集。通过官方提供的配置工具,可以将唤醒词设置为 "小窗帘",指令集设置为 "打开窗帘" 对应指令 0x01、"关闭窗帘" 对应指令 0x02、"停止" 对应指令 0x03、"半开" 对应指令 0x04,并将模块的波特率设置为 9600,与 STM32 单片机的 USART 接口波特率保持一致,同时启用串口输出模式,使得模块在识别成功后能够自动发送对应指令。
在 STM32 单片机的程序中,语音识别程序主要负责通过 USART 接口接收 SU-03T 语音模块发送的指令。当模块识别到用户的语音指令后,会将对应的指令代码通过串口发送给单片机。单片机通过中断方式接收串口数据,将接收到的数据存储在缓冲区中,然后对数据进行解析,判断用户的指令类型。
例如,当接收到指令 0x01 时,判断为 "打开窗帘" 指令,通知主程序控制步进电机正转,带动窗帘打开;当接收到指令 0x02 时,判断为 "关闭窗帘" 指令,通知主程序控制步进电机反转,带动窗帘关闭;当接收到指令 0x03 时,判断为 "停止" 指令,通知主程序控制电机停止运动;当接收到指令 0x04 时,判断为 "半开" 指令,通知主程序控制电机转动到窗帘半开的位置。
为了提高语音识别的可靠性,程序中还加入了指令校验机制,对接收到的指令进行校验,确保指令的正确性。如果接收到的指令无效,则忽略该指令,继续等待下一次指令。
4.4 步进电机控制程序设计
步进电机的控制算法主要包括脉冲分配和调速算法。脉冲分配是根据步进电机的相数和工作方式,生成相应的脉冲序列,控制电机各相绕组的通电顺序,从而实现电机的转动。对于四相八拍步进电机,其脉冲分配序列为正转时 A→AB→B→BC→C→CD→D→DA,反转时 A→AD→D→DC→C→CB→B→BA。
在程序中,通过定义一个数组来存储脉冲分配序列,根据电机的转动方向(正转或反转),从数组中依次取出相应的脉冲信号,通过 GPIO 引脚输出到步进电机驱动器,控制电机转动。
调速算法采用改变脉冲信号频率的方法来实现。脉冲信号的频率越高,电机的转速越快;频率越低,转速越慢。在程序中,通过定时器来生成不同频率的脉冲信号。定时器的定时时间越短,脉冲频率越高。通过设置定时器的比较寄存器值,可以改变定时时间,从而实现电机转速的调节。
例如,当需要电机快速转动时,减小定时器的定时时间,提高脉冲频率;当需要电机慢速转动时,增大定时时间,降低脉冲频率。在窗帘启动和停止阶段,采用较低的转速,以减少冲击;在中间运行阶段,采用较高的转速,以提高运行效率。
此外,程序中还加入了电机步数计数功能,通过记录发送的脉冲数来计算电机的转动角度,从而控制窗帘的运动距离。当窗帘需要运动到指定位置(如半开位置)时,根据预设的步数控制电机转动相应的角度后停止。
4.5 数据处理与交互程序设计
数据处理程序主要负责处理限位传感器采集到