资讯详情

基于51单片机的温度检测

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

文章目录

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


前言

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

使用步骤

1.最终结果代码

代码如下:

#include <reg52.h> #include <intrins.h> #include <stdio.h>  #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;//设置比率96000 }    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;     } }   /// 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;//设置比率96000 }    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.//打开中断,使定时器正常运行,数字管显示温度     } }  /// 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中,如下所示

/// 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 |= 001;//定时器工作模式,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. 多尝试,不害怕出错,及时记录调试方案总结升华

标签: 高精度的体温传感器模块

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

锐单商城 - 一站式电子元器件采购平台