这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 高校专区 » 坤创E-Geek/天科大新电社 » 08. 单片机对DS18B20温度传感器进行数据采集和使用

共22条 1/3 1 2 3 跳转至

08. 单片机对DS18B20温度传感器进行数据采集和使用

高工
2019-01-27 23:36:24     打赏

马上就是腊月23了,也是中华民族传统的小年了。赶在小年之前,大Z在此更新一篇代码~希望给大家助力一波。

在了解了单片机片上的IO口、中断和定时计数器等资源后,今天我们来进行一个单片机与其外部器件进行数据通信的习题操练。当然,今天我们首先进行的是大家学单片机第一个接触的传感器,即温度传感器DS18B20。对这个传感器,我就不过多的进行介绍了,相信大家也都看过它的使用手册和时序图。 DS18B20是蓝桥杯竞赛考过的题目,但是不排除今年不会考到。对于蓝桥板上的传感器资源,也不过DS18B20、超声波、红外、光敏电阻+AD转换了。因此,围绕这四个板上资源出题的概率还是比较大的。很有必要进行相关练习的。

DS18B20的使用,其实并不像大家想的那么难,只需按照其时序图,一步一步的来写它的各个函数,大家只要耐心一点,都能推出来。当然请务必注意一点,对于时序图中的延时函数产生的延时,一定要务必按照时序图中所限定的范围哦~不然是采集不到正确的温度数据的。

接下来我们一起先看一下本节针对DS18B20所设计的习题(注意今天和前几天不同,今天只有一道题哦~)。但是该题却有3个要求,如下:

(1)完成DS18B20温度采集,并使用数码管上显示;

(2)超限(上限30,下限20)声光报警;

(3)利用按键可以设定上下限报警值,超上限控制继电器吸合。

       说明:由于小红板上没有继电器,所以采用第五个led灯(即LED8)来模拟继电器

下面仍照惯例给出本题目的代码,并在文末给出所有代码压缩包的下载链接,需要的小可耐们,请移步下载哦~不要嫌贵,下载免费!!

-----------------------蓝桥板代码---------------------

蓝桥板 代码:

/************************************************************************************************
1、完成DS18B20温度采集,并使用数码管上显示。
2、超限(上限30,下限20)声光报警。
3、利用按键可以设定上下限报警值,超上限控制继电器吸合。
*************************************************************************************************/

#include<stc15f2k60s2.h>
#include"intrins.h"
#
#define uchar unsigned char
#define uint  unsigned int 

sbit DQ = P1^4;  //单总线接口

uchar code smgtab[]={0XC0,0XF9,0XA4,0XB0,0X99,0X92,0X82,0XF8,0X80,0X90,0XBF,0XFF};

uchar tt;
uchar wendu;
uchar min=20,max=30;
uchar yi,er,san,si,wu,liu,qi,ba;
uchar shezhi,jia,jian,qingchu,flag;

void init();      //蓝桥杯板子初始化程序
void keyScan();   //键盘扫描
void delayms(uint z);//ms级延时函数
uchar temget();   //ds18b20获取温度
bit init_ds18b20(void);//ds18b20初始化
void timer0Init(void);//5毫秒@11.0592MHz
void Delay_OneWire(unsigned int t);//ds18b20延时函数
void Write_DS18B20(unsigned char dat);//ds18b20写函数
unsigned char Read_DS18B20(void);//ds18b20读函数

void  displayOne(uchar yi,uchar er);//第一 二个数码管显示函数
void  displayTwo(uchar san,uchar si);//第三 四个数码管显示函数
void  displayThree(uchar wu,uchar liu);//第五 六个数码管显示函数
void  displayFour(uchar qi,uchar ba);//第七 八个数码管显示函数


/**********************主函数***********************************/
void main()
{
	init();
	timer0Init();		//5毫秒@11.0592MHz
	yi=11;er=11;san=11;si=11;wu=11;liu=11; 
	while(1)
	{
		if(shezhi==0)
		{
			EA=1;    //开总中断
			ET0=1;   //开定时器0中断
			P2=0x80;P0=0xef;
			wendu=temget();
			yi=11;er=11;san=11;si=11;wu=11;liu=11; qi=wendu/10; ba=wendu%10;
		}

		else if(shezhi==1)
		{
			EA=0;ET0=0; //关闭总中断与定时器0的中断
			P2=0x80;P0=0x7f;
			if(flag==1)
			{
				if(jia==1)
				{
					jia=0;
					if(min>=0){min=min+1;}
				}
				else if(jian==1)
				{
					jian=0;
					if(min>0){min=min-1;}
					else if(min==0){min=0;}
				}
			}
			else if(flag==0)
			{
				if(jia==1){jia=0;max=max+1;}
				else if(jian==1){jian=0;max=max-1;}
			} 
			yi=10;er=min/10;san=min%10;si=11;wu=11;liu=10;qi=max/10;ba=max%10;
		}
		keyscan();
		displayOne(yi,er);
		displayTwo(san,si);
		displayThree(wu,liu);
		displayFour(qi,ba);
	}
}


