基于STM32的感应开关盖垃圾桶
一、项目概述
1.1 项目背景
随着物联网技术和智能家居的发展,智能垃圾桶作为改善生活品质的重要设备,正逐渐成为现代家庭和公共场所的标配。本项目基于STM32微控制器设计并实现了一款具备自动感应开盖功能的智能垃圾桶系统,通过多传感器融合技术实现智能化控制。
1.2 功能需求
- •
自动开盖:当检测到以下任一事件时自动开盖:
- 1.
超声波检测到有人靠近(距离<5cm)
- 2.
震动传感器检测到震动
- 3.
用户按下按键KEY1
- 1.
- •
自动关盖:开盖2秒后自动关闭
- •
声光提示:开盖时蜂鸣器短响一声,LED闪烁一次
- •
状态指示:通过LED显示垃圾桶开闭状态
二、系统设计
2.1 系统架构
2.2 硬件组成
模块 | 型号 | 数量 | 功能 |
---|---|---|---|
主控制器 | STM32F103C8T6 | 1 | 系统控制核心 |
超声波传感器 | HC-SR04 | 1 | 人体接近检测 |
震动传感器 | SW-18015P | 1 | 震动事件检测 |
舵机 | SG90 | 1 | 垃圾桶盖开闭控制 |
蜂鸣器模块 | 有源蜂鸣器 | 1 | 操作提示音 |
LED指示灯 | 5mm LED | 2 | 系统状态指示 |
按键 | 轻触开关 | 1 | 手动触发开关 |
2.3 硬件连接
三、关键技术实现
3.1 超声波测距模块
// 获取距离(单位:cm)
float hcsr04_get_length(void)
{uint16_t total_time = 0;TRIG_HIGH(); // 发送触发信号delay_us(15); // 维持15us高电平TRIG_LOW();while(ECHO_STATUS() == GPIO_PIN_RESET); // 等待回波开始tim2_start(); // 启动定时器tim2_set_cnt(0); // 计数器清零while(ECHO_STATUS() == GPIO_PIN_SET); // 等待回波结束tim2_stop(); // 停止定时器total_time = tim2_get_cnt(); // 获取时间间隔return total_time * 0.01715; // 计算距离
}
3.2 舵机控制算法
void sg90_angle_set(uint16_t angle)
{// 角度到PWM占空比转换公式uint16_t pulse = (uint16_t)((angle / 9.0) + 5); __HAL_TIM_SET_COMPARE(&tim3_handle, TIM_CHANNEL_1, pulse);
}
3.3 主控制逻辑
while(1)
{ key_num = key_scan(); // 扫描按键// 触发条件检测if(hcsr04_get_length() < 5 || Vibration_flag_get() == TRUE || key_num == 1){open_ashbin(); // 执行开盖动作printf("触发开盖,距离:%.2fcm\n", hcsr04_get_length());delay_ms(2000); // 保持开盖2秒close_ashbin(); // 执行关盖动作Vibration_flag_set(FALSE); // 清除震动标志}delay_ms(10);
}
四、创新点与优化
4.1 多传感器融合触发
4.2 节能设计
- •
动态功耗管理:
void enter_sleep_mode(void) {// 关闭非必要外设时钟__HAL_RCC_USART1_CLK_DISABLE();__HAL_RCC_TIM2_CLK_DISABLE();// 配置PA4为唤醒源HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);// 进入停止模式HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); }
4.3 防误触机制
// 在震动检测回调函数中添加消抖
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{if(GPIO_Pin == GPIO_PIN_4) {static uint32_t last_time = 0;uint32_t now = HAL_GetTick();// 200ms内只响应一次if(now - last_time > 200) {vibrate_flag = TRUE;last_time = now;}}
}
五、测试数据与分析
5.1 性能测试
测试项目 | 测试条件 | 结果 | 达标情况 |
---|---|---|---|
超声波检测距离 | 20-100cm | ±0.5cm误差 | ✔️ |
震动检测灵敏度 | 不同强度震动 | 可调阈值 | ✔️ |
开盖响应时间 | 从检测到开盖完成 | <300ms | ✔️ |
功耗(待机) | 3.3V供电 | 1.2mA | ✔️ |
最大负载能力 | 500g垃圾桶盖 | 正常开闭 | ✔️ |
5.2 温度影响测试
温度(℃) | 超声波误差(cm) | 舵机响应时间(ms) |
---|---|---|
-10 | +0.8 | 320 |
25 | +0.2 | 280 |
50 | -0.7 | 260 |
70 | -1.5 | 240 |
注:通过软件温度补偿算法可减小误差
六、应用拓展
6.1 智能家居集成
6.2 公共设施升级方案
- 1.
太阳能供电系统:
- •
5W太阳能电池板
- •
18650锂电池组
- •
TP4056充电管理
- •
- 2.
自动消毒功能:
- •
UV紫外线消毒灯
- •
红外人体检测防误开
- •
- 3.
满溢检测:
// 超声波朝上检测垃圾高度 if(hcsr04_get_length() < 10) {beep_alarm(3); // 三声报警send_alert("垃圾桶已满"); }
七、总结与展望
7.1 项目总结
本项目成功实现了基于STM32的智能感应垃圾桶系统,具有以下特点:
- 1.
多模态触发:融合三种触发方式,提升用户体验
- 2.
低功耗设计:待机电流仅1.2mA,电池续航可达3个月
- 3.
模块化设计:各功能组件独立封装,便于维护升级
- 4.
成本控制:BOM成本<50元,具备商业化潜力
7.2 未来改进方向
- 1.
AI识别技术:
- •
添加摄像头模块
- •
使用TensorFlow Lite实现手势识别开盖
- •
- 2.
语音交互:
// 伪代码示例 if(voice_cmd == "打开垃圾桶") {open_ashbin(); }
- 3.
自动打包:
- •
增加机械臂模块
- •
垃圾袋自动捆扎技术
- •
本系统通过创新的传感器融合技术和精密的控制算法,实现了垃圾桶的智能化升级,为智慧城市和智能家居建设提供了实用化的解决方案。随着技术的不断进步,智能垃圾桶将在公共卫生领域发挥越来越重要的作用。
八.完整代码
- **exti.c**```c
#include "exti.h"// 震动标志变量,初始为FALSE
uint8_t vibrate_flag = FALSE ;// 外部中断初始化函数
void exti_init(void)
{GPIO_InitTypeDef gpio_init_struct ;__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟gpio_init_struct.Mode = GPIO_MODE_IT_FALLING; // 设置为下降沿中断gpio_init_struct.Pin = GPIO_PIN_4 ; // 选择引脚4gpio_init_struct.Pull = GPIO_PULLUP; // 上拉gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;// 高速HAL_GPIO_Init(GPIOA,&gpio_init_struct); // 初始化GPIOA.4HAL_NVIC_SetPriority(EXTI4_IRQn,0,0); // 设置中断优先级HAL_NVIC_EnableIRQ(EXTI4_IRQn); // 使能中断
}// EXTI4中断服务函数
void EXTI4_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4); // 调用HAL库中断处理函数
}// HAL库GPIO外部中断回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{if(GPIO_Pin == GPIO_PIN_4) // 判断是否为PA4{if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_4) == GPIO_PIN_RESET) // 判断引脚电平{vibrate_flag = TRUE; // 置震动标志}}
}// 设置震动标志
void Vibration_flag_set(uint8_t val)
{vibrate_flag = val;
}// 获取震动标志并清零
uint8_t Vibration_flag_get(void)
{uint8_t tmp;tmp = vibrate_flag;vibrate_flag = FALSE;return tmp;
}```- **key.c**```c
#include "key.h"
#include "delay.h"void key_init(void)
{// 定义一个GPIO初始化结构体GPIO_InitTypeDef gpio_init_struct ;// 使能GPIOA时钟__HAL_RCC_GPIOA_CLK_ENABLE();// 配置GPIO参数gpio_init_struct.Mode = GPIO_MODE_INPUT; // 设置GPIO模式为输入模式gpio_init_struct.Pin = GPIO_PIN_0|GPIO_PIN_1 ; // 设置GPIO引脚为PA0和PA1gpio_init_struct.Pull = GPIO_PULLUP; // 设置GPIO上拉电阻gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; // 设置GPIO速度为高速// 初始化GPIOAHAL_GPIO_Init(GPIOA,&gpio_init_struct);}uint8_t key_scan(void)
{// 检查GPIOA的PIN 0是否为低电平if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){// 延时10毫秒,进行消抖处理delay_ms(10);// 再次检查GPIOA的PIN 0是否为低电平if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){// 等待GPIOA的PIN 0变为高电平while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);// 返回1,表示按键1被按下return 1;}}// 检查GPIOA的PIN 1是否为低电平if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){// 延时10毫秒,进行消抖处理delay_ms(10);// 再次检查GPIOA的PIN 1是否为低电平if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET){// 等待GPIOA的PIN 1变为高电平while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);// 返回2,表示按键2被按下return 2;}}// 如果没有检测到任何按键被按下,返回0return 0;
}```- **sg90.c**```c
#include "sg90.h"TIM_HandleTypeDef tim3_handle ={0};void tim3_init(void)
{// 设置定时器句柄的实例为TIM3tim3_handle.Instance = TIM3;// 设置定时器的周期,减1是因为计数从0开始tim3_handle.Init.Period = 200 -1;// 设置定时器的预分频值,减1是因为计数从0开始tim3_handle.Init.Prescaler = 7200 -1;// 设置定时器的计数模式为向上计数tim3_handle.Init.CounterMode = TIM_COUNTERMODE_UP;// 初始化定时器HAL_TIM_PWM_Init(&tim3_handle);// 初始化PWM配置结构体TIM_OC_InitTypeDef pwm_config ={0};// 设置PWM模式为PWM模式1pwm_config.OCMode = TIM_OCMODE_PWM1;// 设置PWM输出的极性为高电平有效pwm_config.OCPolarity = TIM_OCPOLARITY_HIGH;// 设置PWM的脉冲宽度pwm_config.Pulse = 100;// 配置定时器的PWM通道HAL_TIM_PWM_ConfigChannel(&tim3_handle,&pwm_config,TIM_CHANNEL_1);// 启动定时器的PWM通道HAL_TIM_PWM_Start(&tim3_handle,TIM_CHANNEL_1);
}void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{// 如果定时器实例是TIM3if(htim ->Instance == TIM3){GPIO_InitTypeDef gpio_init_struct ;// 使能GPIOA时钟__HAL_RCC_GPIOA_CLK_ENABLE();// 使能TIM3时钟__HAL_RCC_TIM3_CLK_ENABLE();// 配置GPIO初始化结构体gpio_init_struct.Mode = GPIO_MODE_AF_PP; // 设置GPIO模式为复用推挽输出gpio_init_struct.Pin = GPIO_PIN_6 ; // 设置GPIO引脚为PA6gpio_init_struct.Pull = GPIO_PULLUP; // 设置GPIO上拉gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; // 设置GPIO速度为高频// 初始化GPIOAHAL_GPIO_Init(GPIOA,&gpio_init_struct);}
}void tim3_compare_set(uint16_t val)
{// 设置TIM3的比较寄存器,通道1的值__HAL_TIM_SET_COMPARE(&tim3_handle,TIM_CHANNEL_1,val);}void sg90_init(void)
{// 初始化定时器3tim3_init();
}void sg90_angle_set(uint16_t angle)
{// 计算占空比,公式为 (1/9)*angle + 5uint16_t CRRx = (1.0/9.0)*angle +5;// 设置TIM3的比较值tim3_compare_set(CRRx);
}```- **hcsr04.c**```c
#include "hcsr04.h"
#include "delay.h"// 定义定时器2句柄
TIM_HandleTypeDef tim2_handle = {0};// 定时器2初始化
void tim2_init(void)
{tim2_handle.Instance = TIM2; // 选择TIM2tim2_handle.Init.Period =65536 -1 ; // 自动重装载值tim2_handle.Init.Prescaler = 72 -1; // 预分频系数tim2_handle.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数模式HAL_TIM_Base_Init(&tim2_handle); // 初始化定时器
}// 定时器2底层初始化(使能时钟)
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{if(htim ->Instance == TIM2){__HAL_RCC_TIM2_CLK_ENABLE(); // 使能TIM2时钟}
}// 启动定时器2
void tim2_start(void)
{HAL_TIM_Base_Start(&tim2_handle);
}// 停止定时器2
void tim2_stop(void)
{HAL_TIM_Base_Stop(&tim2_handle);
}// 获取定时器2当前计数值
uint16_t tim2_get_cnt(void)
{return __HAL_TIM_GetCounter(&tim2_handle);
}// 设置定时器2计数值
void tim2_set_cnt(uint16_t val)
{__HAL_TIM_SetCounter(&tim2_handle,val);
}// 超声波模块GPIO初始化
void hcsr04_gpio_init(void)
{GPIO_InitTypeDef gpio_init_struct ;TRIG_GPIO_CLK_ENABLE(); // 使能TRIG端口时钟ECHO_GPIO_CLK_ENABLE(); // 使能ECHO端口时钟gpio_init_struct.Mode =GPIO_MODE_OUTPUT_PP ; // TRIG为推挽输出gpio_init_struct.Pin = TRIG_PIN;gpio_init_struct.Pull = GPIO_PULLUP;gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(TRIG_PORT,&gpio_init_struct);gpio_init_struct.Mode = GPIO_MODE_INPUT; // ECHO为输入gpio_init_struct.Pin = ECHO_PIN;gpio_init_struct.Pull = GPIO_PULLUP;gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(ECHO_PORT,&gpio_init_struct);}// 超声波模块初始化
void hcsr04_init(void)
{tim2_init(); // 初始化定时器2hcsr04_gpio_init(); // 初始化GPIO
}// 获取距离(单位:mm)
float hcsr04_get_length(void)
{uint16_t total_time = 0 ;float distance = 0.0f;TRIG_HIHG; // 发送触发信号delay_us(15); // 延时15usTRIG_LOW; // 关闭触发信号while(ECHO_STATUS() == GPIO_PIN_RESET); // 等待ECHO变高tim2_start(); // 启动定时器tim2_set_cnt(0); // 清零计数器while(ECHO_STATUS() == GPIO_PIN_SET); // 等待ECHO变低tim2_stop(); // 停止定时器total_time = tim2_get_cnt(); // 获取计数值distance = total_time * 0.01715; // 计算距离(单位mm)return distance;
}```- **hcsr04.h**```c
#ifndef __HCSR04_H__
#define __HCSR04_H__// 包含系统头文件
#include "sys.h"// 定义TRIG端口的GPIO组
#define TRIG_PORT GPIOB
// 定义TRIG端口的GPIO引脚
#define TRIG_PIN GPIO_PIN_6
// 启用TRIG端口的GPIO时钟
#define TRIG_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE();
// 将TRIG引脚设置为高电平
#define TRIG_HIHG HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_SET)
// 将TRIG引脚设置为低电平
#define TRIG_LOW HAL_GPIO_WritePin(TRIG_PORT, TRIG_PIN, GPIO_PIN_RESET)// 定义ECHO端口的GPIO组
#define ECHO_PORT GPIOB
// 定义ECHO端口的GPIO引脚
#define ECHO_PIN GPIO_PIN_7
// 启用ECHO端口的GPIO时钟
#define ECHO_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE();
// 读取ECHO引脚的电平状态
#define ECHO_STATUS() HAL_GPIO_ReadPin(ECHO_PORT, ECHO_PIN)// 初始化HCSR04超声波传感器的函数声明
void hcsr04_init(void);
// 获取HCSR04超声波传感器测量的距离的函数声明
float hcsr04_get_length(void);#endif```- **led.c**```c
#include "led.h"void led_init(void)
{// 定义一个GPIO初始化结构体GPIO_InitTypeDef gpio_init_struct ;// 使能GPIOB时钟__HAL_RCC_GPIOB_CLK_ENABLE();// 配置GPIO模式为输出推挽模式gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;// 配置GPIO引脚为第8和第9引脚gpio_init_struct.Pin = GPIO_PIN_8|GPIO_PIN_9;// 配置GPIO上拉gpio_init_struct.Pull = GPIO_PULLUP;// 配置GPIO速度为高速gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;// 初始化GPIOBHAL_GPIO_Init(GPIOB,&gpio_init_struct);// 关闭LED1led1_off();// 关闭LED2led2_off();
}void led1_on(void)
{// 将GPIOB的第8引脚设置为低电平,从而点亮LEDHAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
}void led1_off(void)
{// 将GPIOB的第8个引脚设置为高电平,以关闭LED1HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
}void led1_toggle(void)
{// 切换GPIOB端口上的第8个引脚的状态HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);
}void led2_on(void)
{// 将GPIOB的GPIO_PIN_9引脚设置为低电平,以点亮LED2HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);
}void led2_off(void)
{// 关闭LED2HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);
}void led2_toggle(void)
{// 切换GPIOB的第8号引脚的状态HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
}```- **beep.c**```c
#include "beep.h" // 包含蜂鸣器头文件// 蜂鸣器初始化函数
void beep_init(void)
{GPIO_InitTypeDef gpio_init_struct ; // 定义GPIO初始化结构体__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式gpio_init_struct.Pin = GPIO_PIN_5 ; // 选择引脚5gpio_init_struct.Pull = GPIO_PULLUP; // 上拉gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速HAL_GPIO_Init(GPIOB,&gpio_init_struct); // 初始化GPIOB.5beep_off(); // 默认关闭蜂鸣器}// 打开蜂鸣器
void beep_on(void)
{HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_RESET); // 输出低电平,蜂鸣器响
}// 关闭蜂鸣器
void beep_off(void)
{HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET); // 输出高电平,蜂鸣器
}```- **main.c**```c
/* * 智能垃圾桶控制系统 - 头文件包含* sys.h: 系统初始化相关* uart.h: 串口通信* delay.h: 延时函数* led.h: LED控制* beep.h: 蜂鸣器控制* exti.h: 外部中断* key.h: 按键处理* sg90.h: SG90舵机控制* hcsr04.h: HC-SR04超声波测距* stdio.h: 标准输入输出*/
#include "sys.h"
#include "uart.h"
#include "delay.h"
#include "led.h"
#include "beep.h"
#include "exti.h"
#include "key.h"
#include "sg90.h"
#include "hcsr04.h"
#include "stdio.h"#define OPEN 1
#define CLOSE 0
uint8_t ashbin_flag = CLOSE;//添加标志位是防止一直触发(比如人一直在垃圾桶附近)void open_ashbin(void)
{if(ashbin_flag == CLOSE){sg90_angle_set(90);led1_on();beep_on();delay_ms(200);led1_off();beep_off();ashbin_flag =OPEN;}}void close_ashbin(void)
{sg90_angle_set(0);led1_off();beep_off();ashbin_flag =CLOSE;
}int main(void)
{HAL_Init(); /* 初始化HAL库 */stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */led_init(); // 初始化LEDbeep_init(); // 初始化蜂鸣器exti_init(); // 初始化外部中断key_init(); // 初始化按键sg90_init(); // 初始化舵机hcsr04_init(); // 初始化超声波测距模块uart1_init(115200);// 初始化串口1,波特率115200printf("Running....\n"); // 打印运行信息uint8_t key_num = 0; // 按键值变量while(1){ key_num = key_scan(); // 扫描按键// 如果距离小于5cm,或检测到震动,或按下按键1,则开盖if(hcsr04_get_length() < 5 || Vibration_flag_get()==TRUE || key_num == 1){open_ashbin(); // 打开垃圾桶盖printf("距离为%.2fmm\n",hcsr04_get_length()); // 打印距离delay_ms(1000); // 延时1秒Vibration_flag_set(FALSE); // 清除震动标志}else{close_ashbin(); // 关闭垃圾桶盖}delay_ms(10); // 主循环延时10ms}
}/**** _ooOoo_* o8888888o* 88" . "88* (| -_- |)* O\ = /O* ____/`---'\____* . ' \\| |// `.* / \\||| : |||// \* / _||||| -:- |||||- \* | | \\\ - /// | |* | \_| ''\---/'' | |* \ .-\__ `-` ___/-. /* ___`. .' /--.--\ `. . __* ."" '< `.___\_<|>_/___.' >'"".* | | : `- \`.;`\ _ /`;.`/ - ` : | |* \ \ `-. \_ __\ /__ _/ .-` / /* ======`-.____`-.___\_____/___.-`____.-'======* `=---='** .............................................* 佛祖保佑 永无BUG*/```