[单片机]关于射频卡Mifare-S50卡操作原理

文章目录

  • 一、Mifare-S50卡简介
    • 1.主要指标
    • 2.存储结构
    • 3.简单操作
  • 二、代码(RC522-RFID程例)
    • 1.rfid_rc522.h
    • 2.rfid_rc522.c

一、Mifare-S50卡简介

1.主要指标

/*rfid_rc522.h Card-Information Define*/
//
//
//
//
//
//
//
//
//
//
//
//

2.存储结构

  1. Mifare-S50卡分为16个扇区每个扇区分,为4个块(块0、块1、块2、块3,其中块0~3是数据块,块3是密码)块。但实际上我们一般把所有块统一编址(16个扇区64个块按绝对地址编号为0到63)

[单片机]关于射频卡Mifare-S50卡操作原理_第1张图片
3. 扇区0的块0不能被使用,该块存储厂商代码,已经固化,不能被改变

  1. 其他扇区的块0~块2为数据块,用来存储数据

数据块可作为两种应用:

  • 用作一般的的数据存储,可以进行读、写操作。(充值卡、购电卡等)
  • 用作数据值,可以进行数据的初始化、加值、减值、读值操作。(水卡、洗衣卡等)

那么作为数据(数据块)和作为数据值(值块)有什么区别呢?

其实,无论块中的内容是什么,你都可以把他看成普通数据,即使它是一个值块。但是并不是任何数据都可以看成是值,因为值块有一个比较严格的格式要求。值块中值的长度为4个字节的补码,其表示的范围(-2147483648~2147483647),值块的存储格式如下:

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
ADDR addr ADDR addr value value value value VALUE VALUE VALUE VALUE value value value value

其中,ADDR是addr的取反,VALUE是value的取反,value是补码表示。(实际上水卡、洗衣卡就是这样的构造,但是!不要去试图去破解,违法犯罪啊!!!)

  1. 扇区的块3为密码块

块3又称为尾块,包含以下结构:

Byte[0] 1 2 3 4 5 6 7 8 9 A B C D E F
A0 A1 A2 A3 A4 A5 FF 07 80 69 B0 B1 B2 B3 B4 B5

新卡的出厂密码一般是密码A为A0A1A2A3A4A5,密码B为B0B1B2B3B4B5,或者密码A和密码B都是6个FF。存取控制用以设定扇区中各个块(包括控制块本身)的存取条件,这部分有点复杂,后续有机会继续介绍。

  1. 存取控制

前面已经说了,块分为数据块和密码块。Mifare-S50卡的操作需要严格的密码验证和权限限制。密码每一个扇区都有一个,位于扇区的尾块(块3)中,其中分为KeyA和KeyB,除了密码,尾块中还存储着对本扇区中其他数据块操作的权限。
[单片机]关于射频卡Mifare-S50卡操作原理_第2张图片从上图可以看出(这个图从Mifare-S50卡的技术手册上有列出):
C1C2C3=000:验证密码A或密码B后可以进行任何操作;
C1C2C3=111:无权限(即无法对卡片进行任何操作,卡片被冻结了);
C1C2C3=010和C1C2C3=101都是只读,如果对应的数据块写入的是一些可以给人看但不能改的基本信息,可以设为这两种模式;
C1C2C3=001时只能读和减值,电子钱包一般设为这种模式,比如用S50做的公交电子车票,用户只能查询或扣钱,不能加钱,充值的时候先改变控制位使卡片可以充值,充完值再改回来。

上图是数据块的权限,密码块的权限如下:
[单片机]关于射频卡Mifare-S50卡操作原理_第3张图片
密码A是永远也读不出来的,如果用户的数据块指定了验证密码A却忘了密码A,则密码A对应的数据块无法被操作(因为忘记了密码),但是还是可以操作密码B对应的数据块。
存取控制总是可以读出来的,只要别忘了密码A或密码B;
存取控制的写控制在设置时一定要小心,一旦弄成了“Never”,卡片就被冻结了,无法再被操作!
C1C2C3=001(出厂默认值)时最宽松,除了密码A不能读之外,验证了密码A其他读写操作都可以进行;
特别注意,当C1C2C3=000、C1C2C3=010和C1C2C3=001时,此时密码B是不会被使用到的,这时候密码B占据的6个字节可以提供给用户作为普通数据存储用,相当于每个扇区增加了6字节的用户可用存储容量。
由于卡片出厂的默认值C1C2C3=001。

S50的每个扇区有4个块,这四个块的存取控制是相互独立的,每个块需要3个bit,四个块共使用12个bit。在保存的时候,为了防止控制位出错,同时保存了这12个bit的反码,这样一个区的存储控制位在保存时共占用24bit的空间,正好是3个字节。我们前面说存取控制字有四个字节(区尾块的Byte6~Byte9),实际上只使用的Byte6、Byte7和Byte8,Byte9没有用,用户可以把Byte9作为普通存储空间使用。各块控制位存储格式如下:
[单片机]关于射频卡Mifare-S50卡操作原理_第4张图片
由于出厂时数据块控制位的默认值是C1C2C3=000,控制块的默认值是C1C2C3=001,而Byte9一般是69H,所以出厂的卡的控制字通常是FF078069H。

3.简单操作