void delayms(uint z)
{
	uint x,y;
	for(x=z;x>0;x--)
		for(y=845;y>0;y--);
}
	
	
void init()
{
   P2=0xa0; 
	 P0=0x00;  //关闭继电器蜂鸣器
	 P2=0x80;
	 P0=0xff;  //关闭led灯
	 P2=0xc0;
	 P0=0xff;  //选择数码管
	 P2=0xff;
	 P0=0xff;  //关闭数码管
}

void timer0Init(void)		//5毫秒@11.0592MHz
{
	AUXR |= 0x80;		//定时器时钟1T模式
	TMOD &= 0xF0;		//设置定时器模式
	TL0 = 0x00;		//设置定时初值
	TH0 = 0x28;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
}


void timer0()interrupt 1
{
	tt++; 	
	if(tt>=200)
	{
		tt=0;
		if(wendu<min)
		{
			P2=0xa0;P0=0x40;	 
			P2=0x80;P0=0xfe;
		}
		else if((wendu>min)&&(wendu<max))
		{
			P2=0xa0;P0=0x00;	 
			P2=0x80;P0=0xff;
		}
		else if(wendu>max)
		{
			P2=0xa0;P0=0x50;	 //继电器开启
			P2=0x80;P0=0xfe;
		}	
	} 
}

void keyScan()
{
	if(P30==0)   //s7  设置
	{
		delayms(5);
		if(P30==0)
		{
			if(shezhi==0){shezhi=1;}
			else if(shezhi==1){shezhi=0;}
		}
		while(!P30);
	}

	else if(P31==0)  //s6 修改最小值与最大值的切换
	{
		delayms(5);
		if(P31==0)
		{
			if(flag==0){flag=1;}
			else if(flag==1){flag=0;}
		}
		while(!P31);
	}

	else if(P32==0)   //s5  设置加
	{
		delayms(5);
		if(P32==0)
		{
			jia=1;
		}
		while(!P32);
	}

	else	if(P33==0)  //s4  设置减  
	{
		delayms(5);
		if(P33==0)
		{
			jian=1;  
		}
		while(!P33);
	}
}

//单总线延时函数
void Delay_OneWire(unsigned int t)  
{
	unsigned char i;
	while(t--){
		for(i=0; i<8; i++);
	}
}

//通过单总线向DS18B20写一个字节
void Write_DS18B20(unsigned char dat)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		DQ = 0;
		DQ = dat&0x01;
		Delay_OneWire(5);
		DQ = 1;
		dat >>= 1;
	}
	Delay_OneWire(5);
}

//从DS18B20读取一个字节
unsigned char Read_DS18B20(void)
{
	unsigned char i;
	unsigned char dat;
  
	for(i=0;i<8;i++)
	{
		DQ = 0;
		dat >>= 1;
		DQ = 1;
		if(DQ)
		{
			dat |= 0x80;
		}	    
		Delay_OneWire(5);
	}
	return dat;
}

//DS18B20设备初始化
bit init_ds18b20(void)
{
  	bit initflag = 0;
  	
  	DQ = 1;
  	Delay_OneWire(12);
  	DQ = 0;
  	Delay_OneWire(80);
  	DQ = 1;
  	Delay_OneWire(10); 
    	initflag = DQ;     
  	Delay_OneWire(5);
  
  	return initflag;
}

uchar  temget()
{
   uchar temp,low,high;
	 init_ds18b20();
	 Write_DS18B20(0xcc);
	 Write_DS18B20(0x44);    //转换温度
	
	 Delay_OneWire(200);
   
   init_ds18b20();
	 Write_DS18B20(0xcc);
	 Write_DS18B20(0xbe);
	
	 low=Read_DS18B20();   //读取温度
	 high=Read_DS18B20(); //读取温度
	
	 temp=high<<4;
	 temp|=(low>>4);
	
	 return temp;
}

void  displayOne(uchar yi,uchar er)
{
   P2=0xc0;
	 P0=0x01;     //选通第一个数码管
	 P2=0xff;
	 P0=smgtab[yi];//位选
	 delayms(1);
	
	 P2=0xc0;
	 P0=0x02;    //选通第二个数码管
	 P2=0xff;
	 P0=smgtab[er];//位选
	 delayms(1);
}

