锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

基于单片机倾角检测仪设计分享

时间:2022-11-12 23:30:00 三极管1284pf三极管电阻2006

目录

【功能介绍】

【原理图】

【PCB】

【实物图】

组件清单】

【源代码】

【参考】


【功能介绍】

本设计利用51单片机主控系统采用三轴加速度传感器ADXL345倾角测量可以实现两个功能:

  • 实时显示当前测量的角度值;
  • 可设置角度报警值,达到设定角度报警功能。

【原理图】

原理图包括单片机最小系统、按键电路、显示模块电源模块蜂鸣器模块、倾角检测模块等;如需详细信息,可私信作者或添加微信biyezhan007

【PCB】

【实物图】

组件清单】

10uf电解电容 1
30pf瓷片电容 2
3mm 红色led灯 1
小电源接口 1
开关 1
自锁按键 7
4针排针 1
LCD1602液晶显示屏 16P插座 1
8550 三极管 1
5V 有源蜂鸣器 1
103排阻 1
3K电阻 1
1K电阻 2
10K电阻 3
ADXL345模块 2*5P插座 1
STC89C52 单片机 DIP40插座 1
12M晶振 1

【源代码】

#include       ///调用头文件 #include "ADXL345.h"    ////调用倾角传感器的控制程序函数 #include "LCD1602.h"    //调用LCD1602 显示控制函数 #include "eeprom52.h"    //调用LCD1602 显示控制函数   #define   uchar unsigned char  //简化宏定义 #define   uint unsigned int    //简化宏定义  sbit Key_1=P1^0;  //校零按键定义IO口 sbit Key_2=P1^1;  ///绝对校零按键定义IO口  sbit Key_3=P1^2;  ///暂停显示控制按钮  sbit Key_4=P1^3; sbit Key_5=P1^4; sbit Key_6=P1^5;   sbit Beep=P2^3;   ///蜂鸣器标志位  bit  angle_flag=0;  ////判断是绝对角度还是相对角度 ,angle_flag=0绝对角度显示 Absolute angle、    =1相对显示   relative angle   int xDat,yDat,zDat;  ///倾角传感器的临存数据变量 int x,y,z;    ///角度数据滤波计算暂存变量 int xNum,yNum,zNum;  ///角度数据滤波计算暂存变量 int ShowDatx,ShowDaty,ShowDatz;  ////过滤后角度数据变量 bit SysError,key1,key2,key3,key4,key5,key6,SysMode;  //系统标志位错误,按键控制3个标志位,系统显示状态标志位 uint ButtonTime=0;   //按钮等待延迟变量  int AngleDat,Dat1AngleDat;  ///实时角度值暂存变量 int AngleDat_value=0;  float AngleDatx,CalibrationDat;   ////实际角度值,校准角度值  uchar state,ms;   ///数据显示变量,定时变量 bit s1,Beep1;   ///数据闪烁标志位,蜂鸣器报警标志位   /*  无论是什么单片机,只要使用内部存储区域EEPROM基本使用是这样的  关于内部存储区,EEPROM,不同单片机的使用过程基本相同,单片机内有许多存储单元,或扇区,每个扇区下有许多地址, 数据存储在这些地址下。官方提供了存储函数的程序。我们只需要使用三个程序,一个是风扇区域,另一个是 数据写函数,另一个是数据读取函数。 风扇区域擦除函数-使用哪个风扇区域,先擦拭风扇区域,填写要擦除风扇区域的第一个地址  例如  SectorErase(0x2000)x2000的扇区数据   数据存储-风扇区域擦除后,您可以使用该址可用于存储数据     例如   byte_write(0x2000,123);  也就是说,将123存储在0中x2000地址下 数据读取-直接调用,例如     Dat=byte_read(0x2000);x读取2000地址下的数据  Dat   另外---- //51单片机存储区域为8位,即可存储的最大数据是 一旦我们存储的数据超过256,就会出现一些问题 //所以,如果您的设计需要存储的数据大于256,然后拆开数据存储   /256得到高位    之所以是256,是因为0-255,256个数 // 例如数据257           257/256=1        257%6=1    ,存储两个1,读取时,将高位数据乘以256加低位数据,还原数据  */     //按钮处理函数,通常按钮处理是判断按钮,延迟抖动,再次判断,然后按钮等待释放,但会有 //有些问题是,如果我一直按下按钮,不松开,我会一直等待按钮释放,程序会死在那里,所以我不需要处理这个按钮 //改变变量判断情况,例如,当按钮不按下时,我将变量等于0,当按钮按下时,当数值累加到 //在一定程度上,再次判断按钮是否按下。如果按钮真的按下,就执行。这个想法 void ButtonCode()  /// {  if(!Key_2)     ////恢复绝对零度校准值  {   CalibrationDat=0;  ///清除校0标志位   angle_flag=0;  }  if(!Key_1)     ///校准零度  在绝对零度下记录 角度值   (重要)  {   if(ButtonTime<1000) ButtonTime  ; ///变量累加   if(!Key_1 && ButtonTime>4)   //变量加到4后,再次判断按钮是否下
		{
			if(!key1)						//是按键按下,只要这个变量等于1,就执行一次按键操作,并且变量清零,确保执行一次
			{
				key1=1;
				CalibrationDat=AngleDatx;  //校零时记录当前系统的角度值
				angle_flag=1;
			}
		}else key1=0;
		
	}else if(!Key_3)
	{
		if(ButtonTime<1000) ButtonTime++;
		if(!Key_3 && ButtonTime>4)
		{
			if(!key3)
			{
				key3=1;
				SysMode=!SysMode;	  //显示角度锁定状态标志位置位
			}
		}else key3=0;
	}else if(!Key_4)				 //校准零度  记录在绝对零度下的 角度值   (重要)
	{
		if(ButtonTime<1000) ButtonTime++;
		if(!Key_4 && ButtonTime>4)
		{
			if(!key4)
			{
				key4=1;
				state=(state+1)%2;
			}
		}else key4=0;
		
	}else if(!Key_5)
	{
		if(ButtonTime<1000) ButtonTime++;
		if(!Key_5 && ButtonTime>4)
		{
			if(ButtonTime>80)
			{
				ButtonTime=75;
				key5=0;
			}
			if(!key5)
			{
				key5=1;
				if(state==1)
				{
					if(Dat1AngleDat<180)
					{
						Dat1AngleDat++;	
						SectorErase(0x2000);
						byte_write(0x2000,Dat1AngleDat);

					}
				}
			}
		}else key5=0;
	}else if(!Key_6)
	{
		if(ButtonTime<1000) ButtonTime++;
		if(!Key_6 && ButtonTime>4)
		{
			if(ButtonTime>80)
			{
				ButtonTime=75;
				key6=0;
			}
			if(!key6)
			{
				key6=1;
				if(state==1)
				{
					if(Dat1AngleDat>0)
					{
						Dat1AngleDat--;
						SectorErase(0x2000);
						byte_write(0x2000,Dat1AngleDat);
					}
				}
			}
		}else key6=0;
	}else ButtonTime=0;
}


