垃圾桶有灵性了!还会自动开盖。#51单片机制作感应开关盖垃圾桶【下】

垃圾桶有灵性了!还会自动开盖。#51单片机制作感应开关盖垃圾桶【下】

    • 前言
    • 1.预备知识
    • 2.sg90舵机基本认知
      • 2.1什么是舵机
      • 2.2怎么控制舵机
    • 3.定时中断方式控制LED灯
      • 3.1程序思路
      • 3.2完整程序代码
    • 4.舵机实战编程
      • 4.1程序思路
      • 4.2 PWM设计波形图
      • 4.3完整程序代码
    • 5.超声波测距传感器认知
      • 5.1手册介绍
        • 5.1.1产品特色
        • 5.1.2接口定义
        • 5.1.3产品使用方法
        • 5.1.4模块工作原理
        • 5.1.5超声波时序图
        • 5.1.6注意事项
      • 5.2课程内容
        • 5.1简介
        • 5.2怎么让它发送波
        • 5.3怎么知道它开始发了
        • 5.4怎么知道接收了返回波
        • 5.5怎么算时间
        • 5.6怎么算距离
    • 6.从零编程实现超声波测距(当距离小于10厘米,D5亮,D6灭。当距离大于10厘米,D5灭,D6亮。)
      • 6.1程序思路
        • 6.1.1 Trig信号 ,给Trig端口至少10us的高电平
        • 6.1.2 Echo信号,由低电平跳转到高电平,表示开始发送波
        • 6.1.3 Echo,由高电平跳转回低电平,表示波回来了
        • 6.1.4距离 = 速度 (340m/s)* 时间/2
        • 6.1.5判断距离小于10厘米,D5亮,D6灭。判断距离大于10厘米,D5灭,D6亮。
        • 6.1.6因为需要重复测距,所以得多次给定时器赋初值
      • 6.2完整程序代码
    • 7.感应开关盖垃圾桶需求设计
      • 7.1 项目概述
        • 7.1.1功能描述
        • 7.1.2硬件说明
        • 7.1.3接线说明
    • 8.垃圾桶`01`修改超声波为定过器一控制
      • 8.1开发步骤
        • 8.1.1舵机用定时器`0` 超声波用定时器`1`
        • 8.1.2修改超声波定时器`T0`为`T1`
        • 8.1.3修改后的程序代码
    • 9.垃圾桶`02`封装超声波测距代码
      • 9.1建立测距函数
      • 8.2建立开盖灯状态函数
      • 8.3建立关盖灯状态函数
      • 8.4修改后的程序代码
    • 10.垃圾桶`03`实现距离感应开盖
      • 10.1程序思路:总思路将舵机控制代码和超声波控制给整合到一个程序中
        • 10.1.1将舵机控制的变量和IO口复制到超声波测距程序代码中
        • 10.1.2将300毫秒,2000毫秒软件延时函数复制到超声波测距程序代码中,并建立一个软件延时150毫秒的函数
        • 10.1.3将定时器T0初始化函数和定时器T0中段函数复制到超声波测距程序代码中相应位置
        • 10.1.4分别建立SG90舵机初始化函数,垃圾桶开关盖函数。置于超声波测距程序的相应位置
        • 10.1.5通过超声波测距,小于10厘米就开盖开灯。大于10厘米就关盖关灯。
        • 10.1.6特别提醒:编译时记得输出HEX文件,不然程序会跑错。
      • 10.2完整程序代码
    • 11.垃圾桶04添加按键开盖功能
      • 11.1程序思路
      • 11.2完整程序代码
    • 12.垃圾桶`05`添加震动开盖功能使用外部中断优化
      • 12.1程序思路
        • 12.1.1直接采用软件扫码震动传感器返回的低电平启动开盖。只需要将震动传感器接入单片机P3.2(`根据单片机手册得知P3.2口为外部中断`)口,并在主函数死循环中添加P3.2口接收到低电平的判断条件。
        • 12.1.2依据单片机手册建立外部中断0的初始化函数
        • 12.1.3依据手册建立外部中断执行函数并建立震动标志变量
      • 12.2完整程序代码
    • 13.垃圾桶06添加开盖滴滴声_项目完结
      • 13.1程序思路
      • 13.2完整程序代码
    • 14.垃圾桶的抽抽BUG解决
      • 14.1程序思路
      • 14.2完整程序代码
    • 结束语

前言

  本篇博文介绍的是用51单片机制作感应开关盖垃圾桶【下】,里面包含sg90舵机基本认知,定时中断方式控制LED灯,舵机实战编程,超声波测距传感器认知,从零编程实现超声波测距(当距离小于10厘米,D5亮,D6灭。当距离大于10厘米,D5灭,D6亮。),感应开关盖垃圾桶需求设计,垃圾桶01修改超声波为定过器一控制,垃圾桶02封装超声波测距代码,垃圾桶03实现距离感应开盖,垃圾桶04添加按键开盖功能,垃圾桶05添加震动开盖功能使用外部中断优化,垃圾桶06添加开盖滴滴声_项目完结,垃圾桶的抽抽BUG解决。看到这篇博文的朋友,可以先赞再看吗?

1.预备知识

  一、数学周期,频率等相关知识。
  二、数学对于时间的计算。
  三、数学对于波形的理解能力

  四、数字电子中与或运算
  五、计算机中的进制转换
  六、C变量
  七、基本输入输出
  八、流程控制
  九、函数

  如果以上知识不清楚,请自行学习后再来浏览。

2.sg90舵机基本认知

2.1什么是舵机

  如下图所示,最便宜的舵机sg90,常用三根或者四根接线黄色为PWM信号线。起控制舵机转动角度作用。控制角度的用处:垃圾桶项目开盖用、智能小车的全比例转向、摄像头云台、机械臂等常见的有0-90°0-180°0-360°
垃圾桶有灵性了!还会自动开盖。#51单片机制作感应开关盖垃圾桶【下】_第1张图片

2.2怎么控制舵机

  一、向黄色信号线“灌入”PWM信号。
  二、PWM波的频率不能太高,大约50HZ,即周期=1/频率=1/50=0.02s20ms左右数据:
  0.5ms-------------0度; 2.5% 对应函数中占空比为250
  1.0ms------------45度; 5.0% 对应函数中占空比为500
  1.5ms------------90度; 7.5% 对应函数中占空比为750
  2.0ms-----------135度; 10.0% 对应函数中占空比为1000
  2.5ms-----------180度12.5% 对应函数中占空比为1250
  三、定时器需要定时20ms, 关心的单位0.5ms, 40个0.5ms,初值0.5ms 记录爆表次数变量cnt++

  1s = 10ms * 100

  20ms = 0.5ms * 40

3.定时中断方式控制LED灯

3.1程序思路

  一、在定时器T0初始化函数中分别打开定时器T0中断ET0和总中断EA。

void initTime0()
{
	//1.设置定时器为T0,16位模式
	TMOD &= 0xF0;
	TMOD |= 0x01;
 
	//2.设置初值,定时10毫秒
	TL0 = 0x00;
	TH0 = 0xDC;
 
	//3.启动定时器,开始计时
	TR0 = 1; //TR0为启动定时器标志,值为1启动定时器
	TF0 = 0;
	//4.打开定时器0的中断 赋1打开
	ET0 = 1;
	//5.打开总中断EA  赋1打开
	EA  = 1;
}

  二、利用定时器T0溢出标志TF0的中断,建立中断函数,设置中断号

void Time0Handler() interrupt 1 //定时器T0中断函数,中断号为interrupt 1

  三、在中断函数中设置记录爆表次数变量cnt自增,重新给定时器赋初值,定时10毫秒,以及判断是否加到100,定时1秒,进行led的状态翻转

void Time0Handler() interrupt 1 //定时器T0中断函数,中断号为interrupt 1
{
	cnt++;
	//重新给定时器赋初值,定时10毫秒
	TL0 = 0x00;
	TH0 = 0xDC;
 
	//判断cnt是否加到100,定时是否1秒
	if(cnt == 100)
	{
	   cnt = 0;    //定时到1秒,cnt从0开始加
	   led = !led; //定时到1秒,led灯状态翻转
	}
}

  四、在主函数中的while死循环中对另一颗灯进行软件延时闪烁

