感应开关盖垃圾桶项目(三)

PWM控制SG90舵机

PWM可以对一系列脉冲的宽度进行调制,等效出所需要的波形
在这里插入图片描述
占空比为一个周期内,高电平占据时长的百分比。上图中一个周期为4ms,而高电平占据1ms因此占空比为25%

如何实现PWM信号输出

1.对于自带PWM口的芯片来说,可以直接通过芯片内部模块输出
2.如果没有集成PWM功能,可以通过IO口软件模拟相对硬件PWM来说精准度略低。即用软件代码结合定时器来实现波形的变换,来模拟PWM信号。

如何控制舵机

在这里插入图片描述
定时器需要定时20ms,最小使用单位为0.5ms,因此将初值设置为0.5ms,利用cnt记录爆表次数

编程实现

根据波形图调整角度变量jd即可

#include "reg52.h"
#include <intrins.h>
 
sbit sg90_con = P1^1;

int jd;
int cnt = 0;//用来记录爆表的次数

void Time0Init()
{
	//1.配置定时器0工作模式为16位计时
	//TMOD = 0x01;
	TMOD &= 0xF0;	//将低四位清零,且不改变高四位的值
	TMOD |= 0x01;	//将低四位修改,且不改变高四位的值
	//2.给初值,定一个0.5ms出来
	TL0 = 0x33;
	TH0 = 0xFE;
	//3.开始计时
	TR0 = 1;
	TF0 = 0;
	//4.打开定时器0中断
	ET0 = 1;
	//5.打开总中断EA
	EA = 1;
}

void Delay300ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 3;
	j = 26;
	k = 223;
	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 main()
{
	Delay300ms();//让硬件稳定一下
	Time0Init();//初始化定时器

	jd = 1;//初始化角度,0.5ms高电平
	cnt = 0;
	sg90_con = 1;//一开始从高电平开始
	//4.爆表了,操作led吗,累计到1s再操作led,每次爆表变量+1,变量到100再操作led
	while(1){
		jd = 1;//0°根据波形图来看
		cnt = 0;
		Delay2000ms();
		jd = 4;//135°
		cnt = 0;
		Delay2000ms();
	}
}
 
void Time0Handler() interrupt 1 //爆表了就进入该中断服务程序
{
	cnt++;
	//重新给初值
	TL0 = 0x33;
	TH0 = 0xFE;
	if(cnt < jd){//cnt=1的时候爆表1次,经过0.5ms
		sg90_con = 1;
	}else{
		sg90_con = 0;
	}		
	sg90_con = 1;
	if(cnt == 40){//爆表了40次经过了20ms
		cnt = 0;
		sg90_con = 1;	
	}	
}

超声波测距模块

型号:HC-SR04
超声波模块除了VCC和GND两个接口之外,还有TRIG和ECHO引脚,测距原理就是让波发出去接收回来,统计时间得出距离。

那么如何使波发出去呢?

给Trig口至少10微秒的高电平

怎么知道波开始发送了

Echo信号由低电平跳转到高电平,表示开始发送波

怎么知道波回来了

Echo引脚由高电平跳转回低电平 表示波回来了

怎么算时间

Echo引脚维持高电平的时间
波出去的那一刻,开始启动定时器
波回来的那一刻,停止定时器,计算中间的时间

怎么算距离

距离 = 速度(340m/s) * 时间 / 2
超声波时序图如下:
在这里插入图片描述
利用距离控制小灯和蜂鸣器代码如下:

#include "reg52.h"
#include <intrins.h>
 
sbit D5 = P3^7;
sbit D6 = P3^6;
sbit Trig = P1^5;
sbit Echo = P1^6;
sbit sounder = P1^0;

void Delay10us()		//@11.0592MHz
{
	unsigned char i;

	i = 2;
	while (--i);
}

void time0Init(){
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TH0 = 0;
	TL0 = 0;	//设置定时器0工作模式1.初值设定0开始数数,不着急启动定时器
}

void startHC(){
	Trig = 0;
	Trig = 1;
	Delay10us();
	Trig = 0;
}