Mifare-S50卡片的简单操作:寻卡 -> 防冲撞(获取序列号) -> 选卡 -> 验证密钥 -> 读/写操作

 *		dev_rc522.Search(0x52, (uint8_t *)dev_rc522.TagType);				//寻卡
 *		dev_rc522.Anticoll(dev_rc522.SerialNum);								//防冲撞,获取到序列号(N>=0)
 *
 *		dev_rc522.SelectCard(dev_rc522.SerialNum);							//选卡
 *		dev_rc522.Password(0x60, 20, keyA, dev_rc522.SerialNum);	//验证块20密钥
 *		dev_rc522.Write(20, data_for_write);										//写扇区-块20
 *		dev_rc522.Read(20, dev_rc522.Data);										//读扇区-块20

[单片机]关于射频卡Mifare-S50卡操作原理_第5张图片

二、代码(RC522-RFID程例)

附STM32F407例程(只列出rfid相关操作)
源码下载链接:https://download.csdn.net/download/weixin_39869569/11242479

1.rfid_rc522.h

/**
  ******************************************************************************
  * 
  * RFID-RC522射频模块驱动(stm32f407系列可用,其他暂不支持!)
  * 
  * 
  * 重要说明:
  *		1.本文件还在探索阶段,提供API调用和类C++的类调用方式!
  *		2.目前并未做太多的验证!- 2018.03.08 by seri_liang
  *
  * 版本修订:
  *		修改时间	版本号	修改内容
  *		2017-08-24	v1.0	开始构建本文件;
  *		2017-09-11	v2.0	加入C++类的概念,重构文件;
  *		2018-03-08	v3.0	修改宏New和Destroy(更名为Delete),使用自定义内存管理;
  *
  *	使用示例:
  *		调用过程:寻卡 -> 防冲撞(获取序列号) -> 选卡 -> 验证密钥 -> 读/写操作
  *		dev_rc522.Search(0x52, (uint8_t *)dev_rc522.TagType);		//寻卡
  *		dev_rc522.Anticoll(dev_rc522.SerialNum);					//防冲撞,获取到序列号(N>=0)
  *
  *		dev_rc522.SelectCard(dev_rc522.SerialNum);					//选卡
  *		dev_rc522.Password(0x60, 20, keyA, dev_rc522.SerialNum);	//验证块20密钥
  *		dev_rc522.Write(20, data_for_write);						//写扇区-块20
  *		dev_rc522.Read(20, dev_rc522.Data);							//读扇区-块20
  *
  * 程序出处:
  *		Copyright (C), 2017-2017, TYUT TSS-plan by SERI.LJI
  *		All rights reserved			
  *
  *
  *	Create  :2017年08月24日
  * Update  :2018年03月08日
  * Author  :梁宗南(SERI.LJI)
  * Email	:[email protected]
  * Blog    :liangzongnan.com
  * Tel		:157-3518-5808
  * QQ		:494089986
  * 
  ******************************************************************************
***/

#ifndef __RFID_RC522_H__
#define __RFID_RC522_H__

/*rfid_rc522.h Needed Library Define Define*/
#include "stm32f4xx.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"

#include "sys.h"
#include "delay.h"
#include "malloc_ext.h"

/*rfid_rc522.h Card-Information Define*/
//
//
//
//
//
//
//
//
//
//
//
//

/*rfid_rc522.h Easy-Command Define*/
#define GetCardType(pCardNum)		(uint16_t)(pCardNum[0]<<8 | pCardNum[1])
#define GetCardID(pIDNum)			(uint32_t)(pIDNum[0]<<24 | pIDNum[1]<<16 | pIDNum[2]<<8 | pIDNum[3])

#define New(objectType)				((objectType *)mymalloc(sizeof(objectType)))
#define Delete(object)				myfree(object)

#define	KeyCopy(dest, src)			memcpy(dest, src, 8)

/*rfid_rc522.h Control-Pin Define*/
//
//
//		STM32引脚说明
//		3.3V,不可接错!
//		PA.2,普通I/O
//		GND
//		PA.3,不用接、悬空即可!
//		PA.6,普通I/O
//		PA.7,普通I/O
//		PA.5,普通I/O
//		PA.4,普通I/O
//
//
#define RFID_RC522_M_PORT			GPIOB	//主管脚!
#define RFID_RC522_MOSI_PIN			5
#define RFID_RC522_MISO_PIN			6
#define RFID_RC522_RST_PIN			8

#define RFID_RC522_S_PORT			GPIOB	//辅管脚!
#define RFID_RC522_SCK_PIN			4
#define RFID_RC522_NSS_PIN			3


#define RC522_MOSI_HIGH()			GPIO_SetBits(RFID_RC522_M_PORT, (1<
#define RC522_RST_HIGH()			GPIO_SetBits(RFID_RC522_M_PORT, (1<
#define RC522_SCK_HIGH()			GPIO_SetBits(RFID_RC522_S_PORT, (1<
#define RC522_NSS_HIGH()			GPIO_SetBits(RFID_RC522_S_PORT, (1<

#define RC522_MOSI_LOW()			GPIO_ResetBits(RFID_RC522_M_PORT, (1<
#define RC522_RST_LOW()				GPIO_ResetBits(RFID_RC522_M_PORT, (1<
#define RC522_SCK_LOW()				GPIO_ResetBits(RFID_RC522_S_PORT, (1<
#define RC522_NSS_LOW()				GPIO_ResetBits(RFID_RC522_S_PORT, (1<

#define RC522_MISO_IN()				GPIO_ReadInputDataBit(RFID_RC522_M_PORT, (1<