void main()
{
	led = 1;     //给led一个高电平,灯灭。
	initTime0();
 
 
	//4.爆表了,不操作灯,累计1秒才操作。
	//爆表了,变量加一,加了100次等于计时1秒
	while(1)  
	{
		led2 = 1;
		Delay300ms();
		led2 = 0;
		Delay300ms();
	}
}

3.2完整程序代码

#include "reg52.h"
/*
		时间:    2023年10月3日13:31:57
		程序功能:定时中断方式控制LED灯
*/

sbit led  = P3^7;
sbit led2 = P3^6;
int cnt = 0; //记录爆表次数

void initTime0()
{
	//1.设置定时器为T0,16位模式
	TMOD &= 0xF0;
	TMOD |= 0x01;
 
	//2.设置初值,定时10毫秒
	TL0 = 0x00;
	TH0 = 0xDC;
 
	//3.启动定时器,开始计时
	TR0 = 1; //TR0为启动定时器标志,值为1启动定时器
	TF0 = 0;
	//4.打开定时器0的中断 赋1打开
	ET0 = 1;
	//5.打开总中断EA  赋1打开
	EA  = 1;
}

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

	i = 3;
	j = 26;
	k = 223;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}


void main()
{
	led = 1;     //给led一个高电平,灯灭。
	initTime0();
 
 
	//4.爆表了,不操作灯,累计1秒才操作。
	//爆表了,变量加一,加了100次等于计时1秒
	while(1)  
	{
		led2 = 1;
		Delay300ms();
		led2 = 0;
		Delay300ms();
	}
}
void Time0Handler() interrupt 1 //定时器T0中断函数,中断号为interrupt 1
{
	cnt++;
	//重新给定时器赋初值,定时10毫秒
	TL0 = 0x00;
	TH0 = 0xDC;
 
	//判断cnt是否加到100,定时是否1秒
	if(cnt == 100)
	{
	   cnt = 0;    //定时到1秒,cnt从0开始加
		 led = !led; //定时到1秒,led灯状态翻转
	}
}

4.舵机实战编程

4.1程序思路

  一、设计定时器初值为0.5毫秒,也就是从数数开始到溢出经过0.5毫秒
  计算过程
  已知一个机器周期等于1.085微妙

  定时0.5毫秒 = 500微妙
  机器周期 = 500/1.085 = 461

  已知16位定时器为65536个机器周期

  设置初值= 65536-461 =65075 = 0xFE33(16进制)
  高位TH0 = 0xFE;
  低位TL0 = 0x33;

  二、分别定义角度,记录爆表的全局变量。设置sg90舵机的控制线端口为P3.2口

int cnt = 0;     //记录爆表次数
int angle;       //记录舵机角度变量
sbit sg90_con = P3^2; //sg90舵机PWM控制线接单片机P3.2口,用于模拟PWM波形。

  三、修改定时器T0的初始化函数,将定时器初值修改为计算的值。

void initTime0() //初始化定时器T0
{
	//1.设置定时器为T0,16位模式
	TMOD &= 0xF0;
	TMOD |= 0x01;
 
	//2.设置初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
 
	//3.启动定时器,开始计时
	TR0 = 1; //TR0为启动定时器标志,值为1启动定时器
	TF0 = 0;
	//4.打开定时器0的中断 赋1打开
	ET0 = 1;
	//5.打开总中断EA  赋1打开
	EA  = 1;
}

  四、在中断函数中设置记录爆表次数变量cnt自增。重新给定时器赋初值,定时0.5毫秒。判读记录爆表变量cnt的值是否小于角度变量的值,小于继续给sg90舵机控制线接高电平,大于等于则给低电平;使之呈现PWM波形。判断cnt是否加到40,定时是否20毫秒PWM波形周期。

void Time0Handler() interrupt 1 //定时器T0中断函数,中断号为interrupt 1
{
	cnt++;
	//重新给定时器赋初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
 
	//判读记录爆表变量cnt的值是否小于角度变量的值,小于继续给sg90舵机控制线高电平,大于等于则给低电平
	if(cnt < angle)
	{
    	sg90_con = 1;
	}
	else
	{
    	sg90_con = 0;
	}
	//判断cnt是否加到40,定时是否20毫秒PWM波形周期
	if(cnt == 20)
	{
		cnt = 0;     //定时到20豪秒,cnt从0开始加
		sg90_con = 1;//定时到20豪秒,重新给舵机控制线一个高电平
	}
}

  五、在主函数中单片机上电,给sg90舵机控制线一个高电平。给角度控制变量初始化为0度,因为0.5毫秒的高电平PWM波为0度,angle = 1;时定时器刚好定时0.5毫秒每隔2秒软件延时切换舵机角度,1为0度3为90度,因为舵机为齿轮结构,角度会有偏差。

void main()
{
	Delay300ms(); //因为用软件模拟的PWM有误差,所以让硬件等待300毫秒稳定一下。
	initTime0();
	sg90_con = 1; //单片机上电,给sg90舵机控制线一个高电平。
	angle    = 1; //给角度控制变量初始化为0度,因为0.5毫秒的高电平PWM波为0度,angle = 1;时定时器刚好定时0.5毫秒
	while(1)      //每隔2秒软件延时切换舵机角度,1为0度,3为90度,因为舵机为齿轮结构,角度会有偏差
	{
		angle    = 3;
		cnt      = 0;
		Delay2000ms();
		angle    = 1;
	          cnt      = 0;
		Delay2000ms();
	}
}

4.2 PWM设计波形图

垃圾桶有灵性了!还会自动开盖。#51单片机制作感应开关盖垃圾桶【下】_第2张图片

4.3完整程序代码

#include "reg52.h"
/*
		时间:    2023年10月3日14:52:24
		程序功能:舵机实战编程
*/

sbit sg90_con = P3^2; //sg90舵机PWM控制线接单片机P3.2口,用于模拟PWM波形。

int cnt = 0;     //记录爆表次数
int angle;       //记录舵机角度变量

void initTime0() //初始化定时器T0
{
	//1.设置定时器为T0,16位模式
	TMOD &= 0xF0;
	TMOD |= 0x01;
 
	//2.设置初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
 
	//3.启动定时器,开始计时
	TR0 = 1; //TR0为启动定时器标志,值为1启动定时器
	TF0 = 0;
	//4.打开定时器0的中断 赋1打开
	ET0 = 1;
	//5.打开总中断EA  赋1打开
	EA  = 1;
}

void Delay2000ms()		//@11.0592MHz 软件延时2秒
{
	unsigned char i, j, k;

	i = 15;
	j = 2;
	k = 235;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}


void Delay300ms()		//@11.0592MHz  软件延时300毫秒
{
	unsigned char i, j, k;

	i = 3;
	j = 26;
	k = 223;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}


void main()
{
	Delay300ms(); //因为用软件模拟的PWM有误差,所以让硬件等待300毫秒稳定一下。
	initTime0();
	sg90_con = 1; //单片机上电,给sg90舵机控制线一个高电平。
	angle    = 1; //给角度控制变量初始化为0度,因为0.5毫秒的高电平PWM波为0度,angle = 1;时定时器刚好定时0.5毫秒
	while(1)      //每隔2秒软件延时切换舵机角度,1为0度,3为90度,因为舵机为齿轮结构,角度会有偏差
	{
		angle    = 3;
		cnt       = 0;
		Delay2000ms();
		cnt      = 0;
		angle    = 1;
		Delay2000ms();
	}
}

void Time0Handler() interrupt 1 //定时器T0中断函数,中断号为interrupt 1
{
	cnt++;
	//重新给定时器赋初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
 
	//判读记录爆表变量cnt的值是否小于角度变量的值,小于继续给sg90舵机控制线高电平,大于等于则给低电平
	if(cnt < angle)
	{
            sg90_con = 1;
	}
	else
	{
            sg90_con = 0;
	}
	//判断cnt是否加到40,定时是否20毫秒PWM波形周期
	if(cnt == 20)
	{
	   cnt = 0;     //定时到20豪秒,cnt从0开始加
		 sg90_con = 1;//定时到20豪秒,重新给舵机控制线一个高电平
	}
}

5.超声波测距传感器认知

5.1手册介绍

