目录
说明:前面已经做过两版垃圾桶(arduino板和树莓派),现在用51来做v3.0版。对比前面,arduino和树莓派都提供了可调用的API,方便且快速开发。在v3.0的51版本,可以说更加底层和接近原理,是作为学习51基础知识(定时器、中断等)不错的小项目。
一、项目概述
1.功能描述
🔖检测靠近时,垃圾桶自动开盖并伴随滴一声,2秒后关盖
🔖发生震动时,垃圾桶自动开盖并伴随滴一声,2秒后关盖
🔖按下按键时,垃圾桶自动开盖并伴随滴一声,2秒后关盖
2.所需硬件
🔖SG90舵机
🔖HC-SR04超声波模块
🔖震动传感器、蜂鸣器
🔖433M射频模块等
3.接线说明
需要注意的是,接入震动传感器要使用到外部中断,需要接到特定的引脚(P3^2、P3^3、P4^2)
二、开发步骤
1.SG90和HC-SR04代码整合
这里要做的是整合前面第三篇的代码(第三篇 SG90舵机和HC-SR04测距)
需要修改的是:(SG90用定时器0、HC-SR04用定时器1)
2.添加按键控制(433M)和开盖滴滴声(蜂鸣器)
普通I/O口的开发,较简单略
3.添加震动控制(外部中断配合)
说明:在前面的代码中,已经使用到了定时器中断,这里添加震动传感器来实现震动开盖的功能,也是为了学习一下外部中断。在此处,如果不使用外部中断,由于震动传感器检测到震动时的高电平不稳定,可能因为CPU在执行软件延时的时候难以被捕获,无法实现开盖或造成响应不灵敏的问题。
🔖再识中断 - 外部中断
①外部中断触发
📓通过阅读芯片手册,我们可以知道外部中断的触发行为有两种:下降沿、低电平
📓从原理图可以知道,能触发外部中断的引脚有P3^2、P3^3、P4^2
②外部中断相关寄存器
(前面篇已经稍详细介绍如何使用中断(第二篇 定时器和定时器中断)此处省略)
③中断优先级
三、参考代码(v1.0)
#include "reg52.h"
#include <intrins.h>
sbit Trig = P1^0;
sbit Echo = P1^1;
sbit buzzer = P1^2;
sbit servo = P1^3;
sbit removeCtrl = P1^4;
sbit vibrator = P3^2;
sbit led1 = P3^6;
sbit key1 = P2^1;
char cnt = 0;
char angle = 1;
char vibrator_mark = 0; //震动传感器标志位
/*********软件延时10us(启动HC-SR04用)******/
void Delay10us(){
unsigned char i;
i = 2;
while (--i);
}
/**********软件延时1.5s***********/
void Delay1500ms(){
unsigned char i, j, k;
_nop_();
i = 11;
j = 130;
k = 111;
do{
do{
while (--k);
} while (--j);
} while (--i);
}
/**********软件延时0.3s(蜂鸣器用)***********/
void Delay300ms(){
unsigned char i, j, k;
_nop_();
i = 3;
j = 26;
k = 223;
do{
do{
while (--k);
} while (--j);
} while (--i);
}
/**********定时器1初始化(测距用)***********/
void time1Init(){
TMOD |= 0x01<<4;
TMOD &= ~(0x01<<5);
TL1 = 0;
TH1 = 0;
}
/**********定时器0初始化(模拟PWM用)***********/
void time0Init(){
TMOD |= 0x01<<0;
TMOD &= ~(0x01<<1);
TL0 = 0x33; //设置定时初始值
TH0 = 0xFE; //设置定时初始值
TR0 = 1; //定时器0开始计时
TF0 = 0; //清除TF0标志
ET0 = 1; //定时器中断
EA = 1; //总中断
}
/*********外部中断0初始化**********/
void ext0Init(){
IE |= 0x01<<0;
IT0 = 0;
}
/*********向Trig发送一个10us的脉冲信号************/
void startHC(){
Trig = 0;
Trig = 1;
Delay10us();
Trig = 0;
}
/********测距实现***********/
double getDistance(){
double distance;
startHC();
while(Echo != 1); //echo非高电平时,循环等待 -- 等待波开始发送
TCON |= 0x1<<6; //执行到这里说明echo为高电平,波开始发送 -- 开启定时器
while(Echo != 0); //echo非低电平时,循环等待 -- 等待波的返回
TCON &= ~(0x1<<6); //到这里说明波已经返回
distance = (((TH1<<8) + TL1) * 1.085)*0.034;
TH1 = 0; //重新从0计数
TL1 = 0;
return distance; //定时器得到时间,计算得到距离(已知速度340m/s)
}
/***********各模块初始化*************/
void moduleInit(){
led1 = 1;
buzzer = 1;
servo = 1;
vibrator = 1;
}
void main(void){
double distance;
Delay300ms();
time1Init();
time0Init();
ext0Init();
moduleInit();
while(1){
distance = getDistance();
if((distance/2) < 10 || key1 == 0 || removeCtrl == 1 || vibrator_mark == 1){
led1 = 0;
angle = 4;
buzzer = 0;
Delay300ms();
buzzer = 1;
Delay1500ms();
vibrator_mark = 0;
}else{
buzzer = 1;
led1 = 1;
angle = 1;
}
}
}
/***********定时器0中断处理************/
void timeInterruptHandler() interrupt 1{
cnt++;
TL0 = 0x33; //重新设置定时初始值
TH0 = 0xFE; //重新设置定时初始值
if(cnt < angle){ //若此时angle=4(转至135°),cnt=0时拉高电平到cnt=4时不满足拉低,即获得2ms的高电平
servo = 1; //若此时angle=1(转至0°),cnt=0时拉高到cnt=1时不满足拉低,即获得0.5ms的高电平
}else{
servo = 0;
}
if(cnt == 40){ //PWM的周期20ms
cnt = 0;
servo = 1;
}
}
/***********外部中断0处理函数**********/
void ext0InterruptHandler() interrupt 0{
vibrator_mark = 1;
}
🔖BUG处理
以上参考代码会出现小bug,当超声波测距持续检测到距离小于10,条件恒成立,一直执行开盖部分的代码,舵机会出现一抖一抖的现象,需要进行处理。(具体修改略)