哈尔滨理工大学软件工程专业08-7李万鹏原创作品,转载请标明出处
http://blog.csdn.net/woshixingaaa/archive/2010/09/27/5910469.aspx
温度传感器是各种传感器中最常用的一种,早期使用的是模拟温度传感器,如热敏电阻,随着环境温度的变化,它的阻值也发生线性变化,用处理器采集电阻两端的电压,然后根据某个公式就可以计算出当前环境温度。美国DALLAS半导体公司推出的数字化温度传感器DS18B20采用单总线协议,即与单片机接口仅需占用一个I/O端口,无需任何外部元件,直接将环境温度转化成数字信号,以数字码方式串行输出,从而大大简化了传感器与处理器的接口。
DS18B20的三种封装:
DS18B20的内部结构:
它采用单条信号线,既可传输时钟,又可传输数据,而且数据传输是双向的。如果要控制多个DS18B20进行温度采集,只要将所有的DS18B20的I/O口全部连接到一起就可以了。在具体操作时,通过读取每个DS18B20内部芯片的序列号来识别。64位光刻ROM中的序列号是出场前被光刻好的,他可以看做该DS18B20的地址序列码。
DS18B20的复位时序:
DS18B20复位,确定其存在:
#include <reg52.h> #define uint unsigned int uint i; sbit DQ = P3^3; sbit bell = P3^4; void reset(){ DQ = 1; //开始的时候是高脉冲 DQ=0; //然后是低脉冲 i=103; while(i>0)i--; //低脉冲需要延迟一会儿 DQ=1; //数据线拉高,系统将总线放开,并进入接受状态 i=4; while(i>0)i--; //延时等待,若初始化成功则在15~60ms内产生一个由 if(DQ == 0){ //DS18B20在检测到总线的上升沿后,等待15~60ms,接着 while(DQ == 0); //在T2时刻发出存在脉冲(低电平) bell = 0; } else bell = 1; } void main(){ reset(); while(1); }
DS18B20的写0和写1时序:
DS18B20的读数据时序:
读出光刻ROM中的ID号,在LCD上显示:
#include <reg52.h> #include<intrins.h> sbit DQ = P3^3; sbit RS = P1^0; sbit RW = P1^1; sbit E = P1^2; sbit bell = P3^4; #define uchar unsigned char #define uint unsigned int #define nop() _nop_() uint i; uchar value; uchar DS[8]; uchar Time_Data[]={'0','1','2','3','4','5','6','7', '8','9','A','B','C','D','E','F'}; void delay(uchar t){ while(--t); } void lcd_com(uchar s){ RS = 0; //低电平,写指令 P2 = s; //传数据 delay(14); //看时序图,数据需要稳定一段时间 E = 1; //给一个高脉冲,发送命令 delay(14); //如图,高脉冲延时一段时间,确保命令发送 E = 0; //发送结束E置为低电平 } void lcd_data(uchar s){ RS = 1; P2 = s; delay(14); E = 1; delay(14); E = 0; } void init_lcd(){ RS = 1; //先发指令,在初始时刻RS是高,E和RW是低 E = 0; RW = 0; lcd_com(0x38); //设置为16*2显示,5*7点阵,8位数据接口 lcd_com(0x0f); //开显示,显示光标,光标闪烁 lcd_com(0x06); //读写一个字符后地址指针加一 lcd_com(0x01); } void Display_lcd(uchar y, uchar x, uchar value){ if(y) lcd_com(0x80+0x40+x); //如果y为1,写在第二行 else lcd_com(0x80+x); lcd_data(value); //写到LCD602上 } void DS18B20_reset(){ DQ = 1; //开始的时候是高脉冲 DQ=0; //然后是低脉冲 i=103; while(i>0)i--; //低脉冲需要延迟一会儿 DQ=1; //数据线拉高 i=4; while(i>0)i--; //延时等待,若初始化成功则在15~60ms内产生一个由 if(DQ == 0){ //DS18B20返回的低电平 while(DQ == 0); // bell = 0; } else bell = 1; } uchar DS18B20_read(void) { uchar i = 0; uchar Value = 0; for(i = 0; i < 8; i ++) { DQ = 1; DQ = 0; delay(1); DQ = 1; //在T1时刻将总线拉高,产生读时间隙 delay(1); //读时隙在T1和T2之间有效 if(DQ) { Value |= 0x01 << i; } delay(17); //必须在T3时刻之间主机完成读操作 DQ = 1; nop(); } return Value; } void DS18B20_write(uchar Value){ for(i = 0; i < 8; i++){ DQ = 1; DQ = 0; //当t0从高拉低产生写时隙, delay(5); DQ = Value & 0x01; //必须在t0开始的15us内将数据送到总线上 delay(20); //DS18B20在t0后的15us~60us内对总线采样 DQ = 1; //如果采到低电平则写入0,高电平写入1 Value >>= 1; delay(2); } } void DS18B20_ID_read(){ DS18B20_reset(); DS18B20_write(0x33); for(i = 0; i < 8; i++) DS[i] = DS18B20_read(); } void main(){ init_lcd(); while(1){ DS18B20_ID_read(); Display_lcd(0,0,'>'); Display_lcd(0,1,':'); Display_lcd(0,2,Time_Data[DS[0]/16]); Display_lcd(0,3,Time_Data[DS[0]%16]); Display_lcd(0,4,'>'); Display_lcd(0,5,':'); Display_lcd(0,6,Time_Data[DS[1]/16]); Display_lcd(0,7,Time_Data[DS[1]%16]); Display_lcd(0,8,'>'); Display_lcd(0,9,':'); Display_lcd(0,10,Time_Data[DS[2]/16]); Display_lcd(0,11,Time_Data[DS[2]%16]); Display_lcd(0,12,'>'); Display_lcd(0,13,':'); Display_lcd(0,14,Time_Data[DS[3]/16]); Display_lcd(0,15,Time_Data[DS[3]%16]); Display_lcd(1,0,'>'); Display_lcd(1,1,':'); Display_lcd(1,2,Time_Data[DS[4]/16]); Display_lcd(1,3,Time_Data[DS[4]%16]); Display_lcd(1,4,'>'); Display_lcd(1,5,':'); Display_lcd(1,6,Time_Data[DS[5]/16]); Display_lcd(1,7,Time_Data[DS[5]%16]); Display_lcd(1,8,'>'); Display_lcd(1,9,':'); Display_lcd(1,10,Time_Data[DS[6]/16]); Display_lcd(1,11,Time_Data[DS[6]%16]); Display_lcd(1,12,'>'); Display_lcd(1,13,':'); Display_lcd(1,14,Time_Data[DS[7]/16]); Display_lcd(1,15,Time_Data[DS[7]%16]); } }
单只DS18B20工作流程:
2只DS18B20并联工作流程:
DS18B20温度存储格式:
DS18B20暂存器的分布:
DS18B20内部ROM指令:
DS18B20内部RAM指令:
#include <reg52.h> #include<intrins.h> sbit DQ = P3^3; sbit RS = P1^0; sbit RW = P1^1; sbit E = P1^2; sbit bell = P3^4; #define uchar unsigned char #define uint unsigned int #define nop() _nop_() uint i; uchar value; uchar DS[8]; uchar Time_Data[]={'0','1','2','3','4','5','6','7', '8','9','A','B','C','D','E','F'}; void delay(uchar t){ while(--t); } void Lcd_Com(uchar s){ RS = 0; //低电平,写指令 P2 = s; //传数据 delay(14); //看时序图,数据需要稳定一段时间 E = 1; //给一个高脉冲,发送命令 delay(14); //如图,高脉冲延时一段时间,确保命令发送 E = 0; //发送结束E置为低电平 } void Lcd_Data(uchar s){ RS = 1; P2 = s; delay(14); E = 1; delay(14); E = 0; } void Init_Lcd(){ RS = 1; //先发指令,在初始时刻RS是高,E和RW是低 E = 0; RW = 0; Lcd_Com(0x38); //设置为16*2显示,5*7点阵,8位数据接口 Lcd_Com(0x0f); //开显示,显示光标,光标闪烁 Lcd_Com(0x06); //读写一个字符后地址指针加一 Lcd_Com(0x01); } void Display_Lcd(uchar y, uchar x, uchar value){ if(y) Lcd_Com(0x80+0x40+x); //如果y为1,写在第二行 else Lcd_Com(0x80+x); Lcd_Data(value); //写到LCD602上 } void DS18B20_Reset(){ DQ = 1; //开始的时候是高脉冲 DQ=0; //然后是低脉冲 i=103; while(i>0)i--; //低脉冲需要延迟一会儿 DQ=1; //数据线拉高 i=4; while(i>0)i--; //延时等待,若初始化成功则在15~60ms内产生一个由 if(DQ == 0){ //DS18B20返回的低电平 while(DQ == 0); // bell = 0; } else bell = 1; } uchar DS18B20_Read(void) { uchar i = 0; uchar Value = 0; for(i = 0; i < 8; i ++) { DQ = 1; DQ = 0; delay(1); DQ = 1; delay(1); if(DQ) { Value |= 0x01 << i; } delay(17); DQ = 1; nop(); } return Value; } void DS18B20_Write(uchar Value){ for(i = 0; i < 8; i++){ DQ = 1; DQ = 0; delay(5); DQ = Value & 0x01; delay(20); DQ = 1; Value >>= 1; delay(2); } } uchar DS18B20_Temp_Read(){ uchar temp_h,temp_l,temp; DS18B20_Reset(); //复位 DS18B20_Write(0x0cc); //跳过ROM,只有一个所以跳过 DS18B20_Write(0x44); //开始温度转换 DS18B20_Reset(); //复位 DS18B20_Write(0x0cc); //跳过ROM DS18B20_Write(0x0be); //读暂存器 temp_l = DS18B20_Read(); //读出温度低8位 temp_h = DS18B20_Read(); //读出温度高8位 temp_l >>= 4; //去掉4位小数位 temp_h <<= 4; //去掉4位符号位 temp = temp_h | temp_l; temp = temp & 0x7f; //最高位是符号位 return temp; } void main(){ uchar temp; Init_Lcd(); while(1){ temp = DS18B20_Temp_Read(); if(temp > 25) //如果温度大于25报警 bell = 0; else bell = 1; Display_Lcd(0,0,'T'); Display_Lcd(0,1,'E'); Display_Lcd(0,2,'M'); Display_Lcd(0,3,'P'); Display_Lcd(0,4,':'); Display_Lcd(0,5,Time_Data[temp/100]); Display_Lcd(0,6,Time_Data[temp%100/10]); Display_Lcd(0,7,Time_Data[temp%10]); } }