基于MDB/ICP协议的自动售卖系统的主控制器实现
摘要:介绍一种以DALLAS DS5002FP单片机为主控制器的MDB/ICP协议实现。从硬件和软件描述了如何控制MDB总线,并给出一个较成熟的控制外设会话的总线驱动程序。该协议实现清晰、简单,采用MCS-51兼容单片机降低成本,充分体现了MDB/ICP的优越性。
关键词:单片机 自动售卖系统 主控制器
随着国你经济和技术的发展,自动售卖系统由于其方便性、易管理性和低成本,正得到越来越广泛的应用。而自动售卖系统的外设也越来越复杂,从投币机到读钞机到非现金交易(如IC记账卡),导致对主控制器的要求越来越高。为了简化设计,采用一个简单、稳定的内部通讯总线协议非常必要。
本文介绍了欧洲售机制造者协会(EVMMA)制定的MDB/ICP总线协议。该协议简洁明了,功能强大,可扩展性强,并且对外挂外设备数目没有限制,是理想的自动售卖系统内部总线协议。本文从主控制器的角度给出了对MDB/ICP总线进行操作控制的硬件和软件实现。
1 MDB/ICP协议简介
MDB/ICP协议(Multi-Drop Bus / Internal Communication Protocol)是欧洲售货机制造者协会制定的一套用于协调自动售卖机的主控制器(VMC)javascript:window.open(this.src);" style="cursor:pointer;"/>与多个外设之间通信的协议。硬币机和读钞机的标准首先是由Coinco在美国可口可乐公司的指定下开发的。这个标准于1993年被NAMA协会采用,经过一个专门的工作组修订后,于1994年被EVMMA采用。第二阶段的非现金交易标准由Debitek代表NAMA开发,并于1994年被NAMA采用。EVMMA加入了一些兼容的指令后于1994年采用。
MDB接口实际上是工作于9600波特率的主从型串行总线接口,所外围设备(例如硬币机、读钞机、读卡器等)均为主控制器(传统上称售货机控制器——VMC)的从机。所有外围设备与VMC之间的通信方式都一致。
MDB协议的串行位格式为:1个起始位,8个数据位,1个方式位与1个停止位,共11位。位传递的顺序如图1所示。其中方式位根据传递的方式不同置0或置1。在MDB总线上,VMC通过广播方式向外发送命令。第一字节为地址字节(实际上只有高5位寻址信息,低3位为对外设的指令),该字节被所有的外设读取,但只有符合地址字节所指定的外设才处理其后的数据字节,并做出反应。在VMC到外设的数据中,地址字节的方式位被置1,数据字节的方式位被置0,外设通过检验接收到的方式位确认是地址命令还是数据。当数据从外设发送到主机时,最后送出的字节方式位被置1,标志着数据发送完毕。
VMC向外设传送的指令由一个地址字节、一些可选的数据字节与一个校验和(CHK)字节构成。发送指令后,外设应答VMC的通信块可以由一个数据块和一个CHK字节组成,或者一个应答字节(ACK),或者一个无应答字节(NAK)。如果外设应答数据块的话,VMC将通过一个应答字节(ACK)、无应答字节(NAK)或得发字节(RET)应答外设传回的数据。
图2至图5为几个典型的会话例子,其中“*”表示传送时方式位置1,“ADD”表示地址字节,“CHK”表示传送数据的校验和。
2 硬件系统结构
在MDB/ICP总线协议基础上,笔者构建了一个实际的自动售卖系统,系统原理图如图6所示。VMC通过MDB总线与投币机、读钞机和读卡器交互,控制外设的运转,并从外设获得用户的支持情况。同时,VMC也负责处理与用户之间的交互,驱动液晶显示,处理键盘输入,驱动符合用户需要的电机运转出货。在本项目中,VMC还需通过MODEM自动拨号连接公司总线的服务器,将销售数据传送回公司。
如图6所示,所有MDB外设均挂在同一总线上,由总线提供24V和5V的电源,数据线与单片机的串口相连。
本项目中选用的单片机为DALLAS DS5002FP。该单片机代码级兼容MCS-51系列单片机,在使用外存的情况下仍然可以使用4个PO作为数据I/O,带外设的能力较强。但它只有一个串口,控制MDB总线和控制MODEM都需要使用串口,因此必须将串口进行复用。通过一个译码器实现选通功能。
3 MDB/ICP会话控制
将VMC与外设之间通过MDB总线的会话分为四个层次:
(1)串口初始化
初始化串口时,主要工作是设置波率(9600)和传输方式(11位)。为顺利获得9600波特率,单片机采用的晶振为11.0592MHz。源代码如下(采用Franklin C51语言编写):
// 一点常量定义
#define uchar unsigned char
#define T_RESPONSE 0xee //*5.0毫秒*/
#define MAX_BLOCK_SIZF 36 /*数据块最大长度*/
#define ERR_TIME_OUT 0x81 /*超时*/
#define ERR_NO_MODE_BIT 0x82*/未收到最后字节*/
#define ERR_CHECKSUM 0x83 /*校验和错*/
#define ACK 0x00
#define RET 0xaa
#define NAK 0xff
//初始化串口
void InitSerialPort()
{
SCON = 0xd0; //设置串口为方式3(9bit)
TMOD &=0x0f;
TMOD I=0x20; //设置定时器1为方式2
TR1 =1; //定时器1使能
TH1 = 0xfd; //设定波行率:9600 NRZ
PCON &=0x7f; //Set SMOD=0
RI=0;
TI=0;
}
javascript:window.open(this.src);" style="cursor:pointer;"/>
(2)字节的传送
这里需要注意的是:发送数据时要根据需要设置方式位,而接收数据时要返回方式位的值,以判断是否收完所有数据。
// 传送字节
void TransmitByte(uchar byte,bit mode)
//形参:byte——准备发送的字节
// mode——预备要设置的方式位
{
TB8=mode; //设置方式位
SBUF = byte; //发送字节
While(!TI); //等待发送完毕
TI=0;
}
//接收字节
#pragma disable
uchar ReceiveByte(uchar *byte,uchar *bMode)
//形参:*byte——返回接收到的字节值
// *bMode——返回接收到的字节的方式位
//返回值:0—超时,1—成功接收
{
TMOD &=0xf0;
TMOD =0x01; //设置定时器0为方式1(16位)
TH0=T_RESPONSE;
TL0=0x00; //设置超时门限
TF0=0;
TR0=1; //定时器0使能
While (!RI && !TF0)
; //等待接收字节直至超时
TF0=0;
if(RI){ //已接收字节
RI=0;
*byte=SBUF; //返回字节值
*bMode=RB8; //返回方式位
return 1;
}else{ //超时
RI=0;
return 0;
}
}
(3)数据块的传送
这部分与下面的会话部分放在一个函数体内实现。
(4)会话
javascript:window.open(this.src);" style="cursor:pointer;"/>