摘 要 介绍了射频卡的工做原理及结构,并给出了使用多线程技术实现射频卡监听和读写的方法及其在C#下的具体实现。指出利用多线程实现射频卡的监听和读写可以提升射频卡读写程序的并发性、可靠性和运行效率,从而提升整个应用系统的性能。
关键词 多线程 C# 射频卡 性能
1 引言
射频卡又称非接触式IC卡, 是世界上最近几年发展起来的一项新技术, 它成功的将射频识别技术和IC卡技术结合起来。与传统的接触式IC卡相比, 射频卡具备操做便捷、安全性高、抗干扰性强等优势,而且因为它不须要与读卡机有直接物理性接触,因此增长了读卡机的读写寿命。目前,射频卡已经在不少领域逐步获得应用,特别是在安全性、保密性、便利性要求比较高的应用场所,表明了刷卡领域的发展方向。射频卡的特色要求实现卡中数据读写的自动化,即当有射频卡进入读写器的感应区内,读写器可以当即自动读写卡中数据,并实时传入计算机进行显示和处理,这就要求计算机对读写器进行循环监听检测。本文将利用多线程技术在后台生成一个单独的线程,用来监听射频卡的读写而不影响用户在前台的其它操做,从而提升应用系统的并发性、可靠性和执行效率。
2 射频卡工做原理及结构
笔者选用的射频卡为PHILIPS公司的Mifare S50(M1)卡,卡的电气部分由一个天线和ASIC组成,卡片的天线是几组绕线的线圈,适于封装到IS0卡片中,ASIC由一个高速106KB波特率的RF接口,一个控制单元和一个8K位EEPROM组成。在工做时,读写器向M1卡发送一组固定频率的电磁波,卡片内有一个LC串联谐振电路,其频率与读写器发射的频率相同,在电磁波的激励下,LC谐振电路产生共振,使电容内产生电荷,电容的另外一端接有一个单向导通的电子泵,将电容内的电荷送到另外一个电容内储存,当所积累的电荷达到2V时,该电容可做为电源为其它电路提供工做电压,将卡 内数据发射出去或者获取读写器的数据。在此过程当中使用的M1射频卡结构如图1所示。
射频接口模块用来得到读写器的电源电压、复位信号、时钟信号,同时卡内芯片中的有关电路对此信号进行调制、解码、解密, 而后对命令请求、密码、权限等进行判断,以实现存储区的合法读写。防碰撞模块处理多张卡进入读写器有效范围的状况,这时防碰撞机制会从其中选择一张进行操做,未选中的卡则处于空闲模式等待下一次选卡,该过程会返回被选中卡的序列号。读写验证模块验证用户的读写密码与存储区密码是否相符,M1卡提供了两套密码KeyA 和KeyB,并结合控制位的使用能够实现对每个扇区的读或写的控制,只有在密码合法的状况下,才能对指定区进行操做。控制及ALU单元实现值的特殊形式存储,并能实现基本的加值和减值操做。
图1 M1卡组成结构图
EEPROM是射频卡的数据存储区,分为16个扇区,每一个扇区由4块(块0、块一、块二、块3)组成,每块大小为16个字节,16个扇区的64个块也可按绝对地址编号为0~63。第0扇区的块0用于存放厂商代码,已经固化,不可更改。其它每一个扇区的块0、块一、块2为数据块,可用于存贮数据。 数据块可用做通常的数据保存,进行读、写操做,也能够用做数据值,能够进行初始化值、加值、减值、读值等操做。每一个扇区的块3为控制块,包括了密码A、存取控制、密码B三个部分,每一个扇区的密码和存取控制都是独立的,能够根据实际须要设定各自的密码及存取控制,具体结构如图2所示。
每一个扇区的密码和存取控制都是独立的,能够根据实际须要设定各自的密码及存取控制。扇区中每一个块的存取条件是由密码和存取控制共同决定。
6字节 4字节 6字节
图2 扇区的控制块结构
3 利用多线程技术实现卡读写
目前的Windows系统都是多任务系统, 多任务指系统可同时运行多个进程, 进程就是应用程序的运行实例,而每一个进程也可同时执行多个线程。每一个进程都有本身私有的虚拟地址空间, 都有一个主线程, 在此基础上还能够创建另外的线程。进程中的线程是并发执行的, 每一个线程占用CPU 的时间由系统来划分。
图3 射频卡主要操做流程图
在应用程序中使用多线程,能够将冗长的或很是耗时的任务放在后台处理。即便在只有单处理器的计算机上,使用多线程也能够很是显著地提升应用程序的响应能力和可用性。为了实现射频卡读写器主动寻卡功能而且将返回的数据通知应用程序,能够采用多线程技术。多线程具备异步操做的特色,使得读写器能够只占有不多的CPU资源,提升程序的运行效率。如下将利用C#语言具体实现射频卡的多线程监听读写,当启动多线程读写功能后,主线程将创建一个单独的后台线程监听读写器,当有射频卡进入读写器有效范围内就能够实现该卡的自动识别和读写功能,而且无论卡在读写器有效范围内的时间有多长,只实行一次操做,只有当该卡从新进入感应区才进行下一次读写。在读写以前还要实如今有效范围内检查卡、选择卡、防碰撞、密码验证等预操做。操做射频卡的主要流程如图3所示。
C#提供了进行多线程编程的类和接口,线程的建立能够经过Thread、ThreadPool、Timer三种方法。Thread方法适用于需对线程进行复杂控制的场合; ThreadPool是一种相对较简单的方法,适应于一些须要多个线程而又较短任务,缺点是对建立的线程不能加以控制,也不能设置其优先级;Timer则适用于需周期性调用的方法,它不在建立计时器的线程中运行,而是在由系统自动分配的单独线程中运行。本次采用了Thread方法,相比之下Thread方法实现相对复杂,但控制更为灵活。
为了方便实现射频卡的读写编程功能,本程序利用了读写器附带的动态链接库,.NET框架提供了调用动态连接库的服务,容许受管辖的代码调用动态连接库中实现的非受管辖函数,包括操做系统提供的Windows API函数,可以定位和调用输出函数,根据须要组织其各个参数跨越互操做边界。但动态连接库中不少函数将指针做为参数,而在C#中没有指针的概念,用户在受管理的代码中不容许如直接存取内存等不安全的操做,为了保持函数引用声明中参数类型的一致性,在C#中经过引用封送指向这些数据类型的指针。程序实现的核心代码以下:
…
[DllImport("RC500_232.dll")]
private static extern byte RC500_232_init(int mport,int mbaud);
[DllImport("RC500_232.dll")]
private static extern byte RC500_232_request(byte mmode,ref UInt16 mtagtype);
[DllImport("RC500_232.dll")]
private static extern byte RC500_232_anticoll(byte mbcnt,ref UInt32 msnr);
[DllImport("RC500_232.dll")]
private static extern byte RC500_232_select(UInt32 msnr,ref byte msize);
[DllImport("RC500_232.dll")]
private static extern byte RC500_232_authkey(byte mmode,byte msecnr, byte[] mkey);
[DllImport("RC500_232.dll")]
private static extern byte RC500_232_read(byte maddr,byte[] mdata);
[DllImport("RC500_232.dll")]
private static extern byte RC500_232_write(byte maddr,byte[] mdata);
…
private void Rf_read_Form_Load(object sender, System.EventArgs e)
{
flag=0;
if (RC500_232_init(1,19200)!=0)
MessageBox.Show("端口打开失败!");
else
if (RC500_232_config()!=0)
MessageBox.Show("初始化失败!");
else
MessageBox.Show("端口打开成功,读卡器初始化成功!");
}
…
private void Rf_232_Read()
{ …
while(true)
{
if (RC500_232_request(0, ref tagtype)!=0) //检查有效范围是否有卡
{
continue;
}
if (RC500_232_anticoll(0, ref snr)!=0) //防碰撞控制,snr返回卡的序列号
{
MessageBox.Show("防碰撞错误!");
continue;
}
if (RC500_232_select(snr,ref size)!=0) //选择某一序号的卡,
//size返回卡的容量大小
{
MessageBox.Show("选择错误!");
continue;
}
if (RC500_232_authkey(0,0,key)!=0) //密码验证,密码存放在 key数组中
{
MessageBox.Show("密码验证错误!");
continue;
}
if (RC500_232_read(0,data)!=0)// 将0扇区数据读入byte类型数组data中,
//data为16字节长
{
MessageBox.Show("数据读取失败");
continue;
}
textBox1.Text="";
for(i=0;i<16;i++)
textBox1.Text=textBox1.Text+" "+Convert.ToString(data[i],16);
…
RC500_232_halt();//读写一次后卡挂起,直到卡从新进入有效范围
RC500_232_alarm(1,25,25,1); //读写器发出蜂鸣,指示操做成功
}
}
private void threadstart_Click(object sender, System.EventArgs e)
{ …
Thread mythread = new Thread(new ThreadStart(Rf_232_Read));
mythread.Start();//线程启动,执行Rf_232_Read函数
…
}
以上实现的多线程读卡程序,写卡程序和读卡程序相相似,这里再也不赘述。
4 结束语
采用多线程处理数据能够有效地加快程序的反应速度、提升执行的效率,既能够保证数据处理的实时性,又能及时响应用户的其它操做。经过实践证实,利用多线程实现射频卡的监听和读写是行之有效的技术,可以大大提升射频卡读写程序的并发性、运行效率和可靠性,从而提升整个应用系统的性能。