实验任务:
1)将采集的温度值显示在数码管上(0-99.9度,保留1位小数),
2)通过串口将测量温度值传递给PC。
传送数据格式:发送2位16进制数,比如发送十六进制数19代表十进制数25℃。
3)当温度大于50度时,继电器1动作;当温度小于20度时,继电器2动作。
所需硬件: 1.单片机实验开发板B
2.杜邦头插线
3.DS18B20
4.STC89C51RC单片机
所需软件: 1. Keil C51 V8.08或Keil_uv3 绿色版本
2.STC单片机ISP下载编程软件
18B20中文资料
DS18B20连接图
串口接收到的数据
DS18B20数字温度计是DALLAS公司生产的1-Wire,即单总线器件,具有线路简单,体积小的特点。因此用它来组成一个测温系统,具有线路简单,在一根通信线,可以挂很多这样的数字温度计,十分方便。
1.DS18B20产品的特点
(1)、只要求一个端口即可实现通信。
(2)、在DS18B20中的每个器件上都有独一无二的序列号。
(3)、实际应用中不需要外部任何元器件即可实现测温。
(4)、测量温度范围在-55。C到+125。C之间。
(5)、数字温度计的分辨率用户可以从9位到12位选择。
(6)、内部有温度上、下限告警设置。
2.DS18B20的引脚介绍
TO-92封装的DS18B20的引脚排列见图1,其引脚功能描述见表1。
(底视图)图1
表1 DS18B20详细引脚功能描述
序号 | 名称 | 引脚功能描述 |
1 | GND | 地信号 |
2 | DQ | 数据输入/输出引脚。开漏单总线接口引脚。当被用着在寄生电源下,也可以向器件提供电源。 |
3 | VDD | 可选择的VDD引脚。当工作于寄生电源时,此引脚必须接地。 |
3.DS18B20的使用方法
由于DS18B20采用的是1-Wire总线协议方式,即在一根数据线实现数据的双向传输,而对AT89S51单片机来说,硬件上并不支持单总线协议,因此,我们必须采用软件的方法来模拟单总线的协议时序来完成对DS18B20芯片的访问。
由于DS18B20是在一根I/O线上读写数据,因此,对读写的数据位有着严格的时序要求。DS18B20有严格的通信协议来保证各位数据传输的正确性和完整性。该协议定义了几种信号的时序:初始化时序、读时序、写时序。所有时序都是将主机作为主设备,单总线器件作为从设备。而每一次命令和数据的传输都是从主机主动启动写时序开始,如果要求单总线器件回送数据,在进行写命令后,主机需启动读时序完成数据接收。数据和命令的传输都是低位在先。
DS18B20的复位时序
DS18B20的读时序
对于DS18B20的读时序分为读0时序和读1时序两个过程。
对于DS18B20的读时隙是从主机把单总线拉低之后,在15微秒之内就得释放单总线,以让DS18B20把数据传输到单总线上。DS18B20在完成一个读时序过程,至少需要60us才能完成。
DS18B20的写时序
对于DS18B20的写时序仍然分为写0时序和写1时序两个过程。
对于DS18B20写0时序和写1时序的要求不同,当要写0时序时,单总线要被拉低至少60us,保证DS18B20能够在15us到45us之间能够正确地采样IO总线上的“0”电平,当要写1时序时,单总线被拉低之后,在15us之内就得释放单总线。
4.电路原理图
请参考开发板原理图.
5.系统板上硬件连线
(1).把开发板串口线连接到电脑上。
(2).DS18B20的GND DQ VDD用杜邦头插线分别连接到实验板上单片机的20脚 23脚和40脚上,如上面的连接图,注意接线顺序。
6.C语言源程序
/******************************************************************
** 单片机与PC串口通信1
** 晶 振 频 率:11.0592M
** 线路->单片机实验开发板B
******************************************************************/
#include
#include
sbit PS0=P2^4;//数码管小数点后第1位
sbit PS1=P2^5;//数码管个位
sbit PS2=P2^6;//数码管十位
sbit PS3=P2^7;//数码管百位
sfr P_data=0x80;//P0口为显示数据输出口
sbit P_K_L=P2^2;//键盘列
sbit DQ=P3^6;//DS18B20数据接口
sbit P_L=P0^0;//测量指示
sbit JDQ1=P2^0;
sbit JDQ2=P2^1;
void delay(unsigned int);//延时函数
void DS18B20_init(void); //DS18B20初始化
unsigned int get_temper(void); //读取温度程序
void DS18B20_write(unsigned char in_data); //DS18B20写数据函数
unsigned char DS18B20_read(void);////读取数据程序
unsigned int htd(unsigned int a); //进制转换函数
void display(unsigned int a);//显示函数
float temp;//温度寄存器
bit DS18B20; // 18B20存在标志,1---存在 0---不存在
unsigned char tab[10]={0xfc,0x60,0xda,0xf2,0x66,0xb6,0xbe,0xe0,0xfe,0xf6};//字段转换表
void main(void)
{
unsigned int a,temp;
TMOD=0x20; //定时器1--方式2
TL1=0xfa;
TH1=0xfa; //11.0592MHZ晶振,波特率为4800
SCON=0x50; //方式1
TR1=1; //启动定时
temp=get_temper();//这段程序用于避开刚上电时显示85的问题
for(a=0;a<200;a++)
delay(500);
while(1)
{
temp=get_temper();//测量温度
for(a=0;a<200;a++)//显示,兼有延时的作用
display(htd(temp));
SBUF=(unsigned char)(temp>>8); //将温度发送给PC
while(TI!=1);
TI=0;
SBUF=(unsigned char)temp;
while(TI!=1);
TI=0; if((temp/10)>50)
JDQ1=0;//继电器1动作
else
JDQ1=1;//继电器1复位
if((temp/10)<20)
JDQ2=0;//继电器2动作
else
JDQ2=1;//继电器1复位
}
}
/****************************DS18B20读取温度函数**************************/
/*函数原型:void get_temper(void)
/*函数功能:DS18B20读取温度
/*输入参数:无
/*输出参数:无
/*调用模块:
/*建立时间:2005/11/14
/*作者:站长
/**********************************************************************/
unsigned int get_temper(void)
{
unsigned char k,T_sign,T_L,T_H;
DS18B20_init(); //DS18B20初始化
if(DS18B20) //判断DS1820是否存在?若DS18B20不存在则返回
{
DS18B20_write(0xcc); // 跳过ROM匹配
DS18B20_write(0x44); //发出温度转换命令
DS18B20_init(); //DS18B20初始化
if(DS18B20) //判断DS1820是否存在?若DS18B20不存在则返回
{
DS18B20_write(0xcc);//跳过ROM匹配
DS18B20_write(0xbe);//发出读温度命令
T_L=DS18B20_read();//数据读出
T_H=DS18B20_read();
k=T_H&0xf8;
if(k==0xf8)
T_sign=1; //温度是负数
else
T_sign=0; //温度是正数
T_H=T_H&0x07;
temp=(T_H*256+T_L)*10*0.0625; //温度转换常数 乘以10 是因为要保留1位小数
return (temp);
}
}
}
/****************************DS18B20写数据函数**************************/
/*函数原型:void DS18B20_write(uchar in_data)
/*函数功能:DS18B20写数据
/*输入参数:要发送写入的数据
/*输出参数:无
/*调用模块:_cror_()
/*建立时间:2005/11/14
/*作者:站长
/**********************************************************************/
void DS18B20_write(unsigned char in_data) //写DS18B20的子程序(有具体的时序要求)
{
unsigned char i,out_data,k;
out_data=in_data;
for(i=1;i<9;i++) //串行发送数据
{
DQ=0;
DQ=1;
_nop_();
_nop_();
k=out_data&0x01;
if(k==0x01) //判断数据 写1
{
DQ=1;
}
else //写0
{
DQ=0;
}
delay(4); //延时62us
DQ=1;
out_data=_cror_(out_data,1); //循环左移1位
}
}
/**************************DS18B20读函数**************************/
/*函数原型:void DS18B20_read()
/*函数功能:DS18B20读数据
/*输入参数:无
/*输出参数:读到的一字节内容
/*调用模块:delay()
/*建立时间:2005/11/14
/*作者:站长
/******************************************************************/
unsigned char DS18B20_read()
{
unsigned char i,in_data,k;
in_data=0;
for(i=1;i<9;i++) //串行发送数据
{
DQ=0;
DQ=1;
_nop_();
_nop_();
k=DQ; //读DQ端
if(k==1) //读到的数据是1
{
in_data=in_data|0x01;
}
else
{
in_data=in_data|0x00;
}
delay(3); //延时51us
DQ=1;
in_data=_cror_(in_data,1); //循环右移1位
}
return(in_data);
}
/**************************DS18B20初始化函数**************************/
/*函数原型:void DS18B20_init(void)
/*函数功能:DS18B20初始化
/*输入参数:无
/*输出参数:无
/*调用模块:delay()
/*建立时间:2005/11/14
/*作者:站长
/******************************************************************/
void DS18B20_init(void)
{
unsigned char a;
DQ=1;//主机发出复位低脉冲
DQ=0;
delay(44); //延时540US
DQ=1;
for(a=0;a<0x36&&DQ==1;a++)
{
a++;
a--; //等待DS18B20回应
}
if(DQ)
DS18B20=0; //18B20不存在
else
{
DS18B20=1; // 18B20存在
delay(120); //复位成功!延时240US
}
}
/**************************数码管显示函数**************************/
/*函数原型:void display(void)
/*函数功能:数码管显示
/*输入参数:无
/*输出参数:无
/*调用模块:delay()
/*建立时间:2005/11/14
/*作者:站长
/******************************************************************/
void display(unsigned int a)
{
bit b=P_K_L;
P_K_L=1;//防止按键干扰显示
P_data=tab[a&0x0f];//显示小数点后第1位
PS0=0;
PS1=1;
PS2=1;
PS3=1;
delay(200);
P_data=tab[(a>>4)&0x0f]|0x01; //显示个位
PS0=1;
PS1=0;
delay(200);
P_data=tab[(a>>8)&0x0f];//显示十位
PS1=1;
PS2=0;
delay(200);
//P_data=tab[(a>>12)&0x0f];//显示百位
PS2=1;
//PS3=0;
//delay(200);
//PS3=1;
P_K_L=b;//恢复按键
P_data=0xff;//恢复数据口
}
/**************************十六进制转十进制函数**************************/
/*函数原型:uint htd(uint a)
/*函数功能:十六进制转十进制
/*输入参数:要转换的数据
/*输出参数:转换后的数据
/*调用模块:无
/*建立时间:2005/11/14
/*作者:站长
/******************************************************************/
unsigned int htd(unsigned int a)
{
unsigned int b,c;
b=a%10;
c=b;
a=a/10;
b=a%10;
c=c+(b<<4);
a=a/10;
b=a%10;
c=c+(b<<8);
a=a/10;
b=a%10;
c=c+(b<<12);
return c;
}
/*******************************延时函数*********************************/
/*函数原型:delay(unsigned int delay_time)
/*函数功能:延时函数
/*输入参数:delay_time (输入要延时的时间)
/*输出参数:无
/*调用模块:无
/*建立时间:2005/10/21
/*作者:站长
/**********************************************************************/
void delay(unsigned int delay_time) //延时子程序
{for(;delay_time>0;delay_time--)
{}
}
keil工程文件打包下载chuankou1.rar
/******************************************************************
** 单片机与PC串口通信1
** 晶 振 频 率:11.0592M
** 线路->单片机实验开发板B
******************************************************************/
;单片机内存分配申明!
A_BYTE EQU 20H ;数码管小数点后第1位数存放内存位置
B_BYTE EQU 21H ;数码管个位数存放内存位置
C_BYTE EQU 22H ;数码管十位数存放内存位置
TEMPER_H EQU 23H ;用于保存读出温度的高8位
TEMPER_L EQU 24H ;用于保存读出温度的低8位
TEMP_H EQU 28H ;用于保存温度*0.625后高位
TEMP_L EQU 29H ;用于保存温度*0.625后低位
FLAG1 BIT 38H ;是否检测到DS18B20标志位
DQ BIT P3.6 ;18B20的数据接口
JDQ1 BIT P2.0 ;继电器1控制
JDQ2 BIT P2.1 ;继电器2控制
PS0 BIT P2.4 ;数码管小数点后第1位
PS1 BIT P2.5 ;数码管个位
PS2 BIT P2.6 ;数码管十位
PS3 BIT P2.7 ;数码管百位
;进行温度显示,这里我们考虑用网站提供的两位数码管来显示温度
;显示范围00到99度,显示精度为1度
;因为12位转化时每一位的精度为0.0625度,我们不要求显示小数,所以可以抛弃TEMPER_L的
;低4位,将TEMPER_H中的低4位移入TEMPER_L中的高4位,这样获得一个新字节,这个字节就是
;实际测量获得的温度这个转化温度的方法非常简洁无需乘于0.0625系数
ORG 0000H
SJMP MAIN
ORG 0030H
MAIN: MOV SP,#60H
MOV SCON,#50H ;设置成串口1方式
MOV TMOD,#20H ;波特率发生器T1工作在模式2上
MOV TH1,#0FAH ;预置初值(按照波特率4800BPS预置初值)
MOV TL1,#0FAH ;预置初值(按照波特率4800BPS预置初值)
;ORL PCON, #80H;波特率加倍
SETB TR1 ;启动定时器T1
LOOP: ACALL GET_TEMPER;调用读温度子程序
MOV R3,TEMPER_H
MOV R4,TEMPER_L
MOV R7,#5 ;乘以5再除以8 等与乘以0.625
ACALL NMUL21
MOV R7,#8
ACALL NDIV31
MOV TEMP_L,R4
MOV TEMP_H,R3
MOV A,R3
MOV R2,A
MOV A,R4
MOV R3,A
ACALL HTD ;转换成十进制
MOV A,R6
ANL A,#0FH
MOV A_BYTE,A
MOV A,R6
SWAP A
ANL A,#0FH
MOV B_BYTE,A
MOV A,R5
ANL A,#0FH
MOV C_BYTE,A
ACALL SEND ;调用串口显示子程序
ACALL DISPLAY;调用数码管显示子程序
SETB C;判断温度>500(>50度)
MOV A,R6
SUBB A,#0
MOV A,R5
SUBB A,#05H
JC C0014
CLR JDQ1
SETB JDQ2
SJMP LOOP
C0014: CLR C
MOV A,R6
SUBB A,#0
MOV A,R5
SUBB A,#2H
JNC C0025
SETB JDQ1
CLR JDQ2
SJMP LOOP
C0025: SETB JDQ1
SETB JDQ2
SJMP LOOP
INIT_1820: SETB DQ ; 这是DS18B20复位初始化子程序
NOP
CLR DQ
MOV R1,#3 ;主机发出延时537微秒的复位低脉冲
TSR1: MOV R0,#107
DJNZ R0,$
DJNZ R1,TSR1
SETB DQ ;然后拉高数据线
NOP
NOP
NOP
MOV R0,#25H
TSR2: JNB DQ,TSR3 ;等待DS18B20回应
DJNZ R0,TSR2
AJMP TSR4 ; 延时
TSR3: SETB FLAG1 ; 置标志位,表示DS1820存在
AJMP TSR5
TSR4: CLR FLAG1 ; 清标志位,表示DS1820不存在
AJMP TSR7
TSR5: MOV R0,#117
TSR6: DJNZ R0,TSR6 ; 时序要求延时一段时间
TSR7: SETB DQ
RET
GET_TEMPER: SETB DQ ; 读出转换后的温度值
LCALL INIT_1820;先复位DS18B20
JB FLAG1,TSS2
RET ; 判断DS1820是否存在?若DS18B20不存在则返回
TSS2: MOV A,#0CCH ; 跳过ROM匹配
ACALL WRITE_1820
MOV A,#44H ; 发出温度转换命令
ACALL WRITE_1820
ACALL DISPLAY ;这里通过调用显示子程序实现延时一段时间,等待AD转换结束,12位的话750微秒
ACALL INIT_1820;准备读温度前先复位
MOV A,#0CCH ; 跳过ROM匹配
ACALL WRITE_1820
MOV A,#0BEH ; 发出读温度命令
ACALL WRITE_1820
ACALL READ_18200; 将读出的温度数据保存到TEMPER_L/TEMPER_H
RET
WRITE_1820: MOV R2,#8;写DS18B20的子程序(有具体的时序要求),一共8位数据
CLR C
WR1: CLR DQ
MOV R3,#5
DJNZ R3,$
RRC A
MOV DQ,C
MOV R3,#21
DJNZ R3,$
SETB DQ
NOP
DJNZ R2,WR1
SETB DQ
RET
READ_18200: MOV R4,#2 ; 读DS18B20的程序,从DS18B20中读出两个字节的温度数据,将温度高位和低位从DS18B20中读出
MOV R1,#TEMPER_L ; 低位存入TEMPER_L,高位存入TEMPER_H
RE00: MOV R2,#8;数据一共有8位
RE01: CLR C
SETB DQ
NOP
NOP
CLR DQ
NOP
NOP
NOP
SETB DQ
MOV R3,#8
RE10: DJNZ R3,RE10
MOV C,DQ
MOV R3,#21
RE20: DJNZ R3,RE20
RRC A
DJNZ R2,RE01
MOV @R1,A
DEC R1
DJNZ R4,RE00
RET
;----------------------
;串口发送数据子程序
;----------------------
SEND: CLR TI
MOV A,TEMP_H
MOV SBUF,A
JNB TI,$
CLR TI
MOV A,TEMP_L
MOV SBUF,A
JNB TI,$
RET
DISPLAY: MOV DPTR,#NUMTAB ;指定查表启始地址
MOV R0,#4
DPL1: MOV R1,#250 ;显示1000次
DPLOP: mov a,A_BYTE ;取小数点后第1位数
MOVC A,@A+DPTR ;查个位数的7段代码
mov p0,a ;送出个位的7段代码
CLR PS0
SETB PS1
SETB PS2
SETB PS3
acall d1ms ;显示1ms
mov a,B_BYTE ;取个位数
MOVC A,@A+DPTR ;查十位数的7段代码
ORL A,#01H
mov p0,a ;送出十位的7段代码
SETB PS0
CLR PS1
acall d1ms ;显示1ms
mov a,C_BYTE ;取十位数
MOVC A,@A+DPTR ;查十位数的7段代码
mov p0,a ;送出十位的7段代码
SETB PS1
CLR PS2
acall d1ms ;显示1ms
SETB PS2
djnz r1,dplop ;100次没完循环
djnz r0,dpl1 ;4个100次没完循环
ret
;单字节无符号数乘法程序 (R3R4*R7)=(R2R3R4)
;入口: R3,R4,R7
;占用资源: ACC,B
;堆栈需求: 2字节
;出口: R2,R3,R4
NMUL21 : MOV A,R4
MOV B,R7
MUL AB
MOV R4,A
MOV A,B
XCH A,R3
MOV B,R7
MUL AB
ADD A,R3
MOV R3,A
CLR A
ADDC A,B
MOV R2,A
CLR OV
RET
;单字节无符号除法程序 (R2R3R4/R7)=(R2)R3R4 余数R7
;入口: R2,R3,R4,R7
;占用资源: ACC,B,F0
;堆栈需求: 3字节
;出口: (R2),R3,R4,R7,OV
NDIV31 : MOV A,R2
MOV B,R7
DIV AB
PUSH ACC
MOV R2,B
MOV B,#10H
NDV311 : CLR C
MOV A,R4
RLC A
MOV R4,A
MOV A,R3
RLC A
MOV R3,A
MOV A,R2
RLC A
MOV R2,A
MOV F0,C
CLR C
SUBB A,R7
JB F0,NDV312
JC NDV313
NDV312 : MOV R2,A
INC R4
NDV313 : DJNZ B,NDV311
POP ACC
CLR OV
JZ NDV314
SETB OV
NDV314 : XCH A,R2
MOV R7,A
RET
HTD: CLR A ;R2和R3是程序数据入口
MOV R4,A ;转换后的数放在R4,R5,R6
MOV R5,A ;转换完后在分别屏蔽高位和低位取出数据
MOV R6,A
MOV R7,#10H
ZH: CLR C
MOV A,R3
RLC A
MOV R3,A
MOV A,R2
RLC A
MOV R2,A
MOV A,R6
ADDC A,R6
DA A
MOV R6,A
MOV A,R5
ADDC A,R5
DA A
MOV R5,A
MOV A,R4
ADDC A,R4
DA A
MOV R4,A
DJNZ R7,ZH
RET
D1MS: MOV R7,#80 ;1MS延时
DJNZ R7,$
RET
numtab: DB 0FCH,60H,0DAH,0F2H,66H,0B6H,0BEH,0E0H,0FEH,0F6H ;实验板上的7段数码管0~9数字的共阴显示代码
END
上一篇:红外线遥控器声光测试器
下一篇:单片机与PC串口通信2