多路温度采集的实现(原理图+程序源代码)

实验原理图:

实验源代码:

 

/**************************************************************************************/
/*单片机采用STC89C52,晶振为12MHz。P1.0与DS18B20的数据端相连*/
/*P0为数码管的字段码口,P2口为位选码口*/
/*P3.2与按键0相连,P3.3与按键1相连*/
/*MAXNUM是单总线上最大可扫描DS18B20个数*/
/*flag为温度的正负号标志单元,flag为"1"时表示温度值为负值,为"0"时表示温度值为正值*/
/*温度测量范围为(-55.0~99.9摄氏度)。*/
/*变量cc中保存计算出的温度值的整数部分,xs保存计算出的温度值的小数部分的第一位*/
/*num记录当前单总线上DS18B20的个数*/
/*z是按键标志位,z为1时表明有按键按下*/
/*a,b分别是按键0和按键1的记录变量*/
/*m是轮换显示各DS18B20温度值和序列号的全局标志变量*/
/*二维数组ID用于记录各DS18B20的ROM序列号*/
/*数组disbuffer是LCD显示缓存数组*/
/*数组RomID_temp用于匹配DS18B20时临时记录要匹配DS18B20的序列号*/
/*共用体temp用于存放从DS18B20读入的数据*/
/**************************************************************************************/

#include <reg51.h>	 
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
#define MAXNUM 4        //宏定义单总线上最大可扫描DS18B20个数
sbit DS=P2^2; 			//用P1.0口作为各DS18B20与单片机的I/O口
sbit Key0=P3^2;			//P3.2用作按键0的输入,采用外部中断方式获取按键信号
sbit Key1=P3^3;			//P3.3用作按键1的输入,采用外部中断方式获取按键信号
sbit dula=P2^6;
sbit wela=P2^7;

//unsigned char code chocode[]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};
union{					//定义共用体temp用于存放从DS18B20读入的数据,Keil C51采用大端格式,c[1]是高地址.
	uchar c[2];			//其中存放读取温度数值的低字节;c[0]是低地址,其中存放读取温度数值的高字节.
	uint x;				//这样根据大端格式的要求,即数据的高字节存储在低地址中,数据的低字节存放在高地址中,
}temp;					//x便刚好是读出的温度数值.
						

uchar idata flag;		//温度的正负号标志,flag为"1"时表示温度值为负值,为"0"时表示温度值为正值
uint cc,xs;				//变量cc中保存计算出的温度值的整数部分,xs保存计算出的温度值的小数部分的第一位
uchar idata disbuffer[6];	//LCD显示缓存数组
uchar idata ID[4][8]={0};	//用于记录各DS18B20的ROM序列号
uchar idata RomID_temp[8];	//匹配DS18B20时临时记录要匹配DS18B20的序列号
uchar m=0;				//m是轮换显示各DS18B20温度值和序列号的全局标志变量
uchar num=0;			//num记录当前单总线上DS18B20的个数
uchar z=0;				//z是按键标志位,z为1时表明有按键按下
uchar a=0;				//a是按键0的记录变量
uchar b=0;				//b是按键1的记录变量


void delay(uint i)      //延时i*9.62us 
{
	uint j;
	for(j=i;j>0;j--);
}

void delay_ms(uchar i)  //延时(j*2+1+2)*i+5 个机器周期    
{ uchar j;			    //12MHz时,延时 0.5*i ms
  do{j=248; 		 
     do{j--;}while(j); 
     i--; 
    }while(i); 
}


void delay_2us(uchar i)	  //延时 2*i+5 us
{
  while(--i);
}

uchar DS_init(void)       //18B20复位,初始化函数
{
  uchar presence;
  DS=0;          delay_2us(250); //根据DS18B20的复位时序.先把总线拉低555us
  DS=1;          delay_2us(30);	 //再释放总线,65us后读取DS18B20发出的信号
  presence=DS;   delay_2us(250); //如果复位成功,则presence的值为0;否则为1
  return (presence);	  		 //返回0则初始化成功,否则失败
}

