基于STM32的智能分类垃圾桶系统设计与实现
摘要:本文设计并实现了一种基于STM32F103C8T6单片机的智能分类垃圾桶系统。该系统通过4个舵机分别控制对应的垃圾桶盖,利用语音识别模块识别各类垃圾并自动打开相应垃圾桶盖,同时支持按键手动操作。采用红外对管检测垃圾桶是否装满,通过不同颜色灯光指示状态,并在装满后通过蓝牙模块向管理员手机发送提示信息。系统实现了智能分类、状态监测和远程通知等功能,提高了垃圾分类的效率和便捷性。
关键词:STM32F103C8T6;智能分类垃圾桶;舵机控制;语音识别;红外对管检测;蓝牙通信
一、引言
随着环保意识的不断提高,垃圾分类成为了社会关注的焦点。传统的垃圾桶缺乏智能分类功能,需要人工手动分类投放,效率低下且容易出错。智能分类垃圾桶的出现为解决这一问题提供了新的思路。本文设计了一种基于STM32F103C8T6单片机的智能分类垃圾桶系统,该系统结合了语音识别、舵机控制、红外对管检测和蓝牙通信等技术,实现了垃圾的自动分类、状态监测和远程通知等功能,提高了垃圾分类的准确性和便捷性。
二、系统总体设计
(一)系统架构
本系统主要由STM32F103C8T6单片机、MG90S舵机模块、红外对管检测模块、SNR8016语音模块、HC - 05蓝牙模块、按键模块以及电源模块等部分组成。系统架构图如图1所示。
<img src="https://example.com/system_architecture.png" />
(二)功能需求
- 智能分类:通过SNR8016语音模块识别各类垃圾,并自动打开对应的垃圾桶盖。
- 手动操作:提供按键,用户可以通过按键手动打开垃圾桶盖。
- 状态监测:使用红外对管检测垃圾桶是否装满,没满则亮绿灯,否则亮红灯。
- 远程通知:当垃圾桶装满后,通过HC - 05蓝牙模块向管理员手机发送提示信息。
三、硬件设计
(一)核心控制器——STM32F103C8T6单片机
STM32F103C8T6单片机是基于ARMCortex - M3内核的32位微控制器,具有高性能、低功耗、丰富的外设和灵活的功耗管理等特点。在本系统中,STM32F103C8T6单片机作为核心控制器,负责接收语音模块、按键模块和红外对管检测模块的信号,处理控制逻辑,并输出控制信号给舵机模块和蓝牙模块。
(二)舵机模块——MG90S舵机
MG90S舵机是一种高性能的伺服电机,具有扭矩大、响应速度快等优点。本系统使用4个MG90S舵机分别控制可回收垃圾、厨余垃圾、有害垃圾和其他垃圾四个垃圾桶的盖子。STM32单片机通过输出PWM信号控制舵机的转动角度,从而实现垃圾桶盖的开启和关闭。
(三)红外对管检测模块
红外对管检测模块由红外发射管和红外接收管组成。将红外对管安装在垃圾桶内部,当垃圾桶未装满时,红外接收管能够接收到红外发射管发射的红外信号;当垃圾桶装满时,垃圾会阻挡红外信号,红外接收管无法接收到信号。STM32单片机通过检测红外接收管的输出信号来判断垃圾桶是否装满。
(四)语音识别模块——SNR8016语音模块
SNR8016语音模块是一款基于特定语音识别芯片的模块,能够识别预设的语音指令。在本系统中,将各类垃圾的名称(如“可回收垃圾”“厨余垃圾”等)作为语音指令预先存储在语音模块中。当用户说出垃圾名称时,语音模块识别到指令后,将识别结果发送给STM32单片机。
(五)蓝牙模块——HC - 05蓝牙模块
HC - 05蓝牙模块是一种串口通信蓝牙模块,能够与手机等设备进行蓝牙通信。当垃圾桶装满时,STM32单片机通过串口将提示信息发送给HC - 05蓝牙模块,蓝牙模块再将信息发送到管理员手机上。
(六)按键模块
按键模块包括4个按键,分别对应可回收垃圾、厨余垃圾、有害垃圾和其他垃圾四个垃圾桶。用户可以通过按下相应的按键手动打开垃圾桶盖。
(七)电源模块
电源模块为整个系统提供稳定的电源,可采用锂电池或外部电源适配器供电。
四、软件设计
(一)开发环境
本系统采用Keil uVision作为开发环境。Keil uVision是一款功能强大的单片机开发软件,支持多种编程语言(如C语言),提供了丰富的库函数和开发工具,方便开发者进行程序编写和调试。
(二)软件架构
本系统的软件架构主要包括初始化模块、语音识别处理模块、按键处理模块、舵机控制模块、红外对管检测模块、蓝牙通信模块以及主循环模块等部分。
- 初始化模块:负责初始化STM32单片机的各个外设(如GPIO、PWM、USART、定时器等)以及各个模块(如语音模块、蓝牙模块等)。
- 语音识别处理模块:负责接收语音模块的识别结果,根据识别结果控制相应的舵机打开垃圾桶盖。
- 按键处理模块:负责检测按键的状态,当按键按下时控制相应的舵机打开垃圾桶盖。
- 舵机控制模块:负责根据控制指令输出PWM信号,控制舵机的转动角度。
- 红外对管检测模块:负责检测红外对管的输出信号,判断垃圾桶是否装满,并控制相应的指示灯亮灭。
- 蓝牙通信模块:负责蓝牙模块与手机之间的通信,当垃圾桶装满时向手机发送提示信息。
- 主循环模块:不断循环执行各个模块的功能,确保系统的正常运行。
(三)关键程序实现
-
舵机控制
c复制代码
void Servo_Control(uint8_t servo_num, uint16_t angle) {
uint16_t pulse_width = 1000 + (angle * 1000) / 180; // 计算PWM脉宽
switch (servo_num) {
case 1:
TIM_SetCompare1(TIM2, pulse_width); // 控制可回收垃圾舵机
break;
case 2:
TIM_SetCompare2(TIM2, pulse_width); // 控制厨余垃圾舵机
break;
case 3:
TIM_SetCompare3(TIM2, pulse_width); // 控制有害垃圾舵机
break;
case 4:
TIM_SetCompare4(TIM2, pulse_width); // 控制其他垃圾舵机
break;
}
}
-
语音识别处理
c复制代码
void Voice_Recognition_Process(void) {
uint8_t result = Voice_GetRecognitionResult(); // 获取语音识别结果
switch (result) {
case RECYCLABLE_WASTE:
Servo_Control(1, 90); // 打开可回收垃圾桶盖
break;
case KITCHEN_WASTE:
Servo_Control(2, 90); // 打开厨余垃圾桶盖
break;
case HAZARDOUS_WASTE:
Servo_Control(3, 90); // 打开有害垃圾桶盖
break;
case OTHER_WASTE:
Servo_Control(4, 90); // 打开其他垃圾桶盖
break;
}
}
-
红外对管检测
c复制代码
void Infrared_Sensor_Check(void) {
if (Infrared_Sensor_GetStatus(1) == FULL) { // 检测可回收垃圾桶是否装满
LED_Control(1, RED); // 亮红灯
if (!full_flag[0]) { // 如果之前未满,现在满了,发送蓝牙提示
Bluetooth_SendMessage("可回收垃圾桶已满");
full_flag[0] = 1;
}
} else {
LED_Control(1, GREEN); // 亮绿灯
full_flag[0] = 0;
}
// 其他垃圾桶检测类似
}
-
蓝牙通信
c复制代码
void Bluetooth_Init(void) {
USART_InitTypeDef USART_InitStruct;
// 初始化USART(用于与蓝牙模块通信)
USART_InitStruct.USART_BaudRate = 9600;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStruct);
USART_Cmd(USART1, ENABLE);
}
void Bluetooth_SendMessage(char *message) {
while (*message) {
USART_SendData(USART1, *message++);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
}
五、系统测试与优化
(一)系统测试
系统测试的主要目的是为了验证智能分类垃圾桶控制系统的设计是否满足预期的功能需求和性能指标。测试内容包括硬件测试和软件测试两个方面。
-
硬件测试
- STM32单片机测试:测试STM32单片机的运行稳定性,包括GPIO引脚的输入输出功能、PWM和USART通信等。
- 舵机测试:测试舵机的转动角度和响应速度,确保能够准确控制垃圾桶盖的开启和关闭。
- 红外对管检测测试:测试红外对管的检测精度和稳定性,确保能够准确判断垃圾桶是否装满。
- 语音模块测试:测试语音模块的识别准确率和响应速度,确保能够准确识别各类垃圾名称。
- 蓝牙模块测试:测试蓝牙模块的通信稳定性和数据传输速度,确保能够及时向管理员手机发送提示信息。
-
软件测试
- 功能测试:测试系统的各项功能是否正常工作,如语音识别、按键操作、舵机控制、红外对管检测和蓝牙通信等。
- 性能测试:测试系统的响应时间、稳定性、可靠性等性能指标。
(二)系统优化
在测试过程中,可能会发现系统的某些部分存在性能瓶颈或不足。针对这些问题,可以对系统进行优化和改进。例如:
- 优化语音识别算法:提高语音模块的识别准确率,减少误识别的情况。
- 改进舵机控制策略:优化舵机的转动速度和角度,提高垃圾桶盖的开启和关闭效率。
- 增加抗干扰措施:在硬件和软件上增加抗干扰措施,提高系统的稳定性和可靠性。
六、结论与展望
(一)结论
本文详细阐述了一种基于STM32F103C8T6单片机的智能分类垃圾桶系统的设计与实现。该系统结合了语音识别、舵机控制、红外对管检测和蓝牙通信等技术,实现了垃圾的自动分类、状态监测和远程通知等功能。通过系统测试和优化,验证了系统的可行性和稳定性。
(二)展望
未来,智能分类垃圾桶系统可以进一步拓展其功能,如增加更多的垃圾分类类别、与物联网平台进行更深入的集成,实现数据的实时上传和分析等。同时,可以优化系统的硬件设计和软件算法,降低系统成本,提高系统的性能和可靠性,使其更加适用于各种场景。
以上论文仅供参考,你可以根据实际研究情况对内容进行调整和修改,在撰写过程中还需补充实验数据、图表等内容以丰富论文。
#include "sys.h"
#include "usart.h"
//
//如果使用ucos,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_UCOS
#include "includes.h" //ucos 使用
#endif
//
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
_sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif
/*使用microLib的方法*/
/*
int fputc(int ch, FILE *f)
{
USART_SendData(USART1, (uint8_t) ch);
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {}
return ch;
}
int GetKey (void) {
while (!(USART1->SR & USART_FLAG_RXNE));
return ((int)(USART1->DR & 0x1FF));
}
*/
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA=0; //接收状态标记
//初始化IO 串口1
//bound:波特率
void uart_init(u32 bound){
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
USART_DeInit(USART1); //复位串口1
//USART1_TX PA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA9
//USART1_RX PA.10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA10
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//一般设置为9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
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); //初始化串口
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断
USART_Cmd(USART1, ENABLE); //使能串口
}
#if EN_USART1_RX //如果使能了接收
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
#ifdef OS_TICKS_PER_SEC //如果时钟节拍数定义了,说明要使用ucosII了.
OSIntEnter();
#endif
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
if((USART_RX_STA&0x8000)==0)//接收未完成
{
if(USART_RX_STA&0x4000)//接收到了0x0d
{
if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x8000; //接收完成了
}
else //还没收到0X0D
{
if(Res==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
#ifdef OS_TICKS_PER_SEC //如果时钟节拍数定义了,说明要使用ucosII了.
OSIntExit();
#endif
}
#endif
/**
* @brief 发送单个字符
* @param ch: 要发送的字符
* @retval 无
*/
void USART1_SendChar(uint8_t ch) {
// 等待发送寄存器为空
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
// 发送字符
USART_SendData(USART1, ch);
}
/**
* @brief 发送字符串
* @param str: 要发送的字符串
* @retval 无
*/
void USART1_SendString(char *str) {
while (*str) {
USART1_SendChar(*str++); // 逐个发送字符
}
}
/**
* @brief 发送数据包
* @param data: 要发送的数据指针
* @param len: 数据长度
* @retval 无
*/
void USART1_SendData(unsigned char *data, unsigned short len) {
unsigned short i;
for (i = 0; i < len; i++) {
USART1_SendChar(data[i]); // 逐个发送数据
}
}
/**
* @brief 发送数字
* @param number: 要发送的数字
* @retval 无
*/
void USART1_SendNumber(int32_t number) {
char buffer[12]; // 用于存储转换后的字符串
snprintf(buffer, sizeof(buffer), "%d", number); // 将数字转换为字符串
USART1_SendString(buffer); // 发送字符串
}