锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

Linux驱动开发-编写RFID-RC522射频刷卡模块驱动

时间:2022-10-12 07:00:01 m射频连接器板线连接器s3b存储器连接器智能卡

1. MF-RC522模块介绍

MFRC522是应用于13.56MHz对于三表应用推出的低电压、低成本、体积小的非接触式读写卡芯片,是智能仪表和便携式手持设备研发的较好选择。研发便携式手持设备的更好选择。MFRC522采用先进的调制和解调概念,集成在13.56MHz所有类型的被动非接触式通信方式和协议。支持14443A与应答器信号兼容。数字部分处理ISO14443A帧和错误检测。还支持快速CRYPTO1.用语验证加密算法MIFARE系列产品。MFRC522支持MI FARE双向数据传输速率高达424kbit/s。作为13.56MHz高集成读写卡系列芯片族的新成员,MFRC522与MF RC500和MFRC530有很多相似之处,也有很多特点和差异。它与主机间通信SPI模式有利于减少连接和缩小PCB板体积,降低成本。

淘宝上MFRC522成品模块很多,买的时候会送几张白卡(IC完成读写实验的卡)。

image-20220110135425238

淘宝上买的MF-RC522模块基本引出SPI实际上,接口MF-RC522本身还支持IIC,UART协议,SPI协议更简单、更快。

目前,我使用淘宝购买包装成品模块MFRC522原芯片设计读卡电路,使用方便,成本低。适用于需要射频卡终端设计/生产的设备开发、读卡器开发等高应用用用户。各种读卡器模具可直接安装在本模块中。电压为3.3V,通过SPI几条接口简单的线可以直接与用户使用CPU主板连接通信,可保证模块工作稳定可靠,读卡距离长。

如果是当前文章的介绍Linux系统下编写MF-RC522模块驱动,配合应用层完成IC卡号读取、扇区读写、密码验证等。目前开发板采用友好臂Tiny芯片是三星EXYNOS4412没有使用驱动代码SPI直接控制子系统IO口模拟SPI时序完成与MF-RC522之间通讯。

购买模块时,会发送一个模块IC白卡和钥匙扣虽然形状不同,但内部芯片型号属于S50卡、常用的公交卡、地铁卡、超市会员卡等都属于这一类S50卡。还有一个洗发水S70类卡,空间比S50大4倍。S50卡内部是一张EEPROM任何数据都可以存储在空间中,空间分为16个扇区,每个扇区由4个(0、1、2、3)组成。在实际操作中,将16扇分成64块,按绝对地址编号为0-63。

IC卡没有电源,它是由IC芯片、感应天线组成,封装在一个标准的PVC芯片和天线在卡片中没有暴露部分。这是近年来世界上发展起来的一项新技术。它成功地将射频识别技术和IC结合卡技术,结束了无源(卡中无电源)和免接触问题,是电子设备领域的一大突破。卡在一定距离内(通常是5-10)cm)靠近读写器表面,通过无线电波传输完成数据读写操作。

2. 连接硬件原理

3. 驱动代码示例

3.1 rc522.c 源代码

#include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include "rfid_rc522.h" #include &l;linux/miscdevice.h>
#include 


/*--------------------------------RC522相关操作代码---------------------------------------------*/