void  displayTwo(uchar san,uchar si)
{
   P2=0xc0;
	 P0=0x04;     //选通第三个数码管
	 P2=0xff;
	 P0=smgtab[san];//位选
	 delayms(1);
	
	 P2=0xc0;
	 P0=0x08;    //选通第四个数码管
	 P2=0xff;
	 P0=smgtab[si];//位选
	 delayms(1);
}

void  displayThree(uchar wu,uchar liu)
{
   P2=0xc0;
	 P0=0x10;   //选通第五个数码管
   P2=0xff;
	 P0=smgtab[wu];//位选
	 delayms(1);
	
	 P2=0xc0;
	 P0=0x20;  //选通第六个数码管
	 P2=0xff;
	 P0=smgtab[liu];//位选
	 delayms(1);
}

void  displayFour(uchar qi,uchar ba)
{
   P2=0xc0;
	 P0=0x40;  //选通第七个数码管
	 P2=0xff;
	 P0=smgtab[qi];//位选
	 delayms(1);
	
	 P2=0xc0;
	 P0=0x80;  //选通第八个数码管
	 P2=0xff;
	 P0=smgtab[ba];//位选
	 delayms(1);
}


-----------------------小红板代码----------------------

小红板 代码:

/**************************************************
1、完成DS18B20温度采集,并使用数码管上显示。
2、超限(上限30,下限20)声光报警。
3、利用按键可以设定上下限报警值,超上限控制继电器吸合。
由于小红板没有继电器模块,因此用LED8点亮来模拟继电器开启
**************************************************/
#include"reg52.h"
#include<intrins.h>
#define uchar unsigned char
#define uint  unsigned int 

sbit	P_HC595_SER   = P2^7;	//pin 14	SER		data input
sbit	P_HC595_RCLK  = P2^5;	//pin 12	RCLk	store (latch) clock
sbit	P_HC595_SRCLK = P2^6;	//pin 11	SRCLK	Shift data clock

sbit  s5=P3^0;
sbit  s10=P3^1;
sbit  s15=P3^2;
sbit  s20=P3^3;
sbit DQ = P2^2;  //单总线接口
sbit  beep=P2^3; //蜂鸣器

uchar  code smgtab[]={0X3F,0X06,0X5B,0X4F,0X66,0X6D,0X7D,0X07,0X7F,0X6F,0X40,0X00};	//共阴数码管段值


bit fla;
uchar min=20,max=30;
uchar wendu,tt;
uchar shanshuo;
uchar shezhi,jia,jian,qingchu,flag;
uchar yi,er,san,si,wu,liu,qi,ba;

uchar  temget();//ds18b20获取温度
void smg_Init();//数码管初始化函数
void delayms(uint z);//ms级延时函数
bit init_ds18b20(void);//ds18b20初始化
void timer0Init(void);		//50毫秒@11.0592MHz
void keyScan();//键盘扫描
void Send_595(unsigned char dat);
void Delay_OneWire(unsigned int t);  //ds18b20延时函数
void Write_DS18B20(unsigned char dat);//ds18b20写函数
unsigned char Read_DS18B20(void);//ds18b20读函数
void  displayOne(uchar yi,uchar er);//第一 二个数码管显示函数
void  displayTwo(uchar san,uchar si);//第三 四个数码管显示函数
void  displayThree(uchar wu,uchar liu);//第五 六个数码管显示函数
void  displayFour(uchar qi,uchar ba);//第七 八个数码管显示函数


/*****************************主函数**********************************************/
void main()
{
	smg_Init();
	timer0Init();
	while(1)
	{
		if(shezhi==0)     //未设置前只显示当前室温
		{
			EA=1;    //开总中断
			ET0=1;	 //开定时器0中断
			wendu=temget();
			if(wendu<min)
			{
				shanshuo=0; 
			}
			else if((wendu>=min)&&(wendu<=max))
			{
				shanshuo=1;
			}
			else if(wendu>max)
			{
				beep=0; 
				P1=0xef;  //由于小红板没有继电器模块,因此用LED8点亮来模拟继电器开启
			}	

			yi=11;er=11;san=11;si=11;wu=11;liu=11; qi=wendu/10; ba=wendu%10;
		}
		else if(shezhi==1)  //设置后可以调整温度上下限值
		{
			EA=0;ET0=0;
			beep=1; 
			if(flag==0)
			{
				if(jia==1){jia=0;max=max+1;}
				else if(jian==1){jian=0;max=max-1;}
			}
			else if(flag==1)
			{
				if(jia==1)
				{
					jia=0;
					if(min>=0){min=min+1;}  
				}
				else if(jian==1) 
				{
					jian=0;
					if(min>0){min=min-1;}
					else if(min==0){min=0;}
				}
			}	 
			yi=10;er=min/10;san=min%10;si=11;wu=11;liu=10;qi=max/10;ba=max%10;
		}
		keyScan();
		displayOne(yi,er);
		displayTwo(san,si);
		displayThree(wu,liu);
		displayFour(qi,ba);
	}
}

