此次使用RC522模块和S50卡实现近场通讯功能(开发板与RC522通讯方式为硬件SPI),就实践过程中的一些知识点进行总结:

  • RC522模块和M1卡要点介绍;
  • 驱动代码;
  • 出现问题及解决方法;

1. RC522模块和M1卡要点介绍:

  • MFRC522简化功能框图;
  • MFRC522与主机SPI通讯引脚配置;
  • MFRC522与M1卡的通讯原理和通讯流程;
  • M1卡存储结构;

MFRC522简化功能框图:

先从RC522功能框图入手,可以从大方向上理解通讯原理。

上述主机一般指的就是手上的开发板,通信接口对应天线,MFRC522与主机通讯支持UART、SPI以及IIC,本人采用SPI通讯方式。

MFRC522与主机SPI通讯引脚配置:

MFRC522与M1卡的通讯原理和通讯流程:

工作原理:
读写器向M1卡发一组固定频率的电磁波,卡片内有一个 LC串联谐振电路,其频率与读写器发射的频率相同,在电磁波的激励下,LC谐振电路产生共振,从而使电容内有了电荷,在这个电容的另一端,接有一个单向导通的电子泵,将电容内的电荷送到另一个电容内储存,当所积累的电荷达到2V时,此电容可做为电源为其它电路提供工作电压,将卡内数据发射出去或接取读写器的数据。

通讯流程:


M1卡存储结构

存储结构:


  • 其中第0扇区的块0是用于存放厂商代码的,已经固化,不可更改,为32位(4Bytes);
  • 每个扇区的块0、块1和块2位数据块,可用于存储数据,每块16个字节(只有S50卡是这样);
  • 每个扇区的块3位控制块,包含了密码A、存取控制、密码B,具体结构如下图所示;

2. 驱动代码

RC522.h

#ifndef __RC522_H
#define __RC522_H
#include "stm32f10x.h"
#include "stm32f10x_spi.h"
#include <string.h>
#include <stdio.h>/*******************************
*连线说明:
*1--SDA  <----->PA4
*2--SCK  <----->PA5
*3--MOSI <----->PA7
*4--MISO <----->PA6
*5--悬空
*6--GND <----->GND
*7--RST <----->PB0
*8--VCC <----->VCC
************************************///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_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               //休眠#define DEF_FIFO_LENGTH       64                 //FIFO size=64byte
#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//和RC522通讯时返回的M1卡状态
#define     MI_OK                 0x26
#define     MI_NOTAGERR           0xcc
#define     MI_ERR                0xbb//和MF522通讯时返回的错误代码
#define     SHAQU1                0X01
#define     KUAI4                 0X04
#define     KUAI7                 0X07
#define     REGCARD               0xa1
#define     CONSUME               0xa2
#define     READCARD              0xa3
#define     ADDMONEY              0xa4#define SPI_RC522_ReadByte()        SPI_RC522_SendByte(0)#define SET_SPI_CS  (GPIOF->BSRR=0X01)
#define CLR_SPI_CS  (GPIOF->BRR=0X01)#define SET_RC522RST  GPIOF->BSRR=0X02
#define CLR_RC522RST  GPIOF->BRR=0X02/***********************RC522 函数宏定义**********************/
#define          RC522_CS_Enable()         GPIO_ResetBits ( GPIOA, GPIO_Pin_4 )
#define          RC522_CS_Disable()        GPIO_SetBits ( GPIOA, GPIO_Pin_4 )#define          RC522_Reset_Enable()      GPIO_ResetBits( GPIOB, GPIO_Pin_0 )
#define          RC522_Reset_Disable()     GPIO_SetBits ( GPIOB, GPIO_Pin_0 )#define          RC522_SCK_0()             GPIO_ResetBits( GPIOA, GPIO_Pin_5 )
#define          RC522_SCK_1()             GPIO_SetBits ( GPIOA, GPIO_Pin_5 )#define          RC522_MOSI_0()            GPIO_ResetBits( GPIOA, GPIO_Pin_7 )
#define          RC522_MOSI_1()            GPIO_SetBits ( GPIOA, GPIO_Pin_7 )#define          RC522_MISO_GET()          GPIO_ReadInputDataBit ( GPIOA, GPIO_Pin_6 )u8       SPI_RC522_SendByte         ( u8 byte);
u8       ReadRawRC                  ( u8 ucAddress );
void     WriteRawRC                 ( u8 ucAddress, u8 ucValue );
void     SPI1_Init                  ( void );
void     RC522_Handel               ( void );
void     RC522_Init                 ( void );                       //初始化
void     PcdReset                   ( void );                       //复位
void     M500PcdConfigISOType       ( u8 type );                    //工作方式
char     PcdRequest                 ( u8 req_code, u8 * pTagType ); //寻卡
char     PcdAnticoll                ( u8 * pSnr);                   //防冲撞void     PcdAntennaOn               ( void );                 //开启天线
void     PcdAntennaOff              ( void );                 //关闭天线
void     SetBitMask                 ( u8 ucReg, u8 ucMask );
void     ClearBitMask               ( u8 ucReg, u8 ucMask );
char     PcdSelect                  ( u8 * pSnr );            //选择卡片
char     PcdAuthState               ( u8 ucAuth_mode, u8 ucAddr, u8 * pKey, u8 * pSnr );                                              //验证密码
char     PcdWrite                   ( u8 ucAddr, u8 * pData );
char     PcdRead                    ( u8 ucAddr, u8 * pData );
void     ShowID                     ( u16 x,u16 y, u8 *p, u16 charColor, u16 bkColor);   //显示卡的卡号,以十六进制显示
char             PcdHalt            ( void );           //命令卡片进入休眠状态
void             CalulateCRC                ( u8 * pIndata, u8 ucLen, u8 * pOutData );#endif