/* 函数功能:RC522初始化 Tiny4412硬件连接: DO--MISO :GPB_2 DI--MOSI :GPB_3 CLK-SCLK :GPB_0 CS--CS :GPB_1 RST-- :GPB_4 */
void RC522_IO_Init(void)
{ 
        
	/*1. 注册GPIO*/
	gpio_request(EXYNOS4_GPB(0), "RC522_CLK-SCLK");
	gpio_request(EXYNOS4_GPB(1), "RC522_CS");
	gpio_request(EXYNOS4_GPB(2), "MOSI");
	gpio_request(EXYNOS4_GPB(3), "RC522_MOSI");
	gpio_request(EXYNOS4_GPB(4), "RST");
	
	/*2. 配置GPIO口模式*/
	s3c_gpio_cfgpin(EXYNOS4_GPB(0), S3C_GPIO_OUTPUT);  //时钟
	s3c_gpio_cfgpin(EXYNOS4_GPB(1), S3C_GPIO_OUTPUT);  //片选
	s3c_gpio_cfgpin(EXYNOS4_GPB(2), S3C_GPIO_INPUT);  //输入模式
	s3c_gpio_cfgpin(EXYNOS4_GPB(3), S3C_GPIO_OUTPUT); //输出模式
	s3c_gpio_cfgpin(EXYNOS4_GPB(4), S3C_GPIO_OUTPUT); //输出模式
	
	/*3. 上拉GPIO口*/
	gpio_set_value(EXYNOS4_GPB(0), 1);
	gpio_set_value(EXYNOS4_GPB(1), 1);
	gpio_set_value(EXYNOS4_GPB(3), 1);
	gpio_set_value(EXYNOS4_GPB(4), 1);
}


/* 函数功能:SPI时序读写一个字节 说 明:SPI底层时序,程序的移植接口 */
u8 RC522_SPI_ReadWriteOneByte(u8 data_tx)
{ 
        
	 u8 data_rx=0;
	 u8 i;
	 for(i=0;i<8;i++)
	 { 
        
		gpio_set_value(EXYNOS4_GPB(0), 0);
		if(data_tx&0x80)gpio_set_value(EXYNOS4_GPB(3), 1);
		else gpio_set_value(EXYNOS4_GPB(3), 0);
		data_tx<<=1; //继续发送下一个数据

		gpio_set_value(EXYNOS4_GPB(0), 1);
		data_rx<<=1;
		if(gpio_get_value(EXYNOS4_GPB(2)))data_rx|=0x01;
	 }
	 return data_rx;
}



/* 功能描述:选卡读取卡存储器容量 输入参数:serNum 传入卡序列号 返 回 值:成功返回卡容量 */
u8 RC522_MFRC522_SelectTag(u8 *serNum) //读取卡存储器容量
{ 
             
	u8 i;     
	u8 status;     
	u8 size;     
	u8 recvBits;     
	u8 buffer[9];
	     
	buffer[0]=PICC_ANTICOLL1;	  //防撞码1 
	buffer[1]=0x70;
	buffer[6]=0x00;						     
	for(i=0;i<4;i++)					
	{ 
        
		buffer[i+2]=*(serNum+i);	//buffer[2]-buffer[5]为卡序列号
		buffer[6]^=*(serNum+i);	  //卡校验码
	}
	
	RC522_CalulateCRC(buffer,7,&buffer[7]);	//buffer[7]-buffer[8]为RCR校验码
	RC522_ClearBitMask(Status2Reg,0x08);
	status=RC522_PcdComMF522(PCD_TRANSCEIVE,buffer,9,buffer,&recvBits);
	
	if((status==MI_OK)&&(recvBits==0x18))    
		size=buffer[0];     
	else    
		size=0;
	return size; 
}


/* 延时函数,纳秒级 */
void RC522_Delay(u32 ns)
{ 
        
	ndelay(ns);
}


/* 函数功能:RC522芯片初始化 */
void RC522_Init(void)
{ 
        
  RC522_IO_Init();			 //RC522初始化
  RC522_PcdReset();  		 //复位RC522 
  RC522_PcdAntennaOff();	 //关闭天线
  msleep(2);  		  		 //延时2毫秒
  RC522_PcdAntennaOn();		 //开启天线
  M500PcdConfigISOType('A'); //设置RC632的工作方式
}


/* 函数功能:复位RC522 */
void RC522_Reset(void)
{ 
        
  RC522_PcdReset();				//复位RC522
  RC522_PcdAntennaOff();		//关闭天线
  msleep(2);  		  		    //延时2毫秒
  RC522_PcdAntennaOn();		    //开启天线 
}     


