基于51单片机的温度检测

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 程序代码和调试结果
  • 总结


前言

利用51单片机为核心实现温度测量,利用DS18B20温度传感器获取温度信号,将需要测量的温度信号转化为数字信号,利用单总线和单片机交换数据,最终单片机将识别的温度以数码管显示和串口返回数据的形式输出。本次程序使用的是清翔51单片机,温度测量范围为-10~+85°C,精度为+-0.5°C。

使用步骤

1.最终成果代码

代码如下:

#include 
#include 
#include 

#define uint unsigned int 
#define uchar unsigned char
	
sbit WE = P2^7;
sbit DU = P2^6;
sbit DS = P2^2;
uint temp,a=0;

uchar code SMGwei[] = {0xfe,0xfd,0xfb};//共阴数码管段选表0-9
uchar code SMGduan[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,};//数码管位选码


//us延时函数,执行一次us--需6.5us,进入一次函数需11.95us
void delay_us(uchar us)
{
	while(us--);
}

//数码管显示
void display(uchar i)
{
	static uchar wei;
	P0 = 0xfe;//清除断码
	WE = 1;//打开位选锁存
	P0 = SMGwei[wei];//
	WE = 0;
	switch(wei)
	{
		case 0:DU = 1;P0 = SMGduan[i / 100]; DU = 0;
		break;
		case 1:DU = 1;P0 = SMGduan[i % 100 /10]|0x80; DU = 0;
		break;
		case 2:DU = 1;P0 = SMGduan[i % 10]; DU = 0;
		break;
	}
	wei++;
	if(wei == 3)
		wei = 0;
}

bit ds_init()
{
	bit i;
	DS = 1;
	_nop_();
	DS = 0;
	delay_us(75);//拉低总线499.45us,挂接在总线上的18B20将会全部复位
	DS = 1;//释放总线
	delay_us(4);//延时37.95us等待18B20发挥存在信号
	i = DS;
	delay_us(20);//延时141.95us
	DS = 1;
	_nop_();
	return (i);
}

//写一个字节
void write_byte(uchar dat)
{
	uchar i;
	for(i=0;i<8;i++)
	{
		DS = 0;
		_nop_();
		DS = dat & 0x01;
		delay_us(10);
		DS = 1;//释放总线,准备下一次数据写入
		_nop_();
		dat >>= 1;
	}
}

uchar read_byte()
{
	uchar i,j,dat;
	for(i=0;i<8;i++)
	{
		DS = 0;
		_nop_();//产生读时序
		DS = 1;
		_nop_();
		j = DS;
		delay_us(10);//76.95us
		DS = 1;
		_nop_();
		dat = (j<<7)|(dat>>1);
	}
	return (dat);
}


//定时器0初始化
void timer0Init()
{
	EA = 1;//打开总中断
	ET0 = 1;//打开定时器0中断
	TR0 = 1;//启动定时器0
	TMOD |= 0x01;//定时器工作模式,16位定时模式
	TH0 = 0xed;
	TL0 = 0xff;//定时5ms
}


//串口初始化
void UARTInit()
{
	EA = 1;
	ES = 1;//打开串口中断
	SCON = 0x50;//串口工作方式
	REN = 1;//串口允许接收
	TR1 = 1;//启动定时器1
	TMOD |= 0x20;// 定时器1,工作模式2,8位自动重装
	TH1 |= 0xfd;//
	TL1 |= 0xfd;//设置比特率9600
}



void main ()
{
	uint L,M;
	UARTInit();
	timer0Init();
	while(1)
	{	
		EA = 0;
		ds_init();//初始化DS18B20
		write_byte(0xcc);//发送跳跃ROM指令
		write_byte(0x44);//发送温度转换指令
		ds_init();//初始化DS18B20
		write_byte(0xcc);//发送跳跃ROM指令
		write_byte(0xbe);//读取DS18B20暂存器
		L = read_byte();
		M = read_byte();
		temp = M;
		temp <<= 8;
		temp |= L;
		temp = temp*0.0625*10+0.5;
		EA = 1;
		
	}
}


//定时器0中断函数
void timer0() interrupt 1
{
	a++;
	TH0 = 0xed;
	TL0 =0xff;
	display(temp);
	if(a==200)
	{
		TI = 1;
		printf("现在的温度为%d\n",temp);
		while(!TI);
		TI = 0;
		a = 0;
	}

}

2.出现的一些问题和错误

代码如下(示例):

//定时器0初始化
void timer0Init()
{
	EA = 1;//打开总中断
	ET0 = 1;//打开定时器0中断
	TR0 = 1;//启动定时器0
	TMOD |= 0x01;//定时器工作模式,16位定时模式
	TH0 = 0xed;
	TL0 = 0xff;//定时5ms
}

//定时器1初始化
void timer1Init()
{
	EA = 1;//打开总中断
	ET1 = 1;//打开定时器0中断
	TR1 = 1;//启动定时器0
	TMOD |= 0x10;//定时器1工作模式,16位定时模式
	TH1 |= 0xed;
	TL1 |= 0xff;//定时5ms
}

//串口初始化
void UARTInit()
{
	EA = 1;
	ES = 1;//打开串口中断
	SCON = 0x50;//串口工作方式
	REN = 1;//串口允许接收
	TR1 = 1;//启动定时器1
	TMOD |= 0x20;// 定时器1,工作模式2,8位自动重装
	TH1 |= 0xfd;//
	TL1 |= 0xfd;//设置比特率9600
}



