一、非接触式IC卡简介
非接触式IC卡简介又称射频卡,成功地解决了无源(卡中无电源)和免接触这一难题,是电子器件领域的一大突破。主要用于公交、轮渡、地铁的自动收费系统,也应用在门禁管理、身份证明和电子钱包。
二、工作原理
射频卡工作的基本原理是:射频读写器向IC卡发一组固定频率的电磁波,卡片内有一个IC串联协振电路,其频率与读写器发射的频率相同,这样在电磁波激励下,LC协振电路产生共振,从而使电容内有了电荷;在这个电荷的另一端,接有一个单向导通的电子泵,将电容内的电荷送到另一个电容内存储,当所积累的电荷达到2V时,此电容可作为电源为其它电路提供工作电压,将卡内数据发射出去或接受读写器的数据。射频卡与接触式IC卡相比有以下优点:
1 可靠性高,无机械接触,从而避免了各种故障;
2 操作方便,快捷,使用时没有方向性,个方向操作;
3 安全和保密性能好,采用双向验证机制。读写器验证IC卡的合性,
同时IC卡验证读写器的合法性。每张卡均有唯一的序列号。制造厂家在产品出长前已将此序列号固化,不可再更改,因此可以说世界上没有两张相同的非接触IC卡;
4 可多卡操作,具有快速防冲突机制;
5 可以适合多种应用,它的存储结构特点使它一卡多用,用户可根据不同的应用设定不同的密码和访问条件。
典型应用:公路自动收费、公共汽车、员工卡。
1、主要指标<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
1) 容量为8K位EEPROM
2) 分为16个扇区,每个扇区为4块,每块16个字节,以块为存取单位
3) 每个扇区有独立的一组密码及访问控制
4) 每张卡有唯一序列号,为32位
5) 具有防冲突机制,支持多卡操作
6) 无电源,自带天线,内含加密控制逻辑和通讯逻辑电路
7) 数据保存期为10年,可改写10万次,读无限次
8) 工作温度:-20℃~50℃(温度为90%)
9) 工作频率:13.56MHZ
10) 通信速率:106KBPS
11) 读写距离:10mm以内(与读写器有关)
2、 存储结构
12) M1卡分为16个扇区,每个扇区由4块(块0、块1、块2、块3)组成,(我们也将16个扇区的64个块按绝对地址编号为0~63,存贮结构如下图所示:
13)第0扇区的块0(即绝对地址0块),它用于存放厂商代码,已经固化,不可更改。
14)每个扇区的块0、块1、块2为数据块,可用于存贮数据。
数据块可作两种应用:
★ 用作一般的数据保存,可以进行读、写操作。
★ 用作数据值,可以进行初始化值、加值、减值、读值操作。
15)每个扇区的块3为控制块,包括了密码A、存取控制、密码B。具体结构如下:
A0 A1 A2 A3 A4 A5 FF 07 80 69 B0 B1 B2 B3 B4 B5
<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />
密码A(6字节) 存取控制(4字节) 密码B(6字节)
16)每个扇区的密码和存取控制都是独立的,可以根据实际需要设定各自的密码及存取控制。存取控制为4个字节,共32位,扇区中的每个块(包括数据块和控制块)的存取条件是由密码和存取控制共同决定的,在存取控制中每个块都有相应的三个控制位,定义如下:
块0: C10 C20 C30
块1: C11 C21 C31
块2: C12 C22 C32
块3: C13 C23 C33
三个控制位以正和反两种形式存在于存取控制字节中,决定了该块的访问权限(如
进行减值操作必须验证KEY A,进行加值操作必须验证KEY B,等等)。三个控制
位在存取控制字节中的位置,以块0为例:
对块0的控制:
bit 7 6 5 4 3 2 1 0
字节6 |
|
|
| C20_b |
|
|
| C10_b |
字节7 |
|
|
| C10 |
|
|
| C30_b |
字节8 |
|
|
| C30 |
|
|
| C20 |
字节9 |
|
|
|
|
|
|
|
|
( 注: C10_b表示C10取反 )
存取控制(4字节,其中字节9为备用字节)结构如下所示:
bit 7 6 5 4 3 2 1 0
字节6 | C23_b | C22_b | C21_b | C20_b | C13_b | C12_b | C11_b | C10_b |
字节7 | C13 | C12 | C11 | C10 | C33_b | C32_b | C31_b | C30_b |
字节8 | C33 | C32 | C31 | C30 | C23 | C22 | C21 | C20 |
字节9 |
|
|
|
|
|
|
|
|
( 注: _b表示取反 )
17)数据块(块0、块1、块2)的存取控制如下:
控制位(X=0..2)
| 访 问 条 件 (对数据块 0、1、2) | |||||
C1X | C2X | C3X | Read | Write | Increment | Decrement, transfer, Restore |
0 | 0 | 0 | KeyA|B | KeyA|B | KeyA|B | KeyA|B |
0 | 1 | 0 | KeyA|B | Never | Never | Never |
1 | 0 | 0 | KeyA|B | KeyB | Never | Never |
1 | 1 | 0 | KeyA|B | KeyB | KeyB | KeyA|B |
0 | 0 | 1 | KeyA|B | Never | Never | KeyA|B |
0 | 1 | 1 | KeyB | KeyB | Never | Never |
1 | 0 | 1 | KeyB | Never | Never | Never |
1 | 1 | 1 | Never | Never | Never | Never |
(KeyA|B 表示密码A或密码B,Never表示任何条件下不能实现)
例如:当块0的存取控制位C10 C20 C30=1 0 0时,验证密码A或密码B正确后可读;
验证密码B正确后可写;不能进行加值、减值操作。
18)控制块块3的存取控制与数据块(块0、1、2)不同,它的存取控制如下:
|
|
| 密码A | 存取控制 | 密码B | |||
C13 | C23 | C33 | Read | Write | Read | Write | Read | Write |
0 | 0 | 0 | Never | KeyA|B | KeyA|B | Never | KeyA|B | KeyA|B |
0 | 1 | 0 | Never | Never | KeyA|B | Never | KeyA|B | Never |
1 | 0 | 0 | Never | KeyB | KeyA|B | Never | Never | KeyB |
1 | 1 | 0 | Never | Never | KeyA|B | Never | Never | Never |
0 | 0 | 1 | Never | KeyA|B | KeyA|B | KeyA|B | KeyA|B | KeyA|B |
0 | 1 | 1 | Never | KeyB | KeyA|B | KeyB | Never | KeyB |
1 | 0 | 1 | Never | Never | KeyA|B | KeyB | Never | Never |
1 | 1 | 1 | Never | Never | KeyA|B | Never | Never | Never |
例如:当块3的存取控制位C13 C23 C33=1 0 0时,表示:
密码A:不可读,验证KEYA或KEYB正确后,可写(更改)。
存取控制:验证KEYA或KEYB正确后,可读、可写。
密码B:验证KEYA或KEYB正确后,可读、可写。
3、 M1射频卡与读写器的通讯
复位应答
M1射频卡的通讯协议和通讯波特率是定义好的,当有卡片进入读写器的操作范围时,读写器以特定的协议与它通讯,从而确定该卡是否为M1射频卡,即验证卡片的卡型。
防冲突机制 (Anticollision Loop)
当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作,未选中的则处于空闲模式等待下一次选卡,该过程会返回被选卡的序列号。
选择卡片(Select Tag)
选择被选中的卡的序列号,并同时返回卡的容量代码。
三次互相确认(3 Pass Authentication)
选定要处理的卡片之后,读写器就确定要访问的扇区号,并对该扇区密码进行密码校验,在三次相互认证之后就可以通过加密流进行通讯。(在选择另一扇区时,则必须进行另一扇区密码校验。)
对数据块的操作
读 (Read):读一个块;
写 (Write):写一个块;
加(Increment):对数值块进行加值;
减(Decrement):对数值块进行减值;
存储(Restore):将块中的内容存到数据寄存器中;
传输(Transfer):将数据寄存器中的内容写入块中;
中止(Halt):将卡置于暂停工作状态;
下面是对驱动的简单介绍:
宏定义
#define MODE_INIT 0x01
read 或 write操作时,使用此命令对射频卡进行初始化操作(包括寻卡、防碰撞等)
#define MODE_GENERAL 0x02
read 或 write 操作时,使用此命令对射频卡进行简单的读写,不含寻卡、防碰撞、密码验证等
#define MODE_INCREASE 0x03
write 操作时,使用此命令对射频卡进行加值操作
#define MODE_DECREASE 0x04
write 操作时,使用此命令对射频卡进行减值操作
#define MODE_INITAUTHENTICA 0x05
read 和 write 操作时,使用此命令对射频卡进行初始化、授权、基本读写操作
#define MODE_INITAUTHENTICB 0x06
write 操作时,使用此命令对射频卡进行初始化、授权、增值操作
#define MODE_INITAUTHENTICC 0x07
write 操作时,使用此命令对射频卡进行初始化、授权、减值操作
#define MODE_AUTHENTICA 0x08
read 和 write 操作时,使用此命令对射频卡进行授权、基本读写操作
#define MODE_AUTHENTICB 0x09
write 操作时,使用此命令对射频卡进行授权、增值操作
#define MODE_AUTHENTICC 0x0a
write 操作时,使用此命令对射频卡进行授权、减值操作
#define MODE_GETNUM 0x0b 获取射频卡的卡号
typedef struct
{
unsigned char key[6];
unsigned char mode;
unsigned char* rtValue;
unsigned char* buf;
}key_buf;
key【in】成员在传递参数时提供密码。
mode【in】 将之设置为上面提供的命令字可以使用read和write对射频卡进行相关操作,具体操作请参考前面提到的宏定义命令字。若高4位为全1,采用密码B验证,若为其他值,采用密码A。
rtValue【in】【out】提供一个应用程序的整型变量的指针,可以在做完read 或write操作时获取相应的返回值。当返回值为MI_OK时,说明上次操作成功,否则会返回相应的错误码。错误码的相关含义见附录
buf【in】【out】write操作时,由应用程序提供要写入值的指针(16个字节),read操作时,应用程序提供缓冲区,可以从其指向的缓冲区中获取读出的数据。若命令字为MODE_GETNUM,低四个字节为卡的卡号
应用程序最好使用INITAUTHENTIC访问卡片,这样可以防止由于先前进行过写操作或重新换块进行读写时造成的访问错误。
应用程序可以使用lseek函数来对相关块进行操作,MFRC531共有16个扇区,每个扇区有4个块,其中每个扇区的前三个块为数据块,可以进行读写和增值减值操作(增值减值操作对数据有要求),第四个块为控制块,保存了密码A和密码B以及相关访问权限。
推荐应用程序在使用lseek函数进行块访问的时候,最好使用SEEK_SET参数,防止对控制块进行误操作造成卡不可读写。
在对卡的某块做充值的时候, 这块的数据要符合充值格式:
第一个字节 | 第二个字节 | 第三个字节 | 第四个字节 |
充值的原值 | 原值取反 | 原值 | 块值 |
射频卡驱动的中断是要对卡发命令之后才会调用中断,不是一靠卡就会调用的,这点非常重要。
用户程序代码如下:
/***********************************Demo of the mifare card driver**************************************************/
//this is a demo of MFRC530 driver. The driver supplies fundermental operations of the mifare card. Howerver,it doesn't provide the ability to manipulate the its registers. For a common user,it's enough. If you do need more functions of the driver,you can contact the writer for more information or just modify the source code.
/////for more information,please consult the Documents of the driver////
#include <stdio.h>
#include <fcntl.h>
#define MODE_INIT 0x01
#define MODE_GENERAL 0x02
#define MODE_INCREASE 0x03
#define MODE_DECREASE 0x04
#define MODE_INITAUTHENTICA 0x05
#define MODE_INITAUTHENTICB 0x06
#define MODE_INITAUTHENTICC 0x07
#define MODE_AUTHENTICA 0x08
#define MODE_AUTHENTICB 0x09
#define MODE_AUTHENTICC 0x0a
#define MODE_GETNUM 0x0b
#define MI_OK 0x00
typedef struct
{
unsigned char key[6];
unsigned char mode;
unsigned char* rtValue;
unsigned char* buf;
}key_buf;
int main(void)
{
int ch,nBlock;
unsigned char buf1[16];
unsigned char buf2[16];
unsigned char buffer[16];
unsigned char rtValue1,rtValue2;
unsigned char i;
int fd;
key_buf keyBuf1;
key_buf keyBuf2;
memset(keyBuf1.key,0xff,6);
memset(keyBuf2.key,0xff,6);
keyBuf1.buf=buf1;
keyBuf2.buf=buf2;
keyBuf1.rtValue=&rtValue1;
keyBuf2.rtValue=&rtValue2;
buffer[0] = 0x10; buffer[4] = 0xef; buffer[8] = 0x10;
buffer[1] = 0x0; buffer[5] = 0xff; buffer[9] = 0x0;
buffer[2] = 0x0; buffer[6] = 0xff; buffer[10] = 0x0;
buffer[3] = 0x0; buffer[7] = 0xff; buffer[11] = 0x0;
buffer[12] = 0x04; buffer[13] = 0xfb; buffer[14] = 0x04; buffer[15] = 0xfb;
fd=open("/dev/mifare",O_RDWR);
printf("This is a demo of MFRC531 card\n\n");
start:
printf("[a] Write the card\n");
printf(" Read the card\n");
printf("[c] Increase Value\n");
printf("[c] Get the serial No.\n");
printf("[e] Exit Demo\n");
ch=getchar();
switch(ch)
{
case 'a':
{
printf("Please type in an interger between 0~63\n");
scanf("%d",&nBlock);
printf("nBlock=%d\n",nBlock);
printf("The Demo will write 666... to the card\n");
getchar();
memset(keyBuf1.buf,0x66,16);
keyBuf1.mode=MODE_INITAUTHENTICA;
lseek(fd,nBlock,SEEK_SET);
write(fd,&keyBuf1,6);
if(rtValue1==MI_OK)
printf("Write OK\n");
else
printf("Write Error\n");
printf("press any key to continue\n");
getchar();
break;
}
case 'b':
{
printf("Please type in an interger between 0~63\n");
scanf("%d",&nBlock);
keyBuf2.mode=MODE_INITAUTHENTICA;
lseek(fd,nBlock,SEEK_SET);
read(fd,&keyBuf2,6);
if(rtValue2==MI_OK)
{
printf("Read OK\n");
for(i=0;i<16;i++)
printf("%x ",keyBuf2.buf);
printf("\n");
}
else
printf("Read Error\n");
printf("Press any key to continue\n");
getchar();
getchar();
break;
}
case 'd':
{
keyBuf2.mode=MODE_GETNUM;
printf("The serial No. of the card is\n");
read(fd,&keyBuf2,6);
for(i=0;i<4;i++)
printf("%x ",keyBuf2.buf);
printf("\nPress any key to continue\n");
getchar();
getchar();
break;
}
case 'c':
{
printf("Attention: MFRC531 can only accept special format of data for this operation\n");
printf("Application will first write some value to the target block\n");
printf("Please type an interger between 0~63\n");
scanf("%d",&nBlock);
keyBuf1.buf=buffer;
keyBuf1.mode=MODE_INITAUTHENTICA;
keyBuf2.mode=MODE_INITAUTHENTICA;
lseek(fd,nBlock,SEEK_SET);
write(fd,&keyBuf1,6);
if(rtValue1==MI_OK)
printf("Write OK\n");
else
printf("Write Error\n");
lseek(fd,nBlock,SEEK_SET);
read(fd,&keyBuf2,6);
printf("Now the %d Block's value is\n",nBlock);
for(i=0;i<16;i++)
printf("%x ",keyBuf2.buf);
printf("\n");
printf("Application will Increase the Block Value\n");
keyBuf1.buf=buffer;
keyBuf1.mode=MODE_INITAUTHENTICB;
lseek(fd,nBlock,SEEK_SET);
write(fd,&keyBuf1,6);
if(rtValue1==MI_OK)
printf("Increase OK\n");
else
printf("Increase Error\n");
printf("\n");
printf("Press any key to continue\n");
keyBuf1.buf=buf1;
getchar();
getchar();
break;
}
case 'e':
{
goto exit;
}
}
goto start;
exit:
close(fd);
return 0;
}
设备驱动中的中断程序:
static irqreturn_t sep4020_mifare_irqhandler(int irq, void *dev_id, struct pt_regs *reg)
{
static unsigned char irqBits;
static unsigned char irqMask;
static unsigned char nbytes;
static unsigned char cnt;
//IE0 = 0; // Clear interrupt request flag
mask_irq(RCF_INT);
//printk("ZWM INT1\n");
if (MpIsrInfo && MpIsrOut && MpIsrIn) // transfer pointers have to be set correctly
{
while((ReadIO(RegPrimaryStatus))& 0x08) // loop while IRQ pending
// Attention: IRQ bit is inverted when used with low active IRQ
{
irqMask = ReadIO(RegInterruptEn); // read enabled interrupts
// read pending interrupts
irqBits = ReadIO(RegInterruptRq) & irqMask;
MpIsrInfo->irqSource |= irqBits; // save pending interrupts 所产生中断的真正中断源
//************ LoAlertIRQ ******************
if (irqBits & 0x01) // LoAlert FIFO缓冲区变空,写操作
{
nbytes = MFIFOLength - ReadIO(RegFIFOLength);
// less bytes to send, than space in FIFO
if ((MpIsrInfo->nBytesToSend - MpIsrInfo->nBytesSent) <= nbytes)
{
nbytes = MpIsrInfo->nBytesToSend - MpIsrInfo->nBytesSent;
WriteIO(RegInterruptEn,0x01); // disable LoAlert IRQ
}
// write remaining data to the FIFO
for ( cnt = 0;cnt < nbytes;cnt++)
{
WriteIO(RegFIFOData,MpIsrOut[MpIsrInfo->nBytesSent]);
MpIsrInfo->nBytesSent++;
}
//printk("LoAlertIRQ\n");
WriteIO(RegInterruptRq,0x01); // reset IRQ bit
}
//************* TxIRQ Handling **************
if (irqBits & 0x10) // TxIRQ Transceive 命令所有数据都已发送 Auth1 和Auth2 命令所有数据都已发送
// WriteE2 命令所有数据都已编程 CalcCRC 命令所有数据都已处理
{
WriteIO(RegInterruptRq,0x10); // reset IRQ bit
WriteIO(RegInterruptEn,0x82); // enable HiAlert Irq for response
if (MpIsrInfo->cmd == PICC_ANTICOLL1) // if cmd is anticollision
{ // switch off parity generation
WriteIO(RegChannelRedundancy,0x02); // RXCRC and TXCRC disable, parity disable
}
//printk("TxIRQ Handling\n"); //reset IRQ bit - idle irq will be deleted in a seperate section
}
//************* HiAlertIRQ or RxIRQ Handling ******************
if (irqBits & 0x0E) // HiAlert, Idle or RxIRQ 数据流从卡接收结束RxIRQ 读操作
{
// read some bytes ( length of FIFO queue)
// into the receive buffer
nbytes = ReadIO(RegFIFOLength);
// read date from the FIFO and store them in the receive buffer
for ( cnt = 0; cnt < nbytes; cnt++)
{
MpIsrIn[MpIsrInfo->nBytesReceived] = ReadIO(RegFIFOData);
MpIsrInfo->nBytesReceived++;
}
WriteIO(RegInterruptRq,0x0A & irqBits); //0000 1010
//printk("HiAlert, Idle or RxIRQ\n"); //reset IRQ bit - idle irq will be deleted in a seperate section
}
//************** IdleIRQ Handling ***********
if (irqBits & 0x04) // Idle IRQ 命令执行完成
{
WriteIO(RegInterruptEn,0x20); // disable Timer IRQ
WriteIO(RegInterruptRq,0x20); // disable Timer IRQ request
irqBits &= ~0x20; // clear Timer IRQ in local var
MpIsrInfo->irqSource &= ~0x20; // clear Timer IRQ in info var when idle received, then cancel timeout
WriteIO(RegInterruptRq,0x04); // reset IRQ bit
// status should still be MI_OK
// no error - only used for wake up
//printk("IdleIRQ Handling\n");
}
//************* TimerIRQ Handling ***********
if (irqBits & 0x20) // timer IRQ 定时器TimerValue 寄存器值减为0 时置位
{
WriteIO(RegInterruptRq,0x20); // reset IRQ bit
MpIsrInfo->status = MI_NOTAGERR; // timeout error, therwise ignore the interrupt
//printk("TimerIRQ Handling\n");
}
}
}
CLR_INT(RCF_INT);
//pr_debug("in interupt2\n");
unmask_irq(RCF_INT);
return IRQ_HANDLED;
}
下面的返回值仅供参考,具体错误可能与之不同
// Mifare Error Codes
// Each function returns a status value, which corresponds to the
// mifare error codes.
#define MI_OK 0
#define MI_CHK_OK 0
#define MI_CRC_ZERO 0
#define MI_CRC_NOTZERO 1
#define MI_NOTAGERR (-1)
#define MI_CHK_FAILED (-1)
#define MI_CRCERR (-2)
#define MI_CHK_COMPERR (-2)
#define MI_EMPTY (-3)
#define MI_AUTHERR (-4)
#define MI_PARITYERR (-5)
#define MI_CODEERR (-6)
#define MI_SERNRERR (-8)
#define MI_KEYERR (-9)
#define MI_NOTAUTHERR (-10)
#define MI_BITCOUNTERR (-11)
#define MI_BYTECOUNTERR (-12)
#define MI_IDLE (-13)
#define MI_TRANSERR (-14)
#define MI_WRITEERR (-15)
#define MI_INCRERR (-16)
#define MI_DECRERR (-17)
#define MI_READERR (-18)
#define MI_OVFLERR (-19)
#define MI_POLLING (-20)
#define MI_FRAMINGERR (-21)
#define MI_ACCESSERR (-22)
#define MI_UNKNOWN_COMMAND (-23)
#define MI_COLLERR (-24)
#define MI_RESETERR (-25)
#define MI_INITERR (-25)
#define MI_INTERFACEERR (-26)
#define MI_ACCESSTIMEOUT (-27)
#define MI_NOBITWISEANTICOLL (-28)
#define MI_QUIT (-30)
#define MI_RECBUF_OVERFLOW (-50)
#define MI_SENDBYTENR (-51)
#define MI_SENDBUF_OVERFLOW (-53)
#define MI_BAUDRATE_NOT_SUPPORTED (-54)
#define MI_SAME_BAUDRATE_REQUIRED (-55)
#define MI_WRONG_PARAMETER_VALUE (-60)
#define MI_BREAK (-99)
#define MI_NY_IMPLEMENTED (-100)
#define MI_NO_MFRC (-101)
#define MI_MFRC_NOTAUTH (-102)
#define MI_WRONG_DES_MODE (-103)
#define MI_HOST_AUTH_FAILED (-104)
#define MI_WRONG_LOAD_MODE (-106)
#define MI_WRONG_DESKEY (-107)
#define MI_MKLOAD_FAILED (-108)
#define MI_FIFOERR (-109)
#define MI_WRONG_ADDR (-110)
#define MI_DESKEYLOAD_FAILED (-111)
#define MI_WRONG_SEL_CNT (-114)
#define MI_WRONG_TEST_MODE (-117)
#define MI_TEST_FAILED (-118)
#define MI_TOC_ERROR (-119)
#define MI_COMM_ABORT (-120)
#define MI_INVALID_BASE (-121)
#define MI_MFRC_RESET (-122)
#define MI_WRONG_VALUE (-123)
#define MI_VALERR (-124)
文章评论(0条评论)
登录后参与讨论