void main()
{
	double time;
	double dis;
	time0Init();
	while(1){
		//1.给Trig一个10微秒的脉冲
		startHC();
		//2.Echo信号由低电平跳转到高电平,表示开始发送波
		while(Echo == 0);
		//3.波出去的那一刻,开始启动定时器
		TR0 = 1;
		//4.Echo引脚由高电平跳转回低电平 表示波回来了
		while(Echo == 1);
		//5.波回来的那一刻,停止定时器
		TR0 = 0;
		//6.计算中间的时间
		time = (TH0 * 256 + TL0)*1.085;	//以us为单位。将TL0和TH0拼接起来,即将TH0左移8位(即*256)
		//7.计算距离
		dis = 0.034 * time / 2;
		if(dis < 10){
			sounder = 0;
			D5 = 0;
			D6 = 1;
		}else{
			sounder = 1;
			D5 = 1;
			D6 = 0;
		}
		//定时器数据清零,以便下一次测距
		TH0 = 0;
		TL0 = 0;
	}
}

感应开关盖垃圾桶

因为舵机和超声波模块会同时运行,我们可以分别用两个定时器来管理这两个硬件。
使舵机使用定时器0,超声波使用定时器2.
因此舵机的代码无需修改,只需要修改超声波的定时器初始化代码,定时器1采用高4位,因此TMOD要进行修改,同时修改TR0位TR1即可,代码如下:

#include "reg52.h"
#include <intrins.h>
 
sbit D5 = P3^7;
sbit D6 = P3^6;
sbit Trig = P1^5;
sbit Echo = P1^6;
sbit sounder = P1^0;

void Delay10us()		//@11.0592MHz
{
	unsigned char i;

	i = 2;
	while (--i);
}

void time0Init(){
	TMOD &= 0x0F;		//设置定时器模式
	TMOD |= 0x10;		//设置定时器模式
	TH1 = 0;
	TL1 = 0;	//设置定时器0工作模式1.初值设定0开始数数,不着急启动定时器
}

void startHC(){
	Trig = 0;
	Trig = 1;
	Delay10us();
	Trig = 0;
}

void main()
{
	double time;
	double dis;
	time0Init();
	while(1){
		//1.给Trig一个10微秒的脉冲
		startHC();
		//2.Echo信号由低电平跳转到高电平,表示开始发送波
		while(Echo == 0);
		//3.波出去的那一刻,开始启动定时器
		TR1 = 1;
		//4.Echo引脚由高电平跳转回低电平 表示波回来了
		while(Echo == 1);
		//5.波回来的那一刻,停止定时器
		TR1 = 0;
		//6.计算中间的时间
		time = (TH1 * 256 + TL1)*1.085;	//以us为单位。将TL0和TH0拼接起来,即将TH0左移8位(即*256)
		//7.计算距离
		dis = 0.034 * time / 2;
		if(dis < 10){
			sounder = 0;
			D5 = 0;
			D6 = 1;
		}else{
			sounder = 1;
			D5 = 1;
			D6 = 0;
		}
		//定时器数据清零,以便下一次测距
		TH1 = 0;
		TL1 = 0;
	}
}

另外可以将超声波模块进行封装使main函数更加简洁

double get_distance(){
	double time;
	//定时器数据清零,以便下一次测距
	TH1 = 0;
	TL1 = 0;
	//1.给Trig一个10微秒的脉冲
	startHC();
	//2.Echo信号由低电平跳转到高电平,表示开始发送波
	while(Echo == 0);
	//3.波出去的那一刻,开始启动定时器
	TR1 = 1;
	//4.Echo引脚由高电平跳转回低电平 表示波回来了
	while(Echo == 1);
	//5.波回来的那一刻,停止定时器
	TR1 = 0;
	//6.计算中间的时间
	time = (TH1 * 256 + TL1)*1.085;	//以us为单位。将TL0和TH0拼接起来,即将TH0左移8位(即*256)
	//7.计算距离
	return (0.034 * time / 2);
}

将舵机和超声波模块合并,并对功能代码进行封装后函数如下:

#include "reg52.h"
#include <intrins.h>
 
sbit D5       = P3^7;
sbit D6       = P3^6;
sbit sounder  = P1^0;

sbit sg90_con = P1^1;

sbit Trig     = P1^5;
sbit Echo     = P1^6;

int jd;
int cnt = 0;//用来记录爆表的次数

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 Delay10us()		//@11.0592MHz
{
	unsigned char i;

	i = 2;
	while (--i);
}

