文章目录

  • 前言
  • 一、基本了解
  • 二、M1卡和模块的通信流程
  • 三、RC522模块
  • 四、M1卡
  • 1.主要指标
  • 2.块3结构:
  • 3.密码
  • 4.M1卡命令
  • 五、STC8A8K调试
  • 1.调试
  • 1.模块在使用的过程中,一会可以检测到,一会就检测不到?
  • 2.在写卡和钱包同时操作的时候,不能够操作钱包?
  • 3.在给钱包初始化数值的时候,验证密码正确,写入失败?
  • 4.在进行充值或扣款的过程中,用串口打印的数据含有abcdef,怎样解决?
  • 5.我想让钱包在充值的时候充值个5毛钱的,怎么办?
  • 6.在操作充值和扣款的过程中一旦数比较大就出错,数小就成功?
  • 2.实验界面
  • 六、STM32调试
  • 1.定义数组
  • 2.操作流程
  • 1.先初始化一个钱包
  • 2.往钱包中进行充值
  • 3.进行扣款
  • 3.总结


前言

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

一、基本了解

rfid模块 android RFID模块网关检测测试_MFRC522


我看了一遍之后,发现并没有什么特别有用的信息。

PCD是接近式卡(也就是模块)。PICC是接近式耦合设备(M1卡)。在通信过程中实际上是使用PCD命令控制RC522发出PICC命令与卡进行交互。

通俗点说就是模块和M1卡进行通信,那我们就明白了方向,操作模块是一部分,操作M1卡是一部分.

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

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

rfid模块 android RFID模块网关检测测试_MFRC522_02

三、RC522模块

就是操作一些命令集和寄存器

这里随便贴一些:

rfid模块 android RFID模块网关检测测试_rfid模块 android_03


rfid模块 android RFID模块网关检测测试_MFRC522_04


在手册里面都有,直接抄就行了。还有一些基本的函数

寻卡 ——防冲突——选卡——验证密码等都是网上流传的经典,直接拿来用就行。

在最后我会给出程序代码的连接。这里就不贴了,太占地方。

四、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结构:

rfid模块 android RFID模块网关检测测试_rfid模块 android_05

3.密码

出厂值:AB的密码都是FF FF FF FF FF FF 六个字节的FF默认控制FF078069

rfid模块 android RFID模块网关检测测试_MFRC522_06

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

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

rfid模块 android RFID模块网关检测测试_MFRC522_07


从图上可以看出,默认密码对块0块1块2都是000 对块三是001.

数据块(块0块1块2)的存取控制表:

rfid模块 android RFID模块网关检测测试_存取控制_08


由图可知,在默认情况下 ,数据块验证密码A或密码B正确后可执行所有操作。

控制块的存取控制表:

rfid模块 android RFID模块网关检测测试_rfid模块 android_09


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

4.M1卡命令

M1卡也有一些命令:

rfid模块 android RFID模块网关检测测试_rfid模块 android_10


还有一些操作:写一个块,读一个块,扣款和充值,备份钱包等

这些操作也在代码里。也就不贴了。

五、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块为钱包
			   }
}

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

rfid模块 android RFID模块网关检测测试_存取控制_11


rfid模块 android RFID模块网关检测测试_MFRC522_12


实用版就是对钱包的操作,充值和扣款.不多说,直接上图.

2.实验界面

初始界面

rfid模块 android RFID模块网关检测测试_MFRC522_13


扣款

rfid模块 android RFID模块网关检测测试_射频卡_14


rfid模块 android RFID模块网关检测测试_rfid模块 android_15


充值:

rfid模块 android RFID模块网关检测测试_存取控制_16


rfid模块 android RFID模块网关检测测试_存取控制_17

六、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.总结

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