void main ()
{
	uint L,M;
	UARTInit();
	timer0Init();
	while(1)
	{	
		//EA = 0;//此处应先屏蔽中断,是的18B20进行温度转换时不受干扰,因为我们的51板子是I2C单总线通信,同时进行中断会影响数据输出,数码管数据显示会被其他不知名的数字覆盖,或者显示数据错乱,起初我是没有屏蔽中断的
		ds_init();//初始化DS18B20
		write_byte(0xcc);//发送跳跃ROM指令
		write_byte(0x44);//发送温度转换指令
		ds_init();//初始化DS18B20
		write_byte(0xcc);//发送跳跃ROM指令
		write_byte(0xbe);//读取DS18B20暂存器
		L = read_byte();
		M = read_byte();
		temp = M;
		temp <<= 8;
		temp |= L;
		temp = temp*0.0625*10+0.5;
		//EA = 1;//打开中断,使定时器正常运行,数码管显示温度
		
	}
}

//定时器0中断函数
void timer0() interrupt 1
{
//	a++;
	LED = 0;
	TH0 = 0xed;
	TL0 =0xff;
	display(temp);
//	if(a==200)
//	{
//		TI = 1;
//		printf("现在的温度为%d\n",temp);
//		while(!TI);
//		TI = 0;
//		a = 0;
//	}

}

//定时器1中断函数
void timer1() interrupt 3
{  	
	TH1 |= 0xed;
	TL1 |=0xff;
	LED = 0;
	TI = 1;
	printf("现在的温度为%d\n",temp);
	delay(10);
	while(!TI);
	TI = 0;

}

起初我为了串口也能返回数据,将串口接收打印汉字程序添加到定时器0中,如下

//定时器0中断函数
void timer0() interrupt 1
{
	TH0 = 0xed;
	TL0 =0xff;
	display(temp);
	TI = 1;
	printf("现在的温度为%d\n",temp);
	delay(10);//为了避免串口打印频率过高
	while(!TI);
	TI = 0;
}

但这样即使不使用delay函数,由于其他语句占用太多时间,display函数执行次数减少,数码管动态扫描次数不够,数码管显示出现频闪

于是我将串口打印添加到定时器1中,如下

//定时器0初始化
void timer0Init()
{
	EA = 1;//打开总中断
	ET0 = 1;//打开定时器0中断
	TR0 = 1;//启动定时器0
	TMOD |= 0x01;//定时器工作模式,16位定时模式
	TH0 = 0xed;
	TL0 = 0xff;//定时5ms
}

//定时器1初始化
void timer1Init()
{
	EA = 1;//打开总中断
	ET1 = 1;//打开定时器0中断
	TR1 = 1;//启动定时器0
	TMOD |= 0x10;//定时器1工作模式,16位定时模式
	TH1 |= 0xed;
	TL1 |= 0xff;//定时5ms
}

//串口初始化
void UARTInit()
{
	EA = 1;
	ES = 1;//打开串口中断
	SCON = 0x50;//串口工作方式
	REN = 1;//串口允许接收
	TR1 = 1;//启动定时器1
	TMOD |= 0x20;// 定时器1,工作模式2,8位自动重装
	TH1 |= 0xfd;//
	TL1 |= 0xfd;//设置比特率9600
}



void main ()
{
	uint L,M;
	UARTInit();
	timer0Init();
    timer1Init();
	while(1)
	{	
		ds_init();//初始化DS18B20
		write_byte(0xcc);//发送跳跃ROM指令
		write_byte(0x44);//发送温度转换指令
		ds_init();//初始化DS18B20
		write_byte(0xcc);//发送跳跃ROM指令
		write_byte(0xbe);//读取DS18B20暂存器
		L = read_byte();
		M = read_byte();
		temp = M;
		temp <<= 8;
		temp |= L;
		temp = temp*0.0625*10+0.5;
	}
}

//定时器0中断函数
void timer0() interrupt 1
{
	TH0 = 0xed;
	TL0 =0xff;
	display(temp);
    LED1 = 0;//添加小灯观察是否进入中断

}

//定时器1中断函数
void timer1() interrupt 3
{  	
	TH1 |= 0xed;
	TL1 |=0xff;
	LED2 = 0;//添加小灯,观察是否进入中断函数
	TI = 1;
	printf("现在的温度为%d\n",temp);
	while(!TI);
	TI = 0;

}

结果LED1、2均亮起,数码管显示0,串口打印一次“现在温度为0”;

究其原因,是定时器1既被用作波特率计算,又当定时器计时,会发生混乱,且定时器之间存在优先级,多个定时器设计的定时时间一样时不能同时执行中断函数,故要想达到“串口打印温度且打印频率适中”、“数码管显示温度且不频闪”的目的,在不能使用定时器1中断时,

可按如下程序的构思


//定时器0中断函数
void timer0() interrupt 1
{
	a++;
	TH0 = 0xed;
	TL0 =0xff;
	display(temp);
	if(a==200)//使a加加延长时间,在a加到200之前不会执行if语句,故而不影响数码管动态扫描
	{
		TI = 1;
		printf("现在的温度为%d\n",temp);
		while(!TI);
		TI = 0;
		a = 0;
	}

}

总结

  1. 在一些函数中加入标志性的语句来判断该函数有没有被执行;
  2. 延时有多种方法,不同的延时方式产生不同效果,比如这里使用delay函数就会影响数码管动态扫描,使用定时器过多会影响18B20温度读取、数码管显示,使用if语句则能很好的达到目的;
  3. 多尝试,不害怕出错,及时记录试方案总结升华

你可能感兴趣的:(51单片机,c语言)