>
摘 要:本文提出了一种在微控制器上实现PPP协议,从而控制调制解调器通过互联网传送仪表数据的远程抄表系统。具体说明了系统组成结构、下位机软硬件设计,并分析了系统的性能及优、缺点。
关键词:PPP;自动抄表;调制解调器
1 引言
现在应用的远程抄表系统很多都是利用电话网进行数据传输,不过它们一般都是基于“点对点”的传输模式。即数据收发两端各使用一个调制解调器(MODEM),下位机通过直接拨远程抄表主机的号码建立“硬”连接后传送仪表数据。但是这种“点对点”的模式效率低,抄表中心需要一个一个进行读表操作。为了提高抄表速度,抄表主机往往使用多个MODEM和多根电话线,这又增加了成本。同时,这种模式的通讯费用与语音通话费相同,而且如果待抄仪表与抄表主机在不同地区,还要付长途费。针对这些问题,本文将介绍一种基于互联网的远程抄表系统,其技术特点是:利用互联网传送仪表数据,节省了数据传输费用;同时简化实现代码使其可以在微控制器上实现,有效的控制成本。
2 系统组成及特点
基于互联网的远程抄表系统结构如图1所示。系统由下位机仪表检测部分、Modem数据传输部分、互联网传输部分和抄表中心四部分组成。
图1:基于互联网的远程抄表系统结构图
首先,下位机微控制器的仪表检测部分把仪表的数据和运行状态检测出来,然后通过Modem和电话线登录到ISP(Internet Service Provider)上,经过互联网把数据传送到远端的抄表中心。抄表中心可以通过光纤网、ADSL或DDN专线等多种方式联网接收数据。为了简化程序,下位机的数据以UDP报文的方式发送到抄表中心。考虑到安全问题,抄表中心与下位机之间的通信遵守自定传输协议并对仪表数据进行加密。本系统的优点在于:(1)通过ISP上网通信费用低;(2)下位机依然可采用微控制器,控制了成本;(3)抄表中心使用网络接口,并行处理能力更强,可大大提高抄表效率;(5)抄表中心的程序开发更简单、维护更方便。本系统的实现难点主要在于下位机,下面重点介绍下位机的硬件组成和软件设计。
3 下位机的硬件组成
下位机主要由仪表、传感器、微控制器和Modem组成,其硬件组成结构如图2所示。
图2:下位机硬件组成
系统由传感器或者微控制器的模数转换器检测需要传送的仪表数据;接下来的关键就是微控制器如何控制Modem上网并传送数据。我们进行了精心的简化,发现微控制器可以只使用4个IO端口就可以控制Modem:串口发送(TXD)、串口接收(RXD)、载波检测(Carrier Detect, CD)和终端准备(Data Terminal Ready, DTR)信号。CD信号用于检测Modem是处于数据传送状态还是接收设置命令状态。DTR信号用于通知Modem传送工作已经结束。一般如果使用独立的Modem,微控制器与Modem间需要使用电压转换芯片,如:MAX232等。由于在本方案中为了实现微控制器上网,需要大量的软件实现代码,所以硬件系统的程序存储器不能少于7K字节,数据存储器也至少要有250字节。
4 下位机的软件实现
系统软件采用分层的结构实现,从底到上共分为六层:串口驱动层、MODEM驱动层、PPP协议层、IP协议层、UDP协议层和最上面的应用层。其中每一层都实现一些驱动函数为上层提供服务。例如串口驱动层就提供打开串口(OpenComm)、关闭串口(CloseComm)、读写串口等函数,而在实现Modem驱动层时就可以使用串口驱动层提供的这些函数,而不用关心底层函数的实现细节。分层的结构使代码间的逻辑关系更为清晰,层与层之间只通过函数接口相联系,提高了代码的可复用性。软件的层次结构与每层的主要函数如图3所示:
图3:系统的软件结构
出于节省存储器的考虑,本系统没有使用操作系统,而主要采用有限状态机的控制方式。标准的Modem都分为两种工作状态:一种是命令状态(Command Mode),在此状态下向Modem发送AT命令可以进行拨号、挂机等操作;另一种是在线状态(On-line State)用于电话线两端的用户进行透明的数据传输。在本方案中,下位机的微控制器通过Modem与ISP上网服务器进行连接,而其间的通讯协议是PPP报文协议,所以系统的主要状态只有两个:向Modem发送命令状态和PPP报文通信状态,如图4所示。当下位机发起一个连接远程抄表主机的命令时,则触发拨号命令,当ISP的PPP报文传送回来时,系统则转移到PPP报文处理状态,对PPP报文进行处理,进行网络登录和握手协议。登录成功后用UDP报文向远端的抄表中心发送仪表信息。通讯完毕后,则向ISP发送断开连接的PPP报文。系统返回到收发Modem命令的状态。可见,在软件系统中,最关键的是PPP协议的实现和ISP的登录、握手过程。
图4:状态转移图
PPP(Point to Point Protocol)是数据链路层协议中的一种,是目前应用最广的一种广域网协议。PPP协议假定了两个对等实体间有一个双向全双工的连接,而且数据包是按顺序投递的,这正好符合串行口的通讯方式。PPP协议不需要差错控制、排序和流量控制,易于实现,而且支持对多种高层协议(如IP、TCP、UDP)的复用。所以使用PPP拨号上网是微控制器实现Internet连接的最佳选择。
图5:PPP报文解析
为了保证串口数据的及时接收并避免死锁,系统采用中断接收方式。在串口中断接收程序中,根据PPP报文的帧结构(如图5所示)判断是否接收到一个完整的PPP报文包。收到一个完整的PPP报文后就通知主循环进行报文解析工作。主循环不断等待用户的操作,当有PPP报文到达之后进入PPP报文解析模块,它对PPP包的内容进行较验以确定数据包的完整性和正确性。初次拨号时,系统首先要与ISP进行通信链路的协商,即协商点到点的各种链路参数配置。协商过程遵守LCP、PAP和IPCP等协议,其中LCP协议用于建立、构造、测试链路连接;PAP协议用于处理密码验证部分;IPCP协议用于分配IP地址。协商机制用有限状态机的模型来实现。一旦协商完成,链路已经创建,IP地址已经分配就可以按照协商的标准进行IP报文的传输了。PPP报文接收解析模块代码如下:
void PPPEntry (void) {
if (PPPStatus & IsFrame) {
switch (*(WORD *)(&InBuffer [2])) {
case LCP_PACKET://LCP协议报文处理
HandleLCPOptions ();
break;
case PAP_PACKET:
if (InBuffer [4] == 0x02) {// 身份验证成功
NoOperation;
}
break;
case IPCP_PACKET: // IPCP协议报文处理
HandleIPCPOptions ();
break;
case IP_DATAGRAM: // IP 数据报文处理
if (!IPCompare ((BYTE *)&InBuffer [20])) {
}
else
switch (InBuffer [13]) {
case UDP://上层是UDP报文
UDP_Handler ((UDPDatagram *)&InBuffer[16]);
break;
case TCP:
break;
case ICMP://上层是ICMP报文
IcmpHandler ((IPDatagram *)&InBuffer[4]);
break;
default:
break;
}
break;
default:
RejectProtocol (InBuffer);// 不处理其它报文
break;
}
PPPStatus &= ~IsFrame;
PPPStatus |= ReSync;
}
}
在系统实现时,对PPP协议进行了部分简化,只接受上面提到的LCP、PAP、IPCP和IP协议而拒绝其它协议。并且在协商过程中,系统会拒绝ISP的提供的选项,而只能接受预定的一种配置。根据应用的不同,IP报文中可以携带UDP报文也可以是TCP或ICMP报文。为了简化程序,本系统选用UDP报文作为载体传送仪表信息。数据传输完成之后,下位机会向ISP发送LCP的断开连接报文以终止网络连接。
协商完成后进行IP数据报通信阶段。此时,微控制器向ISP发送的所有包含IP报文的PPP报文都会被ISP传送给IP报文内相应的IP地址,而远端所有向微控制器IP地址发送的报文也都会经ISP传送到微控制器上。由于微控制器的资源有限,为了使程序尽量简化,我们选用IP承载UDP协议来发送数据。IP协议与UDP协议报文的格式如图5所示:
图5:IP与UDP报文格式
在程序中实现IP与UDP报文的数据结构,向指定的主机IP地址发送UDP报文实现起来还是比较简单。在程序中实现IP与UDP报文的数据结构,向指定的主机IP地址发送UDP报文实现起来还是比较简单的,只要向IP数据结构中填充数据,然后用底层的PPP包发送函数就可以了,例如以下的程序是发送一个IP包的代码:
void IPNetSend (IPDatagram* ip) {
static WORD Id = 0xF0; // IP 包的ID
ip_out->Version_HLen = 0x45; // 包头使用IPv4, 长度为 5
ip_out->Service = 0; //
ip_out->LengthUpper = 0; // 高字节数据报长度为0
ip_out->ID = htons(Id++); // ID赋值
ip_out->Frag = 0; // 不允许分帧
ip_out->TTL = 0x80; // 包的存活期
ip_out->Checksum = 0; // 计算校验
ip_out->Checksum = htons(IPCheckSum ((BYTE *)ip_out, 10));
switch (IPAdapter) { // 选择要使用的底层协议
case PPP: // 直接用PPP包发送
OutBuffer [0] = 0xff; // PPP包
OutBuffer [1] = 0x03;
OutBuffer [2] = 0x00;
OutBuffer [3] = 0x21;
ProcPPPSend (OutBuffer, OutBuffer [7] + 6);
break;
default:
break;
}
}
而发送UDP报文时,同样只要填充仪表数据然后调用IPNetSend函数就可向指定IP发送UDP报文:
void UDPSendData (BYTE Ip[], WORD Port, BYTE* Payload, BYTE size) {
WORD Checksum = 0;
ip_out->DestAddress [0] = Ip [0]; // 保存发送和接收的IP地址
ip_out->DestAddress [1] = Ip [1];
ip_out->DestAddress [2] = Ip [2];
ip_out->DestAddress [3] = Ip [3];
ip_out->SourceAddress [0] = IPAddress [0];
ip_out->SourceAddress [1] = IPAddress [1];
ip_out->SourceAddress [2] = IPAddress [2];
ip_out->SourceAddress [3] = IPAddress [3];
udp_out = (UDPDatagram *) &ip_out->SourceAddress;
// 插入要承载的仪表数据信息
if (Payload)
Move (Payload, &udp_out->Payload[0], size);
udp_out->Length = size + UDP_HEADER_LENGTH; // UDP报文长度
ip_out->Length = size + UDP_HEADER_LENGTH + 20;// 计算IP报文长度
ip_out->Protocol = UDP; // 使用UDP协议
udp_out->SourcePort = htons(UDPLocalPort);// 设置端口号
udp_out->DestPort = htons(Port);
udp_out->LengthUpper = 0;)
udp_out->Checksum = 0; // 计算校验值
Checksum = UDP_Checksum ((BYTE *)ip_out); // Obtain the packet checksum
udp_out->Checksum = htons (Checksum);
IPNetSend (ip_out); // 调用IPNetSend发送数据包
}
对于报文的解析也是同样,首先在PPP协议层接收到PPP报文,然后解析也IP报文,最后解析出UDP报文。这时就可以按照应用层协议提取出来抄表中心的命令或状态了。
5 结论