/* 功 能: 寻卡 参数说明: 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 RC522_PcdRequest(u8 req_code,u8 *pTagType)
{ 
        
	char status;  
	u8 unLen;
	u8 ucComMF522Buf[MAXRLEN];  	   		// MAXRLEN 18

	RC522_ClearBitMask(Status2Reg,0x08);	//清RC522寄存器位,/接收数据命令
	RC522_WriteRawRC(BitFramingReg,0x07);   //写RC632寄存器
	RC522_SetBitMask(TxControlReg,0x03);    //置RC522寄存器位
 
	ucComMF522Buf[0]=req_code; 	    //寻卡方式
	
	status=RC522_PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,1,ucComMF522Buf,&unLen); //通过RC522和ISO14443卡通讯
	
	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 RC522_PcdAnticoll(u8 *pSnr)
{ 
        
    char status;
    u8 i,snr_check=0;
    u8 unLen;
    u8 ucComMF522Buf[MAXRLEN]; 
    
    RC522_ClearBitMask(Status2Reg,0x08);  //清RC522寄存器位 
    RC522_WriteRawRC(BitFramingReg,0x00); //写
    RC522_ClearBitMask(CollReg,0x80);     //清
 
    ucComMF522Buf[0]=PICC_ANTICOLL1;   	 //PICC_ANTICOLL1 = 0x93
    ucComMF522Buf[1]=0x20;
	
    status=RC522_PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,2,ucComMF522Buf,&unLen); //0x0c,通过RC522和ISO14443卡通讯
											 //PCD_TRANSCEIVE =发送并接收数据
											 //2:写入卡里的数据字节长度
											 //ucComMF522Buf:存放数据的地址
											 //unLen:从卡里读出的数据长度
    if(status==MI_OK)
    { 
        
    	 for(i=0;i<4;i++)
			 { 
           
					 *(pSnr+i)=ucComMF522Buf[i];  //把读到的卡号赋值给pSnr
					 snr_check^=ucComMF522Buf[i];
			 }
			 if(snr_check!=ucComMF522Buf[i])
			 { 
        
					status = MI_ERR;
			 }
    }   
    RC522_SetBitMask(CollReg,0x80);
    return status;
}


/* 功 能:选定卡片 参数说明:pSnr[IN]:卡片序列号,4字节 返 回:成功返回MI_OK */
char RC522_PcdSelect(u8 *pSnr)
{ 
        
    char status;
    u8 i;
    u8 unLen;
    u8 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);
    }
		
    RC522_CalulateCRC(ucComMF522Buf,7,&ucComMF522Buf[7]); //用MF522计算CRC16函数,校验数据
    RC522_ClearBitMask(Status2Reg,0x08);	                //清RC522寄存器位
    status=RC522_PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,9,ucComMF522Buf,&unLen);
    if((status==MI_OK)&&(unLen==0x18))status=MI_OK;
    else status=MI_ERR;
		
    return status;
}


/* 功 能:验证卡片密码 参数说明:auth_mode[IN]: 密码验证模式 0x60 = 验证A密钥 0x61 = 验证B密钥 addr[IN]:块地址 pKey[IN]:扇区密码 pSnr[IN]:卡片序列号,4字节 返 回:成功返回MI_OK */               
char RC522_PcdAuthState(u8 auth_mode,u8 addr,u8 *pKey,u8 *pSnr)
{ 
        
    char status;
    u8 unLen;
    u8 ucComMF522Buf[MAXRLEN];  //MAXRLEN 18(数组的大小)
	  
	  //验证模式+块地址+扇区密码+卡序列号 
    ucComMF522Buf[0]=auth_mode;		
    ucComMF522Buf[1]=addr;				
    memcpy(&ucComMF522Buf[2],pKey,6); //拷贝,复制
    memcpy(&ucComMF522Buf[8],pSnr,4); 
	 
    status=RC522_PcdComMF522(PCD_AUTHENT,ucComMF522Buf,12,ucComMF522Buf,&unLen);
    if((status!= MI_OK)||(!(RC522_ReadRawRC(Status2Reg)&0x08)))status = MI_ERR;
    return status;
}