/*rfid_rc522.h Command-Word Define*/
//MF522命令字
#define PCD_IDLE              		0x00	//取消当前命令
#define PCD_AUTHENT           		0x0E	//验证密钥
#define PCD_RECEIVE           		0x08	//接收数据
#define PCD_TRANSMIT          		0x04	//发送数据
#define PCD_TRANSCEIVE        		0x0C	//发送并接收数据
#define PCD_RESETPHASE        		0x0F	//复位
#define PCD_CALCCRC           		0x03	//CRC计算

//Mifare_One卡片命令字
#define PICC_REQIDL           		0x26	//寻天线区内未进入休眠状态
#define PICC_REQALL           		0x52	//寻天线区内全部卡
#define PICC_SElECTTAG				0x93	//选卡
#define PICC_ANTICOLL1        		0x93	//防冲撞1
#define PICC_ANTICOLL2        		0x95	//防冲撞2
#define PICC_AUTHENT1A        		0x60	//验证A密钥
#define PICC_AUTHENT1B        		0x61	//验证B密钥
#define PICC_READ             		0x30	//读块
#define PICC_WRITE            		0xA0	//写块
#define PICC_DECREMENT        		0xC0	//扣款
#define PICC_INCREMENT        		0xC1	//充值
#define PICC_RESTORE         	 	0xC2	//调块数据到缓冲区
#define PICC_TRANSFER         		0xB0	//保存缓冲区中数据
#define PICC_HALT             		0x50	//休眠

//MF522 FIFO长度定义
#define DEF_FIFO_LENGTH       		64		//FIFO size=64byte

/*rfid_rc522.h MF522-Register Define*/
//PAGE 0 <---------> Command and Status
#define     RFU00                 	0x00	//    
#define     CommandReg            	0x01	//    
#define     ComIEnReg             	0x02	//    
#define     DivlEnReg             	0x03	//    
#define     ComIrqReg             	0x04	//
#define     DivIrqReg             	0x05	//
#define     ErrorReg              	0x06	//    
#define     Status1Reg            	0x07	//  
#define     Status2Reg            	0x08	//    
#define     FIFODataReg           	0x09	//
#define     FIFOLevelReg          	0x0A	//
#define     WaterLevelReg         	0x0B	//
#define     ControlReg            	0x0C	//
#define     BitFramingReg         	0x0D	//
#define     CollReg               	0x0E	//
#define     RFU0F                 	0x0F	//

//PAGE 1 <---------> Command       
#define     RFU10                 	0x10	//
#define     ModeReg               	0x11	//
#define     TxModeReg             	0x12	//
#define     RxModeReg             	0x13	//
#define     TxControlReg          	0x14	//
#define     TxAutoReg             	0x15	//
#define     TxSelReg              	0x16	//
#define     RxSelReg              	0x17	//
#define     RxThresholdReg        	0x18	//
#define     DemodReg              	0x19	//
#define     RFU1A                 	0x1A	//
#define     RFU1B                 	0x1B	//
#define     MifareReg             	0x1C	//
#define     RFU1D                 	0x1D	//
#define     RFU1E                 	0x1E	//
#define     SerialSpeedReg        	0x1F	//

//PAGE 2 <---------> CFG    
#define     RFU20                 	0x20	//  
#define     CRCResultRegM         	0x21	//
#define     CRCResultRegL         	0x22	//
#define     RFU23                 	0x23	//
#define     ModWidthReg           	0x24	//
#define     RFU25                 	0x25	//
#define     RFCfgReg              	0x26	//
#define     GsNReg                	0x27	//
#define     CWGsCfgReg            	0x28	//
#define     ModGsCfgReg           	0x29	//
#define     TModeReg              	0x2A	//
#define     TPrescalerReg         	0x2B	//
#define     TReloadRegH           	0x2C	//
#define     TReloadRegL           	0x2D	//
#define     TCounterValueRegH     	0x2E	//
#define     TCounterValueRegL     	0x2F	//

//PAGE 3 <---------> TestRegister     
#define     RFU30                 	0x30	//
#define     TestSel1Reg           	0x31	//
#define     TestSel2Reg           	0x32	//
#define     TestPinEnReg          	0x33	//
#define     TestPinValueReg       	0x34	//
#define     TestBusReg            	0x35	//
#define     AutoTestReg           	0x36	//
#define     VersionReg            	0x37	//
#define     AnalogTestReg         	0x38	//
#define     TestDAC1Reg           	0x39	//  
#define     TestDAC2Reg           	0x3A	//
#define     TestADCReg            	0x3B	//   
#define     RFU3C                 	0x3C	//   
#define     RFU3D                 	0x3D	//   
#define     RFU3E                 	0x3E	//   
#define     RFU3F		  			0x3F	//

/*rfid_rc522.h MF522-Error Code Define*/
#define MI_OK						0
#define MI_NOTAGERR					(-1)
#define MI_ERR						(-2)

/*rfid_rc522.h Param Class Define*/
///卡片类型描述集合:
typedef struct
{
	uint32_t	UID;						//卡片序列号		
	uint16_t	TagType;					//卡片类型代码
	
	uint8_t		Size;						//卡片容量

	uint8_t		SerialNum[4];				//卡片序列号数组
	uint8_t		Data[16];					//卡片数据(只保留一块的数据,即16字节!)
	
	uint8_t		KeyA[8];					//卡片A秘钥
	uint8_t		KeyB[8];					//卡片B秘钥
	
	//以下是参考面向对象编程构建的函数列表!仅列举需要的!
	int8_t		(*Search)(uint8_t, uint8_t*);
	int8_t		(*Anticoll)(uint8_t*);
	uint8_t		(*SelectCard)(uint8_t*);   
	int8_t		(*Password)(uint8_t, uint8_t, uint8_t*, uint8_t*);

	int8_t		(*Read)(uint8_t, uint8_t*);
	int8_t		(*Write)(uint8_t, uint8_t*);
}_RFIDCardDeviceCtl;

