18b20时序要求比较严格。
所遇问题:测出温度值不准确,不停的从255于实际温度值跳变。
解决方法:在读取温度值时,关掉中断。不过灯有一些闪。
该程序所测温度的精度不能达到0.5,需要稍微改动。
不知道1602稳定了没,明天再试一次。
反应好像还不够灵敏。
#include "reg52.h" #include<intrins.h> #define uchar unsigned char //温度传感器与单片机接口为 P0.1 sbit DQ=P1^5; // sbit E=P0^7; sbit RW=P0^6; sbit RS=P0^5; sbit LED=P3^5; //sbit lcd_flag=P2^7; //定义是lcd否忙标志 sbit P1_7 = P3 ^ 7; // 控制LED11 sbit P1_6 = P3 ^ 6; // 控制LED10 sbit P1_2 = P3 ^ 4; // uchar timer=0x00; //字节变量timer用于计时,每计数器记满100次,就采样一次温度 uchar chars[]={"GYRGB.Liuzhou^_^"}; // 液晶显示第一行 uchar chars2[]={"Welcome!"}; //液晶显示第二行 uchar scale_0; //灯光灰度 /////////////////////////////// //各个功能子函数 /////////////////////////////// void delay(void); //18b20温度转换后所需的延时 void delay1(void); //动态数码管显示所需的较短的延时 unsigned char Read18B20(void); //读18b20子函数 void Write18B20(uchar ch); //写18b20子函数 void Delay15(uchar n); //读写所需的最基本单位时间的延时 void Init18B20(void); //初始化18b20函数 ///////////////////////////// //新增的液晶显示程序 ///////////////////////////// void WriteLCDcom(uchar ch); void WriteLCDdat(uchar ch); void main(void) { uchar i,tl,th; //变量i用于循环计数,tl和th用于获得二进制温度值 uchar temp; //变量temp用于存放有效的温度值 uchar bith,bitt,biti,bitz; //将要显示在数码管上的百位、十位、个位和十分位的码值 unsigned int n; RCAP2H =0xFE; // 赋T2的预置值,溢出1次是1/2000秒钟 RCAP2L =0x0C; ET2=1; // 打开定时器2中断 EA=1; //总中断允许 TR2=1; // 启动定时器2 WriteLCDcom(0x01); //清屏 WriteLCDcom(0x38); WriteLCDcom(0x0c); WriteLCDcom(0x06); WriteLCDcom(0x80); for(i=0;i<16;i++) WriteLCDdat(chars); WriteLCDcom(0xc0); for(i=0;i<8;i++) WriteLCDdat(chars2); WriteLCDcom(0xce); WriteLCDdat(0xdf); WriteLCDdat(0x43); //////////////////////////////// //12M晶振,16位计数器 //溢出时间 65536us约等于65.5ms //溢出100次采样温度一次,采样周期6.5秒 //////////////////////////////// while(1) { LED=1; Init18B20(); //复位18b20,每次复位18b20都是默认的12位转换精度 Write18B20(0xcc);//向18b20写入跳过激光rom操作 _nop_(); //稍等片刻 Write18B20(0x44);//命令18b20开始温度的测量以及模数转换 delay(); //18b20转换时间较长应该等待稍长时间 Init18B20(); //每一次对18b20的读写都要先复位 TR2=0; //18b20对时序要求比较严格,因此在读取温度时要关中断,否则测的温度值会出错 Write18B20(0xcc);//照例跳过ROM的操作 _nop_(); //等 Write18B20(0xbe); //读18b20的温度数据,可以连续读5个字节 _nop_(); //等 tl=Read18B20(); //读第一个字节,里面是12位有效数字的低八位 th=Read18B20(); //读第二个字节,是扩展的符号位和有效值的高四位 TR2=1; Init18B20(); temp=(th<<4)+(tl>>4); //实际上,temp=(th<<4)+(tl>>4)这个式子得到的是只包含了数整值的温度值 //th向左移4位,抛弃了扩展的符号位;tl向右移4位,抛弃了4位小数位 bith=temp/100; //得到百位数字 bitt=(temp%100)/10; //得到十位数字 biti=temp%10; //得到个位数字,由于要显示小数点,所以要减去一个0x80 bitz=0; //只是作为好看的位数,实际我们在计算temp的时候已经将小数位舍去了 /*****************************************/ // 液晶的显示 /*****************************************/ WriteLCDcom(0xc8); WriteLCDdat(bith+0x30); WriteLCDdat(bitt+0x30); WriteLCDdat(biti+0x30); WriteLCDdat(0x2e); WriteLCDdat(bitz+0x30); LED=1; for(n=0;n<50000;n++); // 每过一会儿就自动加一个档次的亮度 if(temp==26) scale_0=0; else if(temp==27) scale_0=1; else if(temp==28) scale_0=2; else if(temp==29) scale_0=3; else if(temp==30) scale_0=4; else if(temp==31) scale_0=5; else if(temp==32) scale_0=6; else if(temp==33) scale_0=7; else if(temp==34) scale_0=8; else scale_0=9; } } /******************************************************************************************** * 函数名称:Timer2_Server() * 功 能:定时器2溢出中断服务程序。1/2000 秒中断1次。 * 入口参数:无 * 出口参数:无 *********************************************************************************************/ void Timer2_Server(void) interrupt 5 { static uchar tt; // tt用来保存当前时间在1秒中的比例位置 TF2=0; tt++; if(tt==10) // 每1/200秒整开始输出低电平 { tt=0; if(scale_0!=0) // 加入该句的目的是避免灭灯时发生闪烁 { P1_7=0; P1_6=0; P1_2=0; } } if(scale_0==tt) // 按照当前占空比切换输出高电平 { P1_7=1; P1_6=1; P1_2=1; } } void delay(void) //长延时,18b20在执行温度转换操作的时候需要耗费较长时间 { //在这段时间里18b20需要测温,做模数转换,并将转换的二进制数值存储到自带的临时寄存区里去 uchar i,j; for(i=0;i<200;i++) //具体需要多长时间芯片手册上有介绍,这个要继续深入了解 /*************************??******/ for(j=0;j<100;j++) ; } unsigned char Read18B20(void) //最基本的读18b20的函数,并向主函数返回读到的那个字节 { unsigned char ch; //相当于串行缓存器 unsigned char q ; //循环计数器 for(q=0;q<8;q++) //循环8次,读出串行的8位数据,先读到的是数据的最低位 //因此要从ch的最高位存起,然后依次将ch向右移,就像火车进站那样 { // 7 6 5 4 3 2 1 0 (ch) // MSB-6-5-4-3-2-1-LSB ----> (数据) ch=ch>>1; //先移位再赋值,出现赋值8次但是只移位7次的效果 DQ=0; //单线总线的要求,要读器件,就要产生一个上升沿,然后释放总线,现在要回到低电平 _nop_(); // 稍微停顿,让器件探测到电平已经变低了 DQ=1; //拉高总线产生上升沿,同时,在某种意义上,对单片机的端口写1,也就是让端口处于待读的状态,一举两得 _nop_();_nop_(); //4个空操作函数,等待,给18b20响充分的时间响应,具体需要多久的时间要看器件手册 _nop_();_nop_(); // 读响应是多久,需要继续深入了解 /********************??*****/ //而且这个-nop-()函数到底会延时多长时间,要深入了解 /****************??***/ if(DQ==1) //开始读端口,如果为1,则将ch最高位写为1 { ch=ch|0x80;} //ch|0x80就是 XXXX XXXX | 1000 0000 每一位相或的结果就是得到 1XXX XXXX,火车开始进站了 else //如果读到的数是0,那么就把ch的最高位置为0 { ch=ch&0x7f;} // ch & 0x7f 就是 XXXX XXXX & 0111 1111 ,结果自然就是 0XXX XXXX,数字最低位就进站好了 Delay15(3); //延时少少,延时多长要继续深入了解 /****************??*****/ DQ=1; //读完之后再次置端口为1,好为下一次读做准备,其实很关键的一步 } return (ch); //将读到的数据返回给主函数,这就我们想要的结果了 } void Write18B20(uchar ch) //写18b20的函数 { uchar i; //一个循环计数变量 for(i=0;i<8;i++) //循环8次,每次一位二进制 { DQ=0; //从读和写函数的比较可以得知,产生读/写的条件都是要先产生低电平,只是低电平的维持时间长短不一 Delay15(1); //写操作需要的低电平持续时间比读操作要长的多 DQ=ch &0x01; //向总线上写ch的最低位,跟读的操作类似,只是这时“车站”是18b20,而“列车”是ch Delay15(3); // 7 6 5 4 3 2 1 0 (18b20) // MSB-6-5-4-3-2-1-LSB ----> (ch) // ch & 0x01 就是 xxxx xxxx & 0000 0001,结果是0000 000x //为什么可以用一个位变量 DQ= 0000 000x,这个问题需要继续深入了解 /*************??****/ DQ=1; //写完一位后,将总线抬高,为下一次拉低做准备 ch=ch >>1; //将ch第二低位推到最低位,等待发射出去 _nop_(); //等待一段时间 } } void Init18B20(void) //初始化18b20 { DQ=0; //初始化操作同样是由低电平开始,但是这个初始化低电平要持续得最久 Delay15(30);//至少延时480us,到底是多少,要找到-nop-()函数源码,反汇编之后才知道/*******************??****/ DQ=1; //抬高总线,一举两得,可以准备接受18b20的存在低电平 Delay15(10);//至少延时100us } void Delay15(uchar n) //貌似满精确的延时程序 { //具体要在 intrins.h头文件中找到它,那么首先就要找到intrins.h /****************??*****/ do { _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); n--; }while(n); } /* bit LCDbusy() { bit flag; RS=0; //寄存器为LOW RW=1; //是否读写为high E=1; //使能端为high if(lcd_flag==1) flag=1; E=0; return flag; } */ void WriteLCDcom(uchar ch) { // while(LCDbusy()); RS=0; RW=0; E=0; P2=ch; delay(); E=1; E=0; } void WriteLCDdat(uchar ch) { // while(LCDbusy()); RS=1; RW=0; E=0; P2=ch; delay(); E=1; E=0; } 不明白为什么加了检测1602是否忙的程序进去,为什么P2口就没有数据了呢?液晶就不能显示了呢?