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口就没有数据了呢?液晶就不能显示了呢?