uchar read_byte(void)       //读1字节
{
  uchar i,j,dat=0;
  for(i=1;i<=8;i++)			//作8个循环,读出的8位组成一个字节
    {DS=0;   _nop_();       //先将总线拉低1us, 
     DS=1;   delay_2us(2);	//再释放总线,产生读起始信号,延迟9us后读取总线上的DS18B20发出的值
     j=DS;   delay_2us(30);	//一位读完后,延迟65us后读下一位
     dat=(j<<7)|(dat>>1);   //读出的数据最低位在一个字节的最低位,这样刚好一个字节在DAT里
    }
  return(dat);
}

uchar read_2bit(void)       //读2位
{
  uchar i=0,j=0;
  DS=0;   _nop_();          //先将总线拉低1us, 
  DS=1;   delay_2us(2);		//再释放总线,产生读起始信号,延迟9us后读取总线上的DS18B20发出的值
  j=DS;   delay_2us(30);	//一位读完后,延迟65us后读下一位
  DS=0;   _nop_();          
  DS=1;   delay_2us(2);
  i=DS;   delay_2us(30);
  i=j*2+i;  			//将读出的两位放到变量i中,其中第一个读出的位处于i的第1位;而第二个读出的位处于i的第0位
  return(i);
}


void write_byte(uchar dat)	//写1字节
{  
  uchar i;
  for(i=0;i<8;i++)			//作8个循环,写入的8位组成一个字节
    {DS=0;					//先将总线拉低
     DS = dat&0x01;			//向总线上放入要写的值
     delay_2us(50);			//延迟105us,以使DS18B20能采样到要写入的值
     DS = 1;				//释放总线,准备写入下一位
     dat>>=1;				//将要写的下一位移到dat的最低位		
   	}
}


void write_bit(bit dat)	//写1位
{  
  DS=0;				//先将总线拉低
  DS=dat;			//向总线上放入要写的值
  delay_2us(50);	//延迟105us,以使DS18B20能采样到要写入的值
  DS = 1;			//释放总线
}


void display_ROMID(void)			//定义显示序列号的函数
{	
	uchar i,p,t,k;
	uint q;
	uchar disbuffer_rom[8];
	uchar codevalue[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xff,0xbf};
							//共阳极的字段码
	uchar chocode[]={0xf7,0xfb,0xfd,0xfe,0x7f,0xbf,0xdf,0xef};
							//位选码表
	z=0; //z归零,表明没有按键按下
	
	for(q=0;q<500;q++)	//显示各DS18B20的序号
	{		
			if(z==1)	//如果z为1,有按键按下,表明要显示不同的内容,此时从这个显示循环中跳出
				break;		
			t=chocode[7];		//取当前的位选码
			P2=t;				//送出位选码
			t=codevalue[m+10];	//查得显示字符的字段码
			P0=t;				//送出字段码
			delay(100);			
	}
		
	P2=0xFF;			//关断LCD一段时间,产生闪屏效果
	for(q=0;q<500;q++)
	{	
		if(z==1)		//如果z为1,有按键按下,表明要显示不同的内容,此时从这个循环中跳出
			break;
		delay(100);
	}
	
	for(k=0;k<8;k=k+4)	//依次显示序列号的低32位或高32位
	{		if(z==1)	//如果z为1,有按键按下,表明要显示不同的内容,此时从这个循环中跳出
			break;
												//接下来将序列号的低32位或高32位值取出放入disbuffer_rom存储
			 disbuffer_rom[0]=(ID[m][k]&0x0F);
			 disbuffer_rom[1]=((ID[m][k]&0xF0)>>4);
			 disbuffer_rom[2]=(ID[m][k+1]&0x0F);
			 disbuffer_rom[3]=((ID[m][k+1]&0xF0)>>4);
			 disbuffer_rom[4]=(ID[m][k+2]&0x0F);
			 disbuffer_rom[5]=((ID[m][k+2]&0xF0)>>4);
			 disbuffer_rom[6]=(ID[m][k+3]&0x0F);
			 disbuffer_rom[7]=((ID[m][k+3]&0xF0)>>4);	
	
			for(q=0;q<250;q++)		//显示序列号的低32位或高32位
			{	if(z==1)			//如果z为1,有按键按下,表明要显示不同的内容,此时从这个显示循环中跳出
				break;
				for (i=0;i<8;i++)
				{	if(z==1)
					break;
					t=chocode[i];		//取当前的位选码
					P2=t;				//送出位选码
					p=disbuffer_rom[i];	//取当前显示的字符
					t=codevalue[p];		//查得显示字符的字段码
					P0=t;				//送出字段码
					delay(40);			
				}
			}		
			P2=0xFF;			 //显示完一轮,灯灭
			for(q=0;q<500;q++)
			{
				if(z==1)		//如果z为1,有按键按下,表明要显示不同的内容,此时从这个显示循环中跳出
				break;
				delay(100);        
			}
	  }
        
}