RC522.c

#include "rc522.h"
#include "./SysTick/bsp_SysTick.h"
#include "./usart/bsp_usart.h"
#include "stm32f10x_spi.h"// M1卡分为16个扇区,每个扇区由四个块(块0、块1、块2、块3)组成
// 将16个扇区的64个块按绝对地址编号为:0~63
// 第0个扇区的块0(即绝对地址0块),用于存放厂商代码,已经固化不可更改
// 每个扇区的块0、块1、块2为数据块,可用于存放数据
// 每个扇区的块3为控制块(绝对地址为:块3、块7、块11.....)包括密码A,存取控制、密码B等/*******************************
*连线说明:
*1--SDA  <----->PA4
*2--SCK  <----->PA5
*3--MOSI <----->PA7
*4--MISO <----->PA6
*5--悬空
*6--GND <----->GND
*7--RST <----->PB0
*8--VCC <----->VCC
************************************/#define   RC522_DELAY()  delay_us( 2 ) /*全局变量*/
unsigned char CT[2];            //卡类型
unsigned char SN[4];            //卡号
unsigned char RFID[16];             //存放RFID
unsigned char lxl_bit=0;
unsigned char card1_bit=0;
unsigned char card2_bit=0;
unsigned char card3_bit=0;
unsigned char card4_bit=0;
unsigned char total=0;
unsigned char lxl[4]={196,58,104,217};
unsigned char card_1[4]={83,106,11,1};
unsigned char card_2[4]={208,121,31,57};
unsigned char card_3[4]={176,177,143,165};
unsigned char card_4[4]={5,158,10,136};
u8 KEY[6]={0xff,0xff,0xff,0xff,0xff,0xff};
u8 AUDIO_OPEN[6] = {0xAA, 0x07, 0x02, 0x00, 0x09, 0xBC};
unsigned char RFID1[16]={0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x07,0x80,0x29,0xff,0xff,0xff,0xff,0xff,0xff};
/*函数声明*/
unsigned char status;
unsigned char s=0x08;/* 函数名:RC522_Init* 描述  :初始化RC522配置* 输入  :无* 返回  : 无* 调用  :外部调用              */
void RC522_Init ( void )
{SPI1_Init(); RC522_Reset_Disable();          //将RST置高,启动内部复位阶段;PcdReset ();                  //复位RC522 PcdAntennaOff();              //关闭天线RC522_DELAY();                //delay 1msPcdAntennaOn();               //打开天线M500PcdConfigISOType ( 'A' ); //设置工作方式
}/* 函数名:SPI1_Init* 描述  :初始化SPI1的配置* 输入  :无* 返回  : 无* 调用  :外部调用              */
void SPI1_Init (void)
{SPI_InitTypeDef  SPI_InitStructure; GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(    RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE );//PORTB时钟使能 // CSGPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;       //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;        //IO口速度为50MHzGPIO_Init(GPIOA, &GPIO_InitStructure);                     //根据设定参数初始化PF0、PF1// SCKGPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;  GPIO_Init(GPIOA, &GPIO_InitStructure);// MISOGPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;     GPIO_Init(GPIOA, &GPIO_InitStructure);// MOSIGPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;     GPIO_Init(GPIOA, &GPIO_InitStructure);// RSTGPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;  GPIO_Init(GPIOB, &GPIO_InitStructure);//置高CS口RC522_CS_Disable();//其他SPI1配置SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;            //全双工;SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                                //主机模式;SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                            //传输数据为8位;SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;                                   //时钟极性CPOL为空闲时低电平;SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;                                 //时钟采样点为时钟奇数沿(上升沿);SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                                    //NSS引脚由软件改变;SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;          //预分频系数64;SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                           //MSB先行模式;SPI_InitStructure.SPI_CRCPolynomial = 7;                                     //CRC校验;//初始化SPI1SPI_Init(SPI1 , &SPI_InitStructure);//使能SPI1SPI_Cmd(SPI1 , ENABLE); }/* 函数名:PcdRese* 描述  :复位RC522 * 输入  :无* 返回  : 无* 调用  :外部调用              */
void PcdReset ( void )
{RC522_Reset_Disable();delay_us ( 1 );RC522_Reset_Enable();delay_us ( 1 );RC522_Reset_Disable();delay_us ( 1 );WriteRawRC ( CommandReg, 0x0f );while ( ReadRawRC ( CommandReg ) & 0x10 );delay_us ( 1 );WriteRawRC ( ModeReg, 0x3D );                //定义发送和接收常用模式 和Mifare卡通讯,CRC初始值0x6363WriteRawRC ( TReloadRegL, 30 );              //16位定时器低位    WriteRawRC ( TReloadRegH, 0 );               //16位定时器高位WriteRawRC ( TModeReg, 0x8D );                //定义内部定时器的设置WriteRawRC ( TPrescalerReg, 0x3E );             //设置定时器分频系数WriteRawRC ( TxAutoReg, 0x40 );              //调制发送信号为100%ASK
}/* 函数名:SPI_RC522_SendByte* 描述  :向RC522发送1 Byte 数据* 输入  :byte,要发送的数据* 返回  : RC522返回的数据* 调用  :内部调用                 */
u8 SPI_RC522_SendByte ( u8 byte )
{while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);         SPI_I2S_SendData(SPI1, byte);                     while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); return  SPI_I2S_ReceiveData(SPI1);
}/* 函数名:ReadRawRC* 描述  :读RC522寄存器* 输入  :ucAddress,寄存器地址* 返回  : 寄存器的当前值* 调用  :内部调用                 */
u8 ReadRawRC ( u8 ucAddress )
{u8 ucAddr, ucReturn;ucAddr = ( ( ucAddress << 1 ) & 0x7E ) | 0x80;      RC522_CS_Enable();SPI_RC522_SendByte ( ucAddr );ucReturn = SPI_RC522_ReadByte ();RC522_CS_Disable();return ucReturn;
}/* 函数名:WriteRawRC* 描述  :写RC522寄存器* 输入  :ucAddress,寄存器地址  、 ucValue,写入寄存器的值* 返回  : 无* 调用  :内部调用   */
void WriteRawRC ( u8 ucAddress, u8 ucValue )
{  u8 ucAddr;ucAddr = ( ucAddress << 1 ) & 0x7E;   RC522_CS_Enable();    SPI_RC522_SendByte ( ucAddr );SPI_RC522_SendByte ( ucValue );RC522_CS_Disable();
}/* 函数名:M500PcdConfigISOType* 描述  :设置RC522的工作方式* 输入  :ucType,工作方式* 返回  : 无* 调用  :外部调用        */
void M500PcdConfigISOType ( u8 ucType )
{if ( ucType == 'A')                     //ISO14443_A{ClearBitMask ( Status2Reg, 0x08 );        WriteRawRC ( ModeReg, 0x3D );//3F   WriteRawRC ( RxSelReg, 0x86 );//84WriteRawRC ( RFCfgReg, 0x7F );   //4FWriteRawRC ( TReloadRegL, 30 );//tmoLength);// TReloadVal = 'h6a =tmoLength(dec) WriteRawRC ( TReloadRegH, 0 );WriteRawRC ( TModeReg, 0x8D );WriteRawRC ( TPrescalerReg, 0x3E );delay_us   ( 2 );PcdAntennaOn ();//开天线}
}/** 函数名:SetBitMask* 描述  :对RC522寄存器置位* 输入  :ucReg,寄存器地址*         ucMask,置位值* 返回  : 无* 调用  :内部调用*/
void SetBitMask ( u8 ucReg, u8 ucMask )
{u8 ucTemp;ucTemp = ReadRawRC ( ucReg );WriteRawRC ( ucReg, ucTemp | ucMask );         // set bit mask
}/* 函数名:ClearBitMask* 描述  :对RC522寄存器清位* 输入  :ucReg,寄存器地址*         ucMask,清位值* 返回  : 无* 调用  :内部调用           */
void ClearBitMask ( u8 ucReg, u8 ucMask )
{u8 ucTemp;ucTemp = ReadRawRC ( ucReg );WriteRawRC ( ucReg, ucTemp & ( ~ ucMask) );  // clear bit mask}/* 函数名:PcdAntennaOn* 描述  :开启天线 * 输入  :无* 返回  : 无* 调用  :内部调用            */
void PcdAntennaOn ( void )
{u8 uc;uc = ReadRawRC ( TxControlReg );if ( ! ( uc & 0x03 ) )SetBitMask(TxControlReg, 0x03);}/* 函数名:PcdAntennaOff* 描述  :开启天线 * 输入  :无* 返回  : 无* 调用  :内部调用             */
void PcdAntennaOff ( void )
{ClearBitMask ( TxControlReg, 0x03 );
}void ShowID(u16 x,u16 y, u8 *p, u16 charColor, u16 bkColor)  //显示卡的卡号,以十六进制显示
{u8 num[9];printf("ID>>>%s\r\n", num);}/* 函数名:PcdComMF522* 描述  :通过RC522和ISO14443卡通讯* 输入  :ucCommand,RC522命令字*         pInData,通过RC522发送到卡片的数据*         ucInLenByte,发送数据的字节长度*         pOutData,接收到的卡片返回数据*         pOutLenBit,返回数据的位长度* 返回  : 状态值*         = MI_OK,成功* 调用  :内部调用              */
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  允许空闲中断IdleIEnucWaitFor = 0x10;       //认证寻卡等待时候 查询空闲中断标志位break;case PCD_TRANSCEIVE:      //接收发送 发送接收ucIrqEn   = 0x77;       //允许TxIEn RxIEn IdleIEn LoAlertIEn ErrIEn TimerIEnucWaitFor = 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 ] );          //写数据进FIFOdataWriteRawRC ( CommandReg, ucCommand );                 //写命令if ( ucCommand == PCD_TRANSCEIVE )SetBitMask(BitFramingReg,0x80);                //StartSend置位启动数据发送 该位与收发命令使用时才有效ul = 1000;//根据时钟频率调整,操作M1卡最大等待时间25msdo                                                         //认证 与寻卡等待时间    {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 );   }        }elsecStatus = MI_ERR;   }SetBitMask ( ControlReg, 0x80 );           // stop timer nowWriteRawRC ( CommandReg, PCD_IDLE ); return cStatus;
}/* 函数名:PcdRequest* 描述  :寻卡* 输入  :ucReq_code,寻卡方式*                     = 0x52,寻感应区内所有符合14443A标准的卡*                     = 0x26,寻未进入休眠状态的卡*         pTagType,卡片类型代码*                   = 0x4400,Mifare_UltraLight*                   = 0x0400,Mifare_One(S50)*                   = 0x0200,Mifare_One(S70)*                   = 0x0800,Mifare_Pro(X))*                   = 0x4403,Mifare_DESFire* 返回  : 状态值*         = MI_OK,成功* 调用  :外部调用            */
char PcdRequest ( u8 ucReq_code, u8 * pTagType )
{char cStatus;  u8 ucComMF522Buf [ MAXRLEN ]; u32 ulLen;ClearBitMask ( Status2Reg, 0x08 );  //清理指示MIFARECyptol单元接通以及所有卡的数据通信被加密的情况WriteRawRC ( BitFramingReg, 0x07 );   //  发送的最后一个字节的 七位SetBitMask ( TxControlReg, 0x03 ); //TX1,TX2管脚的输出信号传递经发送调制的13.56的能量载波信号ucComMF522Buf [ 0 ] = ucReq_code;      //存入 卡片命令字cStatus = PcdComMF522 ( PCD_TRANSCEIVE,  ucComMF522Buf, 1, ucComMF522Buf, & ulLen ); //寻卡  if ( ( cStatus == MI_OK ) && ( ulLen == 0x10 ) )  //寻卡成功返回卡类型 {    * pTagType = ucComMF522Buf [ 0 ];* ( pTagType + 1 ) = ucComMF522Buf [ 1 ];}elsecStatus = MI_ERR;return cStatus;
}/* 函数名:PcdAnticoll* 描述  :防冲撞* 输入  :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 ];           //读出UIDucSnr_check ^= ucComMF522Buf [ uc ];}if ( ucSnr_check != ucComMF522Buf [ uc ] )cStatus = MI_ERR;    }SetBitMask ( CollReg, 0x80 );return cStatus;
}/* 函数名:PcdSelect* 描述  :选定卡片* 输入  :pSnr,卡片序列号,4字节* 返回  : 状态值*         = MI_OK,成功* 调用  :外部调用         */
char PcdSelect ( u8 * pSnr )
{char ucN;u8 uc;u8 ucComMF522Buf [ MAXRLEN ]; u32  ulLen;ucComMF522Buf [ 0 ] = PICC_ANTICOLL1;ucComMF522Buf [ 1 ] = 0x70;ucComMF522Buf [ 6 ] = 0;for ( uc = 0; uc < 4; uc ++ ){ucComMF522Buf [ uc + 2 ] = * ( pSnr + uc );ucComMF522Buf [ 6 ] ^= * ( pSnr + uc );}CalulateCRC ( ucComMF522Buf, 7, & ucComMF522Buf [ 7 ] );ClearBitMask ( Status2Reg, 0x08 );ucN = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 9, ucComMF522Buf, & ulLen );if ( ( ucN == MI_OK ) && ( ulLen == 0x18 ) )ucN = MI_OK;  elseucN = MI_ERR;    return ucN;
}/* 函数名:CalulateCRC* 描述  :用RC522计算CRC16* 输入  :pIndata,计算CRC16的数组*         ucLen,计算CRC16的数组字节长度*         pOutData,存放计算结果存放的首地址* 返回  : 无* 调用  :内部调用              */
void CalulateCRC ( u8 * pIndata, u8 ucLen, u8 * pOutData )
{u8 uc, ucN;ClearBitMask(DivIrqReg,0x04);WriteRawRC(CommandReg,PCD_IDLE);SetBitMask(FIFOLevelReg,0x80);for ( uc = 0; uc < ucLen; uc ++)WriteRawRC ( FIFODataReg, * ( pIndata + uc ) );   WriteRawRC ( CommandReg, PCD_CALCCRC );uc = 0xFF;do {ucN = ReadRawRC ( DivIrqReg );uc --;} while ( ( uc != 0 ) && ! ( ucN & 0x04 ) );pOutData [ 0 ] = ReadRawRC ( CRCResultRegL );pOutData [ 1 ] = ReadRawRC ( CRCResultRegM );}/* 函数名:PcdAuthState* 描述  :验证卡片密码* 输入  :ucAuth_mode,密码验证模式*                     = 0x60,验证A密钥*                     = 0x61,验证B密钥*         u8 ucAddr,块地址*         pKey,密码*         pSnr,卡片序列号,4字节* 返回  : 状态值*         = MI_OK,成功* 调用  :外部调用          */
char PcdAuthState ( u8 ucAuth_mode, u8 ucAddr, u8 * pKey, u8 * pSnr )
{char cStatus;u8 uc, ucComMF522Buf [ MAXRLEN ];u32 ulLen;ucComMF522Buf [ 0 ] = ucAuth_mode;ucComMF522Buf [ 1 ] = ucAddr;for ( uc = 0; uc < 6; uc ++ )ucComMF522Buf [ uc + 2 ] = * ( pKey + uc );   for ( uc = 0; uc < 6; uc ++ )ucComMF522Buf [ uc + 8 ] = * ( pSnr + uc );   cStatus = PcdComMF522 ( PCD_AUTHENT, ucComMF522Buf, 12, ucComMF522Buf, & ulLen );if ( ( cStatus != MI_OK ) || ( ! ( ReadRawRC ( Status2Reg ) & 0x08 ) ) ){cStatus = MI_ERR; }return cStatus;
}/* 函数名:PcdWrite* 描述  :写数据到M1卡一块* 输入  :u8 ucAddr,块地址*         pData,写入的数据,16字节* 返回  : 状态值*         = MI_OK,成功* 调用  :外部调用           */
char PcdWrite ( u8 ucAddr, u8 * pData )
{char cStatus;u8 uc, ucComMF522Buf [ MAXRLEN ];u32 ulLen;ucComMF522Buf [ 0 ] = PICC_WRITE;ucComMF522Buf [ 1 ] = ucAddr;CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );if ( ( cStatus != MI_OK ) || ( ulLen != 4 ) || ( ( ucComMF522Buf [ 0 ] & 0x0F ) != 0x0A ) )cStatus = MI_ERR;   if ( cStatus == MI_OK ){memcpy(ucComMF522Buf, pData, 16);for ( uc = 0; uc < 16; uc ++ )ucComMF522Buf [ uc ] = * ( pData + uc );  CalulateCRC ( ucComMF522Buf, 16, & ucComMF522Buf [ 16 ] );cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 18, ucComMF522Buf, & ulLen );if ( ( cStatus != MI_OK ) || ( ulLen != 4 ) || ( ( ucComMF522Buf [ 0 ] & 0x0F ) != 0x0A ) )cStatus = MI_ERR;   } return cStatus;}/* 函数名:PcdRead* 描述  :读取M1卡一块数据* 输入  :u8 ucAddr,块地址*         pData,读出的数据,16字节* 返回  : 状态值*         = MI_OK,成功* 调用  :外部调用             */
char PcdRead ( u8 ucAddr, u8 * pData )
{char cStatus;u8 uc, ucComMF522Buf [ MAXRLEN ]; u32 ulLen;ucComMF522Buf [ 0 ] = PICC_READ;ucComMF522Buf [ 1 ] = ucAddr;CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );if ( ( cStatus == MI_OK ) && ( ulLen == 0x90 ) ){for ( uc = 0; uc < 16; uc ++ )* ( pData + uc ) = ucComMF522Buf [ uc ];   }elsecStatus = MI_ERR;   return cStatus;}/* 函数名:PcdHalt* 描述  :命令卡片进入休眠状态* 输入  :无* 返回  : 状态值*         = MI_OK,成功* 调用  :外部调用        */
char PcdHalt( void )
{u8 ucComMF522Buf [ MAXRLEN ]; u32  ulLen;ucComMF522Buf [ 0 ] = PICC_HALT;ucComMF522Buf [ 1 ] = 0;CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );return MI_OK;
}