extern uint8_t mifare_one_default_key[8];//Mifare-One卡缺省密钥

/*rfid_rc522.h Function Define*/
void RC522_InitConfig(void);//RC522控制引脚初始化

void RC522_AntennaON(void);//开启天线
void RC522_AntennaOFF(void);//关闭天线
int8_t RC522_Reset(void);//复位RC522
int8_t RC522_Halt(void);//命令卡片进入休眠状态


//以下是参考面向对象编程构建的函数列表!测试阶段!-- 2017.09.11
void RC522_StructInit(_RFIDCardDeviceCtl *myType);//实例化RFID对象

int8_t RC522_Search(uint8_t reqCode, uint8_t *pTagType);//寻卡
int8_t RC522_Anticoll(uint8_t *pSerialNum);//防冲撞
uint8_t RC522_SelectCard(uint8_t *pSerialNum);//选定卡片   
int8_t RC522_Password(uint8_t veriMode, uint8_t blockAddr, uint8_t *pKey, uint8_t *pSerialNum);//验证卡片密码

int8_t RC522_Read(uint8_t blockAddr, uint8_t *pData);//读取M1卡一块数据
int8_t RC522_Write(uint8_t blockAddr, uint8_t *pData);//写数据到M1卡一块

#endif
/* ******************** Copyright (C), 2017-2017, TYUT TSS-plan by SERI.LJI ******************** */

2.rfid_rc522.c

/**
  ******************************************************************************
  * 
  * RFID-RC522射频模块驱动(stm32f407系列可用,其他暂不支持!)
  * 
  * 
  * 重要说明:
  *		1.本文件还在探索阶段,提供API调用和类C++的类调用方式!
  *		2.目前并未做太多的验证!- 2018.03.08 by seri_liang
  *
  * 版本修订:
  *		修改时间	版本号	修改内容
  *		2017-08-24	v1.0	开始构建本文件;
  *		2017-09-11	v2.0	加入C++类的概念,重构文件;
  *		2018-03-08	v3.0	修改宏New和Destroy(更名为Delete),使用自定义内存管理;
  *
  *	使用示例:
  *		调用过程:寻卡 -> 防冲撞(获取序列号) -> 选卡 -> 验证密钥 -> 读/写操作
  *		dev_rc522.Search(0x52, (uint8_t *)dev_rc522.TagType);		//寻卡
  *		dev_rc522.Anticoll(dev_rc522.SerialNum);					//防冲撞,获取到序列号(N>=0)
  *
  *		dev_rc522.SelectCard(dev_rc522.SerialNum);					//选卡
  *		dev_rc522.Password(0x60, 20, keyA, dev_rc522.SerialNum);	//验证块20密钥
  *		dev_rc522.Write(20, data_for_write);						//写扇区-块20
  *		dev_rc522.Read(20, dev_rc522.Data);							//读扇区-块20
  *
  * 程序出处:
  *		Copyright (C), 2017-2017, TYUT TSS-plan by SERI.LJI
  *		All rights reserved			
  *
  *
  *	Create  :2017年08月24日
  * Update  :2018年03月08日
  * Author  :梁宗南(SERI.LJI)
  * Email	:[email protected]
  * Blog    :liangzongnan.com
  * Tel		:157-3518-5808
  * QQ		:494089986
  * 
  ******************************************************************************
***/

#include "rfid_rc522.h"

/* Mifare-One卡缺省密钥:*/
uint8_t mifare_one_default_key[8] = {255, 255, 255, 255, 255, 255, 255, 255};

/* 全局控制设定,作为模块使用时必须注意! */
#define MAX_REC_LEN					18
#define RC522_DELAY()				Delay_us(3)                 
         
/* 以下函数作用域在本函数中,有些函数未被使用,设置static会警告未使用函数!*/
static uint8_t RC522_SPIReadWriteByte(uint8_t TxData);//SPI通信
static uint8_t RC522_RegRead(uint8_t regAddr);//读RC522寄存器

static void RC522_RegWrite(uint8_t regAddr, uint8_t Value);//写RC522寄存器
static void RC522_SetBitMask(uint8_t regAddr, uint8_t Mask);//置RC522寄存器位
static void RC522_ClearBitMask(uint8_t regAddr, uint8_t Mask);//清RC522寄存器位

static void RC522_CalcCRC(uint8_t *pInData, uint8_t Length, uint8_t *pOutData);//进入CRC
static int8_t RC522_Communication(uint8_t Command, uint8_t *pInData, uint8_t inLength, uint8_t *pOutData, uint16_t *pOutLength);//通讯

/**
  * @brief	:RC522初始化
  * @note   :--引脚配置+模块复位+关闭天线!
  * @param	:void
  * @return	:void
  *
  * @data   :2017/08/24
  * @design :
  **/
