实验原理图:
实验源代码:
/**************************************************************************************/
/*单片机采用STC89C52,晶振为12MHz。P1.0与DS18B20的数据端相连*/
/*P0为数码管的字段码口,P2口为位选码口*/
/*P3.2与按键0相连,P3.3与按键1相连*/
/*MAXNUM是单总线上最大可扫描DS18B20个数*/
/*flag为温度的正负号标志单元,flag为"1"时表示温度值为负值,为"0"时表示温度值为正值*/
/*温度测量范围为(-55.0~99.9摄氏度)。*/
/*变量cc中保存计算出的温度值的整数部分,xs保存计算出的温度值的小数部分的第一位*/
/*num记录当前单总线上DS18B20的个数*/
/*z是按键标志位,z为1时表明有按键按下*/
/*a,b分别是按键0和按键1的记录变量*/
/*m是轮换显示各DS18B20温度值和序列号的全局标志变量*/
/*二维数组ID用于记录各DS18B20的ROM序列号*/
/*数组disbuffer是LCD显示缓存数组*/
/*数组RomID_temp用于匹配DS18B20时临时记录要匹配DS18B20的序列号*/
/*共用体temp用于存放从DS18B20读入的数据*/
/**************************************************************************************/
#include
#include
#define uchar unsigned char
#define uint unsigned int
#define MAXNUM 4 //宏定义单总线上最大可扫描DS18B20个数
sbit DS=P2^2; //用P1.0口作为各DS18B20与单片机的I/O口
sbit Key0=P3^2; //P3.2用作按键0的输入,采用外部中断方式获取按键信号
sbit Key1=P3^3; //P3.3用作按键1的输入,采用外部中断方式获取按键信号
sbit dula=P2^6;
sbit wela=P2^7;
//unsigned char code chocode[]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};
union{ //定义共用体temp用于存放从DS18B20读入的数据,Keil C51采用大端格式,c[1]是高地址.
uchar c[2]; //其中存放读取温度数值的低字节;c[0]是低地址,其中存放读取温度数值的高字节.
uint x; //这样根据大端格式的要求,即数据的高字节存储在低地址中,数据的低字节存放在高地址中,
}temp; //x便刚好是读出的温度数值.
uchar idata flag; //温度的正负号标志,flag为"1"时表示温度值为负值,为"0"时表示温度值为正值
uint cc,xs; //变量cc中保存计算出的温度值的整数部分,xs保存计算出的温度值的小数部分的第一位
uchar idata disbuffer[6]; //LCD显示缓存数组
uchar idata ID[4][8]={0}; //用于记录各DS18B20的ROM序列号
uchar idata RomID_temp[8]; //匹配DS18B20时临时记录要匹配DS18B20的序列号
uchar m=0; //m是轮换显示各DS18B20温度值和序列号的全局标志变量
uchar num=0; //num记录当前单总线上DS18B20的个数
uchar z=0; //z是按键标志位,z为1时表明有按键按下
uchar a=0; //a是按键0的记录变量
uchar b=0; //b是按键1的记录变量
void delay(uint i) //延时i*9.62us
{
uint j;
for(j=i;j>0;j--);
}
void delay_ms(uchar i) //延时(j*2+1+2)*i+5 个机器周期
{ uchar j; //12MHz时,延时 0.5*i ms
do{j=248;
do{j--;}while(j);
i--;
}while(i);
}
void delay_2us(uchar i) //延时 2*i+5 us
{
while(--i);
}
uchar DS_init(void) //18B20复位,初始化函数
{
uchar presence;
DS=0; delay_2us(250); //根据DS18B20的复位时序.先把总线拉低555us
DS=1; delay_2us(30); //再释放总线,65us后读取DS18B20发出的信号
presence=DS; delay_2us(250); //如果复位成功,则presence的值为0;否则为1
return (presence); //返回0则初始化成功,否则失败
}
uchar read_byte(void) //读1字节
{
uchar i,j,dat=0;
for(i=1;i<=8;i++) //作8个循环,读出的8位组成一个字节
{DS=0; _nop_(); //先将总线拉低1us,
DS=1; delay_2us(2); //再释放总线,产生读起始信号,延迟9us后读取总线上的DS18B20发出的值
j=DS; delay_2us(30); //一位读完后,延迟65us后读下一位
dat=(j<<7)|(dat>>1); //读出的数据最低位在一个字节的最低位,这样刚好一个字节在DAT里
}
return(dat);
}
uchar read_2bit(void) //读2位
{
uchar i=0,j=0;
DS=0; _nop_(); //先将总线拉低1us,
DS=1; delay_2us(2); //再释放总线,产生读起始信号,延迟9us后读取总线上的DS18B20发出的值
j=DS; delay_2us(30); //一位读完后,延迟65us后读下一位
DS=0; _nop_();
DS=1; delay_2us(2);
i=DS; delay_2us(30);
i=j*2+i; //将读出的两位放到变量i中,其中第一个读出的位处于i的第1位;而第二个读出的位处于i的第0位
return(i);
}
void write_byte(uchar dat) //写1字节
{
uchar i;
for(i=0;i<8;i++) //作8个循环,写入的8位组成一个字节
{DS=0; //先将总线拉低
DS = dat&0x01; //向总线上放入要写的值
delay_2us(50); //延迟105us,以使DS18B20能采样到要写入的值
DS = 1; //释放总线,准备写入下一位
dat>>=1; //将要写的下一位移到dat的最低位
}
}
void write_bit(bit dat) //写1位
{
DS=0; //先将总线拉低
DS=dat; //向总线上放入要写的值
delay_2us(50); //延迟105us,以使DS18B20能采样到要写入的值
DS = 1; //释放总线
}
void display_ROMID(void) //定义显示序列号的函数
{
uchar i,p,t,k;
uint q;
uchar disbuffer_rom[8];
uchar codevalue[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xff,0xbf};
//共阳极的字段码
uchar chocode[]={0xf7,0xfb,0xfd,0xfe,0x7f,0xbf,0xdf,0xef};
//位选码表
z=0; //z归零,表明没有按键按下
for(q=0;q<500;q++) //显示各DS18B20的序号
{
if(z==1) //如果z为1,有按键按下,表明要显示不同的内容,此时从这个显示循环中跳出
break;
t=chocode[7]; //取当前的位选码
P2=t; //送出位选码
t=codevalue[m+10]; //查得显示字符的字段码
P0=t; //送出字段码
delay(100);
}
P2=0xFF; //关断LCD一段时间,产生闪屏效果
for(q=0;q<500;q++)
{
if(z==1) //如果z为1,有按键按下,表明要显示不同的内容,此时从这个循环中跳出
break;
delay(100);
}
for(k=0;k<8;k=k+4) //依次显示序列号的低32位或高32位
{ if(z==1) //如果z为1,有按键按下,表明要显示不同的内容,此时从这个循环中跳出
break;
//接下来将序列号的低32位或高32位值取出放入disbuffer_rom存储
disbuffer_rom[0]=(ID[m][k]&0x0F);
disbuffer_rom[1]=((ID[m][k]&0xF0)>>4);
disbuffer_rom[2]=(ID[m][k+1]&0x0F);
disbuffer_rom[3]=((ID[m][k+1]&0xF0)>>4);
disbuffer_rom[4]=(ID[m][k+2]&0x0F);
disbuffer_rom[5]=((ID[m][k+2]&0xF0)>>4);
disbuffer_rom[6]=(ID[m][k+3]&0x0F);
disbuffer_rom[7]=((ID[m][k+3]&0xF0)>>4);
for(q=0;q<250;q++) //显示序列号的低32位或高32位
{ if(z==1) //如果z为1,有按键按下,表明要显示不同的内容,此时从这个显示循环中跳出
break;
for (i=0;i<8;i++)
{ if(z==1)
break;
t=chocode[i]; //取当前的位选码
P2=t; //送出位选码
p=disbuffer_rom[i]; //取当前显示的字符
t=codevalue[p]; //查得显示字符的字段码
P0=t; //送出字段码
delay(40);
}
}
P2=0xFF; //显示完一轮,灯灭
for(q=0;q<500;q++)
{
if(z==1) //如果z为1,有按键按下,表明要显示不同的内容,此时从这个显示循环中跳出
break;
delay(100);
}
}
}
void display_error(void) //定义显示总线上没有DS18B20时的函数
{
uchar i,p,t;
uint q;
uchar disbuffer_temp[8]={0,2,8,1,16,0x0f,0x0f,0}; //显示内容为"OFF 1820"
uchar codevalue[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xff,0xbf};
//共阳极的字段码
uchar chocode[]={0xf7,0xfb,0xfd,0xfe,0x7f,0xbf,0xdf,0xef};
//位选码表
for(q=0;q<250;q++)
{
for (i=0;i<8;i++)
{
t=chocode[i]; //取当前的位选码
P2=t; //送出位选码
p=disbuffer_temp[i]; //取当前显示的字符
t=codevalue[p]; //查得显示字符的字段码
P0=t; //送出字段码
delay(40);
}
}
}
uchar search_rom(void) //遍历搜索单线上所连的所有18b20的序列号
{
uchar k,l=0,chongtuwei,m,n,a;
uchar _00web[MAXNUM]={0};
do
{
DS_init(); //复位单总线上的所有DS18B20
write_byte(0xf0); //单片机发布搜索命令
for(m=0;m<8;m++)
{
uchar s=0; //s用来记录本次循环得到的1个字节(8位)序列号
for(n=0;n<8;n++)
{
k=read_2bit(); //读第m*8+n位的原码和反码,保存在k中
k=k&0x03; //屏蔽掉k中其它位的干扰,为下一步判断作准备
s>>=1; //s右移一位,即把上一次循环得到的位值右移一位,
//这样执行完一次n为变量的循环,便可得到一个字节的ROM号
if(k==0x01) //k为01,表明读到的数据为0,即所有器件在这一位都为0,所以向总线上写0
//同时对s的值不进行操作,即这位的序列号记为0
{
write_bit (0);
}
else if(k==0x02)//k为02,表明读到的数据为1,即所有器件在这一位都为1,所以向总线上写1
{
s=s|0x80; //记录下此位的值,即s的最高位置1
write_bit (1);
}
else if(k==0x00)
{
chongtuwei=m*8+n+1; //记录下这个冲突位发生的位置;之所以加1是为了让_00web数组中的第一位保持0不变,
//便于判断搜索循环是否结束;
if(chongtuwei>_00web[l])//如果冲突位比标志00位的位高,即发现了新的冲突位,那么这位写0
{
write_bit (0);
_00web[++l]=chongtuwei; //依次记录位比冲突标志位高的冲突位在_00web数组中
}
else if(chongtuwei<_00web[l]) //如果冲突位比标志00位的位低,那么把ID中这位所在的字节右移n位,
{ //从而得到这位先前已经写过的值,如果为0,说明这位先前写的是0,那么继续写0,
// 如果这位先前写的是1,那么继续写1
a=(ID[num-1][m]>>n)&0x01;
s=s|(a<<7); //记录下此位的值
write_bit(a);
}
else if(chongtuwei==_00web[l])//如果冲突位就是标志00位,那么s的最高位置1,即这位记为1,同时向总线上写1;
//之所以不写0,是因为前面已经写过0,再写0,就得不到遍历的效果.
{
s=s|0x80;
write_bit (1);
l=l-1; //改变标志00位的位置,即向前推一个00位,并且是往低位方向推
}
}
else if(k==0x03) //k为03,表明总线上没有DS18B20,函数结束,同时返回零值
{return(0);}
}
ID[num][m]=s;
}
num++; //DS18B20的个数加1
}while((_00web[l]!=0)&&(num0xf8) {flag=1;temp.x=~temp.x+1;}
//如果为负,则符号标志置1,计算温度值,注意c[0]中放的是读取温度值的高字节
//还要注意读出的数值一共是12位
cc=temp.x/16; //计算出温度值的整数部分,这个语句相当于数值乘0.0625再取整数部分
xs=temp.x&0x0f; //取温度值小数部分的第一位
xs=xs*10; //这两条语句相当于乘0.625,得小数位的第一位,注意不是乘0.0625
xs=xs/16;
}
void display(void) //定义温度底层显示函数
{
uchar codevalue[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00,0x40};//共阴极的字段码
uchar chocode[]={0xdf,0xef,0xf7,0xfb,0xfd,0xfe,};//位选码表
uchar i=0,p,t;
disbuffer[4]=0x10; //让LCD的第5位全灭,即什么也不显示,注意是16进制表示
disbuffer[5]=0x0A+m; //LCD的第6位显示DS18B20序列号,以字母A,B,C,D,E,F来给示,所以加上0x0a
if (flag==1)disbuffer[3]=0x11;//判断是否显示负号
else disbuffer[3]=0x10;
disbuffer[0]=xs; //放入小数位显示的数值
disbuffer[1]=(cc%10);//放入个数要显示的数值
disbuffer[2]=(cc/10);//放入十位要显示的数值
for (i=0;i<6;i++)
{
/*
t=chocode[i]; //取当前的位选码
P2=t; //送出位选码
p=disbuffer[i]; //取当前显示的字符
t=codevalue[p]; //查得显示字符的字段码
if (i==1) t=t+0x80; //个位比较特殊,因为有小数点,所以要加上0x80
P0=t; //送出字段码
delay(40);
*/
t=codevalue[disbuffer[i]];
if (i==1) t=t+0x80;
dula=1;
P0=t;
dula=0;
P0=0xff;
wela=1;
P0=chocode[i];
wela=0;
delay(45);
}
}
void diplay_final(void) //定义温度上层显示函数
{ uint q,r;
z=0;
for(q=0;q<8;q++) //将要显示DS18B20的温度值的序列号放入数组RomID_temp中
{
RomID_temp[q]=ID[m][q];
}
P0=0xFF;
if(a==0) //如果按键0的标志变量a=0,即进行闪烁显示
{
for(q=0;q<3;q++) //闪烁空隙仍在读取温度
{
if(z==1) //如果z为1,有按键按下,表明要显示不同的内容,此时从这个循环中跳出
break;
flag=0;
Read_Temperature_rom(); // 读取双字节温度
Temperature_cov(); //温度转换
for(r=0;r<15;r++)
delay(1000);
}
}
for(q=0;q<5;q++) //读取温度并显示
{
if(z==1) //如果z为1,有按键按下,表明要显示不同的内容,此时从这个显示循环中跳出
break;
flag=0;
Read_Temperature_rom(); // 读取双字节温度
Temperature_cov(); //温度转换
for(r=0;r<100;r++)
display(); //显示温度
}
}
void key_0() interrupt 0 //外部中断0的中断处理函数
{
delay(1200); //延时消抖
if(Key0==0)
{
if(a==0) //a只可能取0值或1值
a=1;
else a=0;
b=0; //同时一旦0键按下就将b归零
z=1; //将有按键按下标志位z置位
}
}
void key_1() interrupt 2 //外部中断1的中断处理函数
{
delay(1200); //延时消抖
if(Key1==0)
{ if(a==0) //只有a等于0时才让b的值在0或1之间转变
{
if(b==0)
b=1;
else b=0;
}
z=1; //将有按键按下标志位z置位
if(a==1) //当a=1时进入固定显示一个DS18B20的温度值的状态
{
m++; //按一次1键m值加1,即在不同的DS18B20切换显示
if(m>=num) //当然,m的值不能超过或等于总线上挂接的DS18B20的数目
m=0;
}
}
}
void main() //主函数
{
uchar p,i;
delay(10);
p=search_rom(); //执行搜索DS18B20算法,同时返回搜索情况,p值为0,表明总线上没有DS18B20,p值为1表明搜索到DS18B20;
if(p==0) //如果总线上没有DS18B20,显示"OFF 1820"
while(1)
display_error();
EA=1; //开中断
EX0=1; //允许外部中断0
IT0=1; //外部中断0为边沿触发方式
EX1=1; //允许外部中断1
IT1=1; //外部中断1为边沿触发方式
while(1)
{
if(a==0&&b==0) //按键0和1的状态变量值都为0,此时轮换显示各DS18B20的温度值
{
diplay_final(); //显示DS18B20的温度值
if(m
由于最近比较忙,所有过多的芯片简介就没介绍,希望大家理解。