3. 出现问题及解决方法:

本人在尝试读取卡片的时候也遇到了一个问题,调试过好几天硬件和软件后,还是不能寻到卡,keil单步调试一直表示寻卡返回状态参数为:MI_ERR,最后终于调试成功,错误原因在于开发板坏了……
不过在找错误的过程中也寻找了一些其他人调试失败的原因,引以为鉴:

  1. 硬件问题:这种情况一般出现在自己设计PCB的童鞋身上,那么这时候就应该先购买现成模块,在调试完代码并成功的基础上再调试硬件;
  2. SPI传输速率设置问题:SPI口例程中的预分频默认为4,而RC522中的SPI最高速率为10MHz/S,计算可知,预分频指数至少为8,所以适当升高预分频数,据反馈,预分频为8也容易出错,所以建议32或者64甚至为256;
  3. SPI时序问题:根据文档中的时序图,仔细设置SPI_InitStructure.SPI_CPOL和SPI_InitStructure.SPI_CPHA这两个参数;
  4. SPI口的GPIO模式设置:我以前在设计TM1638芯片为核心的灯、按键模组时也出现过这个问题,后来一般全部设置为推挽输出就基本不在出现这个问题;
  5. 天线在复位时需要先关闭再开启;

RC522(RFID模块)实践总结相关推荐

  1. 基础篇010.2 STM32驱动RC522 RFID模块之二:STM32硬件SPI驱动RC522

    目录 1. 实验硬件及原理图 1.1 RFID硬件 1.2 硬件原理图 2. 单片机与RFID硬件模块分析 3. 利用STM32CubeMX创建MDK工程 3.1 STM32CubeMX工程创建 3. ...

  2. Arduino使用RFID模块来储存卡信息实现智能门锁(MF RC522)

    使用RFID模块实现门禁的功能 Arduino的RFID模块的使用(MF RC522) 文章目录 使用RFID模块实现门禁的功能 Arduino的RFID模块的使用(MF RC522) 前言 一.RF ...

  3. 【毕设教程】单片机RFID模块的使用 - 物联网 嵌入式 毕业设计 stm32

    文章目录 1 简介 2 RFID模块 2.1 RFID模块组成 3 连接单片机 4 代码 6 最后 1 简介 Hi,大家好,这里是丹成学长,今天向大家介绍单片机项目开发常用外围器件 RFID模块的使用 ...

  4. 【Homeassistant 与RC522 RFID握手】

    [Homeassistant 与RC522 RFID握手] 1. RC522 RFID 2. 组件/集线器 2.1 通过 SPI 2.2 通过 I²C 2.3 on_tag行动 2.4 接线 3. S ...

  5. RFID模块+WIFI模块+振动传感器+有源蜂鸣器+舵机+Arduino UNO R3所构成的门禁系统模块

    该系统模块主要由RFID模块+WIFI模块+振动传感器+有源蜂鸣器+舵机+Arduino UNO R3组成的门禁系统模块.这里使用舵机充当门锁,用户可以刷卡开门,也可以通过APP控制舵机状态达到开门的 ...

  6. vue读取终端硬件信息_双通道RFID模块助力电力数据采集终端(不必多说,直接测试对比)...

    推动超高频技术应用的RFID模块 打包模组.全面简化设计 极高的性价比,让更多领域用得起超高频RFID M6002 是我司自主研发的一款高性能双通道嵌入式超高频RFID 读写模块,该模块主要是解决 单 ...

  7. 西门子S7-1200 S7-1500PLC编程案例,包含V90PN通讯,G120变频器 RFID模块

    西门子S7-1200 S7-1500PLC编程案例,包含V90PN通讯,G120变频器 RFID模块 GSDML HMI 触摸屏组态,FB284应用 ET200SP 多PLC1200 1500组网通讯 ...

  8. Arduino 电子积木 串口RFID模块

    RFID射频识别是一种非接触式的物体识别技术,它可以通过射频信号自动识别目标对象并获取相关数据,因此许多互动项目正是借用这一特性来实现对物体的识别的.通常来讲,一个最基本的RFID识系统主要由以下三部 ...

  9. Arduino提高篇21—RFID模块门禁设计

    RFID可应用于门禁系统,我们可以把数据写入标签,然后将有权限的标签进行记录保存,当有用户标签被扫描时,进行身份信息读取比对,然后根据权限进行相应的操作. 本篇配合OLED屏幕来模拟门禁系统,只对标签 ...

  10. 实践任务1:利用 HBuilderX制作产品展示模块+实践任务2:利用 HBuilderX制作公司网站首页+实践任务3: 利用 HBuilderX制作公司网站首页实现固定侧边菜单

    实践任务1:制作产品展示模块 创建一个产品展示product.html网页,在网页展示推荐产品的信息,包含产品图片.名称和规格说明,网页效果如图所示.本任务利用盒子模型来实现四个产品图的展示,使用盒子 ...

