这一篇就讲讲高频RFID(13.56M)读写模块的应用吧,按照上一篇中制作库文件的方法,可以制作属于自己的类库(有点类似VISUAL C++)吧,然后就是应用,因我开发的模块涉及公司的秘密,咱们就以行业内著名的模块供应商周立功ZLG500读写模块来讲解吧。呵呵,以下部分内容来源网络,目的是来完整表达我的思想,也教会新手一些知识,有例子讲解更容易嘛。然我也实际使用过他们的模块,不要说俺盗版,本章绝大部分的内容是俺原创的。
现在智能卡的应用越来越广泛,如校园一卡通系统、城市公交系统、大型会议签到系统、考勤系统、门禁系统等都使用了智能卡。本文以ZLG500读写模块作为卡与门禁机交换数据的接口模块,介绍了ZLG500在智能卡门禁系统中的应用。
1 读写模块ZLG500简介
1.1 ZLG500与MCU的接口原理
ZLG500模块采用Philips公司最新的高集成ISO14443读卡芯片MF RC500,它能读写RC500内EEPROM,提供三线制SPI接口,并具有控制线输出端口,能与任何MCU连接。ZLG500与MCS51单片机的接口原理图如图1所示。此外该模块的EMC性能优良,并且自带无源蜂鸣器信号输出,能用软件控制其输出频率及输出持续时间。
图中SCLK、SDATA、SS为ZLG500与MCU相连接的控制线,分别为时钟线、数据线和片选。主控制器的MCU和读卡模块内的MCU通过此三线相连。接口空闲时,主机的SS=1,SCLK=O,SDATA=0,而从机的SS=1,SCLK=1,SDATA=O。其中SS和DATA都是双向的,而时钟线SCLK是单向的,即时钟只能由主控制器产生,该信号必须严格遵守时序规范,否则将出现通信错误,此时读卡模块必须释放该线。
SS还作为数据发送使能端。若一方有数据要发送给另一方,则该方控制SS线为低电平,并在发送结束后将该线置高电平。接收数据方不得控制该线,双方必须遵守通信协议,不得同时控制该线。SDATA为数据线,由数据发送端控制数据,接收端必须释放该线。该线在一次传输开始时还同时作为数据接收端的响应信号。
1.2 ZLG500与MCU接口的时序及通信协议
ZLG500与MCU无论数据传输的方向如何,SPI线上信号的波形总是如图2所示。由图中可以看出,在SS为低时,时钟和数据线上的信号才有效;在SCLK为低时SDATA变化,在SCLK为高时SDATA应保持稳定。
以上传输中,从数据发送器请求开始至数据接收器响应的时间是不确定的,取决于接收器内的MCU是否忙,因此有必要设置看门狗定时器对数据接收器的响应进行监视。一旦接收器响应,则MCU必须根据数据传输方向,严格控制以下几个时间,以确保数据传输无误。
t1:数据接收器响应至MCU产生第一个SCLK上升沿的时间。
t2:2个字节传输之间SCLK低电平的持续时间。
t3:传输最后1个字节的最后1位的SCLK信号的上升沿至SS上升沿的时间。
tH:SCLK信号的高电平持续时间。
tL:SCLK信号的低电平持续时间。
在数据传输方向不同时,对时间t1、t2、t3、tH和tL都有不同的要求。
MCU与ZLG500的通信必须先由MCU发送命令和数据给ZLG500,ZLG500执行命令完毕后,将命令执行的状态和响应数据发回MCU。
开始通信前,收发双方必须处于空闲状态。首先由MCU发出SS下降沿信号,然后等待ZLG500在SDATA线上的响应,若在50 ms内未检测到此信号,则退出本次传输。若正确响应,则MCU可将命令和数据发送出去。
然后MCU等待ZLG500发回的状态和响应数据,即等待SS线上的下降沿信号。若在50 ms内未检测到此信号,则退出本次传输;若正确检测到SS信号,则可以接收状态和数据。
2 智能卡门禁系统设计
2.1 总体结构
系统采用Philips公司的非接触智能IC卡Mifare 1(M1)卡。以M1卡作为用户卡,以其全球唯一的序列号SN为依据控制门的开启。由于它是一个高频卡,工作频率为13.5 MHz,因而具有较强的抗干扰能力且读写距离远(2.5~10 cm)。
整个智能卡门禁系统分为三大部分:其一是读写器部分,包括MCU、复位电路、时钟电路、显示电路、键盘、数据存储等主控模块及非接触IC卡读写模块和电锁驱动部分;其二是中央控制电脑的软件管理系统模块;其三是中央控制电脑与读写器之间的数据传输模块。总体系统框图如图3所示。
2.上位机部分程序范例
1)zlg500.h
#define STX 0X7E //正文的开头
#define EXT 0XBB //正文的结束
#define PWD_LEN 0x06 //密码长度
/*错误值*/
#define ERR_NOCARD 0XA0 //没有卡
#define ERR_CMDFAILED 0XA1 //指令执行失败
#define ERR_OVERFLOW 0XA2 //值溢出
#define ERR_COMMERR 0XFF //通讯错误
/*命令值*/
//低级命令
#define CMD_RESET 0X10 //执行一个软复位
#define CMD_CLOSE 0X11 //关闭RC500
#define CMD_AUTHENOPWD 0X20 //密码验证
#define CMD_LOADPWD 0X21 //装载密匙
#define CMD_REQUEST 0X27 //请求
#define CMD_ANTICOLL 0X28 //防碰撞
#define CMD_SELECT 0X29 //选择
#define CMD_READBLOCK 0X40 //对一块进行读操作
#define CMD_WRITEBLOCK 0X50 //对一块进行写操作
#define CMD_VALUE 0X59 //值操作
#define CMD_ADDVAL 0X60 //对一个块的值进行加操作
#define CMD_REDUCEVAL 0X61 //对一个块的值进行减操作
#define CMD_COPYBLOCK 0X62 //把一块的值复制到另一个块
#define CMD_HALT 0X63 //暂停
#define CMD_CLOSEANTENNA 0X64 //关闭天线输出数ms
//高级命令
#define CMD_SELONECARD 0X30 //选择一张卡
#define CMD_SELCARDS 0X31 //多张卡选择/卡列表
#define CMD_READBLOCKVAL 0X41 //读一个块的值
#define CMD_WRITEBLOCKVAL 0X51 //写一个块的值
//通信控制及其它
#define CMD_READADDR 0X70 //取读卡器的地址号
#define CMD_SETPARAMETER 0X80 //读卡器参数设置
#define CMD_READPARAMETER 0X81 //读取读卡器参数值
#define CMD_CONTROL 0X90 //读卡器指示控制
void __stdcall zlg500_Reset(unsigned char addr);
void __stdcall zlg500_Close(unsigned char addr);
int __stdcall zlg500_AuthenPwd(unsigned char addr,unsigned char keytype,
unsigned char blocknr);
int __stdcall zlg500_LoadPwd(unsigned char addr,unsigned char keytype,
unsigned char keynr,unsigned char *key);
int __stdcall zlg500_Request(unsigned char addr,unsigned char questtype,
unsigned short &type);
int __stdcall zlg500_Anticoll(unsigned char addr,unsigned char bitcount,
unsigned long &snr);
int __stdcall zlg500_Select(unsigned char addr,unsigned long cardnr,
unsigned char &size);
int __stdcall zlg500_ReadBlock(unsigned char addr,unsigned char blocknr,
unsigned char *blockdata);
int __stdcall zlg500_WriteBlock(unsigned char addr,unsigned char blocknr,
unsigned char *blockdata);
int __stdcall zlg500_Value(unsigned char addr,unsigned char mode,
unsigned char blocknr,unsigned long val,
unsigned char desblocknr);
int __stdcall zlg500_AddVal(unsigned char addr,unsigned char blocknr,
unsigned long addval);
int __stdcall zlg500_ReduceVal(unsigned char addr,unsigned char blocknr,
unsigned long reduceval);
int __stdcall zlg500_CopyBlock(unsigned char addr,unsigned char sourceblocknr,
unsigned char desblocknr);
int __stdcall zlg500_Halt(unsigned char addr);
int __stdcall zlg500_CloseAntenna(unsigned char addr,unsigned char time);
/
int __stdcall zlg500_SelCards(unsigned char addr,unsigned long selcardnr,
unsigned long *cardnrs,unsigned char &carnumbers);
int __stdcall zlg500_ReadBlockVal(unsigned char addr,unsigned char blocknr,
long &blockval);
int __stdcall zlg500_WriteBlockVal(unsigned char addr,unsigned char blocknr,
long blockval);
int __stdcall zlg500_ChangePwd(unsigned char addr,unsigned char sectornr,
unsigned char keytype,unsigned char *pwd);
/
int __stdcall zlg500_ReadAddr(unsigned char &devicenrs,unsigned char *deviceaddr);
int __stdcall zlg500_SetParameter(unsigned char addr,unsigned char parameter);
int __stdcall zlg500_ReadParameter(unsigned char addr,unsigned char ¶meter);
int __stdcall zlg500_SetControl(unsigned char addr,
unsigned char controltype,unsigned char acttime,
unsigned char halttime,unsigned char actlen);
//
unsigned char __stdcall zlg500_GetLastErr();
short int __stdcall zlg500_init(int nport=1, int nbaud=9600);
void __stdcall zlg500_exit(void);
2)mainfrm.cpp
// MainFrm.cpp : implementation of the CMainFrame class
//
#include "stdafx.h"
#include "Mifare.h"
#include "MainFrm.h"
#include "zlg500.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/
// CMainFrame
IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
ON_WM_CLOSE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
static UINT indicators[] =
{
ID_SEPARATOR,
ID_SEPARATOR,
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,
};
/
// CMainFrame construction/destruction
CMainFrame::CMainFrame()
{
// TODO: add member initialization code here
}
CMainFrame::~CMainFrame()
{
}
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
/*if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("Failed to create toolbar\n");
return -1; // fail to create
}*/
if (!myToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!myToolBar.LoadToolBar(IDR_TOOLBAR1))
{
TRACE0("Failed to create toolbar\n");
return -1; // fail to create
}
if (!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT)))
{
TRACE0("Failed to create status bar\n");
return -1; // fail to create
}
// TODO: Delete these three lines if you don't want the toolbar to
// be dockable
// m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
// EnableDocking(CBRS_ALIGN_ANY);
// DockControlBar(&m_wndToolBar);
myToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&myToolBar);
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
return 0;
}
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
return TRUE;
}
/
// CMainFrame diagnostics
#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
CFrameWnd::AssertValid();
}
void CMainFrame::Dump(CDumpContext& dc) const
{
CFrameWnd::Dump(dc);
}
#endif //_DEBUG
/
// CMainFrame message handlers
void CMainFrame::OnClose()
{
// TODO: Add your message handler code here and/or call default
zlg500_exit();
CFrameWnd::OnClose();
}