void display_error(void)			//定义显示总线上没有DS18B20时的函数
{	
	uchar i,p,t;
	uint q;
	uchar disbuffer_temp[8]={0,2,8,1,16,0x0f,0x0f,0}; //显示内容为"OFF 1820"
	uchar codevalue[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xff,0xbf};
							//共阳极的字段码
	uchar chocode[]={0xf7,0xfb,0xfd,0xfe,0x7f,0xbf,0xdf,0xef};
							//位选码表
				
	    
				for(q=0;q<250;q++)
				{
		 			for (i=0;i<8;i++)
					{
  						t=chocode[i];		 //取当前的位选码
  						P2=t;				 //送出位选码
  						p=disbuffer_temp[i]; //取当前显示的字符
  						t=codevalue[p];		 //查得显示字符的字段码
 						P0=t;				 //送出字段码
						delay(40);			
					}
				}

		  
	  	 	

        
}

uchar search_rom(void)   //遍历搜索单线上所连的所有18b20的序列号
{ 
  uchar k,l=0,chongtuwei,m,n,a;  
  uchar _00web[MAXNUM]={0};
  do
   {
     DS_init();         //复位单总线上的所有DS18B20
     write_byte(0xf0);  //单片机发布搜索命令
     for(m=0;m<8;m++)
       {
         uchar s=0; //s用来记录本次循环得到的1个字节(8位)序列号
         for(n=0;n<8;n++)
            {
             k=read_2bit();  //读第m*8+n位的原码和反码,保存在k中
             k=k&0x03;		 //屏蔽掉k中其它位的干扰,为下一步判断作准备
             s>>=1;			 //s右移一位,即把上一次循环得到的位值右移一位,
			 				 //这样执行完一次n为变量的循环,便可得到一个字节的ROM号
             if(k==0x01)     //k为01,表明读到的数据为0,即所有器件在这一位都为0,所以向总线上写0
			 				 //同时对s的值不进行操作,即这位的序列号记为0
                {     
                 write_bit (0);
                }
             else if(k==0x02)//k为02,表明读到的数据为1,即所有器件在这一位都为1,所以向总线上写1
                {
                 s=s|0x80;  //记录下此位的值,即s的最高位置1
                 write_bit (1);
                }
             else if(k==0x00)
                {
                 chongtuwei=m*8+n+1;   //记录下这个冲突位发生的位置;之所以加1是为了让_00web数组中的第一位保持0不变,
 				 					   //便于判断搜索循环是否结束;
                 if(chongtuwei>_00web[l])//如果冲突位比标志00位的位高,即发现了新的冲突位,那么这位写0
                    {      
                     write_bit (0);
                     _00web[++l]=chongtuwei;    //依次记录位比冲突标志位高的冲突位在_00web数组中  
                    }
                 else if(chongtuwei<_00web[l]) //如果冲突位比标志00位的位低,那么把ID中这位所在的字节右移n位,
                    {						 //从而得到这位先前已经写过的值,如果为0,说明这位先前写的是0,那么继续写0,
				 						    // 如果这位先前写的是1,那么继续写1
					 a=(ID[num-1][m]>>n)&0x01;
                     s=s|(a<<7);			//记录下此位的值
                     write_bit(a);
                    }
                 else if(chongtuwei==_00web[l])//如果冲突位就是标志00位,那么s的最高位置1,即这位记为1,同时向总线上写1;
				 							 //之所以不写0,是因为前面已经写过0,再写0,就得不到遍历的效果.
                    {
                     s=s|0x80;
                     write_bit (1);
                     l=l-1;				//改变标志00位的位置,即向前推一个00位,并且是往低位方向推
                    }
                }
			 else if(k==0x03)  //k为03,表明总线上没有DS18B20,函数结束,同时返回零值
			    {return(0);}
            }    
         ID[num][m]=s;  
       }
     num++;			//DS18B20的个数加1
   }while((_00web[l]!=0)&&(num<MAXNUM));//如果冲突位记录数组已经前推到0值或是DS18B20的数目已经超过最大允许数目,
   									  //就退出循环
  return(1);  //搜索完毕,返回1值
}


