MFRC522模块测试

前言

        早在很久之前就接触过了MFRC522这个模块了,记得还想还是大二上学期的时候,在那一个暑假,我正在学STC8单片机,一个星期天,闲来无事就研究了一下MFRC522模块,当时算是大概了解了一下, 也都通信了,实现了钱包功能,能够扣款和充值。然后就搁置了,没想到,今天又用上了,在使用的时候发现有点问题,以前学的东西都又还回去了。翻博客的时候没有找到,但是在资料中找到了这篇文章,应该是以前写的,但是忘记上传的,今天弄好程序之后,就简单的上传一下吧,也算是为以后做准备。

一、基本了解

在这里插入图片描述
我看了一遍之后,发现并没有什么特别有用的信息。
PCD是接近式卡(也就是模块)。PICC是接近式耦合设备(M1卡)。在通信过程中实际上是使用PCD命令控制RC522发出PICC命令与卡进行交互。
通俗点说就是模块和M1卡进行通信,那我们就明白了方向,操作模块是一部分,操作M1卡是一部分.

二、M1卡和模块的通信流程

MF_RC522初始化—寻卡—防冲突—选卡—对要操作的块所在的扇区进行密码验证—操作卡(读、写、加值、减值、转移、恢复)在这里插入图片描述

三、RC522模块

就是操作一些命令集和寄存器
这里随便贴一些:
在这里插入图片描述
在这里插入图片描述
在手册里面都有,直接抄就行了。还有一些基本的函数
寻卡 ——防冲突——选卡——验证密码等都是网上流传的经典,直接拿来用就行。
在最后我会给出程序代码的连接。这里就不贴了,太占地方。

四、M1卡

1.主要指标

1容量为 8K 位 EEPROM
2分为 16 个扇区,每个扇区为4块,每块16个字节,以块为存取单位
3每个扇区有独立的一组密码及访问控制
4每张卡有唯一序列号,为32 位,没有重复的两张Mifare卡
5具有防冲突机制,支持多卡操作
6工作频率:13.56MHZ
7通信速率:106KBPS
8读写距离:10cm以内(与读写器有关)
这里要了解的是M1卡的容量是8Kb也就是1KB 共分为16个扇区,每个扇区4块,每块16B,每个扇区的4块分为0-4块,总共64块,总的容量也可按绝对地址编号分为0-63,绝对地址0块用于存放厂商代码,已固化,不可更改。块0-2为数据块,块3为控制块。

2.块3结构:

在这里插入图片描述

3.密码

出厂值:AB的密码都是FF FF FF FF FF FF 六个字节的FF默认控制FF078069
在这里插入图片描述

每个扇区的密码和存取控制都是独立的,可以根据实际需求设定各自的密码以及存取控制。存取控制为四个字节,共32位,扇区中的每个块(包括数据块和控制块)的存取条件是由密码和存取控制共同决定的,在存取控制的每个块都有相应的三个控制位,定义如下:
块0:C10 C20 C30
块1:C11 C21 C31
块2:C12 C22 C32
块3:C13 C23 C33
三个控制位以正和反两种形式存在于存取控制字节中,决定了该块的访问权。
如进行减值操作必须验证 KEYA,进行加值操作必须验证 KEYB,等等

存取控制(4 字节,其中字节 9 为备用字节)这里以默认控制字和密码为例:

在这里插入图片描述
从图上可以看出,默认密码对块0块1块2都是000 对块三是001.
数据块(块0块1块2)的存取控制表:
在这里插入图片描述
由图可知,在默认情况下 ,数据块验证密码A或密码B正确后可执行所有操作。
控制块的存取控制表:

在这里插入图片描述
默认情况下001 对密码A任何情况下都不可读,在验证密码A或密码B正确后可进行其他所有操作。

4.M1卡命令

M1卡也有一些命令:
在这里插入图片描述
还有一些操作:写一个块,读一个块,扣款和充值,备份钱包等
这些操作也在代码里。也就不贴了。

五、STC8A8K调试

1.调试

以下操作都是在STC8A8K开发板下执行:

下面说一些我在调试的时候遇到的问题:

1.模块在使用的过程中,一会可以检测到,一会就检测不到?

可能是模块供电的电压不够,换个供电电源试试。

2.在写卡和钱包同时操作的时候,不能够操作钱包?

