基于ARM的IC卡读写模块设计Word格式.docx
《基于ARM的IC卡读写模块设计Word格式.docx》由会员分享,可在线阅读,更多相关《基于ARM的IC卡读写模块设计Word格式.docx(13页珍藏版)》请在冰豆网上搜索。
(1)ATR模块:
AnswerToRequest(“请求之应答”)
当一张MIFARE1卡处在读写器的天线工作范围之内时,程序员控制读写器向卡发出Requestall(或Requeststd)命令后,卡的ATR将启动,将卡片块0中2个字节的卡类型号(TagType)传送给读写器,建立卡与读写器的第一步通信联络。
如果不进行第一步的ATR工作,读写器对卡的其他操作(读/写操作等)将不会进行。
(2)AntiCollision模块:
防(卡片)冲突功能
如果有多张MIFARE1卡处在读写器的天线工作范围之内,则AntiCollision模块的防冲突功能将被启动工作。
读写器将会首先与每一张卡进行通信,读取每一张卡的序列号(SerialNumber)。
由于每一张MIFARE1卡都具有惟一的序列号,决不会相同,因此程序员将启动读写器中的AntiCollision防重叠功能配合卡上的防重叠功能模块,根据卡序列号来选定其中一张卡。
被选中的卡将被激活,可以与读写器进行数据交换;
而未被选中的卡处于等待状态,随时准备与读写器进行通信。
AntiCollision模块(防重叠功能)启动工作时,读写器将得到卡片的序列号(SerialNumber)。
序列号存储在卡的Block0中,共有5个字节,实际有用的为4个字节,另一个字节为序列号的校验字节。
(3)SelectApplication模块:
卡片的选择
当卡与读写器完成了上述两个步骤,读写器要想对卡进行读/写操作时,必须对卡进行“Select”操作,以使卡真正地被选中。
被选中的卡将卡片上存储在Block0中的卡容量“Size”字节传送给读写器。
当读写器收到这一字节后,方可对卡进行进一步的操作,如密码验证等。
(4)Authentication&
AccessControl模块:
认证及存取控制模块
完成上述的三个步骤后,读写器对卡进行读/写操作之前,必须对卡上已经设置的密码进行认证,如果匹配,则允许进一步的读/写操作。
MIFARE1卡上有16个扇区,每个扇区都可分别设置各自的密码,互不干涉,必须分别加以认证,才能对该扇区进行下一步的操作。
因此每个扇区可独立地应用于一个应用场合,整个卡可以设计成一卡多用(一卡通)的形式来应用。
密码的认证采用了三次相互认证的方法,具有很高的安全性。
如果事先不知卡上的密码,则因密码的变化可以极其复杂,试图靠猜测密码而打开卡上一个扇区的可能性几乎为零。
(5)Control&
ArithmeticUnit:
控制及算术运算单元
这一单元是整个卡的控制中心,是卡的“头脑”。
它主要对卡的各个单元进行操作控制,协调卡的各个步骤;
同时它还对各种收/发的数据进行算术运算处理、递增/递减处理和CRC运算处理等,是卡中内建的中央微处理器(MCU)单元。
(6)RAM/ROM单元
RAM主要配合控制及算术运算单元,将运算的结果进行暂时存储,例如将需存储的数据由控制及算术运算单元取出送到EEPROM存储器中;
将需要传送给读写器的数据由控制及算术运算单元取出,经过RF射频接口电路的处理,通过卡片上的天线传送给读写器。
RAM中的数据在卡失掉电源后(卡片离开读写器天线的有效工作范围)将会丢失。
同时,ROM中则固化了卡运行所需要的必要的程序指令,由控制及算术运算单元取出,对每个单元进行指令控制,使卡能有条不紊地与读写器进行数据通信。
(7)CryptoUnit:
数据加密单元
该单元完成对数据的加密处理及密码保护。
加密的算法可以为DES标准算法或其他。
(8)EEPROM存储器及其接口电路:
EEPROMINTERFACE/EEPROMMEMORY
该单元主要用于存储用户数据,在卡失掉电源后(卡片离开读写器天线的有效工作范围)数据仍将被保持。
数据读写方式
由于项目内板上SPI接口已被其他外设占用,故本实验中的RFID卡与板上数据之间读写通过普通IO口时序模拟得到。
引脚分配
如上图所示,RFID卡模块共有8个引脚,其中5脚空置。
另外的1、2、3、4、7口作为通信线分别在板上为其分配普通IO口PD8、PD9、PD10、PD11和PD12。
6脚和8脚则分别和板上的地线以及电源口。
Mifare1S50卡存储EEPROM
Mifare1卡内部有1的EEPROM,分成0~15共16个扇区,每个扇区分成0~3共4块,每块16字节。
1、扇区0的块0是厂商标志字节,保存着只读的卡信息及厂商信息,比如:
AFA73E00360804009944304331343616。
前面四个字节AFA73E0036是卡序列号,08是卡容量,0400是卡类型,后面是厂商自定义的一些信息。
2、每个扇区的块0保存着该块的密钥A密钥B及该块的访问条件,第个扇区都有自己的一套密钥及访问条件,其中,4个字节的访问条件是对每个扇区4个块的读写定义,格式如下:
字节9备用默认值为0x69,_b是取反。
分成C10~C13,C20~C23,C30~C33
对块0、块1、块2存取控制X(0~2):
对块3存取控制:
比如块3的16字节如下:
000000000000FF078069FFFFFFFFFFFF
前面6个字节是密钥A,因为Read永远为Never,所以读到的都是0x00,最后的6字节是密钥B,其值一为0xFF0xFF0xFF0xFF0xFF0xFF,中间的4个字节是访问条件,有:
C1X[0..2]
C2X[0..2]
C3X[0..2]
C1X3
C2X3
C3X3
1
对应上表,可得出对该扇区块的存取控制条件。
2.读写的实现
利用RFID卡厂家编制好的头文件添加到工程中。
对于IC卡的读写需要经历:
判断是否有卡进入;
多卡进入时的防冲撞处理;
对卡操作前的密钥验证操作,以及后续的不同类型的操作(注册,读卡,消费,充值,注销等)。
取块2来进行数据存储。
扇区
块
描述
15
63
第15扇区尾块
62
数据块
61
60
14
59
第14扇区尾块
58
57
56
.
7
第1扇区尾块
6
5
4
3
第0扇区尾块
2
厂商标志块
RC522卡存储区的组织示意图
11
密钥A
权限代码
密钥B
10
交易额1
交易额2
交易额3
序列号1
序列号2
序列号3
9
交易时间1
交易时间2
交易时间3
状态
交易
次数
8
用户余额
用户姓名(区位码储存)
用户卡号
在本程序中初步设计卡内存储的用户信息为用户姓名——四个汉字以内,以区位码形式存储,以便下一步与触摸屏模块连接时通过字库的支持来实现显示。
用户账户余额信息设计为以4个字节存储。
而每张IC卡赋予其一个编号,使用4个字节8位数字及字母组合表示。
至此需要的存储空间为姓名部分(一个汉字的区位码为两个字节)共需八个字节,余额部分4个字节,编号部分4个字节,总共16个字节,对于一个数据块而言恰好完全利用。
由于扇区0中储存着大量的厂商信息,故选用扇区1的块4进行用户信息数据的存储。
对卡不同类型的操作是通过对卡的状态变量OprationCard进行赋值完成。
附自定义状态字表:
0x11有卡进入
0x22防冲撞处理完成
0x33选卡选毕
0x44密钥验证通过
#defineREGCARD0xa1//注册状态
#defineCONSUME0xa2//消费状态
#defineREADCARD0xa3//读卡状态
#defineADDMONEY0xa4//充值状态
#defineLOGOFFCARD0xa5//注销状态
voidOn_NormalCall()
{
//if(MainStage==1)
//{
//ShowStartPage();
//MainStage=2;
//}//MainStage=1;
signedcharStatus,i;
signedlongmoney_value;
//,money_value2;
floatf_money_value;
//,f_money_value2;
Status=PcdRequest(PICC_REQIDL,&
RevBuffer[0]);
//寻天线区内未进入休眠状态的卡,返回卡片类型2字节
if(Status!
=MI_OK)return;
Status=PcdAnticoll(&
RevBuffer[2]);
//防冲撞,返回卡的序列号4字节
GPIO_ResetBits(GPIO_BEEPER,BEEPER_PIN);
/*开启蜂鸣器*/
Delay(0x2FFFFF);
GPIO_SetBits(GPIO_BEEPER,BEEPER_PIN);
/*关闭蜂鸣器*/
//Delay(0x2FFFFF);
memcpy(MLastSelectedSnr,&
RevBuffer[2],4);
//拷贝&
RevBuffer[2]起始地址的4字节至MLastSelectedSnr起始地址的区域
Status=PcdSelect(MLastSelectedSnr);
//选卡
if(OprationCard==REGCARD)//注册0
{
Status=PcdAuthState(PICC_AUTHENT1A,11,NewKeyA,MLastSelectedSnr);
//地址11是如何确定的?
(第2扇区尾块序号)
if(Status!
=MI_OK)
{
Error(Error_RC522);
return;
}
Status=PcdWrite(11,&
NewKey[0]);
for(i=0;
i<
16;
i++)//?
Write_First_Data[i]=0xaa;
memset(Write_First_Data,0,sizeof(Write_First_Data));
//将Write_First_Data起始地址的sizeof(Write_First_Data)个字节替换为0
money_value=8000;
memcpy(Write_First_Data,(u8*)&
money_value,4);
Status=PcdWrite(8,&
Write_First_Data[0]);
//地址4是如何确定的?
(第1扇区数据块1块序号)
//通过注册
//PcdHalt();
}
if(OprationCard==REGCARD)//注册
Status=PcdAuthState(PICC_AUTHENT1A,7,NewKeyA,MLastSelectedSnr);
//地址7是如何确定的?
(第1扇区尾块序号)
Status=PcdWrite(7,&
money_value=5000;
Status=PcdWrite(4,&
PcdHalt();
}本例中注册时用户姓名设置为“叶冠南”——502225583647,用户余额为5000,卡号预设为。
其中5000的余额存储采用的是低位在前高位在后。
十进制下的5000等于十六进制下的1388。
高低位颠倒后存储为8813。
程序中用户的三种数据调用可以分别通过编写的函数USER_NAME(),USER_MONEY()和USER_NO()来实现。
余额方面定义了一个每次充电的消费金额变量change,后期可以通过对于触摸屏的操作来为change幅值实现消费扣钱的功能。
调用以下函数可以分别调用用户的信息到串口。
USER_MONEYOUT();
//用户余额
USER_NAMEOUT();
//用户姓名区位码
USER_NOOUT();
//用户IC卡卡号
USER_TRADE_TIME();
//用户交易时间(年月日)
USER_EXCHANGEOUT();
//用户交易金额
USER_SERIALNUMBEROUT();
//存储用户交易信息
使用voidUSER_TRADEINFO(u8trtime[4],u8trmoney,u8trno[2])函数写入交易信息
由于要在卡中存储最近三次消费或充值的信息,因此需要使用一交易次数变量来用于在卡中不同块的不同位置循环存储交易信息,每次进行消费或充值时从卡中读出这个计数变量并将要存储的交易机器序列号,交易时间,交易金额等信息。
信息存储完后,对计数变量进行判断,进行加1或置零操作后再将其存入卡中以便下次取用。
实验结果
附从注册至读卡再至消费的串口数据显示结果:
其中消费模式下设计change为258,故用户余额为5000-258=4742。
转化为16进制数为1286,倒序存储为8612。
再进入充值模式,设计change为5000,故用户余额为4742+5000=9742。
转化为16进制数为260E,倒序存储为0E26。
原设计方案中,计划使用两个扇区的更多空间来存储最近8次的交易信息,在实际调试时,发现在写卡这一步无法同时对两个扇区进行操作,经过多日查找资料、调试尚未找到解决方法。
更改方案中变更为存储最近3次交易信息,考虑实际应用中多次的交易信息存储在充电桩或后台系统中更具有实际操作意义。
实验总结
经过这次实验,实现了预期的目标,完成了使用基于ARM内核控制芯片的IC卡读写模块的设计。
附录
开发板
RFID模块