在学习了读卡器与M1卡之间通讯指令之后,要想完成读写卡,我们还是要学习如何操纵读卡器完成对M1卡的读写处理。这里的读卡器我选择RC522这个常见的而控制核心构成的读卡器。在弄懂了M1卡的存储模式后,就应该明白:RC522读卡器所要做的工作,基本上就是提供扇区和块的编号,并根据这个编号和M1卡通讯,读写指定扇区、块所在单元的数据。
实际上RC522的构成不是这么简单的,还有很多功能上的配置。学习上,要从简单着手,所以这里我忽略RC522的其他功能,紧紧围绕着读取UID这个处理展开。
首先,相其他外部设备一样,要对RC522进行初始化处理。这个处理是通过对RC522中的命令寄存器进行操作的。这个寄存器的地址是0x01。在RC522的上位机中,无论使用和RC522的哪种接口(SPI、I2C、USRT),都是要通过对命令寄存器发送指令,来完成想要的操作的。在设计上,命令寄存器为8位的寄存器。


4位命令码说明如下:

这样,我们想要读取UID的操作,需要按照以下顺序处理:
1、复位RC522,使之进入工作状态。
2、检查收有卡接近(这里不考虑多卡同时接近产生的碰撞处理)
3、有卡场合,读取该卡的第0号扇区第0块单元的数据
4、这样获得的数据就是UID。上记处理过程中没有对RC522的工作参数进行设置处理,默认使用缺省配置。
按照这个流程,使用51单片机编写以下代码,
#include "STC32G.h"
#include "stdio.h"
#include "intrins.h"
#include "mfrc522.h"
#include <string.h>
#include "gui.h"
#include "oled.h"
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
// 工作主频
#define MAIN_Fosc 24000000UL
sbit PWMOUT = P2^0; // PWM输出
// 计数变量
u8 cnt = 0;
u8 threshold=50;
void delay_ms(u8 ms);
void delay_us(u8 us);
//M1卡的某一块写为如下格式,则该块为钱包,可接收扣款和充值命令
//4字节金额(低字节在前)+4字节金额取反+4字节金额+1字节块地址+1字节块地址取反+1字节块地址+1字节块地址取反
unsigned char code data2[4] = {0x12,0,0,0};
unsigned char code DefaultKey[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
unsigned char g_ucTempbuf[20];
unsigned char idata MLastSelectedSnr[4];
unsigned char idata RevBuffer[30];
unsigned char cardid[20];
unsigned char code ASCII[20] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', ' '};
bit CmdValid;
//========================================================================
// 函数: void delay_ms(unsigned long ms)
// 描述: 延时函数。
// 参数: ms,要延时的ms数, 这里只支持1~255ms. 自动适应主时钟.
//========================================================================
void delay_ms(unsigned int ms) {
unsigned int i;
do {
i = MAIN_Fosc / 6000;
while(--i);
} while(--ms);
}
void delay_us(unsigned int us) {
unsigned int i;
do {
i = MAIN_Fosc / 6;
while(--i);
} while(--us);
}
/******************** 主函数 **************************/
void main(void) {
unsigned char status;
unsigned char i;
unsigned long t;
WTST = 0; //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
EAXFR = 1; //扩展寄存器(XFR)访问使能
CKCON = 0; //提高访问XRAM速度
P0M1 = 0x00; P0M0 = 0x00; // 准双向口
P1M1 = 0x00; P1M0 = 0x00; // 准双向口
P2M1 = 0x00; P2M0 = 0x00; // 准双向口
P3M1 = 0x00; P3M0 = 0x00; // 准双向口
P4M1 = 0x00; P4M0 = 0x00; // 准双向口
P5M1 = 0x00; P5M0 = 0x00; // 准双向口
P6M1 = 0x00; P6M0 = 0x00; // 准双向口
cnt=0;
// 初始化OLED
OLED_Init();
OLED_Clear(0);
// 显示开始处理的标志字符串
GUI_ShowString(0, 0, "Start:",8, 1);
PcdReset();
PcdAntennaOff();
PcdAntennaOn();
M500PcdConfigISOType( 'A' );
t = 0; // 计时
while(1) {
// 寻卡,防冲突,选择卡 返回卡类型(2 bytes)+ 卡系列号(4 bytes)
status= PcdRequest(PICC_REQALL, &RevBuffer[2]);
if(status!=0) {
status= PcdHalt();
} else {
// 防冲突 读卡的系列号
status = PcdAnticoll(&RevBuffer[2]);
if(status!=0) {
status= PcdHalt();
}
t++;
GUI_ShowNum(60, 0, t, 8, 8, 1);
GUI_ShowString(0, 10, " ", 8, 1);
// 复制卡号
//PcdAuthState(RevBuffer[1], RevBuffer[3], DefaultKey, MLastSelectedSnr);// 校验卡密码
memcpy(MLastSelectedSnr, &RevBuffer[2], 4);
// 显示卡ID
cardid[0] = ASCII[MLastSelectedSnr[0]%16];
cardid[1] = ASCII[MLastSelectedSnr[0]/16];
cardid[2] = ASCII[MLastSelectedSnr[1]%16];
cardid[3] = ASCII[MLastSelectedSnr[1]/16];
cardid[4] = ASCII[MLastSelectedSnr[2]%16];
cardid[5] = ASCII[MLastSelectedSnr[2]/16];
cardid[6] = ASCII[MLastSelectedSnr[3]%16];
cardid[7] = ASCII[MLastSelectedSnr[3]/16];
cardid[8] = '\0';
GUI_ShowString(0, 10, cardid, 8, 1);
}
delay_ms(100);
}
}主处理的循环中,延时不能太小。太小了,回导致重复读取一个卡。
RC522的处理代码:
#include <intrins.h>
#include "STC32G.h"
#include "mfrc522.h"
#include <string.h>
//////////////////////////////////
//端口定义
/////////////////////////////////////////////////////////////////////
//MFRC522
sbit MF522_RST = P0^4; // 片选
sbit MF522_NSS = P0^2;
sbit MF522_SCK = P0^1;
sbit MF522_SI = P0^0;
sbit MF522_SO = P0^3;
#define MAXRLEN 18
extern void delay_us(unsigned int us);
extern void delay_ms(unsigned int us);
/////////////////////////////////////////////////////////////////////
//功 能:寻卡
//参数说明: req_code[IN]:寻卡方式
// 0x52 = 寻感应区内所有符合14443A标准的卡
// 0x26 = 寻未进入休眠状态的卡
// pTagType[OUT]:卡片类型代码
// 0x4400 = Mifare_UltraLight
// 0x0400 = Mifare_One(S50)
// 0x0200 = Mifare_One(S70)
// 0x0800 = Mifare_Pro(X)
// 0x4403 = Mifare_DESFire
//返 回: 成功返回MI_OK
/////////////////////////////////////////////////////////////////////
char PcdRequest(unsigned char req_code,unsigned char *pTagType) {
char status;
unsigned int unLen;
unsigned char ucComMF522Buf[MAXRLEN];
ClearBitMask(Status2Reg,0x08);
WriteRawRC(BitFramingReg,0x07);
SetBitMask(TxControlReg,0x03);
ucComMF522Buf[0] = req_code;
status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf, 1, ucComMF522Buf, &unLen);
if ((status == MI_OK) && (unLen == 0x10)) {
*pTagType = ucComMF522Buf[0];
*(pTagType+1) = ucComMF522Buf[1];
} else {
status = MI_ERR;
}
return status;
}
/////////////////////////////////////////////////////////////////////
//功 能:防冲撞
//参数说明: pSnr[OUT]:卡片序列号,4字节
//返 回: 成功返回MI_OK
/////////////////////////////////////////////////////////////////////
char PcdAnticoll(unsigned char *pSnr) {
char status;
unsigned char i,snr_check=0;
unsigned int unLen;
unsigned char ucComMF522Buf[MAXRLEN];
ClearBitMask(Status2Reg,0x08);
WriteRawRC(BitFramingReg,0x00);
ClearBitMask(CollReg,0x80);
ucComMF522Buf[0] = PICC_ANTICOLL1;
ucComMF522Buf[1] = 0x20;
status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf, 2, ucComMF522Buf, &unLen);
if (status == MI_OK) {
for (i=0; i<4; i++) {
*(pSnr+i) = ucComMF522Buf[i];
snr_check ^= ucComMF522Buf[i];
}
if (snr_check != ucComMF522Buf[i]) {
status = MI_ERR;
}
}
SetBitMask(CollReg,0x80);
return status;
}
/////////////////////////////////////////////////////////////////////
//功 能:选定卡片
//参数说明: pSnr[IN]:卡片序列号,4字节
//返 回: 成功返回MI_OK
/////////////////////////////////////////////////////////////////////
char PcdSelect(unsigned char *pSnr) {
char status;
unsigned char i;
unsigned int unLen;
unsigned char ucComMF522Buf[MAXRLEN];
ucComMF522Buf[0] = PICC_ANTICOLL1;
ucComMF522Buf[1] = 0x70;
ucComMF522Buf[6] = 0;
for (i=0; i<4; i++) {
ucComMF522Buf[i+2] = *(pSnr+i);
ucComMF522Buf[6] ^= *(pSnr+i);
}
CalulateCRC(ucComMF522Buf,7,&ucComMF522Buf[7]);
ClearBitMask(Status2Reg,0x08);
status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,9,ucComMF522Buf,&unLen);
if ((status == MI_OK) && (unLen == 0x18)) {
status = MI_OK;
} else {
status = MI_ERR;
}
return status;
}
/////////////////////////////////////////////////////////////////////
//功 能:读取M1卡一块数据
//参数说明: addr[IN]:块地址
// pData[OUT]:读出的数据,16字节
//返 回: 成功返回MI_OK
/////////////////////////////////////////////////////////////////////
char PcdRead(unsigned char addr,unsigned char *pData) {
char status;
unsigned int unLen;
unsigned char i,ucComMF522Buf[MAXRLEN];
ucComMF522Buf[0] = PICC_READ;
ucComMF522Buf[1] = addr;
CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);
status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);
if ((status == MI_OK) && (unLen == 0x90)) {
for (i=0; i<16; i++) {
*(pData+i) = ucComMF522Buf[i]; }
} else {
status = MI_ERR;
}
return status;
}
/////////////////////////////////////////////////////////////////////
//用MF522计算CRC16函数
/////////////////////////////////////////////////////////////////////
void CalulateCRC(unsigned char *pIndata,unsigned char len,unsigned char *pOutData) {
unsigned char i,n;
ClearBitMask(DivIrqReg,0x04);
WriteRawRC(CommandReg,PCD_IDLE);
SetBitMask(FIFOLevelReg,0x80);
for (i=0; i<len; i++) {
WriteRawRC(FIFODataReg, *(pIndata+i));
}
WriteRawRC(CommandReg, PCD_CALCCRC);
i = 0xFF;
do {
n = ReadRawRC(DivIrqReg);
i--;
}
while ((i!=0) && !(n&0x04));
pOutData[0] = ReadRawRC(CRCResultRegL);
pOutData[1] = ReadRawRC(CRCResultRegM);
}
/////////////////////////////////////////////////////////////////////
//功 能:命令卡片进入休眠状态
//返 回: 成功返回MI_OK
/////////////////////////////////////////////////////////////////////
char PcdHalt(void) {
char status;
unsigned int unLen;
unsigned char ucComMF522Buf[MAXRLEN];
ucComMF522Buf[0] = PICC_HALT;
ucComMF522Buf[1] = 0;
CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);
status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);
return MI_OK;
}
/////////////////////////////////////////////////////////////////////
//功 能:复位RC522
//返 回: 成功返回MI_OK
/////////////////////////////////////////////////////////////////////
char PcdReset(void) {
MF522_RST=1;
_nop_();
MF522_RST=0;
_nop_();
MF522_RST=1;
_nop_();
WriteRawRC(CommandReg,PCD_RESETPHASE);
_nop_();
//和Mifare卡通讯,CRC初始值0x6363
WriteRawRC(ModeReg,0x3D);
WriteRawRC(TReloadRegL,30);
WriteRawRC(TReloadRegH,0);
WriteRawRC(TModeReg,0x8D);
WriteRawRC(TPrescalerReg,0x3E);
WriteRawRC(TxAutoReg,0x40);
return MI_OK;
}
//////////////////////////////////////////////////////////////////////
//设置RC632的工作方式
//////////////////////////////////////////////////////////////////////
char M500PcdConfigISOType(unsigned char type) {
//ISO14443_A
if (type == 'A') {
ClearBitMask(Status2Reg,0x08);
WriteRawRC(ModeReg,0x3D);//3F
WriteRawRC(RxSelReg,0x86);//84
WriteRawRC(RFCfgReg,0x7F); //4F
WriteRawRC(TReloadRegL,30);//tmoLength);// TReloadVal = 'h6a =tmoLength(dec)
WriteRawRC(TReloadRegH,0);
WriteRawRC(TModeReg,0x8D);
WriteRawRC(TPrescalerReg,0x3E);
delay_ms(10);
PcdAntennaOn();
} else{
return -1;
}
return MI_OK;
}
/////////////////////////////////////////////////////////////////////
//功 能:读RC632寄存器
//参数说明:Address[IN]:寄存器地址
//返 回:读出的值
/////////////////////////////////////////////////////////////////////
unsigned char ReadRawRC(unsigned char Address) {
unsigned char i, ucAddr;
unsigned char ucResult=0;
MF522_SCK = 0;
MF522_NSS = 0;
ucAddr = ((Address<<1)&0x7E)|0x80;
for(i=8;i>0;i--) {
MF522_SI = ((ucAddr&0x80)==0x80);
MF522_SCK = 1;
ucAddr <<= 1;
MF522_SCK = 0;
}
for(i=8;i>0;i--) {
MF522_SCK = 1;
ucResult <<= 1;
ucResult|=(bit)MF522_SO;
MF522_SCK = 0;
}
MF522_NSS = 1;
MF522_SCK = 1;
return ucResult;
}
/////////////////////////////////////////////////////////////////////
//功 能:写RC632寄存器
//参数说明:Address[IN]:寄存器地址
// value[IN]:写入的值
/////////////////////////////////////////////////////////////////////
void WriteRawRC(unsigned char Address, unsigned char value) {
unsigned char i, ucAddr;
MF522_SCK = 0;
MF522_NSS = 0;
ucAddr = ((Address<<1)&0x7E);
for(i=8;i>0;i--) {
MF522_SI = ((ucAddr&0x80)==0x80);
MF522_SCK = 1;
ucAddr <<= 1;
MF522_SCK = 0;
}
for(i=8;i>0;i--) {
MF522_SI = ((value&0x80)==0x80);
MF522_SCK = 1;
value <<= 1;
MF522_SCK = 0;
}
MF522_NSS = 1;
MF522_SCK = 1;
}
/////////////////////////////////////////////////////////////////////
//功 能:置RC522寄存器位
//参数说明:reg[IN]:寄存器地址
// mask[IN]:置位值
/////////////////////////////////////////////////////////////////////
void SetBitMask(unsigned char reg,unsigned char mask) {
char tmp = 0x0;
tmp = ReadRawRC(reg);
WriteRawRC(reg,tmp | mask); // set bit mask
}
/////////////////////////////////////////////////////////////////////
//功 能:清RC522寄存器位
//参数说明:reg[IN]:寄存器地址
// mask[IN]:清位值
/////////////////////////////////////////////////////////////////////
void ClearBitMask(unsigned char reg,unsigned char mask) {
char tmp = 0x0;
tmp = ReadRawRC(reg);
WriteRawRC(reg, tmp & ~mask); // clear bit mask
}
/////////////////////////////////////////////////////////////////////
//功 能:通过RC522和ISO14443卡通讯
//参数说明:Command[IN]:RC522命令字
// pInData[IN]:通过RC522发送到卡片的数据
// InLenByte[IN]:发送数据的字节长度
// pOutData[OUT]:接收到的卡片返回数据
// *pOutLenBit[OUT]:返回数据的位长度
/////////////////////////////////////////////////////////////////////
char PcdComMF522(unsigned char Command,
unsigned char *pInData,
unsigned char InLenByte,
unsigned char *pOutData,
unsigned int *pOutLenBit) {
char status = MI_ERR;
unsigned char irqEn = 0x00;
unsigned char waitFor = 0x00;
unsigned char lastBits;
unsigned char n;
unsigned int i;
switch (Command) {
case PCD_AUTHENT:
irqEn = 0x12;
waitFor = 0x10;
break;
case PCD_TRANSCEIVE:
irqEn = 0x77;
waitFor = 0x30;
break;
default:
break;
}
WriteRawRC(ComIEnReg,irqEn|0x80);
ClearBitMask(ComIrqReg,0x80);
WriteRawRC(CommandReg,PCD_IDLE);
SetBitMask(FIFOLevelReg,0x80);
for (i=0; i<InLenByte; i++) {
WriteRawRC(FIFODataReg, pInData[i]);
}
WriteRawRC(CommandReg, Command);
if (Command == PCD_TRANSCEIVE) {
SetBitMask(BitFramingReg,0x80);
}
// i = 600;//根据时钟频率调整,操作M1卡最大等待时间25ms
i = 2000;
do {
n = ReadRawRC(ComIrqReg);
i--;
}
while ((i!=0) && !(n&0x01) && !(n&waitFor));
ClearBitMask(BitFramingReg,0x80);
if (i!=0) {
if(!(ReadRawRC(ErrorReg)&0x1B)) {
status = MI_OK;
if (n & irqEn & 0x01) {
status = MI_NOTAGERR; }
if (Command == PCD_TRANSCEIVE) {
n = ReadRawRC(FIFOLevelReg);
lastBits = ReadRawRC(ControlReg) & 0x07;
if (lastBits) {
*pOutLenBit = (n-1)*8 + lastBits;
} else {
*pOutLenBit = n*8;
}
if (n == 0) {
n = 1;
}
if (n > MAXRLEN) {
n = MAXRLEN;
}
for (i=0; i<n; i++) {
pOutData[i] = ReadRawRC(FIFODataReg);
}
}
} else {
status = MI_ERR;
}
}
SetBitMask(ControlReg,0x80); // stop timer now
WriteRawRC(CommandReg,PCD_IDLE);
return status;
}
/////////////////////////////////////////////////////////////////////
//开启天线
//每次启动或关闭天险发射之间应至少有1ms的间隔
/////////////////////////////////////////////////////////////////////
void PcdAntennaOn() {
unsigned char i;
i = ReadRawRC(TxControlReg);
if (!(i & 0x03)) {
SetBitMask(TxControlReg, 0x03);
}
}
/////////////////////////////////////////////////////////////////////
//关闭天线
/////////////////////////////////////////////////////////////////////
void PcdAntennaOff() {
ClearBitMask(TxControlReg, 0x03);
}测试效果:



我要赚赏金