void time0Init()
{
	//1.配置定时器0工作模式为16位计时
	//TMOD = 0x01;
	TMOD &= 0xF0;	//将低四位清零,且不改变高四位的值
	TMOD |= 0x01;	//将低四位修改,且不改变高四位的值
	//2.给初值,定一个0.5ms出来
	TL0 = 0x33;
	TH0 = 0xFE;
	//3.开始计时
	TR0 = 1;
	TF0 = 0;
	//4.打开定时器0中断
	ET0 = 1;
	//5.打开总中断EA
	EA = 1;
}

void time1Init(){
	TMOD &= 0x0F;		//设置定时器模式
	TMOD |= 0x10;		//设置定时器模式
	TH1 = 0;
	TL1 = 0;	//设置定时器0工作模式1.初值设定0开始数数,不着急启动定时器
}

void startHC(){
	Trig = 0;
	Trig = 1;
	Delay10us();
	Trig = 0;
}

double get_distance(){
	double time;
	//定时器数据清零,以便下一次测距
	TH1 = 0;
	TL1 = 0;
	//1.给Trig一个10微秒的脉冲
	startHC();
	//2.Echo信号由低电平跳转到高电平,表示开始发送波
	while(Echo == 0);
	//3.波出去的那一刻,开始启动定时器
	TR1 = 1;
	//4.Echo引脚由高电平跳转回低电平 表示波回来了
	while(Echo == 1);
	//5.波回来的那一刻,停止定时器
	TR1 = 0;
	//6.计算中间的时间
	time = (TH1 * 256 + TL1)*1.085;	//以us为单位。将TL0和TH0拼接起来,即将TH0左移8位(即*256)
	//7.计算距离
	return (0.034 * time / 2);
}

void openStatusLight(){
	sounder = 0;
	D5 = 0;
	D6 = 1;
}
void closeStatusLight(){
	sounder = 1;
	D5 = 1;
	D6 = 0;
}

void initSG90_0()
{
	jd = 1;//初始化角度,0.5ms高电平
	cnt = 0;
	sg90_con = 1;//一开始从高电平开始
}

void openTrashbin()
{
	//舵机开盖
	jd = 3;//90°
	cnt = 0;
	Delay2000ms();
}

void cloaseTrashbin()
{
	//舵机关盖
	jd = 1;//0°
	cnt = 0;
	Delay2000ms();
}

void main()
{
	double dis;
	
	time0Init();
	time1Init();
	initSG90_0();
	
	while(1){
		//超声波测距
		dis = get_distance();
		if(dis < 10){
			//灯和蜂鸣器的状态
			openStatusLight();
			openTrashbin();
		}else{
			closeStatusLight();
			cloaseTrashbin();
		}
	}
}

void Time0Handler() interrupt 1 //爆表了就进入该中断服务程序
{
	cnt++;
	//重新给初值
	TL0 = 0x33;
	TH0 = 0xFE;
	if(cnt < jd){//cnt=1的时候爆表1次,经过0.5ms
		sg90_con = 1;
	}else{
		sg90_con = 0;
	}		
	sg90_con = 1;
	if(cnt == 40){//爆表了40次经过了20ms
		cnt = 0;
		sg90_con = 1;	
	}	
}

添加按键开关盖功能

只需修改开盖条件即可if(dis < 10 || SW1 == 0)

添加震动控制

如果像添加按键一样修改开盖条件位if(dis < 10 || SW1 == 0 || vibrate == 0)会发现效果不好。原因是功能代码处于while循环之中,不断的循环,并且大多数情况是超声波测得距离大于10cm,会出发关盖操作中的delay函数导致延时,此时震动可能不会被响应,导致用户体验不好。因此我们可以添加外部中断来增加震动控制。
在这里插入图片描述
查表得外部中断0 对应中断号为interrupt 0;
在这里插入图片描述
另外外部中断0对应阵脚3.2口,因此我们将震动模块接入3.2口
在这里插入图片描述
使用外部中断时要将EX0和EA打开,EX0=1时表示允许外部中断,EX0=0禁止中断
在这里插入图片描述
查中断触发表得外部中断零,可以通过下降沿来触发,也可以通过低电平来触发。我们这里震动传感器检测到震动时是低电平因此可以低电平触发。设置IT0=0启动低电平触发条件。
整体代码:

