[射频标签]关于射频卡Mifare

文章目录

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

一、Mifare-S50卡简介

1.主要指标

/*rfid_rc522.h Card-Information Define*/
//<!- S50非接触式IC卡性能简介(M1):
//<!- 容量为8K位EEPROM(1K字节)
//<!- 分为16个扇区,每个扇区为4块,每块16个字节,以块为存取单位
//<!- 每个扇区有独立的一组密码及访问控制
//<!- 每张卡有唯一序列号,为32位
//<!- 具有防冲突机制,支持多卡操作
//<!- 无电源,自带天线,内含加密控制逻辑和通讯逻辑电路
//<!- 数据保存期为10年,可改写10万次,读无限次
//<!- 工作温度:-20℃~50℃(湿度为90%)
//<!- 工作频率:13.56MHZ
//<!- 通信速率:106KBPS
//<!- 读写距离:10cm以内(与读写器有关)

2.存储结构

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

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卡的技术手册上有列出):
C1C2C3=000:验证密码A或密码B后可以进行任何操作;
C1C2C3=111:无权限(即无法对卡片进行任何操作,卡片被冻结了);
C1C2C3=010和C1C2C3=101都是只读,如果对应的数据块写入的是一些可以给人看但不能改的基本信息,可以设为这两种模式;
C1C2C3=001时只能读和减值,电子钱包一般设为这种模式,比如用S50做的公交电子车票,用户只能查询或扣钱,不能加钱,充值的时候先改变控制位使卡片可以充值,充完值再改回来。

上图是数据块的权限,密码块的权限如下:

密码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作为普通存储空间使用。各块控制位存储格式如下:

由于出厂时数据块控制位的默认值是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

二、代码(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-24v1.0开始构建本文件;
  *2017-09-11v2.0加入C++类的概念,重构文件;
  *2018-03-08v3.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日
  * Email:liangzongnan0214@163.com
  * 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*/
//<!- S50非接触式IC卡性能简介(M1):
//<!- 容量为8K位EEPROM(1K字节)
//<!- 分为16个扇区,每个扇区为4块,每块16个字节,以块为存取单位
//<!- 每个扇区有独立的一组密码及访问控制
//<!- 每张卡有唯一序列号,为32位
//<!- 具有防冲突机制,支持多卡操作
//<!- 无电源,自带天线,内含加密控制逻辑和通讯逻辑电路
//<!- 数据保存期为10年,可改写10万次,读无限次
//<!- 工作温度:-20℃~50℃(湿度为90%)
//<!- 工作频率:13.56MHZ
//<!- 通信速率:106KBPS
//<!- 读写距离:10cm以内(与读写器有关)

/*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)

#defineKeyCopy(dest, src)memcpy(dest, src, 8)

/*rfid_rc522.h Control-Pin Define*/
//<!- RC522模块与STM32的接线说明(TSS_plan stm32f407_main_board v2.5开发板):
//<!-
//<!- RC522引脚说明<----->STM32引脚说明
//<!- 3.3V:电源<----->3.3V,不可接错!
//<!- RST:复位<----->PA.2,普通I/O
//<!- GND:地<----->GND
//<!- IRQ:中断<----->PA.3,不用接、悬空即可!
//<!- MISO:数据输出<----->PA.6,普通I/O
//<!- MOSI:数据输入<----->PA.7,普通I/O
//<!- SCK:时钟<----->PA.5,普通I/O
//<!- SDA:片选<----->PA.4,普通I/O
//<!-
//<!- 注:尽可能按照默认分配的管脚、引脚进行配置,否则,需要自己修改文件!!!
#define RFID_RC522_M_PORTGPIOB//主管脚!
#define RFID_RC522_MOSI_PIN5
#define RFID_RC522_MISO_PIN6
#define RFID_RC522_RST_PIN8

#define RFID_RC522_S_PORTGPIOB//辅管脚!
#define RFID_RC522_SCK_PIN4
#define RFID_RC522_NSS_PIN3

#define RC522_MOSI_HIGH()GPIO_SetBits(RFID_RC522_M_PORT, (1<<RFID_RC522_MOSI_PIN))
#define RC522_RST_HIGH()GPIO_SetBits(RFID_RC522_M_PORT, (1<<RFID_RC522_RST_PIN) )
#define RC522_SCK_HIGH()GPIO_SetBits(RFID_RC522_S_PORT, (1<<RFID_RC522_SCK_PIN) )
#define RC522_NSS_HIGH()GPIO_SetBits(RFID_RC522_S_PORT, (1<<RFID_RC522_NSS_PIN) )

#define RC522_MOSI_LOW()GPIO_ResetBits(RFID_RC522_M_PORT, (1<<RFID_RC522_MOSI_PIN))
#define RC522_RST_LOW()GPIO_ResetBits(RFID_RC522_M_PORT, (1<<RFID_RC522_RST_PIN) )
#define RC522_SCK_LOW()GPIO_ResetBits(RFID_RC522_S_PORT, (1<<RFID_RC522_SCK_PIN) )
#define RC522_NSS_LOW()GPIO_ResetBits(RFID_RC522_S_PORT, (1<<RFID_RC522_NSS_PIN) )

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

/*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_SElECTTAG0x93//选卡
#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_OK0
#define MI_NOTAGERR(-1)
#define MI_ERR(-2)

/*rfid_rc522.h Param Class Define*/
///卡片类型描述集合:
typedef struct
{
uint32_tUID;//卡片序列号
uint16_tTagType;//卡片类型代码

uint8_tSize;//卡片容量

uint8_tSerialNum[4];//卡片序列号数组
uint8_tData[16];//卡片数据(只保留一块的数据,即16字节!)

uint8_tKeyA[8];//卡片A秘钥
uint8_tKeyB[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-24v1.0开始构建本文件;
  *2017-09-11v2.0加入C++类的概念,重构文件;
  *2018-03-08v3.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日
  * Email:liangzongnan0214@163.com
  * 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_LEN18
#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_InitTypeDefgpio;

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 ******************** */
(0)

相关推荐