void delayms(uint z)
{
	uint x,y;
	for(x=z;x>0;x--)
		for(y=110;y>0;y--);
}

void smg_Init()
{
	Send_595(0x00);  //同时选择8个数码管
	Send_595(0x00);	 //让8个数码管同时显示消隐字符
	P_HC595_RCLK = 0;
	P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选	
}

void Send_595(unsigned char dat)	//发送一个字节
{		
	unsigned char	i;
	for(i=0; i<8; i++)
	{
		if(dat & 0x80)	    //判断dat的最高位是否为1
			P_HC595_SER = 1;  //最高位如果为1,则令P2.7 = 1
		else			
			P_HC595_SER = 0;  //最高位如果为0,则令P2.7 = 0
		
		P_HC595_SRCLK = 0;
		P_HC595_SRCLK = 1;
		P_HC595_SRCLK = 0;  //使用P2.6制造一个上升沿将这一位数据送到74HC595的QA输出端
		dat = dat << 1;     //将dat左移一位,取次高位
	}
}
void timer0Init(void)		//50毫秒@11.0592MHz
{
	TR0 = 1;//启动T0
	TMOD = 0x01;//T0为定时器,工作模式1 16位计数器
	TH0 = 0x4b;
	TL0 = 0xfc;//0x4bfc	定时50ms	
   
}

void timer0()interrupt 1
{
	tt++;
	if((shanshuo==0)&&(tt==8))     //室温小于设定值
	{
		tt=0;
		if(fla==0) {fla=1;P1=0XFE;beep=0;}    //D3闪烁
		else if(fla==1){fla=0;P1=0XFF;beep=0;} 
	}
	else if((shanshuo==1)&&(tt==6))     //室温在最小值与最大值之间
	{
		tt=0;
		if(fla==0) {fla=1;P1=0XFD;beep=1;}   //D4闪烁  
		else if(fla==1){fla=0;P1=0XFF;beep=1;} 
	}
}

void keyScan()
{
	if(s5==0)   //s5             //设置
	{
		delayms(5);
		if(s5==0)
		{
			if(shezhi==0){shezhi=1;}
			else if(shezhi==1){shezhi=0;}
		}
		while(!s5);
	}

	else if(s10==0)  //s10
	{
		delayms(5);
		if(s10==0)
		{
			if(flag==0){flag=1;}     //切换温度上下限的状态
			else if(flag==1){flag=0;}
		}
		while(!s10);
	}

	else if(s15==0)       //s15      设置加
	{
		delayms(5);
		if(s15==0)
		{
			jia=1;
		}
		while(!s15);
	}

	else	if(s20==0)  //s20   设置减
	{
		delayms(5);
		if(s20==0)
		{
			jian=1;  
		}
		while(!s20);
	}
}


void  displayOne(uchar yi,uchar er)
{
	  Send_595(0xfe);     //选通第一个数码管
		Send_595(smgtab[yi]);//段选
		P_HC595_RCLK = 0;
		P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选
    delayms(1);
	
	  Send_595(0xfd);     //选通第二个数码管
		Send_595(smgtab[er]);//段选
		P_HC595_RCLK = 0;
		P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选
    delayms(1);
}

void  displayTwo(uchar san,uchar si)
{
	  Send_595(0xfb);    //选通第三个数码管
		Send_595(smgtab[san]);//段选
		P_HC595_RCLK = 0;
		P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选
    delayms(1);
	 
	  Send_595(0xf7);   //选通第四个数码管
		Send_595(smgtab[si]);//段选
		P_HC595_RCLK = 0;
		P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选
    delayms(1);
}

void  displayThree(uchar wu,uchar liu)
{
	  Send_595(0xef);   //选通第五个数码管
		Send_595(smgtab[wu]);//段选
		P_HC595_RCLK = 0;
		P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选
    delayms(1);
	
	  Send_595(0xdf);  //选通第六个数码管
		Send_595(smgtab[liu]);//段选
		P_HC595_RCLK = 0;
		P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选
    delayms(1);
}