要注意每次进行卡片操作的时候都要进行一遍基本操作。
我已经打包成了一个函数,方便调用。

/*********************************
 * @函数名:MFRC522_Base
 * @描  述:通信基础函数
 * @说  明:
 * @参  数:无 
 * @返回值:成功返回MI_OK
 *********************************/
char  MFRC522_Base()
{
   char   status;
    status = MFRC522_Find_Card(PICC_Request_all, RC522_Receive_Buffer);//寻全部卡
		 if (status == MI_OK) //寻卡成功
         {    
			   status = MFRC522_Avoid_Collision(RC522_Receive_Buffer);//防冲撞
                if (status == MI_OK) //防冲撞成功
				{
				    status = MFRC522_Select_Card(RC522_Receive_Buffer);//选定卡片
					if (status == MI_OK) //选卡成功
					{
					   status = MFRC522_Verify_Password(PICC_Auth_1b, 7, DefaultKey, RC522_Receive_Buffer);//验证卡片密码
//					    if (status == MI_OK)//验证密码成功
//						{
//						  return status; //status = MI_OK //都成功,可以继续下一步
//						}
					}
				}
		 }
  return status; //status = MI_OK //都成功,可以继续下一步		

3.在给钱包初始化数值的时候,验证密码正确,写入失败?

先理解钱包的数据块的结构,

M1卡的某一块写为如下格式,则该块为钱包,可接收扣款和充值命令
4字节金额(低字节在前)+4字节金额取反+4字节金额+1字节块地址+1字节块地址取反+1字节块地址+1字节块地址取反

所以可得,在写入数据的时候,要按格式写入,如下:

uchar code init_Wallet[16]=
{0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x04,~0x04,0x04,~0x04};

4.在进行充值或扣款的过程中,用串口打印的数据含有abcdef,怎样解决?

这是因为用串口打印的数据时16进制,所以打印的数据是含有abcd的。
解决:写一个16进制转化为10进制的函数:

   /*********************************
 * @函数名:Transform_16_10
 * @描  述:16进制装换成10进制
 * @说  明:
 * @参  数:num 要转换的数据
 * @返回值:无 
 *********************************/
uint Transform_16_10(uint  num)
{
	uint temp,result;
	temp = num&0x000f;//获取最后一位 
	result = temp;	
	temp = (num>>4)&0x000f;//获取倒数第二位
	result += temp*16;	
	temp = (num>>8)&0x000f;
	result += temp*16*16;	
	temp = (num>>12)&0x000f;//获取最高位
	result += temp*16*16*16; //最后的结果
	return  result; //十六进制转换为十进制
}

我这里是转化的16位的,只取钱包的前两个字节,65535 ,我觉得足够用。

再问:我转化之后用printf(“余额为%hx元”,money);输出结果为什么还是不对?

这是因为 h是表示16位,但是x是表示的16进制的无符号数,要想输出一个整数,不妨试试printf(“余额为%hd元”,money); //输出10进制

5.我想让钱包在充值的时候充值个5毛钱的,怎么办?

可以把钱包的前两个字节00ff 转化成10进制255 我们用最后一位表示5表示小数,前两位表示整数,只需要在输出的时候printf(“余额为%.1f元”,num/10.0); //输出10进就可以啦

6.在操作充值和扣款的过程中一旦数比较大就出错,数小就成功?

这是因为我在操作的时候,每次扣款和充值的数组

Add_Reduce_Wallet[0] = Key_Operate_Value;//要充值的金额
status=MFRC522_Operate_Wallet(com,block,Add_Reduce_Wallet);//进行充值和扣款功能

这两句就操作了,可以看出,这里的Key_Operate_Value虽然是uint型的,但是你写入的只是一个字节,Add_Reduce_Wallet[0] 所以你输入的数不能大于25.5,。

下面我贴几个自己封装的函数:
读出钱包的数据,并转化成实际的金额

/*********************************
 * @函数名:MFRC522_Read_Wallet
 * @描  述:读出钱包的金额
 * @说  明:
 * @参  数:block 钱包的位置(第几块)
 * @返回值:成功返回MI_OK
 *********************************/
char MFRC522_Read_Wallet(uchar block)
{
	uchar status = MI_ERR;
	uint money; //最后的结果
	float num;
    status = MFRC522_Read_M1_Data(block, RC522_Receive_Buffer);//读块
	if(status == MI_OK) //读数据成功
	{
	    //一个块中的16个字节的前4个字节是金额
//		money = RC522_Receive_Buffer[3]; //最高位
//		money <<= 8;//扩大8倍
//		money += RC522_Receive_Buffer[2]; 
//		money <<= 8;//扩大8倍
		//32位太大,这里只取16位
		money = RC522_Receive_Buffer[1];
		money <<= 8;//扩大8倍
		money += RC522_Receive_Buffer[0]; 
		num = Transform_16_10(money);//把十六进制转换为10进制
	//	printf("余额为%hd元",money); //输出10进制	
		printf("余额为%.1f元",num/10.0); //输出10进制	
		putchar_End();
	}
	return status;
}

钱包充值和扣款,只需调用这一个函数

/*********************************
 * @函数名:MFRC522_Wallet_Add_Reduce_Money
 * @描  述:钱包充值和扣款
 * @说  明:
 * @参  数: com 是充值还是扣款 命令 PICC_Decrement 扣款 PICC_Increment 充值 block 钱包的位置(第几块)
 * @返回值:无
 *********************************/
char MFRC522_Wallet_Add_Reduce_Money(uchar com,uchar block)
{
	uchar status;
        status = MFRC522_Base();//寻卡-防冲突-选卡-验证密码
		if(status == MI_OK)
		 {
				//Send_String("请输入要充值的金额:\n");
			    Add_Reduce_Wallet[0] = Key_Operate_Value%256;//要充值的金额
			    Add_Reduce_Wallet[1] = Key_Operate_Value/256;//高位
			   status = MFRC522_Operate_Wallet(com,block,Add_Reduce_Wallet);//进行充值和扣款功能
			   if(status == MI_OK) //充值成功 或扣款成功
			   {
				   if(com == PICC_Decrement) //扣款
					   Send_String("扣款成功\n");
				   else
				       Send_String("充值成功\n");
			      status = MFRC522_Read_Wallet(4); //读出钱包的金额  //读出成功会自动打印余额
//				   if(status != MI_OK) //如果不成功 
//				   { 
//					   while(!(status==MI_OK)) //一直读  直到读出成功
//				         status = MFRC522_Read_Wallet(4); //读出钱包的金额  //读出成功会自动打印余额
//				   }
			    }
			 }
		return status;
}

确保充值和扣款只执行一次

/*********************************
 * @函数名:Ensure_Once_Execute
 * @描  述:确保一次执行 扣款和充值只执行一次
 * @说  明:
 * @参  数:无 
 * @返回值:无 
 *********************************/
void Ensure_Once_Execute(uchar com,uchar block)
{
	        uchar status;
				status = MI_ERR;  //第一次初始化错误 ,确保至少执行一次
			   while(status!=MI_OK) //不成功 ,进去 确保有一次是成功的 
			   {
			      status = MFRC522_Wallet_Add_Reduce_Money(com,block); //扣款 ,第4块为钱包
			   }
}

在连接中,我会给出两个代码,一个是测试版,一个是实用版。测试版就是对模块进行测试的,所有的信息都在主函数里,寻卡,防冲撞 ,选卡,验证密码,读卡,写卡,充值等都是一步一步执行的,适合于新手。在调试的时候把后面的注释掉,前一步操作成功,再执行后一步,直到最后成功。程序中每一步都有串口打印提示信息,方便调试。
在这里插入图片描述
在这里插入图片描述
实用版就是对钱包的操作,充值和扣款.不多说,直接上图.

2.实验界面

初始界面
在这里插入图片描述
扣款
在这里插入图片描述
在这里插入图片描述
充值:
在这里插入图片描述
在这里插入图片描述

六、STM32调试

今天是4月22日早上8点钟,今天也是学校运动会的日子,我们大三既不出观众也不上课,nice,今天7点醒的,7点15起来的,可能昨天实在是太困了吧。

闲话少说,开始搞。

关于STM32的调试,我昨天从STC8移植了过来,中间碰到了一点问题,这里简单说明一下。

1.定义数组

uchar RC522_Receive_Buffer[20]; //接收数据的数组    

uchar  DefaultKey[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; //默认AB密码
//钱包初始化:
uchar Init_Wallet[16] = {0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x04,~0x04,0x04,~0x04};
/*M1卡的某一块写为如下格式,则该块为钱包,可接收扣款和充值命令
4字节金额(低字节在前)+4字节金额取反+4字节金额+1字节块地址+1字节块地址取反+1字节块地址+1字节块地址取反 */
uchar  Add_Reduce_Wallet[4]  = {0x00,0,0,0}; //低字节在前

从上面我们可以知道,M1卡的默认密码是6个FF,我们从上面也可以知道控制权限,读写权限。基本上都是验证A密码或B密码之后就可以执行一切操作。
DefaultKey:默认的密码 AB都一样
Init_Wallet:初始化钱包数据,可以把某一块作为一个钱包使用
Add_Reduce_Wallet:这个存放的是要扣款(/充值)的金额数据

2.操作流程

1.先初始化一个钱包

我们如果要使用钱包功能,那么我们就需要把M1卡的某一块作为一个钱包使用,即我们需要往这个块写入指定的信息格式
函数已经封装好,我们在调用的时候只需要执行下面一条语句:

MFRC522_Init_Wallet(0x04); // 把第4块作为钱包使用

要注意这一句话只需要执行一次,并且后续也不需要再执行。

关于这个初始化钱包函数,我们可以修改Init_Wallet这个数组的前4个字节来直接初始化一个金额,我这里默认是0,我们统一用下面这种方法进行写入。

2.往钱包中进行充值

在我们初始化钱包之后我们还需要对钱包中的金额进行充值,我们一般初始化一个比较大的金额进去,比如下面这条语句:

Ensure_Once_Execute(PICC_Increment,0x04,10000);//充值100元

为什么我这里明明写的是10000,为什么注册却是100元呢,这是因为我们默认保留两位小数,所以呢整体要除以100,这一点大家一定要注意。

3.进行扣款

我们使用了钱包功能,那我们肯定要消费,由于我们的函数都封装好了,所以我们直接拿来使用就好了,只需要执行下面这一条指令:

 Ensure_Once_Execute(PICC_Decrement,0x04,5);//扣款 每次0.05元

这一句表示我们每次扣款5分钱,也就是说我们每次刷一次卡,就扣5分钱,这一个数值可以是不固定的,只需要把5换成一个变量,每次改变这个变量,那么每次刷卡扣钱都不一样了。

3.总结

基本流程大概就是这样,一般的基本操作我们也只需要调用这两个函数,实现对这两个函数的操作就可以任意的去充值和消费,
更多其他的功能欢迎大家前去扩展。

  • 3
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: MFRC522模块是一种射频芯片,主要用于与近距离通信中的射频卡片进行交互。在项目中,我们可以使用MFRC522模块读取和写入射频卡片的数据。下面是一个采用模拟SPI时序的MFRC522模块项目代码示例。 首先,我们需要包含MFRC522模块的库文件。在代码中添加如下语句: ``` #include <SPI.h> #include <MFRC522.h> ``` 接下来,我们需要定义MFRC522模块的引脚连接。MFRC522模块有多个引脚,包括RST(复位)、SDA(数据)、SCK(时钟)、MOSI(主输出从输入)、MISO(主输入从输出)。根据自己的连接方式,定义这些引脚的接口编号,示例如下: ``` #define RST_PIN 9 #define SDA_PIN 10 #define SCK_PIN 13 #define MOSI_PIN 11 #define MISO_PIN 12 ``` 然后,我们需要创建MFRC522对象,并初始化MFRC522模块。代码如下: ``` MFRC522 mfrc522(SDA_PIN, RST_PIN); void setup() { SPI.begin(); mfrc522.PCD_Init(); } ``` 在loop()函数中,我们可以使用MFRC522的函数进行射频卡片的读取和写入操作。例如,读取射频卡片的UID(唯一识别码)可以使用以下代码: ``` void loop() { if(mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) { Serial.print("UID tag :"); String content = ""; byte letter; for(byte i = 0; i < mfrc522.uid.size; i++) { Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? "0" : ""); Serial.print(mfrc522.uid.uidByte[i], HEX); content.concat(String(mfrc522.uid.uidByte[i] < 0x10 ? "0" : "")); content.concat(String(mfrc522.uid.uidByte[i], HEX)); } Serial.println(); Serial.print("Message : "); content.toUpperCase(); } } ``` 以上示例代码展示了如何初始化MFRC522模块并读取射频卡片的UID。通过这些示例代码,我们可以根据自己的实际需要进行进一步的读取和写入操作,实现更复杂的功能。 希望以上内容对于理解MFRC522模块项目代码示例有所帮助。 ### 回答2: MFRC522是一款常用于射频识别(RFID)应用的模块,常常用于身份识别、门禁系统等领域。它采用模拟SPI时序与主控板连接,下面是一个MFRC522模块项目代码示例: #include <MFRC522.h> #define SS_PIN 10 #define RST_PIN 9 MFRC522 mfrc522(SS_PIN, RST_PIN); //创建MFRC522实例 void setup() { Serial.begin(9600); //初始化串口通信 SPI.begin(); //初始化SPI mfrc522.PCD_Init(); //初始化MFRC522 } void loop() { //寻卡 if (!mfrc522.PICC_IsNewCardPresent() || !mfrc522.PICC_ReadCardSerial()) { return; } //获取卡片类型 MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); //验证卡片 if (piccType != MFRC522::PICC_TYPE_MIFARE_MINI && piccType != MFRC522::PICC_TYPE_MIFARE_1K && piccType != MFRC522::PICC_TYPE_MIFARE_4K) { Serial.println("卡片类型不正确"); return; } //获取卡号 String cardNumber = ""; for (byte i = 0; i < mfrc522.uid.size; i++) { cardNumber += (mfrc522.uid.uidByte[i] < 0x10 ? "0" : ""); cardNumber += String(mfrc522.uid.uidByte[i], HEX); } Serial.print("卡号:"); Serial.println(cardNumber); mfrc522.PICC_HaltA(); //挂起卡片 mfrc522.PCD_StopCrypto1(); //停止密码认证 delay(500); } 这段代码用于实现MFRC522模块的基本功能:寻卡、验证卡片类型、获取卡号并输出。在setup()函数中,我们初始化了串口通信、SPI以及MFRC522模块。 在loop()函数中,首先通过mfrc522.PICC_IsNewCardPresent()和mfrc522.PICC_ReadCardSerial()来判断是否有新的卡片出现并读取其序列号。然后通过mfrc522.PICC_GetType()获取卡片的类型,并进行验证。 如果卡片类型满足条件,我们将通过遍历mfrc522.uid.uidByte数组获取卡片的卡号,并输出至串口。最后,通过mfrc522.PICC_HaltA()和mfrc522.PCD_StopCrypto1()来挂起卡片并停止密码认证。 以上就是一个使用MFRC522模块的项目代码示例,可以根据实际需求进行修改和扩展。 ### 回答3: MFRC522模块是一种低功耗、高集成度的射频读卡器模块,广泛应用于电子门禁、身份验证、费用结算等领域。下面给出一个采用模拟SPI时序的MFRC522模块项目代码示例: ```c #include <MFRC522.h> #define RST_PIN 9 // 定义RST引脚连接的Arduino引脚 #define SS_PIN 10 // 定义SS引脚连接的Arduino引脚 MFRC522 mfrc522(SS_PIN, RST_PIN); // 创建一个MFRC522实例 void setup() { Serial.begin(9600); // 初始化串口通信 SPI.begin(); // 初始化SPI总线 mfrc522.PCD_Init(); // 初始化MFRC522模块 Serial.println("RFID测试开始"); } void loop() { if ( ! mfrc522.PICC_IsNewCardPresent()) { // 检测是否有新的RFID卡片出现 return; } if ( ! mfrc522.PICC_ReadCardSerial()) { // 读取RFID卡片序列号 return; } Serial.print("卡片 UID:"); for (byte i = 0; i < mfrc522.uid.size; i++) { // 打印卡片序列号 Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "); Serial.print(mfrc522.uid.uidByte[i], HEX); } Serial.println(); mfrc522.PICC_HaltA(); // 暂停卡片操作 delay(1000); // 延时一段时间 } ``` 上述代码使用了MFRC522库,该库封装了MFRC522模块的一些基本功能。在setup()函数中,初始化了串口通信、SPI总线和MFRC522模块。在loop()函数中,首先判断是否有新的RFID卡片出现,然后读取RFID卡片序列号,并通过串口将序列号打印出来,最后暂停卡片操作,延时一段时间。 需要注意的是,该代码示例使用SPI通信协议与MFRC522模块进行通信,模拟SPI时序是指将SS引脚接到模块上,RST引脚接到模块上。具体的引脚连接和时序设置需要根据实际电路进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值