void Read_Temperature_rom(void) //读取温度函数
{	uchar i;
	DS_init();
	write_byte(0x55);		//匹配ROM
	for(i=0;i<8;i++)		//发出64位ROM编码
			write_byte(RomID_temp[i]);
	write_byte(0x44);		//开始转换
	DS_init();
	write_byte(0x55);		//匹配ROM
	for(i=0;i<8;i++)		//发出64位ROM编码
			write_byte(RomID_temp[i]);
	write_byte(0xBE);		//发读温度命令
	temp.c[1]=read_byte();  //读低字节,之所以c[1]中放低字节,是因为C51采用的是大端格式,具体详见共用体定义的说明
	temp.c[0]=read_byte();	//读高字节,之所以c[0]中放低字节,是因为C51采用的是大端格式,具体详见共用体定义的说明
}


void Temperature_cov(void)	//温度转换
{
	if (temp.c[0]>0xf8) {flag=1;temp.x=~temp.x+1;}
							//如果为负,则符号标志置1,计算温度值,注意c[0]中放的是读取温度值的高字节
							//还要注意读出的数值一共是12位
	
	cc=temp.x/16;			//计算出温度值的整数部分,这个语句相当于数值乘0.0625再取整数部分
	xs=temp.x&0x0f;			//取温度值小数部分的第一位
	xs=xs*10;				//这两条语句相当于乘0.625,得小数位的第一位,注意不是乘0.0625
	xs=xs/16;
}


void display(void)			//定义温度底层显示函数
{
	uchar codevalue[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00,0x40};//共阴极的字段码
	uchar chocode[]={0xdf,0xef,0xf7,0xfb,0xfd,0xfe,};//位选码表
	uchar i=0,p,t;
	disbuffer[4]=0x10;   //让LCD的第5位全灭,即什么也不显示,注意是16进制表示
	disbuffer[5]=0x0A+m; //LCD的第6位显示DS18B20序列号,以字母A,B,C,D,E,F来给示,所以加上0x0a
	if (flag==1)disbuffer[3]=0x11;//判断是否显示负号
     	 else disbuffer[3]=0x10;
	disbuffer[0]=xs;   //放入小数位显示的数值
	disbuffer[1]=(cc%10);//放入个数要显示的数值
	disbuffer[2]=(cc/10);//放入十位要显示的数值
	for (i=0;i<6;i++)
	{
  		/*
		t=chocode[i];		//取当前的位选码
  		P2=t;				//送出位选码
  		p=disbuffer[i];		//取当前显示的字符
  		t=codevalue[p];		//查得显示字符的字段码
 		if (i==1) t=t+0x80;	//个位比较特殊,因为有小数点,所以要加上0x80
 		P0=t;				//送出字段码
		delay(40);
		*/	
		t=codevalue[disbuffer[i]];
		if (i==1) t=t+0x80;
	   	dula=1;	
		P0=t;
		dula=0;
		P0=0xff;
		wela=1;
		P0=chocode[i];
		wela=0;	
		delay(45);	
	}
}