void  displayFour(uchar qi,uchar ba)
{
	  Send_595(0xbf);   //选通第七个数码管
		Send_595(smgtab[qi]);//段选
		P_HC595_RCLK = 0;
		P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选
    delayms(1);
	
	  Send_595(0x7f);   //选通第八个数码管
		Send_595(smgtab[ba]);//段选
		P_HC595_RCLK = 0;
		P_HC595_RCLK = 1; //使用P2.5制造一个上升沿将这两个字节数据同时送给数码管,完成段选和位选
    delayms(1);
}

/**********************************************************************************************
                        ds18b20驱动函数
/***********************************************************************************************/
//单总线延时函数
void Delay_OneWire(unsigned int t)  
{
	while(t--);
}

//通过单总线向DS18B20写一个字节
void Write_DS18B20(unsigned char dat)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		DQ = 0;
		DQ = dat&0x01;
		Delay_OneWire(5);
		DQ = 1;
		dat >>= 1;
	}
	Delay_OneWire(5);
}

//从DS18B20读取一个字节
unsigned char Read_DS18B20(void)
{
	unsigned char i;
	unsigned char dat;
  
	for(i=0;i<8;i++)
	{
		DQ = 0;
		dat >>= 1;
		DQ = 1;
		if(DQ)
		{
			dat |= 0x80;
		}	    
		Delay_OneWire(5);
	}
	return dat;
}

//DS18B20设备初始化
bit init_ds18b20(void)
{
  	bit initflag = 0;
  	
  	DQ = 1;
  	Delay_OneWire(12);
  	DQ = 0;
  	Delay_OneWire(80);
  	DQ = 1;
  	Delay_OneWire(10); 
    	initflag = DQ;     
  	Delay_OneWire(5);
  
  	return initflag;
}

uchar  temget()       
{
   uchar temp,low,high;
	 init_ds18b20();
	 Write_DS18B20(0xcc);
	 Write_DS18B20(0x44);    //温度转换
	
	 Delay_OneWire(200);
   
   init_ds18b20();
	 Write_DS18B20(0xcc);
	 Write_DS18B20(0xbe);
	
	 low=Read_DS18B20();   //读温度
	 high=Read_DS18B20();  //读温度
	
	 temp=high<<4;
	 temp|=(low>>4);
	 return temp;
}
/***************************************************************************************/


----------------所有代码打包下载链接------------------

08单片机对DS18B20温度传感器进行数据和使用_全部代码打包.zip

 

    大家可以继续提前接着往下进行 09单片机 进行AD转换并存储(EEPROM) 的题目练习~如对代码有任何问题,请回帖留言咯。别忘了,常来留言会增加得到U盘的概率哦~

今天文末依旧没鸡汤,赶在小年前,提前祝大家小年快乐,回家不“挨打”,嘿嘿~!





关键词: DS18B20     DS18B20     单片机     数据采集         

助工
2019-01-27 23:55:46     打赏
2楼
哈哈,占个沙发,给楼主点赞

管理员
2019-01-28 09:28:44     打赏
3楼

谢谢楼主分享


院士
2019-01-28 09:59:31     打赏
4楼

这个温传非常考验时序的处理。

可以充分理解定时器的使用,大家努力学习啊


高工
2019-01-28 10:45:49     打赏
5楼

支持楼主,点赞。如果多取几个温度值来求平均值,输出数据更稳定准确呦。


菜鸟
2019-01-28 12:15:55     打赏
6楼

       关于定时器装初值有个小疑问

平时用的比较多的是

TH0 = (65536-46080)/256;
 TL0 = (65536-46080)%256;

而这种确实没见过 ,为啥0x4bfc是50ms  ?     

   TH0 = 0x4b;  

   TL0 = 0xfc;//0x4bfc   定时50ms    


菜鸟
2019-01-28 15:24:21     打赏
7楼

一样道理的,只是16进制转10进制,没啥不同的


菜鸟
2019-01-28 15:26:10     打赏
8楼

只是不同的人写法不一样而已,


高工
2019-01-28 15:38:13     打赏
9楼

用stc-isp烧录软件可以生成定时器的,比较节省时间。生成的就是初值就是十六进制的。了解一下QQ图片20190128153139.png


菜鸟
2019-01-28 15:38:57     打赏
10楼

(65536-(50000/1.08))/256算出结果为19452,而19452的高8位为4B,低八位为fc,高8位给值是TH0,低八位给值是Tl0;



共22条 1/3 1 2 3 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]