/*rfid_rc522.h Card-Information Define*/
//
//
//
//
//
//
//
//
//
//
//
//
3. 扇区0的块0不能被使用,该块存储厂商代码,已经固化,不能被改变
数据块可作为两种应用:
那么作为数据(数据块)和作为数据值(值块)有什么区别呢?
其实,无论块中的内容是什么,你都可以把他看成普通数据,即使它是一个值块。但是并不是任何数据都可以看成是值,因为值块有一个比较严格的格式要求。值块中值的长度为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是补码表示。(实际上水卡、洗衣卡就是这样的构造,但是!不要去试图去破解,违法犯罪啊!!!)
块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。存取控制用以设定扇区中各个块(包括控制块本身)的存取条件,这部分有点复杂,后续有机会继续介绍。
前面已经说了,块分为数据块和密码块。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。
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
附STM32F407例程(只列出rfid相关操作)
源码下载链接:https://download.csdn.net/download/weixin_39869569/11242479
/**
******************************************************************************
*
* 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 ******************** */
/**
******************************************************************************
*
* 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 ******************** */