• 02:项目二:感应开关盖垃圾桶



    需要材料:
    1、SG90舵机模块
    2、HC-SR04超声波模块
    3、震动传感器
    4、蜂鸣器
    5、若干杜邦线

    1、PWM开发SG90

    • PWM波为脉冲宽度调制,对模拟信号电平进行数字编码。通过调节占空比的变化来调节信号。
    • 占空比:高电平的时间/整个信号的周期。

    1.1、怎样通过C51单片机输出PWM波?

    如果芯片内部模块能集成输出,一般观察手册或者芯片IO口都会标明这个是否是PWM口
    如果没有集成PWM功能,可以通过IO口软件模拟,

    1.2、通过定时器输出PWM波来控制SG90

    在这里插入图片描述
    如图为SG90舵机模块,黄色为PWM信号控制,红色和褐色分别为VCC和GND。当输入的PWM的占空比不同的时候,舵机模块的摆头幅度不同。一般情况如下:

    • PWM波的频率不能太高,大约50HZ,即周期=1/频率=1/50=0.02s,20ms左右
      0.5ms-------------0度; 2.5% 对应1/40,
      1.0ms------------45度; 5.0% 对应2/40
      1.5ms------------90度; 7.5% 对应3/40
      2.0ms-----------135度; 10.0% 对应4/40
      2.5ms-----------180度; 12.5% 对应5/40

    接下来通过C51单片机输出PWM波控制舵机的摆头(黄线连接P1.1口)代码如下:

    #include 
    
    sbit sg_90 = P1^1;//黄线连接P1.1口
    int cnt = 0;	//标志位
    int jd;			//占空比的分子
    
    void Delay300ms()		//@11.0592MHz
    {
    	unsigned char i, j, k;
    	i = 3;
    	j = 26;
    	k = 223;
    	do
    	{
    		do
    		{
    			while (--k);
    		} while (--j);
    	} while (--i);
    }
    
    void Delay2000ms()		//@11.0592MHz
    {
    	unsigned char i, j, k;
    	i = 15;
    	j = 2;
    	k = 235;
    	do
    	{
    		do
    		{
    			while (--k);
    		} while (--j);
    	} while (--i);
    }
    
    void Timer0_Init(void)
    {
    	TMOD = 0x01;//配置定时器T0为16位定时器
    	
    	TL0 = 0x33;	//定时器计1个数为1.085us,则当舵机为0度的时候,需要0.5ms,            
    	TH0 = 0xFE;	//则定一个0.5ms的定时器
    	TF0 = 0;		//清除TF0标志
    	TR0 = 1;		//定时器0开始计时
    	
    	ET0 = 1;
    	EA = 1;
    }
    
    
    void main(void)
    { 
    	Delay300ms();//给硬件准备时间
    	Timer0_Init();
    	jd = 1;//一上电,开始占空比为1/40,为0度
    	cnt = 0;
    	sg_90 = 1;//先给输出引脚一个高电平
    	while(1)
    	{//角度由135度到0度来回摆动
    		jd = 4;
    		cnt = 0;
    		Delay2000ms();
    		jd = 1;
    		cnt = 0;
    		Delay2000ms();
    	}
    }
    
    void Timer0_Handler() interrupt 1//中断函数
    {
    	cnt++;
    	TL0 = 0x33;	           
    	TH0 = 0xFE;
    	//控制PWM波的占空比
    	if(cnt < jd){
    		sg_90 = 1;
    	}else{
    		sg_90 = 0;
    	}
    	if(cnt == 40){
    		cnt = 0;
    		sg_90 = 1;
    	}
    }
    

    2、超声波测距模块的使用

    在这里插入图片描述

    • 怎么让它发送波
      Trig ,给Trig端口至少10us的高电平
    • 怎么知道它开始发了
      Echo信号,由低电平跳转到高电平,表示开始发送波
    • 怎么知道接收了返回波
      Echo,由高电平跳转回低电平,表示波回来了
    • 怎么算时间
      Echo引脚维持高电平的时间!
      波发出去的那一下,开始启动定时器。波回来的那一下,我们开始停止定时器,计算出中间经过多少时间。距离 = 速度 (340m/s)* 时间/2

    在这里插入图片描述

    /*通过超声波模块控制LED1灯的亮灭,当手靠近超声波模块时,灯亮*/
    #include 
    
    sbit Trig = P1^5;
    sbit Echo = P1^6;
    sbit LED1 = P3^7;
    
    void Delay10us()		//@11.0592MHz
    {
    	unsigned char i;
    
    	i = 2;
    	while (--i);
    }
    
    void Timer0_Init(void)
    {
    	TMOD = 0x01;
    	
    	TL0 = 0;
    	TH0 = 0;//设置定时器T0从0开始数数
    }
    
    void Delay200ms()		//@11.0592MHz
    {
    	unsigned char i, j, k;
    	i = 2;
    	j = 103;
    	k = 147;
    	do
    	{
    		do
    		{
    			while (--k);
    		} while (--j);
    	} while (--i);
    }
    
    void main(void)
    {
    	double time;
    	double dis;
    	Timer0_Init();
    	while(1){
    		Delay200ms();//先给单片机准备时间
    		//1、开始发波
    		Trig = 0;
    		Trig = 1;
    		Delay10us();
    		Trig = 0;
    		//2、检测ECHO引脚电平
    		while(Echo == 0);
    		TR0 = 1;													//启动定时器
    		while(Echo == 1);
    		TR0 = 0;													//关闭定时器
    		//3、计算定时开到定时关的时间(计算数的个数),
    		//2位二进制11,01。合并位1101怎么算?1101为(11*2^2)+01=13
    		time = (TH0 * 256 + TL0) * 1.085; //us为单位
    		//34000cm/s = 34cm/ms = 0.034cm/us
    		dis = time * 0.017;							  //cm为单位
    		if(dis < 10)
    		{
    				LED1 = 0;
    		}else
    		{
    				LED1 = 1;
    		}
    		
    		TL0 = 0;
    		TH0 = 0;//定时器清零,以便下次测距
    	}
    }
    

    代码优化②:

    /**优化:将定时器0改为定时器1,然后将超声波测距封装成为一个函数*/
    #include 
    
    sbit Trig = P1^5;
    sbit Echo = P1^6;
    sbit LED1 = P3^7;
    
    void Delay10us()		//@11.0592MHz
    {
    	unsigned char i;
    
    	i = 2;
    	while (--i);
    }
    
    void Delay200ms()		//@11.0592MHz
    {
    	unsigned char i, j, k;
    	i = 2;
    	j = 103;
    	k = 147;
    	do
    	{
    		do
    		{
    			while (--k);
    		} while (--j);
    	} while (--i);
    }
    
    void Timer1_Init(void)
    {
    	//使用定时器1
    	TMOD &= 0x0F;
    	TMOD |= 0x10;
    	
    	TL1 = 0;
    	TH1 = 0;//设置定时器T1从0开始数数
    }
    
    double get_distance()//超声波获得距离的函数
    {
    		double time;
    	
    		TL1 = 0;
    		TH1 = 0;//定时器清零,以便下次测距
    	  //1、开始发波
    		Trig = 0;
    		Trig = 1;
    		Delay10us();
    		Trig = 0;
    		//2、检测ECHO引脚电平
    		while(Echo == 0);
    		TR1 = 1;													//启动定时器
    		while(Echo == 1);
    		TR1 = 0;													//关闭定时器
    		//3、计算定时开到定时关的时间(计算数的个数),
    		//2位二进制11,01。合并位1101怎么算?1101为(11*2^2)+01=13
    		time = (TH1 * 256 + TL1) * 1.085; //us为单位
    		//34000cm/s = 34cm/ms = 0.034cm/us
    		return (time * 0.017);							  //cm为单位;
    }
    
    void main(void)
    {
    	double dis;
    	Timer1_Init();
    	while(1){
    		Delay200ms();//先给单片机准备时间
    		dis = get_distance();
    		if(dis < 10)
    		{
    				LED1 = 0;
    		}else
    		{
    				LED1 = 1;
    		}
    		
    	}
    }
    

    3、感应开关盖垃圾桶

    舵机和超声波代码整合,舵机用定时器0,超声波用定时器1。
    1、实现物体靠近后,自动开盖,2秒后关盖。
    2、查询的方式添加按键控制
    3、 查询的方式添加震动控制

    #include 
    
    sbit SW1   =   P2^1;//按键SW1连接的是P2.1口
    sbit Trig  =   P1^5;
    sbit Echo  =   P1^6;
    sbit LED1  =   P3^7;
    sbit sg_90 =   P1^1;//黄线连接P1.1口
    sbit vibrate = P3^2;//震动传感器连接P3.2口,使用外部中断0
    
    int cnt = 0;	//标志位
    int jd;			//占空比的分子
    int vib_mark;   //震动传感器的标志位
    
    void Delay10us()		//@11.0592MHz
    {
    	unsigned char i;
    
    	i = 2;
    	while (--i);
    }
    
    void Delay200ms()		//@11.0592MHz
    {
    	unsigned char i, j, k;
    	i = 2;
    	j = 103;
    	k = 147;
    	do
    	{
    		do
    		{
    			while (--k);
    		} while (--j);
    	} while (--i);
    }
    
    void Delay2000ms()		//@11.0592MHz
    {
    	unsigned char i, j, k;
    	i = 15;
    	j = 2;
    	k = 235;
    	do
    	{
    		do
    		{
    			while (--k);
    		} while (--j);
    	} while (--i);
    }
    
    void EX0_Init()//触发中断0初始化
    {
    	EX0 = 1;
    	EA = 1;
    	
    	IT0 = 0;//低电平触发
    }
    
    void Timer0_Init(void)//定时器T0中断初始化
    {
    	TMOD &= 0xF0;//配置定时器T0为16位定时器
    	TMOD |= 0x01;
    	TL0 = 0x33;	//定时器计1个数为1.085us,则当舵机为0度的时候,需要0.5ms,            
    	TH0 = 0xFE;	//则定一个0.5ms的定时器
    	TF0 = 0;		//清除TF0标志
    	TR0 = 1;		//定时器0开始计时
    	
    	ET0 = 1;
    	EA = 1;
    }
    
    void Timer1_Init(void)//定时器T1初始化
    {
    	//使用定时器1
    	TMOD &= 0x0F;
    	TMOD |= 0x10;
    	
    	TL1 = 0;
    	TH1 = 0;//设置定时器T1从0开始数数
    }
    
    double get_distance()
    {
    		double time;
    	
    		TL1 = 0;
    		TH1 = 0;//定时器清零,以便下次测距
    	  //1、开始发波
    		Trig = 0;
    		Trig = 1;
    		Delay10us();
    		Trig = 0;
    		//2、检测ECHO引脚电平
    		while(Echo == 0);
    		TR1 = 1;													//启动定时器
    		while(Echo == 1);
    		TR1 = 0;													//关闭定时器
    		//3、计算定时开到定时关的时间(计算数的个数),
    		//2位二进制11,01。合并位1101怎么算?1101为(11*2^2)+01=13
    		time = (TH1 * 256 + TL1) * 1.085; //us为单位
    		//34000cm/s = 34cm/ms = 0.034cm/us
    		return (time * 0.017);							  //cm为单位;
    }
    
    void sg90_0(void)	//舵机0度
    {
    	sg_90 = 1;//先给输出引脚一个高电平
    	jd = 1;//一上电,开始占空比为1/40,为0度
    	cnt = 0;
    }
    
    void sg90_90(void)//舵机90度
    {
    	sg_90 = 1;//先给输出引脚一个高电平
    	jd = 3;//一上电,开始占空比为3/40,为90度
    	cnt = 0;
    }
    
    void main(void)
    {
    	double dis;
    	
    	Timer0_Init();
    	Timer1_Init();
    	EX0_Init();
    	
    	sg90_0();
    	
    	while(1){
    		Delay200ms();//先给单片机准备时间
    		dis = get_distance();
    		if(dis < 10 || SW1 == 0 || vib_mark == 1)
    		{
    			  vib_mark = 0;
    				LED1 = 0;
    				sg90_90();
    			  Delay2000ms();
    		}
    		else
    		{
    				LED1 = 1;
    				sg90_0();
    			  Delay200ms();
    		}	
    	}
    }
    
    void Timer0_Handler() interrupt 1//中断函数
    {
    		cnt++;
    		TL0 = 0x33;	           
    		TH0 = 0xFE;
    		//控制PWM波的占空比
    		if(cnt < jd){
    			sg_90 = 1;
    		}else{
    			sg_90 = 0;
    		}
    		if(cnt == 40){
    			cnt = 0;
    			sg_90 = 1;
    		}
    }
    
    void EX0_Handler() interrupt 0//触发中断0函数
    {
    	vib_mark = 1;
    }
    
     为什么我们使用震动传感器控制的时候不直接if(dis < 10 || SW1 == 0 ||  vibrate== 0)喃?这样判断不是跟简单吗?
     原因:因为震动传感器因为震动而发出的低电平0不仅微弱,而且时间比较断。当震动传感器给出低电平的时候,而单片机还在执
     Delay2000ms();而当进入判断的时候,可能震动传感器发出的低电平已经消失了,已经变成高电平了。这样就会导致震动传感器不灵敏。
     所以,通过外部中断来改变标志位,这样就会规避这个问题。当震动时,触发中断,标志位变为1,等待判断。只有进入判断后标志位才变回0。
    
  • 相关阅读:
    通过阿里云宕机这件事,来看国内程序员的畸形职场文化
    zsh和ohmyzsh安装指南+插件推荐
    解释Java虚拟机(JVM)的工作原理
    一文带你认知定时消息发布RocketMQ
    Google Chrome如何同步书签
    Web自动化测试详细流程和步骤
    学习新语言方法总结(一)
    mybatisplus快速实现动态数据源切换
    IntelliJ IDEA 下 JavaWeb 配置MySQL 连接
    分享从零开始学习网络设备配置--2.5 提高骨干链路带宽(链路聚合)
  • 原文地址:https://blog.csdn.net/qq_51284092/article/details/140381006