1、项目概述
(1) 功能描述
检测靠近时,垃圾桶自动开盖并伴随滴一声,2秒后关盖
发生震动时,垃圾桶自动开盖并伴随滴一声,2 秒后关盖按下按键时,垃圾桶自动开盖并伴随滴一声,2秒后关盖
(2)硬件说明
SG90舵机,超声波模块,震动传感器,蜂鸣器
(3)接线说明
舵机控制口 P1.1 ;超声波 Trig 接 P1.5 ,Echo 接 P1.6 ;蜂鸣器接 P2.0 口 ; 震动传感器接 P3.2 口 ( 外部中断0)
2、 编程实现
(1) 舵机和超声波代码整合
舵机用定时器0,超声波用定时器1,实现物靠近后开盖,两秒后自动关盖
#include "reg52.h"
#include <intrins.h>
sfr AUXR = 0x8e; /*声明AUXR寄存器的地址*/
//距离小于10cm,led5里亮,led6灭,反之现象相反
sbit led5 = P3^7;
sbit led6 = P3^6;
sbit Trig = P1^5;
sbit Echo = P1^6;
sbit sg_90 = P1^1;
int angle = 1;
int cnt = 0;
void Delay10us() //@11.0592MHz
{
unsigned char i;
i = 2;
while (--i);
}
void Delay200ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 2;
j = 103;
k = 147;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay2000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 15;
j = 2;
k = 235;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Trig_HC_SR04()
{
Trig = 0;
Trig = 1;
Delay10us();
Trig = 0;
}
void Time0Init(void) //0.5毫秒@11.0592MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01;
TL0 = 0x33; //设置定时初值
TH0 = 0xFE; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //打开定时器中断
EA = 1;
}
void Time1Init()
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x10; //设置定时器模式
TH1 = 0;
TL1 = 0;
}
double get_distance()
{
double time;
TH1 = 0;
TL1 = 0;
// 1.Trig,给Trig端口至少10us的高电平
Delay200ms();
Trig_HC_SR04();
//2.Echo由低电平跳转到高电平,表示开始发送波
while(Echo == 0);
//波出去的那一下子开启定时器
TR1 = 1;
//3.Echo由高电平到低电平,表示波回来了
while(Echo == 1);
//波回来的那一下,开始关闭定时器
TR1 = 0;
//4.计算出中间经过多长时间
time = ( TH1 * 256 + TL1 )* 1.085;
//5.距离=速度*时间; 340m/s=34000cm/s=34cm/ms=0.034cm/us
return (0.017*time);
}
void openStatuesLight()
{
led5 = 0;
led6 = 1;
}
void closeStatusLight()
{
led5 = 1;
led6 = 0;
}
void openDustbin()
{
angle = 3; //转90度
cnt = 0;
Delay2000ms();
}
void closeDustbin()
{
angle = 1 ;//转0度
cnt = 0;
Delay200ms();
}
void main()
{
double dis;
led5 = 1;
led6 = 1;
Time1Init();
Time0Init();
while(1){
dis = get_distance();
if(dis <10){ //距离判断,小于10cm,开盖,led5亮
openStatuesLight();//led5亮
openDustbin();//开盖
}
else{ //距离超过10cm,关盖,led6亮
closeStatusLight();
closeDustbin();
}
}
}
//执行中断处理函数
void Time0Handler() interrupt 1
{
cnt++;//统计爆表的次数
TL0 = 0x33; //重新设置定时值
TH0 = 0xFE;
if( cnt < angle){
sg_90 = 1;
}
else{
sg_90 = 0;
}
if(cnt == 40){
cnt = 0;//当100次表示1s,cnt重新从0开始计算下一次的一秒
sg_90 = 1;
}
}
(2)添加按键方式控制垃圾桶
增加按键的引脚配置并修改主函数中对于开关垃圾桶的判断,这里按键直接通过轮询方式判断
sbit Sw1 = P2^1; if(dis <10 || Sw1 == 0){ openStatuesLight();//led5亮 openDustbin();//开盖 mark_vibrate = 0; }
(3) 添加震动控制垃圾桶
前面做电动车报警器中我们已经用到过震动传感器,这里我们就不再说明了
sbit Vibrate = P3^2;
if(dis <10 || Sw1 == 0 || Vibrate == 0){//距离判断,小于10cm,开盖,led5亮
openStatuesLight();//led5亮
openDustbin();//开盖
mark_vibrate = 0;
}
<1> 查询方式添加震动传感器的弊端
需要注意的是,当我们把震动模块配置好后,烧入程序中执行发现,当震动发生时,垃圾桶开关盖反应很是迟钝。 原因是 我们在当条件成立或不成立时,调用了开关盖函数,实际上也就是控制舵机转动的函数,这个函数里面又调用了延迟函数,导致cpu在执行时延迟函数时,不能同时判断是否有其他影响因素,这里解决办法是通过设置外部中断来判断震动传感器。
那么为什么按键判断就不需要外部中断呢,这是因为按键的信号输入比较的稳定,cpu在执行延时函数后还能捕获到。但是振动传感器传来的信号是不稳定的,cpu不一定能捕获的到,所以我们采用中断方式
<2> 采用外部中断方式添加震动传感器
这里我们就可以设置一个标志位,专门用来判断是否产生了振动。程序开始时我们设置标识mark = 0;当有振动时,执行中断函数,将mark = 0改为mark = 1;一旦关盖后,又将 mark 设置为0。
char angle = 1; char angle_bak = 1; char cnt = 0; char mark_vibrate = 0; //主函数内部修改条件判断 if(dis <10 || Sw1 == 0 || mark_vibrate == 1){//距离判断,小于10cm,开盖,led5亮 openStatuesLight();//led5亮 openDustbin();//开盖 mark_vibrate = 0; } //外部中断处理函数 void Exit0_Handler() interrupt 0 { mark_vibrate = 1; }
(4) 添加蜂鸣器
这里为了使得我们的功能更多我们可以再添加一个蜂鸣器,当垃圾桶打打开时响一会儿
sbit Beep = P2^0; void openDustbin() { angle = 3; //转90度 cnt = 0; Beep = 0; for(i=0;i<2;i++) Delay150ms(); Beep = 1; Delay2000ms(); }
到这里我们的项目就完成的差不多了,可以开始搭建硬件了
3、硬件搭建
用塑料胶袋简单粘了下,这些模块以后会用到,就不用热熔胶了。
4、存在的问题
搭建好了硬件然后我们将代码烧录进去后又发现马,当手持续放在感应开关前时,垃圾桶盖子会上下抖动,其实是舵机在抖动。原因是当我们将手放在10cm的范围内时,垃圾桶不停的判断,开盖函数不断调用,里面的cnt也跟着清零,导致垃圾桶仿佛抽搐了一般,所以这里我们修改一下,增加一个变量,表示上一次判断时角度值 angle_bak,每次开盖时判断一下,要是不相等,再将cnt清零,将angle赋给angle_bak,同时改变angle。代码如下:
这里为了节省空间,我们将变量设置为char 型
( 因为 在C51的编译环境里,char就是一个范围小一些的int ,一般都用unsigned char 因为这种变量在C51中刚好满足最大为0XFF,就是255,可以方便通常的运算,单片机是八位系统 那么对于int 来说 ,是和char是一样的空间 都是1B 也就是8bits,以上来自网上的解释)
char angle = 1; char angle_bak = 1; char cnt = 0; char mark_vibrate = 0; void openDustbin() { char i; angle = 3; //转90度 if( angle_bak != angle){ cnt = 0; Beep = 0; for(i=0;i<2;i++) Delay150ms(); Beep = 1; Delay2000ms(); } angle_bak = angle; } void closeDustbin() { angle_bak = angle; angle = 1 ;//转0度 cnt = 0; Delay150ms();
这样我们的感应垃圾桶就没有问题啦!