提示:文章完成后,目录可以自动生成,如何生成可以参考右边的帮助文档
文章目录
- 前言
- 程序代码和调试结果
- 总结
前言
以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;
}
}
总结
- 在一些函数中加入标志性的语句来判断该函数有没有被执行;
- 延时有多种方法,不同的延时方式产生不同效果,比如这里使用delay函数就会影响数码管动态扫描,使用定时器过多会影响18B20温度读取、数码管显示,使用if语句则能很好的达到目的;
- 多尝试,不害怕出错,及时记录调试方案总结升华