/*
    1602液晶,是常用的显示器件,一共是16个管脚,其中有八个管脚是数据传输管脚,有三个管脚是数据命令使能端管脚,还有两组电源管脚,
其中一组电源管脚是给整个液晶进行供电的,还有一组电源是单纯的背景光电源,还剩下的最后一个管脚是对比度调节管脚,一般接上一个3K电
阻再接地即可。
一般我们用的函数,无非就是  LCD1602_write 和 LCD1602_writebyte
LCD1602_write(x,y);   这个函数括号里面可以填写两个数据,第一个数据只能是 0  1 ,是0就说明第二个数据对液晶来说就是命令,填1就说明
第二个数据对于液晶来说就是要显示的数据。
LCD1602_writebyte();  这个函数里面直接填上要显示的字符串即可,自动进行显示
 
 
*/


void ShowCode()	   //显示控制函数
{
	if(ShowDaty>=0)	//判断当前系统设备当前角度处于正半轴还是负半轴		 //计算绝对零度时的  角度数据
	{
		if(ShowDatz>=0)	//判断当前角度是否为0°-90°  / 还是90°-180°的饭
		{
			AngleDatx=(float)ShowDaty*90/257;	//计算当前角度值0-90  (当前角度=Y周角度数据*90°  / Y轴最大90°时对应的角度数据)
		}else
		{
		   	AngleDatx=((float)2570-ShowDaty)*90/257+900;  //计算当前角度值90-180  (当前角度=Y周角度数据*90°  / Y轴最大90°时对应的角度数据+90°)-
		}
	}else
	{
		if(ShowDatz>=0)	   //判断当前角度是否为-0°→ -90°  / 还是-90°  →  -180°的饭
		{
			AngleDatx=(float)ShowDaty*90/257;	 //计算当前角度值0--90  (当前角度=Y周角度数据*90°  / Y轴最大90°时对应的角度数据)
		}else
		{
			AngleDatx=(float)((ShowDaty*-1)-2570)*90/257-900;	  //计算当前角度值90-180  (当前角度=Y周角度数据*90°  / Y轴最大90°时对应的角度数据-90°)-
		}
	}

	ButtonCode();		 //调用按键控制程序
	AngleDatx=AngleDatx-CalibrationDat; //零点校准部分程序
	if(AngleDatx>1800)		   //角度值划分  超限制计算实际角度值
	{
		AngleDatx=-1800+(AngleDatx-1800); //角度值划分  超限制计算实际角度值	
	}else if(AngleDat<-1800)
	{
		AngleDatx=1800+(AngleDatx+1800);    //角度值划分  超限制计算实际角度值
	}
	AngleDat=(int) AngleDatx;  //赋值当前角度值并显示
	
	if(state==0)
	{
		LCD1602_write(0,0x80);		// 81   95    90   -90
		if(angle_flag==0) 	LCD1602_writebyte(" Absolute angle ");
		else 				LCD1602_writebyte(" Relative angle ");
		
		LCD1602_write(0,0xC0);		 //显示到第二行
		LCD1602_writebyte("DAT:");	//显示当前角度值
		if(SysMode==0)
		{	AngleDat_value=AngleDat;
			if(AngleDat<0)			//如果角度为负数
			{
				LCD1602_writebyte("-");				//显示对应的数据
				LCD1602_write(1,0x30+AngleDat*-1/1000%10);
				LCD1602_write(1,0x30+AngleDat*-1/100%10);				
				LCD1602_write(1,0x30+AngleDat*-1/10%10);
				LCD1602_writebyte(".");				
				LCD1602_write(1,0x30+AngleDat*-1%10);	
			}else
			{					   	//如果角度为正数
				LCD1602_writebyte(" ");
				LCD1602_write(1,0x30+AngleDat/1000%10);	   //显示对应的数据
				LCD1602_write(1,0x30+AngleDat/100%10);				
				LCD1602_write(1,0x30+AngleDat/10%10);
				LCD1602_writebyte(".");				
				LCD1602_write(1,0x30+AngleDat%10);			
			}
			LCD1602_write(1,0xdf);	//单位显示符号
			if((Dat1AngleDat*10AngleDat) && Dat1AngleDat!=0) Beep1=1;
			else Beep1=0;
		}
		else 
		 {
		    
        	if(AngleDat_value<0)			//如果角度为负数
			{
				LCD1602_writebyte("-");				//显示对应的数据
				LCD1602_write(1,0x30+AngleDat_value*-1/1000%10);
				LCD1602_write(1,0x30+AngleDat_value*-1/100%10);				
				LCD1602_write(1,0x30+AngleDat_value*-1/10%10);
				LCD1602_writebyte(".");				
				LCD1602_write(1,0x30+AngleDat_value*-1%10);	
			}else
			{					   	//如果角度为正数
				LCD1602_writebyte(" ");
				LCD1602_write(1,0x30+AngleDat_value/1000%10);	   //显示对应的数据
				LCD1602_write(1,0x30+AngleDat_value/100%10);				
				LCD1602_write(1,0x30+AngleDat_value/10%10);
				LCD1602_writebyte(".");				
				LCD1602_write(1,0x30+AngleDat_value%10);			
			}
		 }

		LCD1602_write(0,0xC0+11);
		if(SysMode==0)				  //显示系统状态
		{
			LCD1602_writebyte("  ON ");
		}else
		{
			LCD1602_writebyte("  OFF");
		}
	}else
	{
		LCD1602_write(0,0x80);
		LCD1602_writebyte("Alarm Value:");
		if(state==1&&s1==1)
		{
			LCD1602_writebyte("    ");	
		}else
		{
			LCD1602_write(1,0x30+Dat1AngleDat/100%10);	   //显示对应的数据
			LCD1602_write(1,0x30+Dat1AngleDat/10%10);				
			LCD1602_write(1,0x30+Dat1AngleDat%10);		
		}
		LCD1602_write(1,0xdf);	//单位显示符号	
		LCD1602_write(0,0xC0);
	    LCD1602_writebyte("                ");
	}
}

