STM32通过RC522刷卡模块读取IC卡号,读写IC卡数据
欢迎入群共同学习交流
时间记录:2024/11/5
一、知识点介绍
1.RC522模块引脚介绍
名称 | 接线 |
---|---|
3.3V | 电源引脚,接3.3V,接5V会烧坏 |
RST | 复位引脚,低电平复位 |
GND | 电源地引脚 |
IRQ | 内部中断信号输出引脚,暂时没有用到悬空即可 |
MISO | SPI协议主机输入引脚 |
MOSI | SPI协议主机输出引脚 |
SCK | 时钟引脚 |
SDA | 片选信号引脚 |
2.RC522模块介绍
RC522是一种工作于13.56MHz(高频)的模块,可以与IC卡通过ISO/IEC 14443A协议进行数据交互。
SPI协议使用模式3,即空闲状态SCK高电平,第二个变换沿交换数据,片选引脚低电平选中模块
3.S50(MI)卡介绍
1)卡内有唯一的32位(4字节)序列号(ID卡号)
2)卡容量为8Kbit的EEPROM
3)卡内分为16个扇区,每个扇区分为4个块,每块16字节,以块为存储单位
4.IC内存介绍
1)扇区0块0用于固化厂商代码,前4字节为IC卡卡号,第五字节为卡号异或值,后11字节为厂商信息
2)每个扇区的块3用于控制块,前6字节为密码A,默认全是0xFF,7-9字节为存取控制字节,决定了扇区内每个块的数据存取方式,默认为0xFF,0x07,0x80,10字节为保留字节,默认为0x69,11-16字节为密码B,默认全是0xFF
3)除去这些特殊块外其他块用来存储数据,特殊块也可以进行读写但是一般不建议动
5.扇区内块3存取控制位介绍
默认为0xFF,0x07,0x80,即
bit7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
数据块存取控制表:
通过默认值的bit4可以发现C20 = 0,C10 = 0,C30 = 0,则C10 C20 C30 = 000,对于块0的访问权限就是验证密码A或B后可以对数据读、写、加值、减值。
控制块存取控制表:
6.IC卡操作流程
请求卡->防冲突(在这一步就会返回卡片ID)->选卡->验证密码->操作数据
每次扇区改变后都需要验证密码才能够操作数据
7.基本通信指令集
二、示例代码
1.头文件
#ifndef __RC522_H__
#define __RC522_H__
/**
* 模块介绍,工作于13.56MHz(高频),支持ISO/IEC 14443A协议。
* 工作流程:复位应答(request) -> 防冲突(Anti collision Loop),这一步就会返回卡4位ID
* -> 选择卡片(Select Tag) -> 三次相互验证(Authentication) -> 读写数据(Read/Write) -> 停止工作(Stop)
* 每次改变扇区都需要进行三次相互验证
*/
#include "stm32f10x.h"
void rc522Init(void); // 初始化
u8 rc522IsConnected(void); // 判断RC522是否连接
char pcdRequest(u8 req_code,u8 *pTagType); // 请求卡片
char pcdAnticoll(u8* pSnr); // 防冲撞
char pcdSelectTag(u8 *pSnr); // 选卡
char pcdAuthPasswd(u8 auth_mode,u8 addr,u8* pKey,u8* pSnr); // 验证密码
char pcdReadData(u8 addr,u8* p); // 读块数据
char pcdWriteData(u8 addr,u8* p); // 写块数据
#if 0 // 以下函数未使用,待再看
char pcdHalt(void); // 卡片进入休眠模式,没有试过
u8 pcdGetSize(u8* serNum); // 获取卡片存储容量大小,待再看目前不准或者未找到使用方法
#endif
#endif
2.配置文件
#ifndef __RC522CONF_H__
#define __RC522CONF_H__
#include "stm32f10x.h"
// 引脚定义
#define RST_PORT GPIOB
#define RST_PIN GPIO_Pin_9 // 复位引脚
#define MISO_PORT GPIOB
#define MISO_PIN GPIO_Pin_8 // 主入从出引脚,数据接收引脚
#define MOSI_PORT GPIOB
#define MOSI_PIN GPIO_Pin_7 // 主出从入引脚,数据发送引脚
#define SCK_PORT GPIOB
#define SCK_PIN GPIO_Pin_6 // 时钟引脚
#define SDA_PORT GPIOB
#define SDA_PIN GPIO_Pin_5 // 片选引脚
// 引脚电平定义
#define RSTHIGH GPIO_SetBits(RST_PORT, RST_PIN)
#define RSTLOW GPIO_ResetBits(RST_PORT, RST_PIN)
#define MOSIHIGH GPIO_SetBits(MOSI_PORT, MOSI_PIN)
#define MOSILOW GPIO_ResetBits(MOSI_PORT, MOSI_PIN)
#define SCKHIGH GPIO_SetBits(SCK_PORT, SCK_PIN)
#define SCKLOW GPIO_ResetBits(SCK_PORT, SCK_PIN)
#define SDAHIGH GPIO_SetBits(SDA_PORT, SDA_PIN)
#define SDALOW GPIO_ResetBits(SDA_PORT, SDA_PIN)
#define MISOVALUE GPIO_ReadInputDataBit(MISO_PORT, MISO_PIN)
/* MF522 命令字,操作RC522模块寄存器的命令 ------------------------------------------------- */
#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计算
/* ifare_One卡片命令字,通过RC522模块操作MIFARE卡片的命令 ---------------------------------------------------- */
#define PICC_REQIDL 0x26 //寻天线区内未进入休眠状态
#define PICC_REQALL 0x52 //寻天线区内全部卡
#define PICC_ANTICOLL1 0x93 //防冲撞
#define PICC_ANTICOLL2 0x95 //防冲撞
#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
#define MAXRLEN 18
/* MF522寄存器 ------------------------- */
// PAGE 0
#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
#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
#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
#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
/* 通信返回码 ----------------------- */
#define MI_OK 0
#define MI_NOTAGERR 1
#define MI_ERR 2
#endif
3.源文件
#include "rc522.h"
#include "rc522Conf.h"
#include "delay.h"
#include "string.h"
static void pcdAntennaOn(void); // 开启天线
static void pcdAntennaOff(void); // 关闭天线,每次启动或者关闭天线发射之间必须有1ms间隔
static char configWorkISOType(u8 type); // 配置工作模式为ISO14443A
static void rc522GpioInit(void); // 使用模拟SPI协议,模式3,空闲状态CLK为高电平,CS为低电平开始通信
static void spiSendByte(uint8_t data); // SPI协议发送1字节数据
static uint8_t spiReadByte(void); // SPI协议读取1字节数据
static void writeRawRC(uint8_t addr, uint8_t data); // 写寄存器
static uint8_t readRawRC(uint8_t addr); // 读寄存器
static void setBitMask(u8 reg, u8 mask);
static void clearBitMask(u8 reg, u8 mask);
static char pcdComMF522(u8 ucCommand, u8* pInData, u8 ucInLenByte, u8* pOutData, u32* pOutLenBit);
static void rc522Reset(void); // 复位RC522
static void calulateCRC(u8* pIn, u8 len,u8* pOut); // 计算CRC校验码
void rc522Init(void)
{
// 初始化使用的GPIO口
rc522GpioInit();
// 复位RC522
rc522Reset();
pcdAntennaOff(); //关闭天线
delayMs(2); //延时2毫秒
pcdAntennaOn(); //开启天线
// 设置工作模式
configWorkISOType('A');
}
static void rc522Reset(void)
{
RSTHIGH;
delayUs(5);
RSTLOW;
delayUs(5);
RSTHIGH;
delayUs(5);
writeRawRC(CommandReg,PCD_RESETPHASE); //写RC632寄存器,复位
while(readRawRC(CommandReg) & 0x10);
writeRawRC(ModeReg, 0x3D); //定义发送和接收常用模式 和Mifare卡通讯,CRC初始值0x6363
writeRawRC(TReloadRegL, 30); //16位定时器低位
writeRawRC(TReloadRegH, 0); //16位定时器高位
writeRawRC(TModeReg, 0x8D); //定义内部定时器的设置
writeRawRC(TPrescalerReg, 0x3E); //设置定时器分频系数
writeRawRC(TxAutoReg, 0x40); //调制发送信号为100%ASK
}
static char configWorkISOType(u8 type)
{
if(type=='A') //ISO14443_A
{
clearBitMask(Status2Reg,0x08); //清RC522寄存器位
writeRawRC(ModeReg,0x3D); //3F//CRC初始值0x6363
writeRawRC(RxSelReg,0x86); //84
writeRawRC(RFCfgReg,0x7F); //4F //调整卡的感应距离//RxGain = 48dB调节卡感应距离
writeRawRC(TReloadRegL,30); //tmoLength);// TReloadVal = 'h6a =tmoLength(dec)
writeRawRC(TReloadRegH,0);
writeRawRC(TModeReg,0x8D);
writeRawRC(TPrescalerReg,0x3E);
delayUs(20);
pcdAntennaOn(); //开启天线
}
else
return 1; //失败,返回1
return MI_OK; //成功返回0
}
static void rc522GpioInit(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = RST_PIN;
GPIO_Init(RST_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = MOSI_PIN;
GPIO_Init(MOSI_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = SCK_PIN;
GPIO_Init(SCK_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = SDA_PIN;
GPIO_Init(SDA_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 数据接收引脚
GPIO_InitStructure.GPIO_Pin = MISO_PIN;
GPIO_Init(MISO_PORT, &GPIO_InitStructure);
// 初始化引脚电平,SPI空闲状态
SCKHIGH;
SDAHIGH;
}
static void spiSendByte(uint8_t data)
{
for(u8 i=0;i<8;i++)
{
SCKLOW; // 准备数据
if(data & 0x80)
MOSIHIGH;
else
MOSILOW;
SCKHIGH; // 将数据发送出去
data <<= 1;
}
SCKHIGH; // 空闲状态
}
static uint8_t spiReadByte(void)
{
uint8_t data = 0x00;
for(u8 i=0;i<8;i++)
{
data <<= 1;
SCKLOW; // 等待从机准备数据
SCKHIGH; // 获取从机交换过来的数据
if(MISOVALUE)
data |= 0x01;
}
SCKHIGH; // 空闲状态
return data;
}
/**
* 写RC522寄存器
* @param addr 寄存器地址
* @param data 要写入的数据
*/
static void writeRawRC(uint8_t addr, uint8_t data)
{
u8 tempAddr = (addr << 1) & 0x7E; // 最后一位保留不使用,最高位为0表示写入
SDALOW; // 开始通信
spiSendByte(tempAddr); // 写入地址
spiSendByte(data); // 写入数据
SDAHIGH; // 结束通信
}
/**
* 读RC522寄存器
* @param addr 寄存器地址
* @return 读取到的数据
*/
static uint8_t readRawRC(uint8_t addr)
{
uint8_t retData = 0x00;
u8 tempAddr = ((addr << 1) & 0x7E) | 0x80; // 最后一位保留不使用,最高位为1表示读取
SDALOW; // 开始通信
spiSendByte(tempAddr); // 写入地址
retData = spiReadByte(); // 读取数据
SDAHIGH; // 结束通信
return retData;
}
/**
* @Brief 设置寄存器指定位
* @Para Reg 寄存器地址
* @Para Mask
*/
static void setBitMask(u8 reg, u8 mask)
{
u8 temp;
temp = readRawRC(reg);
writeRawRC(reg, temp | mask); // set bit mask
}
/**
* 清除寄存器指定位
* @param reg 寄存器地址
* @param mask 要清除的位
*/
static void clearBitMask(u8 reg, u8 mask)
{
u8 temp;
temp = readRawRC(reg);
writeRawRC(reg, temp & ( ~ mask)); // clear bit mask
}
static void pcdAntennaOn(void)
{
u8 uc = 0x00;
uc = readRawRC(TxControlReg);
if((uc & 0x03) != 0x03)
setBitMask(TxControlReg, 0x03);
}
static void pcdAntennaOff(void)
{
clearBitMask(TxControlReg, 0x03);
}
/*
* 描述 :通过RC522和ISO14443卡通讯
* 输入 :ucCommand,RC522命令字
* pInData,通过RC522发送到卡片的数据
* ucInLenByte,发送数据的字节长度
* pOutData,接收到的卡片返回数据
* pOutLenBit,返回数据的位长度
* 返回 : 状态值
* = MI_OK,成功
* 调用 :内部调用
*/
static char pcdComMF522(u8 ucCommand, u8* pInData, u8 ucInLenByte, u8* pOutData, u32* pOutLenBit)
{
char cStatus = MI_ERR;
u8 ucIrqEn = 0x00;
u8 ucWaitFor = 0x00;
u8 ucLastBits;
u8 ucN;
u32 ul;
switch(ucCommand)
{
case PCD_AUTHENT: //Mifare认证
ucIrqEn = 0x12; //允许错误中断请求ErrIEn 允许空闲中断IdleIEn
ucWaitFor = 0x10; //认证寻卡等待时候 查询空闲中断标志位
break;
case PCD_TRANSCEIVE: //接收发送 发送接收
ucIrqEn = 0x77; //允许TxIEn RxIEn IdleIEn LoAlertIEn ErrIEn TimerIEn
ucWaitFor = 0x30; //寻卡等待时候 查询接收中断标志位与 空闲中断标志位
break;
default:
break;
}
writeRawRC(ComIEnReg, ucIrqEn | 0x80); //IRqInv置位管脚IRQ与Status1Reg的IRq位的值相反
clearBitMask(ComIrqReg, 0x80); //Set1该位清零时,CommIRqReg的屏蔽位清零
writeRawRC(CommandReg, PCD_IDLE); //写空闲命令
setBitMask(FIFOLevelReg, 0x80); //置位FlushBuffer清除内部FIFO的读和写指针以及ErrReg的BufferOvfl标志位被清除
for(ul = 0; ul < ucInLenByte; ul ++)
writeRawRC(FIFODataReg, pInData[ul]); //写数据进FIFOdata
writeRawRC(CommandReg, ucCommand); //写命令
if(ucCommand == PCD_TRANSCEIVE)
setBitMask(BitFramingReg,0x80); //StartSend置位启动数据发送 该位与收发命令使用时才有效
ul = 1000;//根据时钟频率调整,操作M1卡最大等待时间25ms
do //认证 与寻卡等待时间
{
ucN = readRawRC(ComIrqReg); //查询事件中断
ul --;
}while((ul != 0 ) && ( ! ( ucN & 0x01 ) ) && ( ! ( ucN & ucWaitFor))); //退出条件i=0,定时器中断,与写空闲命令
clearBitMask(BitFramingReg, 0x80); //清理允许StartSend位
if(ul != 0)
{
if(!((readRawRC(ErrorReg) & 0x1B ))) //读错误标志寄存器BufferOfI CollErr ParityErr ProtocolErr
{
cStatus = MI_OK;
if(ucN & ucIrqEn & 0x01) //是否发生定时器中断
cStatus = MI_NOTAGERR;
if(ucCommand == PCD_TRANSCEIVE)
{
ucN = readRawRC(FIFOLevelReg); //读FIFO中保存的字节数
ucLastBits = readRawRC(ControlReg) & 0x07; //最后接收到得字节的有效位数
if(ucLastBits)
*pOutLenBit = (ucN - 1) * 8 + ucLastBits; //N个字节数减去1(最后一个字节)+最后一位的位数 读取到的数据总位数
else
*pOutLenBit = ucN * 8; //最后接收到的字节整个字节有效
if(ucN == 0)
ucN = 1;
if(ucN > MAXRLEN)
ucN = MAXRLEN;
for(ul = 0; ul < ucN; ul ++)
pOutData[ul] = readRawRC(FIFODataReg);
}
}
else
cStatus = MI_ERR;
}
setBitMask(ControlReg, 0x80); // stop timer now
writeRawRC(CommandReg, PCD_IDLE);
return cStatus;
}
u8 rc522IsConnected(void)
{
return (readRawRC(VersionReg)==0x92);
}
/*
功 能: 寻卡
参数说明: req_code[IN]:寻卡方式
0x52 = 寻感应区内所有符合14443A标准的卡
0x26 = 寻未进入休眠状态的卡
pTagType[OUT]:卡片类型代码
0x4400 = Mifare_UltraLight
0x0400 = Mifare_One(S50)
0x0200 = Mifare_One(S70)
0x0800 = Mifare_Pro(X)
0x4403 = Mifare_DESFire
返 回 值: 成功返回MI_OK
*/
char pcdRequest(u8 req_code,u8 *pTagType)
{
char status;
u32 unLen;
u8 ucComMF522Buf[MAXRLEN]; // MAXRLEN 18
clearBitMask(Status2Reg,0x08); //清RC522寄存器位,/接收数据命令
writeRawRC(BitFramingReg,0x07); //写RC632寄存器
setBitMask(TxControlReg,0x03); //置RC522寄存器位
ucComMF522Buf[0]=req_code; //寻卡方式
status=pcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,1,ucComMF522Buf,&unLen); //通过RC522和ISO14443卡通讯
if((status==MI_OK)&&(unLen==0x10))
{
*pTagType=ucComMF522Buf[0];
*(pTagType+1)=ucComMF522Buf[1];
}
else
{
status = MI_ERR;
}
return status;
}
/*
* 描述 :防冲撞
* 输入 :pSnr,卡片序列号,4字节
* 返回 : 状态值
* = MI_OK,成功
*/
char pcdAnticoll(u8* pSnr)
{
char cStatus;
u8 uc, ucSnr_check = 0;
u8 ucComMF522Buf[MAXRLEN];
u32 ulLen;
clearBitMask(Status2Reg, 0x08); //清MFCryptol On位 只有成功执行MFAuthent命令后,该位才能置位
writeRawRC(BitFramingReg, 0x00); //清理寄存器 停止收发
clearBitMask(CollReg, 0x80); //清ValuesAfterColl所有接收的位在冲突后被清除
ucComMF522Buf[0] = 0x93; //卡片防冲突命令
ucComMF522Buf[1] = 0x20;
cStatus = pcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 2, ucComMF522Buf, &ulLen);//与卡片通信
if(cStatus == MI_OK) //通信成功
{
for(uc = 0; uc < 4; uc ++)
{
*(pSnr + uc) = ucComMF522Buf[uc]; //读出UID
ucSnr_check ^= ucComMF522Buf[uc];
}
if(ucSnr_check != ucComMF522Buf[uc])
cStatus = MI_ERR;
}
setBitMask(CollReg, 0x80);
return cStatus;
}
/**
* 功 能:选定卡片
* 参数说明:pSnr[IN]:卡片ID,4字节
* 返 回:成功返回MI_OK
*/
char pcdSelectTag(u8 *pSnr)
{
char status;
u8 i;
u32 unLen;
u8 ucComMF522Buf[MAXRLEN];
ucComMF522Buf[0]=PICC_ANTICOLL1;
ucComMF522Buf[1]=0x70;
ucComMF522Buf[6]=0;
for(i=0;i<4;i++)
{
ucComMF522Buf[i+2]=*(pSnr+i);
ucComMF522Buf[6]^=*(pSnr+i);
}
calulateCRC(ucComMF522Buf, 7, &ucComMF522Buf[7]); //用MF522计算CRC16函数,校验数据
clearBitMask(Status2Reg, 0x08); //清RC522寄存器位
status=pcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 9, ucComMF522Buf, &unLen);
if((status==MI_OK)&&(unLen==0x18)) status=MI_OK;
else status=MI_ERR;
return status;
}
#if 0
/**
* 功 能:命令卡片进入休眠状态
* 返 回:成功返回MI_OK
*/
char pcdHalt(void)
{
u8 status;
u32 unLen;
u8 ucComMF522Buf[MAXRLEN];
ucComMF522Buf[0]=PICC_HALT;
ucComMF522Buf[1]=0;
calulateCRC(ucComMF522Buf, 2, &ucComMF522Buf[2]);
status=pcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, &unLen);
return status;
}
/**
* 功能:选卡读取卡存储器容量
* 输入参数:serNum 传入卡ID,4字节
* 返 回 值:成功返回卡容量
*/
u8 pcdGetSize(u8* serNum)
{
u8 i;
u8 status;
u8 size;
u32 recvBits;
u8 buffer[9];
buffer[0]=PICC_ANTICOLL1; //防撞码1
buffer[1]=0x70;
buffer[6]=0x00;
for(i=0;i<4;i++)
{
buffer[i+2]=*(serNum+i); //buffer[2]-buffer[5]为卡序列号
buffer[6]^=*(serNum+i); //卡校验码
}
calulateCRC(buffer,7,&buffer[7]); //buffer[7]-buffer[8]为RCR校验码
clearBitMask(Status2Reg,0x08);
status=pcdComMF522(PCD_TRANSCEIVE,buffer,9,buffer,&recvBits);
if((status==MI_OK)&&(recvBits==0x18))
size=buffer[0];
else
size=0;
return size;
}
#endif
/**
* 功 能:验证卡片密码
* 参数说明:auth_mode[IN]: 密码验证模式
* 0x60 = 验证A密钥
* 0x61 = 验证B密钥
* addr[IN]:块地址0-63
* pKey[IN]:扇区密码,6字节
* pSnr[IN]:卡片序列号,4字节
* 返 回:成功返回MI_OK
*/
char pcdAuthPasswd(u8 auth_mode,u8 addr,u8* pKey,u8* pSnr)
{
char status;
u32 unLen;
u8 ucComMF522Buf[MAXRLEN]; //MAXRLEN 18(数组的大小)
//验证模式+块地址+扇区密码+卡序列号
ucComMF522Buf[0]=auth_mode;
ucComMF522Buf[1]=addr;
memcpy(&ucComMF522Buf[2],pKey,6); //拷贝,复制
memcpy(&ucComMF522Buf[8],pSnr,4);
status=pcdComMF522(PCD_AUTHENT,ucComMF522Buf,12,ucComMF522Buf,&unLen);
if((status != MI_OK) || (!(readRawRC(Status2Reg)&0x08)))
status = MI_ERR;
return status;
}
/**
* 功 能:读取M1卡一块数据
* 参数说明:
* addr:块地址0-63
* p :读出的块数据,16字节
* 返 回:成功返回MI_OK
*/
char pcdReadData(u8 addr,u8* p)
{
char status;
u32 unLen;
u8 i,ucComMF522Buf[MAXRLEN]; //18
ucComMF522Buf[0]=PICC_READ;
ucComMF522Buf[1]=addr;
calulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);
status=pcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);
if((status==MI_OK&&(unLen==0x90)))
{
for(i=0;i<16;i++)
*(p+i)=ucComMF522Buf[i];
}
else
{
status=MI_ERR;
}
return status;
}
/**
* 功 能:写数据到M1卡指定块
* 参数说明:addr:块地址0-63
* p :向块写入的数据,16字节
* 返 回:成功返回MI_OK
*/
char pcdWriteData(u8 addr,u8* p)
{
char status;
u32 unLen;
u8 i,ucComMF522Buf[MAXRLEN];
ucComMF522Buf[0]=PICC_WRITE;// 0xA0 //写块
ucComMF522Buf[1]=addr; //块地址
calulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);
status=pcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);
if((status!= MI_OK) || (unLen != 4) || ((ucComMF522Buf[0]&0x0F) != 0x0A))
{
status = MI_ERR;
}
if(status == MI_OK)
{
for(i=0;i<16;i++)//向FIFO写16Byte数据
{
ucComMF522Buf[i]=*(p+i);
}
calulateCRC(ucComMF522Buf,16,&ucComMF522Buf[16]);
status = pcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,18,ucComMF522Buf,&unLen);
if((status != MI_OK)||(unLen != 4) || ((ucComMF522Buf[0]&0x0F)!=0x0A))
{
status = MI_ERR;
}
}
return status;
}
/**
* 功 能:用MF522计算CRC16函数
* 参 数:
* *pIn :要读数CRC的数据
* len:数据长度
* *pOut:计算的CRC结果
*/
static void calulateCRC(u8* pIn, u8 len,u8* pOut)
{
u8 i,n;
clearBitMask(DivIrqReg,0x04); //CRCIrq = 0
writeRawRC(CommandReg,PCD_IDLE);
setBitMask(FIFOLevelReg,0x80); //清FIFO指针
//向FIFO中写入数据
for(i=0;i<len;i++)
{
writeRawRC(FIFODataReg,*(pIn + i)); //开始RCR计算
}
writeRawRC(CommandReg,PCD_CALCCRC); //等待CRC计算完成
i=0xFF;
do
{
n=readRawRC(DivIrqReg);
i--;
}
while((i!=0)&&!(n&0x04)); //CRCIrq = 1
//读取CRC计算结果
pOut[0]=readRawRC(CRCResultRegL);
pOut[1]=readRawRC(CRCResultRegM);
}
4.主文件测试程序
#include "stm32f10x.h"
#include "sys.h"
#include "delay.h"
#include "rc522.h"
int main()
{
NVICGroupConfig();
delayInit();
rc522Init();
u8 status = 0;
u8 pTagType[2];
u8 ID[4];
while(1)
{
status = pcdRequest(0x52, pTagType); // 请求全部卡
if(status == 0)
{
status = pcdAnticoll(ID); // 防冲撞获取ID
if(status == 0)
{
status = pcdSelectTag(ID); // 根据ID选卡
if(status == 0)
{
u8 APASS[] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
status = pcdAuthPasswd(0x60, 4, APASS, ID); // 验证A密码
if(status == 0)
{
u8 blockData[16] = {0x00};
status = pcdReadData(4, blockData); // 读取扇区1块0数据
}
}
}
}
}
}
作者:KINO32