void RC522_InitConfig(void)
{
	GPIO_InitTypeDef	gpio;
	
	RCC_AHB1PeriphClockCmd((1<<(((uint32_t)RFID_RC522_M_PORT - AHB1PERIPH_BASE)>>10)), ENABLE);//port clock enable!
	RCC_AHB1PeriphClockCmd((1<<(((uint32_t)RFID_RC522_S_PORT - AHB1PERIPH_BASE)>>10)), ENABLE);//port clock enable!
	
	
	gpio.GPIO_Pin	= (1<<RFID_RC522_MOSI_PIN)
					| (1<<RFID_RC522_RST_PIN);		
    gpio.GPIO_Mode  = GPIO_Mode_OUT;
    gpio.GPIO_Speed = GPIO_Speed_2MHz;//速度不能太快,2MHz适合!
    gpio.GPIO_OType = GPIO_OType_PP;
    gpio.GPIO_PuPd  = GPIO_PuPd_UP;	
    GPIO_Init(RFID_RC522_M_PORT, &gpio);//主管脚! 

	gpio.GPIO_Pin	= (1<<RFID_RC522_MISO_PIN);
    gpio.GPIO_Mode	= GPIO_Mode_IN;
    gpio.GPIO_PuPd  = GPIO_PuPd_UP;	
    GPIO_Init(RFID_RC522_M_PORT, &gpio);//主管脚!    
	
	gpio.GPIO_Pin	= (1<<RFID_RC522_SCK_PIN)
					| (1<<RFID_RC522_NSS_PIN);		
	gpio.GPIO_Mode	= GPIO_Mode_OUT;
	gpio.GPIO_OType	= GPIO_OType_PP;
	gpio.GPIO_PuPd	= GPIO_PuPd_UP;
	gpio.GPIO_Speed	= GPIO_Speed_2MHz;
	GPIO_Init(RFID_RC522_S_PORT, &gpio);//辅管脚!
	
	
	RC522_Reset();
	RC522_AntennaOFF();
	Delay_ms(50);//保证天线关闭后不会立即被开启!至少大于1ms!
}

/**
  * @brief	:开启天线
  * @note   :--每次启动或关闭天险发射之间应至少有1ms的间隔!
  * @param	:void
  * @return	:void
  *
  * @data   :2017/08/24
  * @design :
  **/
void RC522_AntennaON(void)
{
    uint8_t tmp;
	
    tmp = RC522_RegRead(TxControlReg);
    if(!(tmp & 0x03)){
        RC522_SetBitMask(TxControlReg, 0x03);
    }
}

/**
  * @brief	:关闭天线
  * @note   :--
  * @param	:void
  * @return	:void
  *
  * @data   :2017/08/24
  * @design :
  **/
void RC522_AntennaOFF(void)
{
    RC522_ClearBitMask(TxControlReg, 0x03);
}

/**
  * @brief	:复位RC522
  * @note   :--
  * @param	:void
  * @return	:int8_t, 成功返回MI_OK
  *
  * @data   :2017/08/24
  * @design :
  **/
int8_t RC522_Reset(void)
{
    RC522_RST_HIGH();
    Delay_ms(10);
    RC522_RST_LOW();
    Delay_ms(10);
    RC522_RST_HIGH();
    Delay_ms(10);
    RC522_RegWrite(CommandReg, PCD_RESETPHASE);
	while(RC522_RegRead(CommandReg) & 0x10);
    Delay_ms(200);
	
	//和Mifare卡通讯,CRC初始值0x6363
    RC522_RegWrite(ModeReg, 0x3D);	
    Delay_ms(5);
    RC522_RegWrite(TReloadRegL, 30);           
	Delay_ms(5);
    RC522_RegWrite(TReloadRegH, 0);
	Delay_ms(5);
    RC522_RegWrite(TModeReg, 0x8D);
	Delay_ms(5);
    RC522_RegWrite(TPrescalerReg,0x3E);
	Delay_ms(5);
	RC522_RegWrite(TxAutoReg, 0x40);
	
    return MI_OK;
}

/**
  * @brief	:命令卡片进入休眠状态
  * @note   :--
  * @param	:void
  * @return	:int8_t, 成功返回MI_OK
  *
  * @data   :2017/08/24
  * @design :
  **/
int8_t RC522_Halt(void)
{
    uint8_t ucComRC522Buf[MAX_REC_LEN]; 
    uint16_t length;

    ucComRC522Buf[0] = PICC_HALT;
    ucComRC522Buf[1] = 0;
    RC522_CalcCRC(ucComRC522Buf, 2, &ucComRC522Buf[2]);
 
    RC522_Communication(PCD_TRANSCEIVE, ucComRC522Buf, 4, ucComRC522Buf, &length);
    return MI_OK;
}

/**
  * @brief	:寻卡
  * @note   :--
  * @param	:reqCode  ,寻卡方式
                0x52   = 寻感应区内所有符合14443A标准的卡
                0x26   = 寻未进入休眠状态的卡
			 *pTagType,卡片类型代码
                0x4400 = Mifare_UltraLight
                0x0400 = Mifare_One(S50)
                0x0200 = Mifare_One(S70)
                0x0800 = Mifare_Pro(X)
                0x4403 = Mifare_DESFire
  * @return	:int8_t, 成功返回MI_OK
  *
  * @data   :2017/08/24
  * @design :
  **/