void interrupt_Init(void)
{
	TMOD=0x01;	   //定义两个定时器
	TL0 = 0x00;		//设置定时初值
	TH0 = 0x4C;		//设置定时初值	
	EA=1;          //开总中断
	ET0=1;
	TR0=1; 	
}

void main()	   //系统主程序
{ 
	uchar IDDat,cs;
	interrupt_Init();
	LCD1602_cls();	 //系统初始化,   液晶显示初始化
	Dat1AngleDat=byte_read(0x2000);
	


	if(Dat1AngleDat>180 || Dat1AngleDat<0)
	{				 
		Dat1AngleDat=45;
		SectorErase(0x2000);
		byte_write(0x2000,Dat1AngleDat);
					
	}

	Init_ADXL345();                 	//初始化ADXL345
	IDDat=Single_Read_ADXL345(0X00);	//读出的数据为0XE5,表示正确
	if(IDDat!=0XE5)	SysError=1;		//判断传感器是否正常
	else SysError=0;
	while(1)                         	//循环
	{ 
		if(SysError==0)		  //如果系统传感器正常  则
		{
			if(cs<10)	//读取角度滤波计算函数部分	    连续记录10次X、Y、Z角度数据
			{
				cs++;
				Multiple_Read_ADXL345();       	//连续读出数据,存储在BUF中	  
				xDat=BUF[1]*256+BUF[0];		  //连续读取XYZ的实时角度距离
				yDat=BUF[3]*256+BUF[2];
				zDat=BUF[5]*256+BUF[4];
				xNum+=xDat;					  //记录10次的XYZ总数据
				yNum+=yDat;
				zNum+=zDat;
			}else
			{
				x=x*0.7+xNum*0.3;		//滤波计算函数部分
				y=y*0.7+yNum*0.3;
				z=z*0.7+zNum*0.3;
				ShowDatx= x;		   //得出滤波后的系统角度数据
				ShowDaty= y;
				ShowDatz= z;
				cs=0;
				xNum=yNum=zNum=0;	 //清除	 记录10次的XYZ总数据
			}		
		}   
		ShowCode();		     		   //调用系统显示计算角度函数
	}
} 												  //传感器错误