5.1.1产品特色

  1、典型工作用电压:5V
  2、超小静态工作电流:小于 2mA
  3、感应角度:不大于15度 。
  4、探测距离:2cm-400cm
  5、高精度:可达 0.3cm
  6、盲区(2cm)超近。
  7、完全谦容 GH-311 防盗模块。
  8、带金属 USB 外壳,坚固耐用。

5.1.2接口定义

  VccTrig(控制端)、 Echo(接收端)、 Gnd

5.1.3产品使用方法

  控制口发一个 10us 以上的高电平,就可以在接收口等待高电平输出.一有输出就可以开定时器计时,当此口变为低电平时就可以读取定时器的值,此时就为此次测距的时间,方可算出距离.如此不断的周期测,就可以达到你移动测量的值了。

5.1.4模块工作原理

  (1)采用 IO 触发测距,给至少 10us 的高电平信号;

  (2)模块自动发送 840khz 的方波,自动检测是否有信号返回;

  (3)有信号返回,通过 IO 输出一高电平,高电平持续的时间就是

  (4)超声波从发射到返回的时间.测试距离=(高电平时间*声速(340M/S))/2;

5.1.5超声波时序图

垃圾桶有灵性了!还会自动开盖。#51单片机制作感应开关盖垃圾桶【下】_第3张图片

5.1.6注意事项

  1:此模块不宜带电连接,如果要带电连接,则先让模块的 Gnd 端先连接。否则会影响模块工作。
  2:测距时,被测物体的面积不少于 0.5 平方米且要尽量平整。否则会影响测试结果。

5.2课程内容

5.1简介

  型号:HC-SR04

  接线参考:模块除了两个电源引脚外,还有TRIGECHO引脚,这两个引脚分别接我开发板的P1.5P1.6端口

  超声波测距模块是用来测量距离的一种产品,通过发送接收超声波,利用时间差和声音传播速度,计算出模块到前方障碍物的距离。

5.2怎么让它发送波

  Trig ,给Trig端口至少10us的高电平。

5.3怎么知道它开始发了

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

5.4怎么知道接收了返回波

  Echo,由高电平跳转回低电平,表示波回来了。

5.5怎么算时间

  Echo引脚维持高电平的时间!
  波发出去的那一下,开始启动定时器
  波回来的那一下,我们开始停止定时器,计算出中间经过多少时间

5.6怎么算距离

  距离 = 速度 (340m/s)* 时间/2

6.从零编程实现超声波测距(当距离小于10厘米,D5亮,D6灭。当距离大于10厘米,D5灭,D6亮。)

6.1程序思路

6.1.1 Trig信号 ,给Trig端口至少10us的高电平

  一、软件延时10us函数