int8_t RC522_Search(uint8_t reqCode, uint8_t *pTagType)
{
	int8_t status;  
	uint8_t ucComRC522Buf[MAX_REC_LEN]; 
	uint16_t length;
	
	RC522_ClearBitMask(Status2Reg, 0x08);//寄存器包含接收器和发送器和数据模式检测器的状态标志
	RC522_RegWrite(BitFramingReg, 0x07);//不启动数据发送
	RC522_SetBitMask(TxControlReg, 0x03);//TX1、TX2输出信号将传递经发送数据调制的13.56MHz的能量载波信号

	ucComRC522Buf[0] = reqCode;//设定寻卡方式

	//通过RC522发送reqCode命令,并接收返回数据,存到ucComRC522Buf[]中! 
	status = RC522_Communication(PCD_TRANSCEIVE, ucComRC522Buf, 1, ucComRC522Buf, &length);//读卡,获取卡片类型
	if((status == MI_OK) && (length == 0x10))//这个为啥是0x10?因为是2个字节共16bit!
	{    
	   *(pTagType+0) = ucComRC522Buf[0];
	   *(pTagType+1) = ucComRC522Buf[1];//获取卡类型
	}else{  
	   status = MI_ERR;
	}
	return status;
}

/**
  * @brief	:防冲撞
  * @note   :--实际读出5个字节,包含1字节校验,本函数只返回通过
  * @param	:*pSerialNum, 卡片序列号,4字节
  * @return	:int8_t, 成功返回MI_OK
  *
  * @data   :2017/08/24
  * @design :
  **/
int8_t RC522_Anticoll(uint8_t *pSerialNum)
{
    int8_t status;
    uint8_t i;
	uint8_t serial_num_check=0;
    uint8_t ucComRC522Buf[MAX_REC_LEN]; 
    uint16_t length;
    
    RC522_ClearBitMask(Status2Reg, 0x08);//寄存器包含接收器和发送器和数据模式检测器的状态标志
    RC522_RegWrite(BitFramingReg, 0x00);//不启动数据发送,接收的LSB位存放在位0,接收到的第二位放在位1,定义发送的最后一个字节的位数为8
    RC522_ClearBitMask(CollReg, 0x80);//所有接收的位在冲突后将被清除
 
    ucComRC522Buf[0] = PICC_ANTICOLL1;
    ucComRC522Buf[1] = 0x20;

    status = RC522_Communication(PCD_TRANSCEIVE, ucComRC522Buf, 2, ucComRC522Buf, &length);
    if(status == MI_OK)
    {
    	 for(i=0; i<4; i++)
         {   
             *(pSerialNum+i)   = ucComRC522Buf[i];
             serial_num_check ^= ucComRC522Buf[i];
         }
		 
         if(serial_num_check != ucComRC522Buf[i])//返回四个字节,最后一个字节为校验位!
		 {
			 status = MI_ERR;
		 }
    }
    
    RC522_SetBitMask(CollReg, 0x80);
    return status;
}

/**
  * @brief	:对创建的RFID对象进行实例化
  * @note   :--
  * @param	:_RFIDCardDeviceCtl *, 卡片类型描述集合结构体
  * @return	:void
  *
  * @data   :2017/09/11
  * @design :
  **/
void RC522_StructInit(_RFIDCardDeviceCtl *myType)
{	
	myType->Search		= RC522_Search;
	myType->Anticoll	= RC522_Anticoll;
	myType->SelectCard	= RC522_SelectCard;
	myType->Password	= RC522_Password;
	
	myType->Read		= RC522_Read;
	myType->Write		= RC522_Write;		
}

/**
  * @brief	:选定卡片
  * @note   :--
  * @param	:*pSerialNum, 卡片序列号,4字节!
  * @return	:uint8_t, 返回卡片容量
  *
  * @data   :2017/08/24
  * @design :
  **/
uint8_t RC522_SelectCard(uint8_t *pSerialNum)
{
    int8_t status;
	int8_t size;
    uint8_t i;
    uint8_t ucComRC522Buf[MAX_REC_LEN]; 
	uint16_t length;
    
	ucComRC522Buf[0] = PICC_SElECTTAG;
    ucComRC522Buf[1] = 0x70;
    ucComRC522Buf[6] = 0;
    for(i=0; i<4; i++)
    {
    	ucComRC522Buf[i+2] = *(pSerialNum+i);
    	ucComRC522Buf[6]  ^= *(pSerialNum+i);
    }
    RC522_CalcCRC(ucComRC522Buf, 7, &ucComRC522Buf[7]);//计算CRC装填至ucComRC522Buf[7]
  
    RC522_ClearBitMask(Status2Reg, 0x08);//寄存器包含接收器和发送器和数据模式检测器的状态标志

    status = RC522_Communication(PCD_TRANSCEIVE, ucComRC522Buf, 9, ucComRC522Buf, &length);
    if((status == MI_OK) && (length == 0x18))
    {   
		size = ucComRC522Buf[0];
	}else{   
		size = 0;    
	}
    return size;
}

/**
  * @brief	:验证卡片密码
  * @note   :--
  * @param	:veriMode	, 密码验证模式
                 0x60 = 验证A密钥
                 0x61 = 验证B密钥 
			 blockAddr	, 块地址
			 *pKey		, 密码
			 *pSerialNum, 卡片序列号,4字节
  * @return	:int8_t, 成功返回MI_OK
  *
  * @data   :2017/08/24
  * @design :
  **/
