
| 单片机与PC串口通信1 | |||||
| 作者:本站 | 来源:本站 | 浏览次数: 152 | 添加时间:09-05-03 |
| 摘要: 用一片DS18B20构成测温系统,将测得的温度用数码管显示,并通过串口上传给PC,当温度达到要求的值时,驱动相应的继电器动作. 实验任务: 1)将采集的温度值显示在数码管上(0-99.9度,保留1位小数), 2)通过串口将测量温度值传递给PC。 传送数据格式:发送2位16进制数,比如发送十六进制数19代表十进制数25℃。 3)当温度大于50度时,继电器1动作;当温度小于20度时,继电器2动作。 所需硬件: 1.单片机实验开发板B 2.杜邦头插线 3.DS18B20 4.STCC51RC单片机 所需软件: 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总线协议方式,即在一根数据线实现数据的双向传输,而对ATS51单片机来说,硬件上并不支持单总线协议,因此,我们必须采用软件的方法来模拟单总线的协议时序来完成对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 |
