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

物联沃分享整理
物联沃-IOTWORD物联网 » STM32通过RC522刷卡模块读取IC卡号,读写IC卡数据

发表回复