int8_t RC522_Password(uint8_t veriMode, uint8_t blockAddr, uint8_t *pKey, uint8_t *pSerialNum)
{
    int8_t status;
    uint8_t i;
	uint8_t ucComRC522Buf[MAX_REC_LEN]; 
    uint16_t length;

    ucComRC522Buf[0] = veriMode;
    ucComRC522Buf[1] = blockAddr;
    
	for(i=0; i<6; i++)
	{    
		ucComRC522Buf[i+2] = *(pKey+i);   
	}
    
	for(i=0; i<6; i++)
	{    
		ucComRC522Buf[i+8] = *(pSerialNum+i);   
	}
    
    status = RC522_Communication(PCD_AUTHENT, ucComRC522Buf, 12, ucComRC522Buf, &length);
    if((status != MI_OK) || (!(RC522_RegRead(Status2Reg) & 0x08))) 
	{
		status = MI_ERR; 
	}
    return status;
}

/**
  * @brief	:读取M1卡一块数据
  * @note   :--
  * @param	:blockAddr, 块地址
			 *pData   , 读出的数据,16字节
  * @return	:int8_t, 成功返回MI_OK
  *
  * @data   :2017/08/24
  * @design :
  **/
int8_t RC522_Read(uint8_t blockAddr, uint8_t *pData)
{
    int8_t status;
    uint8_t i;
	uint8_t ucComRC522Buf[MAX_REC_LEN]; 
    uint16_t length;

    ucComRC522Buf[0] = PICC_READ;
    ucComRC522Buf[1] = blockAddr;
    RC522_CalcCRC(ucComRC522Buf, 2, &ucComRC522Buf[2]);
   
    status = RC522_Communication(PCD_TRANSCEIVE, ucComRC522Buf, 4, ucComRC522Buf, &length);
    if((status == MI_OK) && (length == 0x90))
    {
        for(i=0; i<16; i++) 
		{
			*(pData+i) = ucComRC522Buf[i]; 
		}			
    }else{ 
		status = MI_ERR;   
	}
    return status;
}

/**
  * @brief	:写数据到M1卡一块
  * @note   :--
  * @param	:blockAddr, 块地址
			 *pData   , 写入的数据,16字节
  * @return	:int8_t, 成功返回MI_OK
  *
  * @data   :2017/08/24
  * @design :
  **/
int8_t RC522_Write(uint8_t blockAddr, uint8_t *pData)
{
    int8_t status;
    uint8_t i;
	uint8_t ucComRC522Buf[MAX_REC_LEN]; 
    uint16_t length;
    
    ucComRC522Buf[0] = PICC_WRITE;
    ucComRC522Buf[1] = blockAddr;
    RC522_CalcCRC(ucComRC522Buf, 2, &ucComRC522Buf[2]);
 
    status = RC522_Communication(PCD_TRANSCEIVE, ucComRC522Buf, 4, ucComRC522Buf, &length);
    if((status != MI_OK) || (length != 4) || ((ucComRC522Buf[0] & 0x0F) != 0x0A)) 
	{
		status = MI_ERR;
	}		
        
    if(status == MI_OK)
    {
        for(i=0; i<16; i++) 
		{
			ucComRC522Buf[i] = *(pData+i); 
		}			
		RC522_CalcCRC(ucComRC522Buf, 16, &ucComRC522Buf[16]);

		status = RC522_Communication(PCD_TRANSCEIVE, ucComRC522Buf, 18, ucComRC522Buf, &length);
		if((status != MI_OK) || (length != 4) || ((ucComRC522Buf[0] & 0x0F) != 0x0A)) 
		{
			status = MI_ERR;   
		}
    }
    return status;
}

///////////////////////////////////////////////////////////////////
//              以下函数作用域仅作用域本文件内!!!                  
///////////////////////////////////////////////////////////////////
/**
  * @brief	:软件SPI发送并接收一个字节
  * @note   :--注意,返回最近一次接收的数据!
  * @param	:TxData, 主机发送的数据
  * @return	:uint8_t, 从机发送的数据
  *
  * @data   :2017/01/10
  * @design :
  **/
static uint8_t RC522_SPIReadWriteByte(uint8_t TxData)
{                        
    //不需要另外开辟空间存储接收值!
    //根据数据的传送规律,TxData传送最高位、移位、接收最低位
    uint8_t i=0;   

    for(i=0;i<8;i++)
	{
        if(TxData&0x80)
		{
            RC522_MOSI_HIGH();
        }else{
            RC522_MOSI_LOW();
        }
        
        TxData <<= 1;
        
        RC522_SCK_HIGH();
        if(RC522_MISO_IN()){//捕获MISO引脚的数据
            TxData |= 0x01;
        }
        RC522_SCK_LOW();//沿传输数据!   
    }
    
    return TxData;
} 	    



/**
  * @brief	:读RC522寄存器
  * @note   :--
  * @param	:regAddr, 寄存器地址
  * @return	:uint8_t, 读出的值
  *
  * @data   :2017/08/24
  * @design :
  **/
static uint8_t RC522_RegRead(uint8_t regAddr)
{
	uint8_t rec=0;

	RC522_NSS_LOW();//CS=low!

	//地址格式:1XXXXXX0
	RC522_SPIReadWriteByte(((regAddr<<1)&0x7E) | 0x80);
	rec = RC522_SPIReadWriteByte(0x00);
	
	RC522_NSS_HIGH();//CS=high!
	return rec;
}



/**
  * @brief	:写RC522寄存器
  * @note   :--
  * @param	:regAddr, 寄存器地址
			 Value  , 写入的值
  * @return	:void
  *
  * @data   :2017/08/24
  * @design :
  **/