#include "reg52.h"
#include <intrins.h>
 
sbit D5       = P3^7;
sbit D6       = P3^6;
sbit sounder  = P1^0;

sbit sg90_con = P1^1;

sbit Trig     = P1^5;
sbit Echo     = P1^6;

sbit SW1      = P2^1;
sbit vibrate  = P3^2;

int jd;
int cnt = 0;//用来记录爆表的次数
int mark_vibrate = 0;

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 Delay10us()		//@11.0592MHz
{
	unsigned char i;

	i = 2;
	while (--i);
}

void time0Init()
{
	//1.配置定时器0工作模式为16位计时
	//TMOD = 0x01;
	TMOD &= 0xF0;	//将低四位清零,且不改变高四位的值
	TMOD |= 0x01;	//将低四位修改,且不改变高四位的值
	//2.给初值,定一个0.5ms出来
	TL0 = 0x33;
	TH0 = 0xFE;
	//3.开始计时
	TR0 = 1;
	TF0 = 0;
	//4.打开定时器0中断
	ET0 = 1;
	//5.打开总中断EA
	EA = 1;
}

void time1Init(){
	TMOD &= 0x0F;		//设置定时器模式
	TMOD |= 0x10;		//设置定时器模式
	TH1 = 0;
	TL1 = 0;	//设置定时器0工作模式1.初值设定0开始数数,不着急启动定时器
}

void startHC(){
	Trig = 0;
	Trig = 1;
	Delay10us();
	Trig = 0;
}

double get_distance(){
	double time;
	//定时器数据清零,以便下一次测距
	TH1 = 0;
	TL1 = 0;
	//1.给Trig一个10微秒的脉冲
	startHC();
	//2.Echo信号由低电平跳转到高电平,表示开始发送波
	while(Echo == 0);
	//3.波出去的那一刻,开始启动定时器
	TR1 = 1;
	//4.Echo引脚由高电平跳转回低电平 表示波回来了
	while(Echo == 1);
	//5.波回来的那一刻,停止定时器
	TR1 = 0;
	//6.计算中间的时间
	time = (TH1 * 256 + TL1)*1.085;	//以us为单位。将TL0和TH0拼接起来,即将TH0左移8位(即*256)
	//7.计算距离
	return (0.034 * time / 2);
}

void openStatusLight(){
	sounder = 0;
	D5 = 0;
	D6 = 1;
}
void closeStatusLight(){
	sounder = 1;
	D5 = 1;
	D6 = 0;
}

void initSG90_0()
{
	jd = 1;//初始化角度,0.5ms高电平
	cnt = 0;
	sg90_con = 1;//一开始从高电平开始
}

void openTrashbin()
{
	//舵机开盖
	jd = 3;//90°
	cnt = 0;
	Delay2000ms();
}

void cloaseTrashbin()
{
	//舵机关盖
	jd = 1;//0°
	cnt = 0;
	Delay2000ms();
}

void EX0_Init()
{
	//打开外部中断
	EX0 = 1;
	//查中断触发表,设置低电平触发
	IT0 = 0;
}

void main()
{
	double dis;
	
	time0Init();
	time1Init();
	
	EX0_Init();
	
	initSG90_0();
	
	while(1){
		//超声波测距
		dis = get_distance();
		if(dis < 10 || SW1 == 0 || mark_vibrate == 1){
			//灯和蜂鸣器的状态
			openStatusLight();
			openTrashbin();
			mark_vibrate = 0;
		}else{
			closeStatusLight();
			cloaseTrashbin();
		}
	}
}

void Time0Handler() interrupt 1 //爆表了就进入该中断服务程序
{
	cnt++;
	//重新给初值
	TL0 = 0x33;
	TH0 = 0xFE;
	if(cnt < jd){//cnt=1的时候爆表1次,经过0.5ms
		sg90_con = 1;
	}else{
		sg90_con = 0;
	}		
	sg90_con = 1;
	if(cnt == 40){//爆表了40次经过了20ms
		cnt = 0;
		sg90_con = 1;	
	}	
}

void EX0_Handler() interrupt0
{
	mark_vibrate  = 1;
}

至此51感应垃圾桶项目完结。

  • 27
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值