void diplay_final(void)  //定义温度上层显示函数
{		uint q,r;
		z=0;
		for(q=0;q<8;q++)  //将要显示DS18B20的温度值的序列号放入数组RomID_temp中
			{
				RomID_temp[q]=ID[m][q];
			 }

		P0=0xFF;

		if(a==0)		//如果按键0的标志变量a=0,即进行闪烁显示
		{
			for(q=0;q<3;q++)    //闪烁空隙仍在读取温度
			{	
				if(z==1)		//如果z为1,有按键按下,表明要显示不同的内容,此时从这个循环中跳出
				break;
				flag=0;
				Read_Temperature_rom();     // 读取双字节温度 
				Temperature_cov();      	//温度转换		
				for(r=0;r<15;r++)
				delay(1000);
	
		 	}
		}
		for(q=0;q<5;q++)			//读取温度并显示
		{	
			if(z==1)				//如果z为1,有按键按下,表明要显示不同的内容,此时从这个显示循环中跳出
				break;
			flag=0;
			Read_Temperature_rom();  // 读取双字节温度 
			Temperature_cov();      //温度转换
			for(r=0;r<100;r++)
				display();             //显示温度
		}
}



void key_0() interrupt 0    //外部中断0的中断处理函数
{
	delay(1200); //延时消抖
	if(Key0==0)
	{
	if(a==0)	//a只可能取0值或1值
    	a=1;
	else a=0;
	b=0;		//同时一旦0键按下就将b归零
	z=1;		//将有按键按下标志位z置位
	}
 }

 
void key_1() interrupt 2	//外部中断1的中断处理函数
{
	delay(1200); //延时消抖
	if(Key1==0)
	{ 	if(a==0)	//只有a等于0时才让b的值在0或1之间转变
		{
		if(b==0)
    		b=1;
		else b=0;
		}
		z=1;		//将有按键按下标志位z置位
		if(a==1)	//当a=1时进入固定显示一个DS18B20的温度值的状态
		{	
			m++;	//按一次1键m值加1,即在不同的DS18B20切换显示
			if(m>=num) //当然,m的值不能超过或等于总线上挂接的DS18B20的数目
			m=0;
		}
	}
 }

void main()					//主函数
{	
	uchar p,i;
	delay(10);
	p=search_rom();  //执行搜索DS18B20算法,同时返回搜索情况,p值为0,表明总线上没有DS18B20,p值为1表明搜索到DS18B20; 
	if(p==0) 		//如果总线上没有DS18B20,显示"OFF 1820"
	while(1)
	display_error();
	EA=1;  	//开中断
	EX0=1;	//允许外部中断0
	IT0=1;  //外部中断0为边沿触发方式
	EX1=1;	//允许外部中断1
	IT1=1;	//外部中断1为边沿触发方式
	while(1)
	{   
		if(a==0&&b==0)	//按键0和1的状态变量值都为0,此时轮换显示各DS18B20的温度值
		{
			diplay_final(); //显示DS18B20的温度值
		 	if(m<num-1)	//为了实现轮换显示,要改变m的值
			m++;
			else m=0;
			if(z==1)	//如果有按键按下,将m清零
			{
				m=0;
			}
		}
		
		else if(a==0&&b==1) //按键0和按键1的状态变量值分别为0和1,此时轮换显示各DS18B20的序列号
		{   
		    
			display_ROMID(); //显示DS18B20的序列号
			if(m<num-1)		//为了实现轮换显示,要改变m的值
			m++;
			else m=0;
			if(z==1)	//如果有按键按下,将m清零
			{
				m=0;
			}
		 }
		else	//按键0的状态变量值为1,此时固定显示各DS18B20的温度值,按键1用于不同DS18B20间的切换
		{  
		   diplay_final(); //显示DS18B20的温度值

 		}
	}
}

由于最近比较忙,所有过多的芯片简介就没介绍,希望大家理解。

 

  • 2
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值