
2.3 串行接口网络通信应用开发实例
2.3.1 RS-232C转RS-485通信电路的设计分析
RS-232C转RS-485通信电路图,即RS-232C/RS-485转换器的电路图如图2-16所示。

图2-16 RS-232C/RS-485转换器的电路图
RS-232C/RS-485转换器主要包括了电源、232电平转换、485电路三部分。本电路的232电平转换电路采用了NIH232或者也可以直接使用MAX232集成电路,485电路采用了MAX485集成电路。为了使用方便,电源部分设计成无源方式,整个电路的供电直接从PC的RS-232C接口中的DTR(4脚)和RTS(7脚)截取。PC串口每根线可以提供大约9mA的电流,因此,两根线提供的电流足够供给这个电路使用了。经实验,本电路只使用其中一条线也能够正常工作。使用本电路需注意PC程序必须使串口的DTR和RTS输出高电平,经过VD3稳压后得到VCC,经过实际测试,VCC电压大约在4.7V左右。因此,电路中要说VD3起的作用是稳压还不如说是限压功能。
MAX485接口芯片是Maxim公司的一种RS-485芯片,采用单一电源+5V工作,额定电流为300μA,采用半双工通信方式。它完成将TTL电平转换为RS-485电平的功能,其引脚结构如图2-16右侧所示。从图中可以看出,MAX485芯片的结构和引脚都非常简单,内部含有一个驱动器和接收器。RO和DI引脚分别为接收器的输出端和驱动器的输入端,与单片机连接时只需分别与单片机的RXD和TXD相连即可。RE和DE引脚分别为接收和发送的使能端,当RE为逻辑0时,器件处于接收状态;当DE为逻辑1时,器件处于发送状态。因为MAX485工作在半双工状态,所以只需用单片机的一个引脚控制这两个引脚即可。A和B引脚分别为接收和发送的差分信号端,当A的电平高于B时,代表发送的数据为1;当A的电平低于B时,代表发送的数据为0。在与单片机连接时接线也非常简单,只需要一个信号控制MAX485的接收和发送即可,同时将A和B引脚之间加匹配电阻,一般可选100Ω的电阻。
MAX485是通过两个引脚RE(2脚)和DE(3脚)来控制数据的输入和输出的。当RE为低电平时,MAX485数据输入有效;当DE为高电平时,MAX485数据输出有效。在半双工使用中,通常可以将这两个引脚直接相连,然后由PC或者单片机输出的高低电平就可以让MAX485在接收和发送状态之间转换了。由于本电路DTR和RTS都用于了电路供电,因此使用TX线和HIN232的另外一个通道及VT1来控制MAX485的状态切换。平时NIH232的9脚输出高电平,经VT1倒相后,使MAX485的RE和DE为低电平而处于数据接收状态。当PC发送数据时,NIH232的9引脚输出低电平,经VT1倒相后,使MAX485的RE和DE为高电平而处于数据发送状态。
如图2-17所示为RS-232C转RS-485通信电路图和RS-232C转TTL电平(UART)通信电路图的合并,注意:在图中关键的接口和元器件处都有相应的标注。

图2-17 RS-232C转RS-485通信电路图
2.3.2 基于RS-485主从通信协议的实现
1.软件示意图
协议软件必须准确识别出一个完整帧并进行处理,如果有响应数据,则发送。软件实现状态表示如图2-18所示。

图2-18 软件实现状态表示
2.软件程序
下面用T1的时间产生并在各状态之间切换,接收和发送在串行中断中实现,用Keil C编写。
void timer1(void) interrupt 3 { switch(get_commstste()) { case comm_s_init: /* 初始化 */ set_commstate(comm_s_idle); /* 切换初始化至空闲状态 */ break; case comm_s_idle: /* 空闲状态 */ break; case comm_s_recv: /* 接收 */ set_commstate(comm_s_ctrl); /* 接收完毕,切换至处理状态 */ break; case comm_s_ctrl: /* 处理 */ break; case comm_s_emit: /* 发送 */ break; case comm_s_emit: set_commstate(comm_s_idle); /* 发送完毕,切换至空闲状态 */ break; } /* 定时器重装 */ } void serial_interrupt(void) interrupt 4 { T1=0; /* 关定时器 */ switch(getcommstate()) { case comm_s_idle: setcommstate(comm_s_recv); /* 接收首位字符 */ break; case comm_s_recv: /* 接收其他字符 */ break; case comm_s_emit: /* 发送 */ break; default: break; } /* 重装定时器并启动 */ }