void init_1() interrupt 1    //定时器0中断服务程序
{
	TH0=0x3c;	   	//重新赋值
	TL0=0xb0;
	ms++;		 //计时变量计时
	if(ms%2==0)						//250ms定时
	{
		if(Beep1==1) Beep=!Beep;	//只要beep等于1,蜂鸣器就会滴滴滴滴的响
		else Beep=1;				//否则就是不响
	}
	if(ms%10==0)			   //500ms定时
	{	
		s1=!s1;
	}
	if(ms>19)	      //1s定时
	{
		ms=0;
	}
}

本文介绍了在设计的过程的关键点,供大家参考学习,如果有错误或者不明白的可以直接私信作者,或者添加微信biyezhan007。

其他资料

【参考文献】

[1]肖金球,冯翼.增强型51单片机与仿真技术[M].北京:清华大学出版社社, 

[2]肖金球.单片机原理与接口技术[M].北京:清华大学出版社,2004,17-128. 

[3]周鸿武.基于单片机的酒精浓度检测仪设计[J].制造业自动化2012,02. 

[4] 康华光.电子技术基础模拟部分(第五版)[M].高等教育出版社,2006年

[5] 康华光.电子技术基础数字部分(第五版)[M].高等教育出版社,2006年

[6] 纪宗南.单片机外围器件使用手册—输入通道器件分册[M].北京航空航天大学出版社,2005年 

[7]贾伯年.传感器技术[M].东南大学出版社,2000年

[8]何希才.传感器及其应用[M].国防工业出版社,2001年

[9]郑学坚.微型计算机原理及应用[M].清华大学出版社,2006年

[10]张水利. 单片机原理及应用. 黄河水利出版社,出版年:2008年8月 

[11]谭浩强. C程序设计. 第三版. 清华大学出版社,出版年:2005年7月

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章