static void RC522_RegWrite(uint8_t regAddr, uint8_t Value)
{  
	RC522_NSS_LOW();//CS=low!

	//地址格式:0XXX XXX0
	RC522_SPIReadWriteByte(((regAddr<<1)&0x7E));
	RC522_SPIReadWriteByte(Value);
	
	RC522_NSS_HIGH();//CS=high!
	Delay_ms(5);
}



/**
  * @brief	:置RC522寄存器位
  * @note   :--
  * @param	:regAddr, 寄存器地址
			 Mask	, 清位值
  * @return	:void
  *
  * @data   :2017/08/24
  * @design :
  **/
static void RC522_SetBitMask(uint8_t regAddr, uint8_t Mask)  
{
    int8_t tmp;
	
    tmp = RC522_RegRead(regAddr);
    RC522_RegWrite(regAddr, (tmp | Mask) );
}



/**
  * @brief	:清RC522寄存器位
  * @note   :--
  * @param	:regAddr, 寄存器地址
			 Mask	, 清位值
  * @return	:void
  *
  * @data   :2017/08/24
  * @design :
  **/
static void RC522_ClearBitMask(uint8_t regAddr, uint8_t Mask)  
{
    int8_t tmp;
	
    tmp = RC522_RegRead(regAddr);
    RC522_RegWrite(regAddr, (tmp & ~Mask) );
}



/**
  * @brief	:计算CRC
  * @note   :--
  * @param	:*pInData , CRC输入
			 Length   , 数据长度
			 *pOutData, CRC输出
  * @return	:void
  *
  * @data   :2017/08/24
  * @design :
  **/
static void RC522_CalcCRC(uint8_t *pInData, uint8_t Length, uint8_t *pOutData)
{
    uint8_t i;
	uint8_t n;
	
    RC522_ClearBitMask(DivIrqReg, 0x04);
    RC522_RegWrite(CommandReg, PCD_IDLE);
    RC522_SetBitMask(FIFOLevelReg, 0x80);
	
    for(i=0; i<Length; i++)
	{   
		RC522_RegWrite(FIFODataReg, *(pInData+i));   
	}
    RC522_RegWrite(CommandReg, PCD_CALCCRC);
    
	i = 0xFF;
    do 
    {
        n = RC522_RegRead(DivIrqReg);
        i--;
    }while((i!=0) && !(n&0x04));
	
    pOutData[0] = RC522_RegRead(CRCResultRegL);
    pOutData[1] = RC522_RegRead(CRCResultRegM);
}



/**
  * @brief	:通讯
  * @note   :--
  * @param	:Command	, 命令字
             *pInData	, 通过RC522发送到卡片的数据
             inLength   , 发送数据的字节长度
             *pOutData  , 接收到的卡片返回数据
             *pOutLength, 返回数据的位长度
  * @return	:void
  *
  * @data   :2017/08/24
  * @design :
  **/
static int8_t RC522_Communication(uint8_t Command, uint8_t *pInData, uint8_t inLength, uint8_t *pOutData, uint16_t  *pOutLength)
{
	int8_t status=MI_ERR;
	uint8_t irq_en=0x00;
	uint8_t wait_time=0x00;
	uint8_t last_bits;
	uint8_t n;
	uint16_t i;

	switch(Command)
	{
		case PCD_AUTHENT	: irq_en = 0x12; wait_time = 0x10; break;
		case PCD_TRANSCEIVE	: irq_en = 0x77; wait_time = 0x30; break;

		default: break;
	}

	RC522_RegWrite(ComIEnReg, irq_en|0x80);
	RC522_ClearBitMask(ComIrqReg, 0x80);
	RC522_RegWrite(CommandReg, PCD_IDLE);
	RC522_SetBitMask(FIFOLevelReg, 0x80);

	for(i=0; i<inLength; i++)
	{   
		RC522_RegWrite(FIFODataReg, pInData[i]);    
	}
	RC522_RegWrite(CommandReg, Command);


	if(Command == PCD_TRANSCEIVE)
	{    
		RC522_SetBitMask(BitFramingReg, 0x80);  
	}

	i = 25000;//根据时钟频率调整,操作M1卡最大等待时间25ms
	do 
	{
		 n = RC522_RegRead(ComIrqReg);
		 i--;
	}while((i!=0) && !(n&0x01) && !(n&wait_time));
	RC522_ClearBitMask(BitFramingReg, 0x80);
		  
	if(i != 0)
	{    
		if(!(RC522_RegRead(ErrorReg)&0x1B))
		{
			status = MI_OK;
			if(n & irq_en & 0x01)
			{   
				status = MI_NOTAGERR;   
			}

			if(Command == PCD_TRANSCEIVE)
			{
				n = RC522_RegRead(FIFOLevelReg);
				last_bits = RC522_RegRead(ControlReg) & 0x07;
				if(last_bits)
				{  
					*pOutLength = (n-1)*8 + last_bits;   
				}else{   
					*pOutLength = n*8;   
				}

				if(n == 0){
					n = 1; 
				}
				
				if(n > MAX_REC_LEN){   
					n = MAX_REC_LEN;   
				}

				for(i=0; i<n; i++){   
					pOutData[i] = RC522_RegRead(FIFODataReg);    
				}
			}
		}else{   
			status = MI_ERR;   
		}
	}

	RC522_SetBitMask(ControlReg, 0x80);
	RC522_RegWrite(CommandReg, PCD_IDLE); 
	return status;
}

/* ******************** Copyright (C), 2017-2017, TYUT TSS-plan by SERI.LJI ******************** */

你可能感兴趣的:(#,STM32,#,射频标签)