485通讯例程.docx
《485通讯例程.docx》由会员分享,可在线阅读,更多相关《485通讯例程.docx(92页珍藏版)》请在冰豆网上搜索。
485通讯例程
RS-485总线的接口电路硬件设计
笔者在火灾自动报警系统的联网设计中,经大量试验,发现在使用RS-485总线时,如果简单地按常规方式设计电路,那么在实际工程中可能存在以下两个问题:
一是通信数据收发不可靠;二是在多机通信方式下,一个节点的故障(如死机)往往会使得整个系统的通信框架崩溃,而且给故障的排查带来困难。
针对上述问题,对485总线接口的软硬件设计采取了有效的改进措施,大大提高了联网系统的可靠性和稳定性。
1RS-485总线接口硬件电路的设计
如图1所示,89C51单片机自带异步通信接口,外接RS-485收发器75LHCl84,89C5l的异步通信口与75LBCl84之间采用3片光耦进行电气隔离。
1.175LBCl84DE控制端的设计
由于火灾报警控制系统中主机与分机相隔较远,通信线路的总长度往往超过l000m,而分机系统上电或复位又常常不在同一个时刻完成。
如果此时某个75LBCl84的DE端电位为1,那么它的485总线输出将处于发送状态,也就是占用了通信总线,这样其他分机就无法与主机进行通信。
这种情况尤其表现在某个分扰出现异常情况(如死机)下,会使整个系统通信崩溃。
因此在电路设计时,应保证系统上电复位时75LBCl84的DE端电位为O。
由于89C51在复位期间,I/O口输出高电平,故图1中电路的接法可有效地解决复位期间分机“咬”总线的问题。
1.2隔离光耦电路的参数选取
在火灾报警系统中,要对现场情况进行实时监控及响应,因此通信数据的波特率往往做得较高(本系统中控制器与显示盘之间的通信速率在6250bps)。
限制通信波特率提高的“瓶颈”并不是现场的导线(现场施工一般使用非屏蔽的双绞线),而是单片机系统进行信号隔离的光耦电路。
此处采用TIL117,电路设计中可以考虑采用高速光耦,如6N137、6N136等芯片;也可以优化普通光耦电路参数的设计,使之工作在最佳状态。
例如:
电阻R2、R3如果选取得较大,则会使光耦的发光管由截止进入饱和变得较慢;如果选取得过小,则退出饱和会很慢。
所以这两只电阻的数值要精心选取,不同型号的光耦及驱动电路使得这两个电阻值略有差异,在电路设计中应特别慎重,通常需要通过实验确定。
1.3485总线输出电路部分的设计
输出电路的设计要充分考虑线路上的各种干扰及线路特性阻抗的匹配。
信号在传输过程中会产生电磁干扰和终端反射.使有效信号和无效信号在传输线上相互叠加,严重时会使通信无法正常进行。
为解决这一问题,某些芯片的驱动器设计成限斜率方式,使输出信号边沿不要过陡,以免在传输线上产生过多的高频分量,从而有效地扼制干扰的产生。
在没计选型时,最好选择具有该种功能的RS-485接口芯片。
同时,RS-485接口芯片在使用、焊接或设备的运输途中都有可能受到静电的冲击而损坏;而且由于工程环境比较复杂,现场常有各种形式的干抗源,在传输线架设于户外的使用场合,接口芯片乃至整个系统还有可能遭致雷电的袭击。
所以485总线的传输端一定要加有保护措施,在电路设计中选用抗静电或抗雷击的芯片可有效避免此类损失。
本系统中选用的是75LBCl84,它的驱动器不但设计成限斜率方式输出,而且能抗雷电的冲击,承受高达8kV的静电放电冲击,在实际使用中效果十分理想。
考虑到线路的特殊情况(例如某台分机的485芯片被击穿短路),为防止总线中其他分机的通信受到影响,在75LBCl84的485信号输出端串联了两个20Ω的电阻R10和R11。
这样本机的硬件故障就不会使整个总线的通信受到影响。
在消防报警产品的现场拖工中,通信载体一般采用双绞线。
其特性阻抗为120Ω左右,所以线路设计时,在485网络传输线的始端和末端应各接1只120Ω的匹配电阻(如图1中的R8),以减少线路上传输信号的反射。
由于RS-485芯片的特性,接收器的检测灵敏度为±200mV,即差分输入端VA-VB≥+200mV,输出逻辑l,VA-VB≤-200mV,输出逻辑0;而当A、B端屯位差的绝对值小于200mv时,输出不确定。
如果在总线上所有发送器被禁止,则接收器输出逻辑0,这会误认为通信帧的起始引起工作不正常。
解决这个问题的办法是人为地使A端电位高于B两端电位。
这样RXD的电平在485总线不发送期间(总线悬浮时)呈现唯一的高电平,89C5l单片机就不会被误中断而收到乱字符。
通过在485电路的A、B输出端加接上、下拉电阻R7、R9,可以很好地解决这个问题。
2RS-485总线接口软件设计
RS-485接口的软件设计对系统联网的可靠性有很大影响。
由于485总线是异步半双工的通信总线,在某一个时刻总线只可能呈现一种状态,所以这种方式一般适用于主机对分机的查询方式通信,总线上必然有一台始终处于主机地位的设备在巡检其他分机,这就需要制定一套合理的通信协议来协调总线的分时共用。
这里采用的是数据包通信方式,通信数据是成帧、成包发送的,每包数据都由引导码,长度码、地址码、命令码、内容和校验码等部分组成。
其中:
引导码是用于同步每一包数据的引导头;长度码是这一包数据的总长度;命令码是主机对分机(或分机应答主机)的控制命令;地址码是分机的本机地址号;“内容”是这一包数据里的各种信息;校验码是这一包数据的校验标志,可以采用奇偶校验、“和”校验以及CRC校验等不同方式。
在设计RS-485通信软件时,尤其要注意对485控制端DE的软件编程。
为了保证数据收发可靠,在485总线状态切换时需要加适当延时,再进行数据的收发。
具体做法是在数据发送状态下,先将控制端DE置1;延时1ms左右后,再发送有效的数据。
一个数据包发送结束后再延时lms,然后将控制端DE清0。
控制端DE经过这样处理后,会使总线在状态切换时有一个稳定的工作过程。
结语
RS-485总线具有电路设计简单、软件设计方便、成本低等优点,在火灾报警系统中应用非常广泛。
本系统经过对软硬件接口进行改进后,大大提高了通信可靠性、稳定性,可满足消防报警系统的实际需要。
采用本文改进的485总线接口已成功地应用于数百个消防报警系统,未出现任何通信故障。
应用程序
main()
{//初始化
TMOD=0x20;
TL1=0xfd;//设定波特率为9600bit
THL=0xfd;
PCON=0x80;
TCON=0x00;
SMOD=0x00;
SCON=0xF0;
TRL=1;
REN=1;
ES=1;
EA=1;
P27=1;///处于接收状态
}
{SCON=0x50;
TMOD=0x22;//mode2,8bitautotimerTR1TH1letTL1startat0xfd.
PCON=0x80;//equalSMOD=1
TH1=0x0fd;
TR1=1;//?
TR0=1;//?
both
ET0=1;//admitTimer0interruptequalIE=0x02
EA=1;//**meansIE|=0x90;
TH0=TL0=0x00;
}
//串行口中断程序
Voidserial()interrupt4using1{
Unsignedcharaddress;
RI=0;//关中断
Address=SBUF;
//判断是否符合本机地址,如1号机
if(address==0x01)
{P35=0;
SM2=0;///清SM2位,准备接收数据
//收数据及相应处理
//(发送功能可在此部分完成)
SM2=1;
}
elseSM2=1;//不是发给本机数据,
//置位SM2,退出
}
#include
#include
#defineINBUF_LEN4//数据长度
unsignedcharinbuf1[INBUF_LEN];
unsignedcharchecksum,count3;
bitread_flag=0;
voidinit_serialcomm(void)
{
SCON=0x50;//SCON:
serailmode1,8-bitUART,enableucvr
TMOD|=0x20;//TMOD:
timer1,mode2,8-bitreload
PCON|=0x80;//SMOD=1;
TH1=0xF4;//Baud:
4800fosc=11.0592MHz
IE|=0x90;//EnableSerialInterrupt
TR1=1;//timer1run
//TI=1;
}
//向串口发送一个字符
voidsend_char_com(unsignedcharch)
{
SBUF=ch;
while(TI==0);
TI=0;
}
//向串口发送一个字符串,strlen为该字符串长度
voidsend_string_com(unsignedchar*str,unsignedintstrlen)
{
unsignedintk=0;
do
{
send_char_com(*(str+k));
k++;
}while(k}
//串口接收中断函数
voidserial()interrupt4using3
{
if(RI)
{
unsignedcharch;
RI=0;
ch=SBUF;
if(ch>127)
{
count3=0;
inbuf1[count3]=ch;
checksum=ch-128;
}
else
{
count3++;
inbuf1[count3]=ch;
checksum^=ch;
if((count3==(INBUF_LEN-1))&amt;&amt;(!
checksum))
{
read_flag=1;//如果串口接收的数据达到INBUF_LEN个,且校验没错,
//就置位取数标志
}
}
}
}
main()
{
init_serialcomm();//初始化串口
while
(1)
{
if(read_flag)//如果取数标志已置位,就将读到的数从串口发出
{
read_flag=0;//取数标志清0
send_string_com(inbuf1,INBUF_LEN);
}
}
}
/****************************************Copyright(c)**************************************************
**广州周立功单片机发展有限公司
**研究所
**产品一部
**
**
**
**--------------文件信息--------------------------------------------------------------------------------
**文件名:
PCF8563.C
**创建人:
叶皓贲
**最后修改日期:
2003-3-4
**描述:
DP-1581的8563T及7289a键盘LED演示程序
**
**--------------历史版本信息----------------------------------------------------------------------------
**创建人:
叶皓贲
**版本:
1.0
**日 期:
2003-3-4
**描 述:
DP-1581演示程序
**
**------------------------------------------------------------------------------------------------------
**修改人:
**版本:
**日 期:
**描 述:
**
**--------------当前版本修订------------------------------------------------------------------------------
**修改人:
**日 期:
**描 述:
**
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
#include"REG52.h"
#include
#include
#include"VIIC_C51.h"
#include"ZLG7290.h"
sbitKEY_INT=P3^2;
sbitRS485_RE=P1^5;
bitrec_data;
unsignedcharrec_temp=1;
/*全局变量*/
unsignedchardisp_buf[8]={0,0,0,0,0,0,0,0};
/*********************************************************************************************************
**函数名称:
display
**功能描述:
7298Aled显示
**输 入:
*sd:
显示缓冲区的头地址
**
**输 出:
0:
OK;
**1:
FAIL;
**全局变量:
无
**调用模块:
ZLG7289_SendBuf
**
**作 者:
叶皓贲
**日 期:
2003-3-4
**-------------------------------------------------------------------------------------------------------
**修改人:
**日 期:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
unsignedchardisplay_send(unsignedcharsd)
{
disp_buf[0]=sd;//装载"dp-932"
disp_buf[1]=31;
disp_buf[2]=0x1E;
disp_buf[3]=0x0e;
disp_buf[4]=0x05;
disp_buf[5]=31;
disp_buf[6]=31;
disp_buf[7]=31;
ZLG7290_SendBuf(disp_buf,8);
return0;
}
unsignedchardisplay_rec(unsignedcharsd)
{
disp_buf[0]=sd;//装载"dp-932"
disp_buf[1]=31;
disp_buf[2]=0x0c;
disp_buf[3]=0x0e;
disp_buf[4]=0x18;
disp_buf[5]=31;
disp_buf[6]=31;
disp_buf[7]=31;
ZLG7290_SendBuf(disp_buf,8);
return0;
}
/*********************************************************************************************************
**函数名称:
DelayNS
**功能描述:
长软件延时
**输 入:
no:
延时参数,值越大时延时越久
**
**输 出:
0:
OK;
**1:
FAIL;
**全局变量:
无
**调用模块:
无
**
**作 者:
叶皓贲
**日 期:
2003-3-4
**-------------------------------------------------------------------------------------------------------
**修改人:
**日 期:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
unsignedcharDelayNS(unsignedcharno)
{
unsignedchari,j;//延时参数
for(;no>0;no--)
{
for(i=0;i<100;i++)
for(j=0;j<100;j++);
}
return0;
}
/*********************************************************************************************************
**函数名称:
delay
**功能描述:
短软件延时
**输 入:
j:
延时参数,值越大时延时越久
**
**输 出:
0:
OK;
**1:
FAIL;
**全局变量:
无
**调用模块:
无
**
**作 者:
叶皓贲
**日 期:
2003-3-4
**-------------------------------------------------------------------------------------------------------
**修改人:
**日 期:
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/
unsignedchardelay(unsignedcharj)
{
unsignedchark,l;
for(l=0;l<=j;l++)
for(k=0;k<=250;k++);
return0;
}
voidmain()
{
unsignedcharkey;
rec_data=0;
//====设置波特率===================
delay(100);
TMOD=0X20;
SCON=0X50;
TH1=0XFA;
TL1=0XFA;
PCON=0X80;
TR1=1;
EA=1;
ES=1;
RS485_RE=0;
while
(1)
{
if(KEY_INT==0)
{
key=ZLG7290_GetKey();
delay(20);
display_send(key);
RS485_RE=1;
SBUF=key;
delay(30);
}
if(rec_data)
{
rec_data=0;
display_rec(rec_temp);
delay(20);
}
}
}
voidSerial_ISR()interrupt4
{
if(TI)
{
TI=0;
RS485_RE=0;
}
else
{
RI=0;
rec_data=1;
rec_temp=SBUF;
}
}
――――――――――――――二.485例程2―――――――――――――――――
#ifndef__485_C__
#define__485_C__
#include
#include
#defineunsignedcharuchar
#defineunsignedintuint
/*通信命令*/
#define__ACTIVE_0x01//主机询问从机是否存在
#define__GETDATA_0x02//主机发送读设备请求
#define__OK_0x03//从机应答
#define__STATUS_0x04//从机发送设备状态信息
#define__MAXSIZE0x08//缓冲区长度
#define__ERRLEN12//任何通信帧长度超过12则表示出错
uchardbuf[__MAXSIZE];//该缓冲区用于保存设备状态信息
uchardev;//该字节用于保存本机设备号
sbitM_DE=P1^0;//驱动器使能,1有效
sbitM_RE=P1^1;//接收器使能,0有效
voidget_status();//调用该函数获得设备状态信息,函数代码未给出
voidsend_data(uchartype,ucharlen,uchar*buf);//发送数据帧
bitrecv_cmd(uchar*type);//接收主机命令,主机请求仅包含命令信息
voidsend_byte(ucharda);//该函数发送一帧数据中的一个字节,由send_data()函数调用
voidmain()
{
uchartype;
ucharlen;
/*系统初始化*/
P1=0xff;//读取本机