/* 功 能:读取M1卡一块数据 参数说明: addr:块地址 p :读出的块数据,16字节 返 回:成功返回MI_OK */ 
char RC522_PcdRead(u8 addr,u8 *p)
{ 
        
    char status;
    u8 unLen;
    u8 i,ucComMF522Buf[MAXRLEN]; //18

    ucComMF522Buf[0]=PICC_READ;
    ucComMF522Buf[1]=addr;
    RC522_CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);
    status=RC522_PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);//通过RC522和ISO14443卡通讯
    if((status==MI_OK&&(unLen==0x90)))
    { 
        
		for(i=0;i<16;i++)
		{ 
        
				*(p +i)=ucComMF522Buf[i];
		}
    }
    else
	{ 
           
		status=MI_ERR;
	}
    return status;
}


/* 功 能:写数据到M1卡指定块 参数说明:addr:块地址 p :向块写入的数据,16字节 返 回:成功返回MI_OK */                  
char RC522_PcdWrite(u8 addr,u8 *p)
{ 
        
    char status;
    u8 unLen;
    u8 i,ucComMF522Buf[MAXRLEN]; 
    
    ucComMF522Buf[0]=PICC_WRITE;// 0xA0 //写块
    ucComMF522Buf[1]=addr;      //块地址
    RC522_CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);
 
    status=RC522_PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);

    if((status!= MI_OK)||(unLen != 4)||((ucComMF522Buf[0]&0x0F)!=0x0A))
	{ 
        
		status = MI_ERR;
	}
		
    if(status==MI_OK)
    { 
        
        for(i=0;i<16;i++)//向FIFO写16Byte数据 
        { 
            
        	ucComMF522Buf[i]=*(p +i);   
        }
        RC522_CalulateCRC(ucComMF522Buf,16,&ucComMF522Buf[16]);
        status = RC522_PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,18,ucComMF522Buf,&unLen);
        if((status != MI_OK)||(unLen != 4)||((ucComMF522Buf[0]&0x0F)!=0x0A))
		{ 
           
			status = MI_ERR;   
		}
    }
    return status;
}


/* 功 能:命令卡片进入休眠状态 返 回:成功返回MI_OK */
char RC522_PcdHalt(void)
{ 
        
    u8 status;
    u8 unLen;
    u8 ucComMF522Buf[MAXRLEN]; //MAXRLEN==18
	status=status;
    ucComMF522Buf[0]=PICC_HALT;
    ucComMF522Buf[1]=0;
    RC522_CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);
    status=RC522_PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);
    return MI_OK;
}


/* 功 能:用MF522计算CRC16函数 参 数: *pIn :要读数CRC的数据 len:-数据长度 *pOut:计算的CRC结果 */
void RC522_CalulateCRC(u8 *pIn ,u8 len,u8 *pOut )
{ 
        
    u8 i,n;
    RC522_ClearBitMask(DivIrqReg,0x04);  //CRCIrq = 0 
    RC522_WriteRawRC(CommandReg,PCD_IDLE);
    RC522_SetBitMask(FIFOLevelReg,0x80); //清FIFO指针
    
  //向FIFO中写入数据 
	for(i=0;i<len;i++)
	{ 
          
		RC522_WriteRawRC(FIFODataReg,*(pIn +i));  //开始RCR计算
	}
	
	RC522_WriteRawRC(CommandReg,PCD_CALCCRC);   //等待CRC计算完成 
	i=0xFF;
	do 
	{ 
        
		n=RC522_ReadRawRC(DivIrqReg);
		i--;
	}
    while((i!=0)&&!(n&0x04));//CRCIrq = 1
	  
	//读取CRC计算结果 
	pOut[0]=RC522_ReadRawRC(CRCResultRegL);
	pOut[1]=RC522_ReadRawRC(CRCResultRegM);
}


/* 功 能:复位RC522 返 回:成功返回MI_OK */
char RC522_PcdReset(void)
{ 
        
	gpio_set_value(EXYNOS4_GPB(4), 1);   //PF1写1
    RC522_Delay(10);
	gpio_set_value(EXYNOS4_GPB(4), 0);   //PF1清0
    RC522_Delay元器件数据手册IC替代型号,打造电子元器件IC百科大全!
          

相关文章