void Delay10us()		//@11.0592MHz 软件延时10us
{
	unsigned char i;

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

  二、触发信息函数

void startHC()     //给超声波模块控制为Trig一个10us的触发信息
{
	Trig = 0;
	Trig = 1;
	Delay10us();
	Trig = 0;
}

  三、主函数调用触发函数 startHC();

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

  一、

while(Echo == 0); //等待发送波

  二、波发出去的那一下,开始启动定时器
  定时器T0初始化函数

void time0Init() //设置定时器T0为16位模式,从0开始数数,不忙启动定时器
{
	TMOD &= 0xF0;
	TMOD |= 0x01;
	TL0   = 0;
	TH0   = 0;
}

  定时器T0启动

TR0 = 1;  //启动定时器T0
6.1.3 Echo,由高电平跳转回低电平,表示波回来了

  一、

while(Echo == 1); //等待波回来

  二、波回来的那一下,我们开始停止定时器

TR0 = 0;  //关闭定时器

  三、计算出中间经过多少时间

time = ((TH0 * 256) + TL0)*1.085; //单位为us。
/*
256的含义:
对于十进制数2向左移一位位20,相当于乘以10
那么对于二进制数2向左移移一位,相当于乘以2。移动8位,乘以2的8次方256
*/
6.1.4距离 = 速度 (340m/s)* 时间/2

  dis = 340m/s = 34000cm/s= 34cm/ms = 0.034cm/us
  dis = 0.034 * time/2; <==> 0.017*time;
  dis = 0.017*time;

6.1.5判断距离小于10厘米,D5亮,D6灭。判断距离大于10厘米,D5灭,D6亮。
if(dis < 10)
{
	D5 = 0;
	D6 = 1;
}
else
{
	D5 = 1;
	D6 = 0;
}
6.1.6因为需要重复测距,所以得多次给定时器赋初值

  重新给初值,重新计时

TL0 = 0;
TH0 = 0;

6.2完整程序代码

#include "reg52.h"
/*
		时间:    2023年10月3日19:22:25
		程序功能:从零编程实现超声波测距,
		当距离小于10厘米,D5亮,D6灭。
		当距离大于10厘米,D5灭,D6亮。
*/

sbit Trig = P1^5;  //超声波测距的控制Trig接单片机1.5口
sbit Echo = P1^6;  //超声波测距的控制Echo接单片机1.6口
sbit D5   = P3^7;  //根据原理图D5 led灯接单片机3.7口
sbit D6   = P3^6;  //根据原理图D6 led灯接单片机3.6口

void Delay10us()		//@11.0592MHz 软件延时10us
{
	unsigned char i;

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

void startHC()     //给超声波模块控制为Trig一个10us的触发信息
{
	Trig = 0;
	Trig = 1;
	Delay10us();
	Trig = 0;
}

void time0Init() //设置定时器T0为16位模式,从0开始数数,不忙启动定时器
{
	TMOD &= 0xF0;
	TMOD |= 0x01;
	TL0   = 0;
	TH0   = 0;
}

void main()
{
	double time;
	double dis;
 
	time0Init();
 
	while(1)
	{
		//1.Trig ,给Trig端口至少10us的高电平
		startHC();
		//2.Echo信号,由低电平跳转到高电平,表示开始发送波
		while(Echo == 0); //等待发送波
		//     波发出去的那一下,开始启动定时器
		TR0 = 1;  //启动定时器T0
		//3.Echo,由高电平跳转回低电平,表示波回来了
		while(Echo == 1); //等待波回来
		//     波回来的那一下,我们开始停止定时器,计算出中间经过多少时间
		TR0 = 0;  //关闭定时器
		time = ((TH0 * 256) + TL0)*1.085; //单位为us。
		/*
			256的含义:
			对于十进制数2向左移一位位20,相当于乘以10
			那么对于二进制数2向左移移一位,相当于乘以2。移动8位,乘以2的8次方256
		*/
		//4.距离 = 速度 (340m/s)* 时间/2
		//dis = 340m/s = 34000cm/s = 34cm/ms = 0.034cm/us
		//dis = 0.034 * time/2; <==> 0.017*time;
		dis = 0.017*time;
 
		//判断距离小于10厘米,D5亮,D6灭。
		//判断距离大于10厘米,D5灭,D6亮。
		if(dis < 10)
		{
			D5 = 0;
			D6 = 1;
		}
		else
		{
			D5 = 1;
			D6 = 0;
		}
		//重新给初值,重新计时
		TL0 = 0;
		TH0 = 0;
	}
}

7.感应开关盖垃圾桶需求设计

7.1 项目概述

7.1.1功能描述

  检测靠近时,垃圾桶自动开盖并伴随滴一声,2秒后关盖
  发生震动时,垃圾桶自动开盖并伴随滴一声,2秒后关盖
  按下按键时,垃圾桶自动开盖并伴随滴一声,2秒后关盖

7.1.2硬件说明

  SG90舵机超声波模块震动传感器蜂鸣器

7.1.3接线说明

  舵机控制口 P1.1
  超声波Trig接 P1.5
  Echo接 P1.6
  蜂鸣器接 P2.0 口
  震动传感器接 P3.2口(外部中断0)

8.垃圾桶01修改超声波为定过器一控制

8.1开发步骤

8.1.1舵机用定时器0 超声波用定时器1
8.1.2修改超声波定时器T0T1

  TMOD &= 0xF0 修改为 TMOD &= 0x0F
  TMOD |= 0x01 修改为 TMOD &= 0x10
  所有的TL0修改为TL1
  所有的TH0修改为TH1
  所有的TR0修改为TR1
  time0Init() 修改为 time1Init()

8.1.3修改后的程序代码
#include "reg52.h"
/*
		时间:    2023年10月3日19:22:25
		程序功能:从零编程实现超声波测距,
		当距离小于10厘米,D5亮,D6灭。
		当距离大于10厘米,D5灭,D6亮。
*/

sbit Trig = P1^5;  //超声波测距的控制Trig接单片机1.5口
sbit Echo = P1^6;  //超声波测距的控制Echo接单片机1.6口
sbit D5   = P3^7;  //根据原理图D5 led灯接单片机3.7口
sbit D6   = P3^6;  //根据原理图D6 led灯接单片机3.6口

void Delay10us()		//@11.0592MHz 软件延时10us
{
	unsigned char i;

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

void startHC()     //给超声波模块控制为Trig一个10us的触发信息
{
	Trig = 0;
	Trig = 1;
	Delay10us();
	Trig = 0;
}

void time1Init() //设置定时器T0为16位模式,从0开始数数,不忙启动定时器
{
	TMOD &= 0x0F;
	TMOD |= 0x10;
	TL1   = 0;
	TH1   = 0;
}

void main()
{
	double time;
	double dis;
 
	time1Init();
 
	while(1)
	{
		//1.Trig ,给Trig端口至少10us的高电平
		startHC();
		//2.Echo信号,由低电平跳转到高电平,表示开始发送波
		while(Echo == 0);
		//     波发出去的那一下,开始启动定时器
		TR1 = 1;  //启动定时器T0
		//3.Echo,由高电平跳转回低电平,表示波回来了
		while(Echo == 1);
		//     波回来的那一下,我们开始停止定时器,计算出中间经过多少时间
		TR1 = 0;  //关闭定时器
		time = ((TH1 * 256) + TL1)*1.085; //单位为us。
		/*
			256的含义:
			对于十进制数2向左移一位位20,相当于乘以10
			那么对于二进制数2向左移移一位,相当于乘以2。移动8位,乘以2的8次方256
		*/
		//4.距离 = 速度 (340m/s)* 时间/2
		//dis = 340m/s = 34000cm/s = 34cm/ms = 0.034cm/us
		//dis = 0.034 * time/2; <==> 0.017*time;
		dis = 0.017*time;
		if(dis < 10)
		{
			D5 = 0;
			D6 = 1;
		}
		else
		{
			D5 = 1;
			D6 = 0;
		}
		//重新给初值,重新计时
		TL1 = 0;
		TH1 = 0;
	}
}

9.垃圾桶02封装超声波测距代码

9.1建立测距函数

double getDistance()
{
	double time;
 
	//重新给初值,重新计时
		TL1 = 0;
		TH1 = 0;
 
	//1.Trig ,给Trig端口至少10us的高电平
	startHC();
	//2.Echo信号,由低电平跳转到高电平,表示开始发送波
	while(Echo == 0);
	//     波发出去的那一下,开始启动定时器
	TR1 = 1;  //启动定时器T0
	//3.Echo,由高电平跳转回低电平,表示波回来了
	while(Echo == 1);
	//     波回来的那一下,我们开始停止定时器,计算出中间经过多少时间
	TR1 = 0;  //关闭定时器
	time = ((TH1 * 256) + TL1)*1.085; //单位为us。
	/*
		256的含义:
		对于十进制数2向左移一位位20,相当于乘以10
		那么对于二进制数2向左移移一位,相当于乘以2。移动8位,乘以2的8次方256
	*/
	//4.距离 = 速度 (340m/s)* 时间/2
	//dis = 340m/s = 34000cm/s = 34cm/ms = 0.034cm/us
	//dis = 0.034 * time/2; <==> 0.017*time;
 
	return (0.017*time);
}

8.2建立开盖灯状态函数

void opencCoverLight()
{
	D5 = 0;
	D6 = 1;
}

8.3建立关盖灯状态函数

void closeCoverLight()
{
	D5 = 1;
	D6 = 0;
}

8.4修改后的程序代码

#include "reg52.h"
/*
		时间:    2023年10月3日19:22:25
		程序功能:从零编程实现超声波测距,
		当距离小于10厘米,D5亮,D6灭。
		当距离大于10厘米,D5灭,D6亮。
*/

sbit Trig = P1^5;  //超声波测距的控制Trig接单片机1.5口
sbit Echo = P1^6;  //超声波测距的控制Echo接单片机1.6口
sbit D5   = P3^7;  //根据原理图D5 led灯接单片机3.7口
sbit D6   = P3^6;  //根据原理图D6 led灯接单片机3.6口

void Delay10us()		//@11.0592MHz 软件延时10us
{
	unsigned char i;

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

void startHC()     //给超声波模块控制为Trig一个10us的触发信息
{
	Trig = 0;
	Trig = 1;
	Delay10us();
	Trig = 0;
}

void time1Init() //设置定时器T0为16位模式,从0开始数数,不忙启动定时器
{
	TMOD &= 0x0F;
	TMOD |= 0x10;
	TL1   = 0;
	TH1   = 0;
}

double getDistance()
{
	double time;
 
	//重新给初值,重新计时
	TL1 = 0;
	TH1 = 0;
 
	//1.Trig ,给Trig端口至少10us的高电平
	startHC();
	//2.Echo信号,由低电平跳转到高电平,表示开始发送波
	while(Echo == 0);
	//     波发出去的那一下,开始启动定时器
	TR1 = 1;  //启动定时器T0
	//3.Echo,由高电平跳转回低电平,表示波回来了
	while(Echo == 1);
	//     波回来的那一下,我们开始停止定时器,计算出中间经过多少时间
	TR1 = 0;  //关闭定时器
	time = ((TH1 * 256) + TL1)*1.085; //单位为us。
	/*
		256的含义:
		对于十进制数2向左移一位位20,相当于乘以10
		那么对于二进制数2向左移移一位,相当于乘以2。移动8位,乘以2的8次方256
	*/
	//4.距离 = 速度 (340m/s)* 时间/2
	//dis = 340m/s = 34000cm/s = 34cm/ms = 0.034cm/us
	//dis = 0.034 * time/2; <==> 0.017*time;
 
	return (0.017*time);
}

void opencCoverLight()
{
	D5 = 0;
	D6 = 1;
}

void closeCoverLight()
{
	D5 = 1;
	D6 = 0;
}

void main()
{
 
	double dis;
 
	time1Init();
 
	while(1)
	{
		dis = getDistance();
		if(dis < 10)
		{
			opencCoverLight();
		}
		else
		{
			closeCoverLight();
		}
	}
}

10.垃圾桶03实现距离感应开盖

10.1程序思路:总思路将舵机控制代码和超声波控制给整合到一个程序中

10.1.1将舵机控制的变量和IO口复制到超声波测距程序代码中
sbit sg90_con = P1^1; //sg90舵机PWM控制线接单片机P1.1口,用于模拟PWM波形。

int cnt = 0;     //记录爆表次数
int angle;       //记录舵机角度变量
10.1.2将300毫秒,2000毫秒软件延时函数复制到超声波测距程序代码中,并建立一个软件延时150毫秒的函数
void Delay2000ms()		//@11.0592MHz 软件延时2秒
{
	unsigned char i, j, k;

	i = 15;
	j = 2;
	k = 235;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
void Delay300ms()		//@11.0592MHz  软件延时300毫秒
{
	unsigned char i, j, k;

	i = 3;
	j = 26;
	k = 223;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
void Delay150ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	i = 2;
	j = 13;
	k = 237;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
10.1.3将定时器T0初始化函数和定时器T0中段函数复制到超声波测距程序代码中相应位置
void initTime0() //初始化定时器T0
{
	//1.设置定时器为T0,16位模式
	TMOD &= 0xF0;
	TMOD |= 0x01;
 
	//2.设置初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
 
	//3.启动定时器,开始计时
	TR0 = 1; //TR0为启动定时器标志,值为1启动定时器
	TF0 = 0;
	//4.打开定时器0的中断 赋1打开
	ET0 = 1;
	//5.打开总中断EA  赋1打开
	EA  = 1;
}
void Time0Handler() interrupt 1 //定时器T0中断函数,中断号为interrupt 1
{
	cnt++;
	//重新给定时器赋初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
 
	//判读记录爆表变量cnt的值是否小于角度变量的值,小于继续给sg90舵机控制线高电平,大于等于则给低电平
	if(cnt < angle)
	{
     sg90_con = 1;
	}
	else
	{
     sg90_con = 0;
	}
	//判断cnt是否加到40,定时是否20毫秒PWM波形周期
	if(cnt == 40)
	{
	   cnt = 0;     //定时到20豪秒,cnt从0开始加
	   sg90_con = 1;//定时到20豪秒,重新给舵机控制线一个高电平
	}
}
10.1.4分别建立SG90舵机初始化函数,垃圾桶开关盖函数。置于超声波测距程序的相应位置
void initSG90_0() //SG90舵机初始化函数。
{
	sg90_con = 1; //单片机上电,给sg90舵机控制线一个高电平。
	angle    = 1; //给角度控制变量初始化为0度,因为0.5毫秒的高电平PWM波为0度,angle = 1;时定时器刚好定时0.5毫秒
    cnt      = 0; //初始化爆表变量
}
void openCoverDustbin() //垃圾桶开盖函数
{
	angle    = 3;       //舵机开盖
	cnt      = 0;
	Delay2000ms();
}
void closeCoverDustbin()//垃圾桶关盖函数
{
	angle    = 1;       //舵机关盖
	cnt      = 0;
	Delay150ms();
}
10.1.5通过超声波测距,小于10厘米就开盖开灯。大于10厘米就关盖关灯。
while(1)
{
	dis = getDistance();  //超声波测距
	if(dis < 10)
	{
		openCoverLight();  //开盖,D5亮
		openCoverDustbin();
	}
	else
	{
		closeCoverLight();  //关盖,D5灭
		closeCoverDustbin();
	}
}
10.1.6特别提醒:编译时记得输出HEX文件,不然程序会跑错。

10.2完整程序代码

#include "reg52.h"
/*
		时间:    2023年10月4日14:00:24
		程序功能:垃圾桶03实现距离感应开盖
		注意:一定要在魔术手中Output内勾选输出Hex文件,不然代码无效,执行其他代码。
*/

sbit Trig     = P1^5;  //超声波测距的控制Trig接单片机1.5口
sbit Echo     = P1^6;  //超声波测距的控制Echo接单片机1.6口
sbit D5       = P3^7;  //根据原理图D5 led灯接单片机3.7口
sbit D6       = P3^6;  //根据原理图D6 led灯接单片机3.6口
sbit sg90_con = P1^1; //sg90舵机PWM控制线接单片机P1.1口,用于模拟PWM波形。

int cnt = 0;     //记录爆表次数
int angle;       //记录舵机角度变量


void Delay10us()		//@11.0592MHz 软件延时10us
{
	unsigned char i;

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

void Delay2000ms()		//@11.0592MHz 软件延时2秒
{
	unsigned char i, j, k;

	i = 15;
	j = 2;
	k = 235;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

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

	i = 2;
	j = 13;
	k = 237;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void Delay300ms()		//@11.0592MHz  软件延时300毫秒
{
	unsigned char i, j, k;

	i = 3;
	j = 26;
	k = 223;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void startHC()     //给超声波模块控制为Trig一个10us的触发信息
{
	Trig = 0;
	Trig = 1;
	Delay10us();
	Trig = 0;
}

void initTime1() //设置定时器T0为16位模式,从0开始数数,不忙启动定时器
{
	TMOD &= 0x0F;
	TMOD |= 0x10;
	TL1   = 0;
	TH1   = 0;
}

void initTime0() //初始化定时器T0
{
	//1.设置定时器为T0,16位模式
	TMOD &= 0xF0;
	TMOD |= 0x01;
 
	//2.设置初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
 
	//3.启动定时器,开始计时
	TR0 = 1; //TR0为启动定时器标志,值为1启动定时器
	TF0 = 0;
	//4.打开定时器0的中断 赋1打开
	ET0 = 1;
	//5.打开总中断EA  赋1打开
	EA  = 1;
}

void initSG90_0() //SG90舵机初始化函数。
{
	sg90_con = 1; //单片机上电,给sg90舵机控制线一个高电平。
	angle    = 1; //给角度控制变量初始化为0度,因为0.5毫秒的高电平PWM波为0度,angle = 1;时定时器刚好定时0.5毫秒
    cnt      = 0; //初始化爆表变量
}

double getDistance()
{
	double time;
 
	//重新给初值,重新计时
	TL1 = 0;
	TH1 = 0;
 
	//1.Trig ,给Trig端口至少10us的高电平
	startHC();
	//2.Echo信号,由低电平跳转到高电平,表示开始发送波
	while(Echo == 0);
	//     波发出去的那一下,开始启动定时器
	TR1 = 1;  //启动定时器T0
	//3.Echo,由高电平跳转回低电平,表示波回来了
	while(Echo == 1);
	//     波回来的那一下,我们开始停止定时器,计算出中间经过多少时间
	TR1 = 0;  //关闭定时器
	time = ((TH1 * 256) + TL1)*1.085; //单位为us。
	/*
		256的含义:
		对于十进制数2向左移一位位20,相当于乘以10
		那么对于二进制数2向左移移一位,相当于乘以2。移动8位,乘以2的8次方256
	*/
	//4.距离 = 速度 (340m/s)* 时间/2
	//dis = 340m/s = 34000cm/s = 34cm/ms = 0.034cm/us
	//dis = 0.034 * time/2; <==> 0.017*time;
 
    return (0.017 * time);
}

void openCoverLight()  //开盖灯的状态
{
	D5 = 0;
	D6 = 1;
}

void closeCoverLight() //关盖垃圾桶的状态
{
	D5 = 1;
	D6 = 0;
}

void openCoverDustbin() //垃圾桶开盖函数
{
	angle    = 3;       //舵机开盖
	cnt      = 0;
	Delay2000ms();
}

void closeCoverDustbin()//垃圾桶关盖函数
{
	angle    = 1;       //舵机关盖
	cnt      = 0;
	Delay150ms();
}

void main()
{
 
	double dis;
 
	Delay300ms();
 
	initTime0();
	initTime1();
	initSG90_0();
 
	while(1)
	{
		dis = getDistance();  //超声波测距
		if(dis < 10)
		{
			openCoverLight();  //开盖,D5亮
			openCoverDustbin();
		}
		else
		{
			closeCoverLight();  //关盖,D5灭
			closeCoverDustbin();
		}
	}
}

void Time0Handler() interrupt 1 //定时器T0中断函数,中断号为interrupt 1
{
	cnt++;
	//重新给定时器赋初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
 
	//判读记录爆表变量cnt的值是否小于角度变量的值,小于继续给sg90舵机控制线高电平,大于等于则给低电平
	if(cnt < angle)
	{
     sg90_con = 1;
	}
	else
	{
     sg90_con = 0;
	}
	//判断cnt是否加到40,定时是否20毫秒PWM波形周期
	if(cnt == 40)
	{
	   cnt = 0;     //定时到20豪秒,cnt从0开始加
	   sg90_con = 1;//定时到20豪秒,重新给舵机控制线一个高电平
	}
}

11.垃圾桶04添加按键开盖功能

11.1程序思路

  一、根据开发板原理图,将SW1定义接到单片机P2.1

sbit SW1      = P2^1;  //按键1根据原理图接单片机2.1口

  二、在主函数while死循环中添加或上按键被按下的条件

while(1)
	{
		dis = getDistance();  //超声波测距
		if(dis < 10 || SW1 == 0)
		{
			openCoverLight();  //开盖,D5亮
			openCoverDustbin();
		}
		else
		{
			closeCoverLight();  //关盖,D5灭
			closeCoverDustbin();
		}
	}

11.2完整程序代码

#include "reg52.h"
/*
		时间:    2023年10月4日15:12:22
		程序功能:垃圾桶04添加按键开盖功能
*/

sbit SW1      = P2^1;  //按键1根据原理图接单片机2.1口
sbit Trig     = P1^5;  //超声波测距的控制Trig接单片机1.5口
sbit Echo     = P1^6;  //超声波测距的控制Echo接单片机1.6口
sbit D5       = P3^7;  //根据原理图D5 led灯接单片机3.7口
sbit D6       = P3^6;  //根据原理图D6 led灯接单片机3.6口
sbit sg90_con = P1^1; //sg90舵机PWM控制线接单片机P1.1口,用于模拟PWM波形。

int cnt = 0;     //记录爆表次数
int angle;       //记录舵机角度变量


void Delay10us()		//@11.0592MHz 软件延时10us
{
	unsigned char i;

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

void Delay2000ms()		//@11.0592MHz 软件延时2秒
{
	unsigned char i, j, k;

	i = 15;
	j = 2;
	k = 235;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

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

	i = 2;
	j = 13;
	k = 237;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void Delay300ms()		//@11.0592MHz  软件延时300毫秒
{
	unsigned char i, j, k;

	i = 3;
	j = 26;
	k = 223;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void startHC()     //给超声波模块控制为Trig一个10us的触发信息
{
	Trig = 0;
	Trig = 1;
	Delay10us();
	Trig = 0;
}

void initTime1() //设置定时器T0为16位模式,从0开始数数,不忙启动定时器
{
	TMOD &= 0x0F;
	TMOD |= 0x10;
	TL1   = 0;
	TH1   = 0;
}

void initTime0() //初始化定时器T0
{
	//1.设置定时器为T0,16位模式
	TMOD &= 0xF0;
	TMOD |= 0x01;
 
	//2.设置初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
 
	//3.启动定时器,开始计时
	TR0 = 1; //TR0为启动定时器标志,值为1启动定时器
	TF0 = 0;
	//4.打开定时器0的中断 赋1打开
	ET0 = 1;
	//5.打开总中断EA  赋1打开
	EA  = 1;
}

void initSG90_0()
{
	sg90_con = 1; //单片机上电,给sg90舵机控制线一个高电平。
	angle    = 1; //给角度控制变量初始化为0度,因为0.5毫秒的高电平PWM波为0度,angle = 1;时定时器刚好定时0.5毫秒
    cnt      = 0; //初始化爆表变量
}

double getDistance()
{
	double time;
 
	//重新给初值,重新计时
	TL1 = 0;
	TH1 = 0;
 
	//1.Trig ,给Trig端口至少10us的高电平
	startHC();
	//2.Echo信号,由低电平跳转到高电平,表示开始发送波
	while(Echo == 0);
	//     波发出去的那一下,开始启动定时器
	TR1 = 1;  //启动定时器T0
	//3.Echo,由高电平跳转回低电平,表示波回来了
	while(Echo == 1);
	//     波回来的那一下,我们开始停止定时器,计算出中间经过多少时间
	TR1 = 0;  //关闭定时器
	time = ((TH1 * 256) + TL1)*1.085; //单位为us。
	/*
		256的含义:
		对于十进制数2向左移一位位20,相当于乘以10
		那么对于二进制数2向左移移一位,相当于乘以2。移动8位,乘以2的8次方256
	*/
	//4.距离 = 速度 (340m/s)* 时间/2
	//dis = 340m/s = 34000cm/s = 34cm/ms = 0.034cm/us
	//dis = 0.034 * time/2; <==> 0.017*time;
 
    return (0.017 * time);
}

void openCoverLight()
{
	D5 = 0;
	D6 = 1;
}

void closeCoverLight()
{
	D5 = 1;
	D6 = 0;
}

void openCoverDustbin()
{
	angle    = 3;       //舵机开盖
	cnt      = 0;
	Delay2000ms();
}

void closeCoverDustbin()
{
	angle    = 1;       //舵机关盖
	cnt      = 0;
	Delay150ms();
}

void main()
{
 
	double dis;
 
	Delay300ms();
 
	initTime0();
	initTime1();
	initSG90_0();
 
	while(1)
	{
		dis = getDistance();  //超声波测距
		if(dis < 10 || SW1 == 0)  //距离小于10或SW1被按下,舵机开盖。
		{
			openCoverLight();  //开盖,D5亮
			openCoverDustbin();
		}
		else
		{
			closeCoverLight();  //关盖,D5灭
			closeCoverDustbin();
		}
	}
}

void Time0Handler() interrupt 1 //定时器T0中断函数,中断号为interrupt 1
{
	cnt++;
	//重新给定时器赋初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
 
	//判读记录爆表变量cnt的值是否小于角度变量的值,小于继续给sg90舵机控制线高电平,大于等于则给低电平
	if(cnt < angle)
	{
     sg90_con = 1;
	}
	else
	{
     sg90_con = 0;
	}
	//判断cnt是否加到40,定时是否20毫秒PWM波形周期
	if(cnt == 40)
	{
	   cnt = 0;     //定时到20豪秒,cnt从0开始加
	   sg90_con = 1;//定时到20豪秒,重新给舵机控制线一个高电平
	}
}

12.垃圾桶05添加震动开盖功能使用外部中断优化

12.1程序思路

12.1.1直接采用软件扫码震动传感器返回的低电平启动开盖。只需要将震动传感器接入单片机P3.2(根据单片机手册得知P3.2口为外部中断)口,并在主函数死循环中添加P3.2口接收到低电平的判断条件。

  一、判断条件

if(dis < 10 || SW1 == 0 || Vibrate == 0)

  二、 会出现震动开盖不灵敏的BUG,因为再关盖后会延时150毫秒。并且此时测距的距离大于10厘米。震动触发的低电平容易卡在软件延时的150毫秒内。解决办法就是设立外部中断,建立震动传感器标志变量。当发生震动,标志为0,触发开盖。反之则不。
  三、

垃圾桶有灵性了!还会自动开盖。#51单片机制作感应开关盖垃圾桶【下】_第4张图片

12.1.2依据单片机手册建立外部中断0的初始化函数

  一、
垃圾桶有灵性了!还会自动开盖。#51单片机制作感应开关盖垃圾桶【下】_第5张图片
  二、
在这里插入图片描述
  三、

void initEX0_vib() //初始化外部中断0关于震动传感器
{
	EX0 = 1;
	IT0 = 0;
}
12.1.3依据手册建立外部中断执行函数并建立震动标志变量

  一、
在这里插入图片描述
  二、

void EX0_Handler() interrupt 0
{
	markVibrate = 0;
}

  三、

int markVibrate = 1; //震动传感器标志变量  一定要默认初始化为1,因为为0时单片机一上电就会开盖。

12.2完整程序代码

#include "reg52.h"
/*
		时间:    2023年10月4日15:48:22
		程序功能:垃圾桶05添加震动开盖功能使用外部中断优化
*/

sbit SW1      = P2^1;  //按键1根据原理图接单片机2.1口
sbit Trig     = P1^5;  //超声波测距的控制Trig接单片机1.5口
sbit Echo     = P1^6;  //超声波测距的控制Echo接单片机1.6口
sbit D5       = P3^7;  //根据原理图D5 led灯接单片机3.7口
sbit D6       = P3^6;  //根据原理图D6 led灯接单片机3.6口
sbit vibrate  = P3^2; //根据原理图接把震动传感器接外部中断0 P3.2口
sbit sg90_con = P1^1; //sg90舵机PWM控制线接单片机P1.1口,用于模拟PWM波形。

int cnt = 0;         //记录爆表次数
int angle;           //记录舵机角度变量
int markVibrate = 1; //震动传感器标志变量

void Delay10us()		//@11.0592MHz 软件延时10us
{
	unsigned char i;

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

void Delay2000ms()		//@11.0592MHz 软件延时2秒
{
	unsigned char i, j, k;

	i = 15;
	j = 2;
	k = 235;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

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

	i = 2;
	j = 13;
	k = 237;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void Delay300ms()		//@11.0592MHz  软件延时300毫秒
{
	unsigned char i, j, k;

	i = 3;
	j = 26;
	k = 223;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void startHC()     //给超声波模块控制为Trig一个10us的触发信息
{
	Trig = 0;
	Trig = 1;
	Delay10us();
	Trig = 0;
}

void initTime1() //设置定时器T0为16位模式,从0开始数数,不忙启动定时器
{
	TMOD &= 0x0F;
	TMOD |= 0x10;
	TL1   = 0;
	TH1   = 0;
}

void initTime0() //初始化定时器T0
{
	//1.设置定时器为T0,16位模式
	TMOD &= 0xF0;
	TMOD |= 0x01;
 
	//2.设置初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
 
	//3.启动定时器,开始计时
	TR0 = 1; //TR0为启动定时器标志,值为1启动定时器
	TF0 = 0;
	//4.打开定时器0的中断 赋1打开
	ET0 = 1;
	//5.打开总中断EA  赋1打开
	EA  = 1;
}

void initEX0_vib() //初始化外部中断0关于震动传感器
{
	  EX0 = 1;
	  IT0 = 0;
}

void initSG90_0()
{
	sg90_con = 1; //单片机上电,给sg90舵机控制线一个高电平。
	angle    = 1; //给角度控制变量初始化为0度,因为0.5毫秒的高电平PWM波为0度,angle = 1;时定时器刚好定时0.5毫秒
    cnt      = 0; //初始化爆表变量
}

double getDistance()
{
	double time;
 
	//重新给初值,重新计时
	TL1 = 0;
	TH1 = 0;
 
	//1.Trig ,给Trig端口至少10us的高电平
	startHC();
	//2.Echo信号,由低电平跳转到高电平,表示开始发送波
	while(Echo == 0);
	//     波发出去的那一下,开始启动定时器
	TR1 = 1;  //启动定时器T0
	//3.Echo,由高电平跳转回低电平,表示波回来了
	while(Echo == 1);
	//     波回来的那一下,我们开始停止定时器,计算出中间经过多少时间
	TR1 = 0;  //关闭定时器
	time = ((TH1 * 256) + TL1)*1.085; //单位为us。
	/*
		256的含义:
		对于十进制数2向左移一位位20,相当于乘以10
		那么对于二进制数2向左移移一位,相当于乘以2。移动8位,乘以2的8次方256
	*/
	//4.距离 = 速度 (340m/s)* 时间/2
	//dis = 340m/s = 34000cm/s = 34cm/ms = 0.034cm/us
	//dis = 0.034 * time/2; <==> 0.017*time;
 
    return (0.017 * time);
}

void openCoverLight()
{
	D5 = 0;
	D6 = 1;
}

void closeCoverLight()
{
	D5 = 1;
	D6 = 0;
}

void openCoverDustbin()
{
	angle    = 3;       //舵机开盖
	cnt      = 0;
	Delay2000ms();
}

void closeCoverDustbin()
{
	angle    = 1;       //舵机关盖
	cnt      = 0;
	Delay150ms();
}

void main()
{
 
	double dis;
 
	Delay300ms();
 
	initSG90_0();
	initTime0();
	initTime1();
    initEX0_vib();
 
	while(1)
	{
		dis = getDistance();  //超声波测距
		if(dis < 10 || SW1 == 0 || markVibrate == 0) //距离小于10或SW1被按下,舵机开盖。
		{
			openCoverLight();  //开盖,D5亮
			openCoverDustbin();
			markVibrate = 1;
		}
		else
		{
			closeCoverLight();  //关盖,D5灭
			closeCoverDustbin();
		}
	}
}

void Time0Handler() interrupt 1 //定时器T0中断函数,中断号为interrupt 1
{
	cnt++;
	//重新给定时器赋初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
 
	//判读记录爆表变量cnt的值是否小于角度变量的值,小于继续给sg90舵机控制线高电平,大于等于则给低电平
	if(cnt < angle)
	{
     	sg90_con = 1;
	}
	else
	{
    	sg90_con = 0;
	}
	//判断cnt是否加到40,定时是否20毫秒PWM波形周期
	if(cnt == 40)
	{
	   cnt = 0;     //定时到20豪秒,cnt从0开始加
	   sg90_con = 1;//定时到20豪秒,重新给舵机控制线一个高电平
	}
}

void EX0_Handler() interrupt 0
{
	markVibrate = 0;
}

13.垃圾桶06添加开盖滴滴声_项目完结

13.1程序思路

  一、把蜂鸣器接单片机P2.0

sbit beep     = P2^0;  //蜂鸣器接单片机2.0口

  二、在舵机开盖函数中添加蜂鸣器启动关闭语句,给予300ms软件延时。

void openCoverDustbin()
{
	angle    = 3;       //舵机开盖
	cnt      = 0;
	beep     = 0;       //低电平响
	Delay300ms();
	beep     = 1;
	Delay2000ms();
}

13.2完整程序代码

#include "reg52.h"
/*
		时间:    2023年10月4日17:24:06
		程序功能:垃圾桶06添加开盖滴滴声_项目完结
*/

sbit SW1      = P2^1;  //按键1根据原理图接单片机2.1口
sbit beep     = P2^0;  //蜂鸣器接单片机2.0口
sbit Trig     = P1^5;  //超声波测距的控制Trig接单片机1.5口
sbit Echo     = P1^6;  //超声波测距的控制Echo接单片机1.6口
sbit D5       = P3^7;  //根据原理图D5 led灯接单片机3.7口
sbit D6       = P3^6;  //根据原理图D6 led灯接单片机3.6口
sbit vibrate  = P3^2;  //根据原理图接把震动传感器接外部中断0 P3.2口
sbit sg90_con = P1^1; //sg90舵机PWM控制线接单片机P1.1口,用于模拟PWM波形。

int cnt = 0;         //记录爆表次数
int angle;           //记录舵机角度变量
int markVibrate = 1; //震动传感器标志变量

void Delay10us()		//@11.0592MHz 软件延时10us
{
	unsigned char i;

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

void Delay2000ms()		//@11.0592MHz 软件延时2秒
{
	unsigned char i, j, k;

	i = 15;
	j = 2;
	k = 235;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

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

	i = 2;
	j = 13;
	k = 237;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void Delay300ms()		//@11.0592MHz  软件延时300毫秒
{
	unsigned char i, j, k;

	i = 3;
	j = 26;
	k = 223;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void startHC()     //给超声波模块控制为Trig一个10us的触发信息
{
	Trig = 0;
	Trig = 1;
	Delay10us();
	Trig = 0;
}

void initTime1() //设置定时器T0为16位模式,从0开始数数,不忙启动定时器
{
	TMOD &= 0x0F;
	TMOD |= 0x10;
	TL1   = 0;
	TH1   = 0;
}

void initTime0() //初始化定时器T0
{
	//1.设置定时器为T0,16位模式
	TMOD &= 0xF0;
	TMOD |= 0x01;
 
	//2.设置初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
 
	//3.启动定时器,开始计时
	TR0 = 1; //TR0为启动定时器标志,值为1启动定时器
	TF0 = 0;
	//4.打开定时器0的中断 赋1打开
	ET0 = 1;
	//5.打开总中断EA  赋1打开
	EA  = 1;
}

void initEX0_vib() //初始化外部中断0关于震动传感器
{
	  EX0 = 1;
	  IT0 = 0;
}

void initSG90_0()
{
	sg90_con = 1; //单片机上电,给sg90舵机控制线一个高电平。
	angle    = 1; //给角度控制变量初始化为0度,因为0.5毫秒的高电平PWM波为0度,angle = 1;时定时器刚好定时0.5毫秒
    cnt      = 0; //初始化爆表变量
}

double getDistance()
{
	double time;
 
	//重新给初值,重新计时
	TL1 = 0;
	TH1 = 0;
 
	//1.Trig ,给Trig端口至少10us的高电平
	startHC();
	//2.Echo信号,由低电平跳转到高电平,表示开始发送波
	while(Echo == 0);
	//     波发出去的那一下,开始启动定时器
	TR1 = 1;  //启动定时器T0
	//3.Echo,由高电平跳转回低电平,表示波回来了
	while(Echo == 1);
	//     波回来的那一下,我们开始停止定时器,计算出中间经过多少时间
	TR1 = 0;  //关闭定时器
	time = ((TH1 * 256) + TL1)*1.085; //单位为us。
	/*
		256的含义:
		对于十进制数2向左移一位位20,相当于乘以10
		那么对于二进制数2向左移移一位,相当于乘以2。移动8位,乘以2的8次方256
	*/
	//4.距离 = 速度 (340m/s)* 时间/2
	//dis = 340m/s = 34000cm/s = 34cm/ms = 0.034cm/us
	//dis = 0.034 * time/2; <==> 0.017*time;
 
    return (0.017 * time);
}

void openCoverLight()
{
	D5 = 0;
	D6 = 1;
}

void closeCoverLight()
{
	D5 = 1;
	D6 = 0;
}

void openCoverDustbin()
{
	angle    = 3;       //舵机开盖
	cnt      = 0;
	beep     = 0;       //低电平响
	Delay300ms();
	beep     = 1;
	Delay2000ms();
}

void closeCoverDustbin()
{
	angle    = 1;       //舵机关盖
	cnt      = 0;
	Delay150ms();
}

void main()
{
 
	double dis;
 
	Delay300ms();
 
	initSG90_0();
	initTime0();
	initTime1();
    initEX0_vib();
 
	while(1)
	{
		dis = getDistance();  //超声波测距
		if(dis < 10 || SW1 == 0 || markVibrate == 0) //距离小于10或SW1被按下,舵机开盖。
		{
			openCoverLight();  //开盖,D5亮
			openCoverDustbin();
			markVibrate = 1;
		}
		else
		{
			closeCoverLight();  //关盖,D5灭
			closeCoverDustbin();
		}
	}
}

void Time0Handler() interrupt 1 //定时器T0中断函数,中断号为interrupt 1
{
	cnt++;
	//重新给定时器赋初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
 
	//判读记录爆表变量cnt的值是否小于角度变量的值,小于继续给sg90舵机控制线高电平,大于等于则给低电平
	if(cnt < angle)
	{
     	sg90_con = 1;
	}
	else
	{
     	sg90_con = 0;
	}
	//判断cnt是否加到40,定时是否20毫秒PWM波形周期
	if(cnt == 40)
	{
	   cnt = 0;     //定时到20豪秒,cnt从0开始加
	   sg90_con = 1;//定时到20豪秒,重新给舵机控制线一个高电平
	}
}

void EX0_Handler() interrupt 0
{
	markVibrate = 0;
}

14.垃圾桶的抽抽BUG解决

14.1程序思路

  一、定义一个角度标志变量

int angleBak    = 0; //角度标志变量

  二、在舵机开盖关盖函数中分别让角度变量angle等于角度标志变量angleBak

angleBak = angle;

  三、在舵机开盖函数中添加if判断angleBak != angle;再执行后面语句。

if(angleBak != angle)
	{
		cnt      = 0;
	    beep     = 0;
		Delay300ms();
		beep     = 1;
		Delay2000ms();
	}

  四、最后解决挡住超声波测距撤出后盖子快速盖上情况可以利用在angleBak = angle;后添加软件延时150ms解决

angleBak = angle;
Delay150ms();

14.2完整程序代码

#include "reg52.h"
/*
		时间:    2023年10月4日17:24:06
		程序功能:垃圾桶06添加开盖滴滴声_项目完结
*/

sbit SW1      = P2^1;  //按键1根据原理图接单片机2.1口
sbit beep     = P2^0;  //蜂鸣器接单片机2.0口
sbit Trig     = P1^5;  //超声波测距的控制Trig接单片机1.5口
sbit Echo     = P1^6;  //超声波测距的控制Echo接单片机1.6口
sbit D5       = P3^7;  //根据原理图D5 led灯接单片机3.7口
sbit D6       = P3^6;  //根据原理图D6 led灯接单片机3.6口
sbit vibrate  = P3^2;  //根据原理图接把震动传感器接外部中断0 P3.2口
sbit sg90_con = P1^1; //sg90舵机PWM控制线接单片机P1.1口,用于模拟PWM波形。

int cnt         = 0; //记录爆表次数
int angle;           //记录舵机角度变量
int markVibrate = 1; //震动传感器标志变量
int angleBak    = 0; //角度标志变量

void Delay10us()		//@11.0592MHz 软件延时10us
{
	unsigned char i;

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

void Delay2000ms()		//@11.0592MHz 软件延时2秒
{
	unsigned char i, j, k;

	i = 15;
	j = 2;
	k = 235;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

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

	i = 2;
	j = 13;
	k = 237;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void Delay300ms()		//@11.0592MHz  软件延时300毫秒
{
	unsigned char i, j, k;

	i = 3;
	j = 26;
	k = 223;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void startHC()     //给超声波模块控制为Trig一个10us的触发信息
{
	Trig = 0;
	Trig = 1;
	Delay10us();
	Trig = 0;
}

void initTime1() //设置定时器T0为16位模式,从0开始数数,不忙启动定时器
{
	TMOD &= 0x0F;
	TMOD |= 0x10;
	TL1   = 0;
	TH1   = 0;
}

void initTime0() //初始化定时器T0
{
	//1.设置定时器为T0,16位模式
	TMOD &= 0xF0;
	TMOD |= 0x01;
 
	//2.设置初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
 
	//3.启动定时器,开始计时
	TR0 = 1; //TR0为启动定时器标志,值为1启动定时器
	TF0 = 0;
	//4.打开定时器0的中断 赋1打开
	ET0 = 1;
	//5.打开总中断EA  赋1打开
	EA  = 1;
}

void initEX0_vib() //初始化外部中断0关于震动传感器
{
	  EX0 = 1;
	  IT0 = 0;
}

void initSG90_0()
{
	sg90_con = 1; //单片机上电,给sg90舵机控制线一个高电平。
	angle    = 1; //给角度控制变量初始化为0度,因为0.5毫秒的高电平PWM波为0度,angle = 1;时定时器刚好定时0.5毫秒
    cnt      = 0; //初始化爆表变量
}

double getDistance()
{
	double time;
 
	//重新给初值,重新计时
	TL1 = 0;
	TH1 = 0;
 
	//1.Trig ,给Trig端口至少10us的高电平
	startHC();
	//2.Echo信号,由低电平跳转到高电平,表示开始发送波
	while(Echo == 0);
	//     波发出去的那一下,开始启动定时器
	TR1 = 1;  //启动定时器T0
	//3.Echo,由高电平跳转回低电平,表示波回来了
	while(Echo == 1);
	//     波回来的那一下,我们开始停止定时器,计算出中间经过多少时间
	TR1 = 0;  //关闭定时器
	time = ((TH1 * 256) + TL1)*1.085; //单位为us。
	/*
		256的含义:
		对于十进制数2向左移一位位20,相当于乘以10
		那么对于二进制数2向左移移一位,相当于乘以2。移动8位,乘以2的8次方256
	*/
	//4.距离 = 速度 (340m/s)* 时间/2
	//dis = 340m/s = 34000cm/s = 34cm/ms = 0.034cm/us
	//dis = 0.034 * time/2; <==> 0.017*time;
 
  return (0.017 * time);
}

void openCoverLight()
{
	D5 = 0;
	D6 = 1;
}

void closeCoverLight()
{
	D5 = 1;
	D6 = 0;
}

void openCoverDustbin()
{
	angle    = 3;       //舵机开盖
	if(angleBak != angle)
	{
		cnt      = 0;
	    beep     = 0;
		Delay300ms();
		beep     = 1;
		Delay2000ms();
	}
	angleBak = angle;
	Delay150ms();
}

void closeCoverDustbin()
{
	angle    = 1;       //舵机关盖
	cnt      = 0;
	Delay150ms();																							
}

void main()
{
 
	double dis;
 
	Delay300ms();
 
	initSG90_0();
	initTime0();
	initTime1();
    initEX0_vib();
 
	while(1)
	{
		dis = getDistance();  //超声波测距
		if(dis < 10 || SW1 == 0 || markVibrate == 0) //距离小于10或SW1被按下,舵机开盖。
		{
			openCoverLight();  //开盖,D5亮
			openCoverDustbin();
			markVibrate = 1;
		}
		else
		{
			closeCoverLight();  //关盖,D5灭
			closeCoverDustbin();
		}
	}
}

void Time0Handler() interrupt 1 //定时器T0中断函数,中断号为interrupt 1
{
	cnt++;
	//重新给定时器赋初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
 
	//判读记录爆表变量cnt的值是否小于角度变量的值,小于继续给sg90舵机控制线高电平,大于等于则给低电平
	if(cnt < angle)
	{
     	sg90_con = 1;
	}
	else
	{
     	sg90_con = 0;
	}
	//判断cnt是否加到40,定时是否20毫秒PWM波形周期
	if(cnt == 40)
	{
	   cnt = 0;     //定时到20豪秒,cnt从0开始加
	   sg90_con = 1;//定时到20豪秒,重新给舵机控制线一个高电平
	}
}

void EX0_Handler() interrupt 0
{
	markVibrate = 0;
}

结束语

  很高兴您能看到这里,点个赞再走呗。谢谢您啦!!!

你可能感兴趣的:(51单片机,嵌入式硬件,单片机,c语言,学习)