这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 综合技术 » 基础知识 » 我的NFC学习之旅(4)---使用STC32G读取M1卡ID

共2条 1/1 1 跳转至

我的NFC学习之旅(4)---使用STC32G读取M1卡ID

专家
2026-07-08 10:41:50     打赏

在学习了读卡器与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);
}

测试效果:





关键词: 懒猫的学习笔记     NFC    

院士
2026-07-08 16:47:38     打赏
2楼

学习了,谢谢分享。


共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]