最新文章

  1. 康威生命游戏是如何搭建计算机的?
  2. 很多工程师问ESP32彩屏能不能在arduino上面来玩,这个是没有问题的
  3. 深入了解SpringCloud Hystrix
  4. 深度学习之循环神经网络(11-a)LSTM情感分类问题代码
  5. linux u盘 uid pid,linux下的pid文件的作用
  6. P4:神经网络与反向传播
  7. LeetCode 142. 环形链表 II(Linked List Cycle II)
  8. 讨论:多核CPU+ASIC的防火墙在以后是发展方向吗?
  9. 类的加载顺序(例题)
  10. 力扣-56 合并区间
  11. H3C PBR(策略路由)实验
  12. lua MVC框架 Orbit初探
  13. visio安装问题总结
  14. EasyExcel模板数据填充
  15. 因为相信所以看见,既然看见注定坚信《12》
  16. 全国计算机注册时密码为什么老是错误,电脑密码正确却显示密码错误怎么办
  17. 基于真实案例,浅谈EPC项目成本管理控制
  18. 菜鸡做题·20200418会员登陆页面(CTF)
  19. 压缩/解压(ICSharpCode.SharpZipLib 类库)
  20. 请列举出3个Vue中常用的生命周期钩子函数?

热门文章

  1. 码云 zheng 项目部署过程记录 eclispe部署过程
  2. 我看QQ与360之争
  3. windows2003下ISA防火墙的安装及简单配置
  4. mysql 修改max_allowed_packet_Mysql修改max_allowed_packet参数
  5. 自己动手搭建公司Git服务器
  6. 【10.16 胡测】Day4 第四波胡策题
  7. JavaScript hash 与 history 实现客户端路由的原理
  8. [问题已处理]-centos7 history命令没有任何记录
  9. 弱电安防监控系统的安装要点
  10. PIL (Python Imaging Library) 教程