STM32—驱动RFID-RC522模块

article/2025/8/23 0:46:05

文章目录

  • 一.S50(M1)卡介绍
    • 1.S50(M1)卡基础知识
    • 2.内部信息
    • 3.存取控制
    • 4.数据块的存取控制
    • 5.控制块的存取控
    • 6.工作原理
    • 7.M1与读卡器的通信
  • 二.RC522工程代码详解
    • 1.RC522与M1通信
    • 2.STM32对RC522寄存器的操作
    • 3.STM32对RC522的基础通信
    • 4.STM32控制RC522与M1的通信
    • 5.测试函数

一.S50(M1)卡介绍

1.S50(M1)卡基础知识

1.每张卡有唯一的序列号,32位
2.卡的容量是8Kbit的EEPROM
3.分为16个扇区,每个扇区分为4块,每块16个字节,以块为存取单位
4.每个扇区都有独立的一组密码和访问控制

2.内部信息

扇区0的块0用来固化厂商代码;
每个扇区的块3作为控制块,存放:密码A(6字节)、存取控制(4字节)、密码B(6字节)

每个扇区的块0、1、2作为数据块,其作用如下:
1.作为一般的数据存储,可以对其中的数据进行读写操作
2.用作数据值,可以进行初始化值、加值、减值、读值操作

3.存取控制

每个扇区的密码和存取控制都是独立的,存取控制是4个字节,即32位(在块3中)。
每个块都有存取条件,存取条件是由密码和存取控制共同决定的。
每个块都有相应的三个控制位,这三个控制位存在于存取控制字节中,相应的控制位决定了该块的访问权限,控制位如图:
在这里插入图片描述

就是说,每个扇区的所有块的存取条件控制位,都放在了该扇区的块3中,如图:
在这里插入图片描述

4.数据块的存取控制

对数据块,与就是块0、1、2的存取控制是由对应块的控制位来决定的:
在这里插入图片描述

从表中得知:对数据块的存取控制,由于存取控制由三个控制位所决定,所以相应的访问条件就产生了9种。
要想对数据块进行操作,首先要看该数据块的控制位是否允许对数据块的操作,如果允许操作,再看需要验证什么密码,只有验证密码正确后才可以对该数据块执行相应操作。
一般密码A的初始值都是0xFF…

5.控制块的存取控

块3(控制块)的存取操作与数据块不同,如图:
在这里插入图片描述

6.工作原理

电气部分:
卡片的电气部分由一个天线和一个ASIC组成。
天线:就是几组绕线的线圈,体积小,已经封装在卡片内
ASIC:ASIC即专用集成电路,是指应特定用户要求和特定电子系统的需要而设计、制造的集成电路。 目前用CPLD(复杂可编程逻辑器件)和 FPGA(现场可编程逻辑阵列)来进行ASIC设计是最为流行的方式之一,它们的共性是都具有用户现场可编程特性,都支持边界扫描技术,但两者在集成度、速度以及编程方式上具有各自的特点,这样理解,ASIC就是卡片特点的一个集成电路。
卡片的ASIC包含了一个高速(106KB)的RF接口、一个控制单元、一个8K的EEPROM

工作过程:
读卡器会向M1卡发送一组固定频率的电磁波,卡片内有一个LC串联谐振电路,其工作频率与读卡器发送的电磁波频率相同,遂在电磁波的激励下,LC串联谐振电路会发生共振,从而使电容内产生电荷,在电容的另一端接有一个单向导电的电子泵,电子泵将产生的电荷转移到另一个电容中存储。当存储电容中的电荷达到2V的时候,此时电容就作为电源为其他电路提供工作电压,所以卡片就可以向读卡器发送数据,或者从读卡器接收数据,实现了读卡器与卡片的通信。

7.M1与读卡器的通信

通信的流程图如示:
、、
复位应答(Request)
M1卡的通信协议和通信波特率是定义好的,当有卡片进入读卡器的工作范围时,读卡器要以特定的协议与卡片通信,从而确定卡片的卡型。

防冲突机制(Anticollision Loop)
当有多张卡片进入读写器操作范围时,会从中选择一张卡片进行操作,并返回选中卡片的序列号。

选择卡片(Select Tag)
选择被选中的卡的序列号,并同时返回卡的容量代码。

三次相互确认(3 Pass Authentication)
选定要处理的卡片后,读写器就要确定访问的扇区号,并且对扇区密码进行密码校验。在三次互相认证后就可以通过加密流进行通信。每次在选择扇区的时候都要进行扇区的密码校验。

对数据块的操作
读(Read):读一个块的数据;
写(Write):在一个块中写数据;
加(Increment):对数据块中的数值进行加值;
减(Decrement):对数据块中的数值进行减值;
传输(Transfer):将数据寄存器中的内容写入数据块中;
中止(Halt):暂停卡片的工作;

二.RC522工程代码详解

1.RC522与M1通信

用户通过单片机初始化RC522,然后通过单片机控制RC522与M1通信,那单片机是怎样与RC522通信的呢?
RC522通过SPI接口与单片机(STM32)通信,单片机向RC522内的寄存器写入特定的指令,RC522会根据寄存器中的值来执行相关操作,并与M1通信。所以要控制RC522,就必须了解RC522的寄存器和一些相关指令,这些东西厂家都会提供,所以我们只需要复制粘贴到我们的工程中使用即可。下面分享一下相关寄存器的地址和指令:

/
//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计算/
//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               //休眠/* RC522  FIFO长度定义 */
#define DEF_FIFO_LENGTH       64                 //FIFO size=64byte
#define MAXRLEN  18/* RC522寄存器定义 */
// PAGE 0
#define     RFU00                 0x00    //保留
#define     CommandReg            0x01    //启动和停止命令的执行
#define     ComIEnReg             0x02    //中断请求传递的使能(Enable/Disable)
#define     DivlEnReg             0x03    //中断请求传递的使能
#define     ComIrqReg             0x04    //包含中断请求标志
#define     DivIrqReg             0x05    //包含中断请求标志
#define     ErrorReg              0x06    //错误标志,指示执行的上个命令的错误状态
#define     Status1Reg            0x07    //包含通信的状态标识
#define     Status2Reg            0x08    //包含接收器和发送器的状态标志
#define     FIFODataReg           0x09    //64字节FIFO缓冲区的输入和输出
#define     FIFOLevelReg          0x0A    //指示FIFO中存储的字节数
#define     WaterLevelReg         0x0B    //定义FIFO下溢和上溢报警的FIFO深度
#define     ControlReg            0x0C    //不同的控制寄存器
#define     BitFramingReg         0x0D    //面向位的帧的调节
#define     CollReg               0x0E    //RF接口上检测到的第一个位冲突的位的位置
#define     RFU0F                 0x0F    //保留
// PAGE 1     
#define     RFU10                 0x10    //保留
#define     ModeReg               0x11    //定义发送和接收的常用模式
#define     TxModeReg             0x12    //定义发送过程的数据传输速率
#define     RxModeReg             0x13    //定义接收过程中的数据传输速率
#define     TxControlReg          0x14    //控制天线驱动器管教TX1和TX2的逻辑特性
#define     TxAutoReg             0x15    //控制天线驱动器的设置
#define     TxSelReg              0x16    //选择天线驱动器的内部源
#define     RxSelReg              0x17    //选择内部的接收器设置
#define     RxThresholdReg        0x18    //选择位译码器的阈值
#define     DemodReg              0x19    //定义解调器的设置
#define     RFU1A                 0x1A    //保留
#define     RFU1B                 0x1B    //保留
#define     MifareReg             0x1C    //控制ISO 14443/MIFARE模式中106kbit/s的通信
#define     RFU1D                 0x1D    //保留
#define     RFU1E                 0x1E    //保留
#define     SerialSpeedReg        0x1F    //选择串行UART接口的速率
// PAGE 2    
#define     RFU20                 0x20    //保留
#define     CRCResultRegM         0x21    //显示CRC计算的实际MSB值
#define     CRCResultRegL         0x22    //显示CRC计算的实际LSB值
#define     RFU23                 0x23    //保留
#define     ModWidthReg           0x24    //控制ModWidth的设置
#define     RFU25                 0x25    //保留
#define     RFCfgReg              0x26    //配置接收器增益
#define     GsNReg                0x27    //选择天线驱动器管脚(TX1和TX2)的调制电导
#define     CWGsCfgReg            0x28    //选择天线驱动器管脚的调制电导
#define     ModGsCfgReg           0x29    //选择天线驱动器管脚的调制电导
#define     TModeReg              0x2A    //定义内部定时器的设置
#define     TPrescalerReg         0x2B    //定义内部定时器的设置
#define     TReloadRegH           0x2C    //描述16位长的定时器重装值
#define     TReloadRegL           0x2D    //描述16位长的定时器重装值
#define     TCounterValueRegH     0x2E   
#define     TCounterValueRegL     0x2F    //显示16位长的实际定时器值
// PAGE 3      
#define     RFU30                 0x30    //保留
#define     TestSel1Reg           0x31    //常用测试信号配置
#define     TestSel2Reg           0x32    //常用测试信号配置和PRBS控制 
#define     TestPinEnReg          0x33    //D1-D7输出驱动器的使能管脚(仅用于串行接口)
#define     TestPinValueReg       0x34    //定义D1-D7用作I/O总线时的值
#define     TestBusReg            0x35    //显示内部测试总线的状态
#define     AutoTestReg           0x36    //控制数字自测试
#define     VersionReg            0x37    //显示版本
#define     AnalogTestReg         0x38    //控制管脚AUX1和AUX2
#define     TestDAC1Reg           0x39    //定义TestDAC1的测试值
#define     TestDAC2Reg           0x3A    //定义TestDAC2的测试值
#define     TestADCReg            0x3B    //显示ADCI和Q通道的实际值
#define     RFU3C                 0x3C    //保留
#define     RFU3D                 0x3D    //保留
#define     RFU3E                 0x3E    //保留
#define     RFU3F		  		        0x3F    //保留/* 和RC522通信时返回的错误代码 */
#define 	MI_OK                 0x26
#define 	MI_NOTAGERR           0xcc
#define 	MI_ERR                0xbb

既然RC522是通过SPI与单片机通信的,所以就会有相应的引脚配置,下面给出相关引脚的配置和一些引脚操作宏定义:

/* RC522引脚连接说明(SPI1的引脚) :
CS:PA4( 接的SDA引脚 )
SCK:PA5
MISO:PA6
MOSI:PA7
RST:PB0
*/
void RC522_GPIO_Init( void )
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE );GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;GPIO_Init( GPIOA, &GPIO_InitStructure );GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;GPIO_Init( GPIOA, &GPIO_InitStructure );GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;GPIO_Init( GPIOA, &GPIO_InitStructure );GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_Init( GPIOB, &GPIO_InitStructure );GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_Init( GPIOA, &GPIO_InitStructure );
}/* IO口操作函数 */
#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 )

我是通过软件模拟SPI与RC522通信的,SPI发送接收字节的代码如下(高位先行):

/* 软件模拟SPI发送一个字节数据,高位先行 */
void RC522_SPI_SendByte( uint8_t byte )
{uint8_t n;for( n=0;n<8;n++ ){if( byte&0x80 )RC522_MOSI_1();elseRC522_MOSI_0();Delay_us(200);RC522_SCK_0();Delay_us(200);RC522_SCK_1();Delay_us(200);byte<<=1;}
}/* 软件模拟SPI读取一个字节数据,先读高位 */
uint8_t RC522_SPI_ReadByte( void )
{uint8_t n,data;for( n=0;n<8;n++ ){data<<=1;RC522_SCK_0();Delay_us(200);if( RC522_MISO_GET()==1 )data|=0x01;Delay_us(200);RC522_SCK_1();Delay_us(200);}return data;
}

单片机和RC522之间的通信基础机制就建立起来了,下一步就是建立在通信基础上的操作了。

2.STM32对RC522寄存器的操作

上面说了,单片机是向RC522的寄存器操作来驱动RC522的,所以会有这几种基本操作:

  • 读取RC522指定寄存器的值
  • 向RC522指定寄存器中写入指定的数据
  • 置位RC522指定寄存器的指定位
  • 清位RC522指定寄存器的指定位

下面给出这些操作的函数实现:

/*** @brief  :读取RC522指定寄存器的值* @param  :Address:寄存器的地址* @retval :寄存器的值
*/
uint8_t RC522_Read_Register( uint8_t Address )
{uint8_t data,Addr;Addr = ( (Address<<1)&0x7E )|0x80;RC522_CS_Enable();RC522_SPI_SendByte( Addr );data = RC522_SPI_ReadByte();//读取寄存器中的值RC522_CS_Disable();return data;
}/*** @brief  :向RC522指定寄存器中写入指定的数据* @param  :Address:寄存器地址data:要写入寄存器的数据* @retval :无
*/
void RC522_Write_Register( uint8_t Address, uint8_t data )
{uint8_t Addr;Addr = ( Address<<1 )&0x7E;RC522_CS_Enable();RC522_SPI_SendByte( Addr );RC522_SPI_SendByte( data );RC522_CS_Disable();}/*** @brief  :置位RC522指定寄存器的指定位* @param  :Address:寄存器地址mask:置位值* @retval :无
*/
void RC522_SetBit_Register( uint8_t Address, uint8_t mask )
{uint8_t temp;/* 获取寄存器当前值 */temp = RC522_Read_Register( Address );/* 对指定位进行置位操作后,再将值写入寄存器 */RC522_Write_Register( Address, temp|mask );
}/*** @brief  :清位RC522指定寄存器的指定位* @param  :Address:寄存器地址mask:清位值* @retval :无
*/
void RC522_ClearBit_Register( uint8_t Address, uint8_t mask )
{uint8_t temp;/* 获取寄存器当前值 */temp = RC522_Read_Register( Address );/* 对指定位进行清位操作后,再将值写入寄存器 */RC522_Write_Register( Address, temp&(~mask) );
}

知道了对RC522寄存器的操作,就可以结合相关的指令,对RC522写入指令控制RC522了,下面接收一下RC522的基本操作。

3.STM32对RC522的基础通信

上面说了寄存器、指令、对寄存器的操作,这里介绍一些对RC522的基本操作,包括:

  • 开启天线
  • 关闭天线
  • 复位RC522
  • 设置RC522工作方式

RC522与M1通信前必须开启天线,进行复位,然后设置RC522的工作方式!下面介绍一下相关代码:

/*** @brief  :开启天线* @param  :无* @retval :无
*/
void RC522_Antenna_On( void )
{uint8_t k;k = RC522_Read_Register( TxControlReg );/* 判断天线是否开启 */if( !( k&0x03 ) )RC522_SetBit_Register( TxControlReg, 0x03 );
}/*** @brief  :关闭天线* @param  :无* @retval :无
*/
void RC522_Antenna_Off( void )
{/* 直接对相应位清零 */RC522_ClearBit_Register( TxControlReg, 0x03 );
}/*** @brief  :复位RC522* @param  :无* @retval :无
*/
void RC522_Rese( void )
{RC522_Reset_Disable();Delay_us ( 1 );RC522_Reset_Enable();Delay_us ( 1 );RC522_Reset_Disable();Delay_us ( 1 );RC522_Write_Register( CommandReg, 0x0F );while( RC522_Read_Register( CommandReg )&0x10 );/* 缓冲一下 */Delay_us ( 1 );RC522_Write_Register( ModeReg, 0x3D );       //定义发送和接收常用模式RC522_Write_Register( TReloadRegL, 30 );     //16位定时器低位RC522_Write_Register( TReloadRegH, 0 );      //16位定时器高位RC522_Write_Register( TModeReg, 0x8D );      //内部定时器的设置RC522_Write_Register( TPrescalerReg, 0x3E ); //设置定时器分频系数RC522_Write_Register( TxAutoReg, 0x40 );     //调制发送信号为100%ASK
}/*** @brief  :设置RC522的工作方式* @param  :Type:工作方式* @retval :无M500PcdConfigISOType
*/
void RC522_Config_Type( char Type )
{if( Type=='A' ){RC522_ClearBit_Register( Status2Reg, 0x08 );RC522_Write_Register( ModeReg, 0x3D );RC522_Write_Register( RxSelReg, 0x86 );RC522_Write_Register( RFCfgReg, 0x7F );RC522_Write_Register( TReloadRegL, 30 );RC522_Write_Register( TReloadRegH, 0 );RC522_Write_Register( TModeReg, 0x8D );RC522_Write_Register( TPrescalerReg, 0x3E );Delay_us(2);/* 开天线 */RC522_Antenna_On();}
}

对于这些寄存器和指令的宏定义,查一下前面的说明即可。

4.STM32控制RC522与M1的通信

这部分是最重要的步骤,RC522与M1的通信是工程要实现的目的,而且要遵守前面提到的M1卡与RC522通信的步骤以及M1卡的内部构造,包括以下操作:

  • 通过RC522和M1卡通讯(数据的双向传输)
  • 寻卡
  • 防冲突
  • 用RC522计算CRC16(循环冗余校验)
  • 选定卡片
  • 校验卡片密码
  • 在M1卡的指定块地址写入指定数据
  • 读取M1卡的指定块地址的数据
  • 让卡片进入休眠模式

话不多说,上代码,代码中都有按照我理解的一些注释:

/*** @brief  :通过RC522和ISO14443卡通讯
* @param  :ucCommand:RC522命令字*          pInData:通过RC522发送到卡片的数据*          ucInLenByte:发送数据的字节长度*          pOutData:接收到的卡片返回数据*          pOutLenBit:返回数据的位长度* @retval :状态值MI_OK,成功
*/
char PcdComMF522 ( uint8_t ucCommand, uint8_t * pInData, uint8_t ucInLenByte, uint8_t * pOutData, uint32_t * pOutLenBit )		
{char cStatus = MI_ERR;uint8_t ucIrqEn   = 0x00;uint8_t ucWaitFor = 0x00;uint8_t ucLastBits;uint8_t ucN;uint32_t 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;}RC522_Write_Register ( ComIEnReg, ucIrqEn | 0x80 );		//IRqInv置位管脚IRQ与Status1Reg的IRq位的值相反 RC522_ClearBit_Register ( ComIrqReg, 0x80 );			//Set1该位清零时,CommIRqReg的屏蔽位清零RC522_Write_Register ( CommandReg, PCD_IDLE );		//写空闲命令RC522_SetBit_Register ( FIFOLevelReg, 0x80 );			//置位FlushBuffer清除内部FIFO的读和写指针以及ErrReg的BufferOvfl标志位被清除for ( ul = 0; ul < ucInLenByte; ul ++ )RC522_Write_Register ( FIFODataReg, pInData [ ul ] );    		//写数据进FIFOdataRC522_Write_Register ( CommandReg, ucCommand );					//写命令if ( ucCommand == PCD_TRANSCEIVE )RC522_SetBit_Register(BitFramingReg,0x80);  				//StartSend置位启动数据发送 该位与收发命令使用时才有效ul = 1000;//根据时钟频率调整,操作M1卡最大等待时间25msdo 														//认证 与寻卡等待时间	{ucN = RC522_Read_Register ( ComIrqReg );							//查询事件中断ul --;} while ( ( ul != 0 ) && ( ! ( ucN & 0x01 ) ) && ( ! ( ucN & ucWaitFor ) ) );		//退出条件i=0,定时器中断,与写空闲命令RC522_ClearBit_Register ( BitFramingReg, 0x80 );					//清理允许StartSend位if ( ul != 0 ){if ( ! ( RC522_Read_Register ( ErrorReg ) & 0x1B ) )			//读错误标志寄存器BufferOfI CollErr ParityErr ProtocolErr{cStatus = MI_OK;if ( ucN & ucIrqEn & 0x01 )					//是否发生定时器中断cStatus = MI_NOTAGERR;   if ( ucCommand == PCD_TRANSCEIVE ){ucN = RC522_Read_Register ( FIFOLevelReg );			//读FIFO中保存的字节数ucLastBits = RC522_Read_Register ( 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 ] = RC522_Read_Register ( FIFODataReg );   					}					}			elsecStatus = MI_ERR;   			}RC522_SetBit_Register ( ControlReg, 0x80 );           // stop timer nowRC522_Write_Register ( CommandReg, PCD_IDLE ); return cStatus;		
}/*** @brief  :寻卡
* @param  ucReq_code,寻卡方式
*                      = 0x52:寻感应区内所有符合14443A标准的卡*                     = 0x26:寻未进入休眠状态的卡*         pTagType,卡片类型代码*                   = 0x4400:Mifare_UltraLight*                   = 0x0400:Mifare_One(S50)*                   = 0x0200:Mifare_One(S70)*                   = 0x0800:Mifare_Pro(X))*                   = 0x4403:Mifare_DESFire* @retval :状态值MI_OK,成功
*/
char PcdRequest ( uint8_t ucReq_code, uint8_t * pTagType )
{char cStatus;  uint8_t ucComMF522Buf [ MAXRLEN ]; uint32_t ulLen;RC522_ClearBit_Register ( Status2Reg, 0x08 );	//清理指示MIFARECyptol单元接通以及所有卡的数据通信被加密的情况RC522_Write_Register ( BitFramingReg, 0x07 );	//	发送的最后一个字节的 七位RC522_SetBit_Register ( TxControlReg, 0x03 );	//TX1,TX2管脚的输出信号传递经发送调制的13.56的能量载波信号ucComMF522Buf [ 0 ] = ucReq_code;		//存入寻卡方式/* PCD_TRANSCEIVE:发送并接收数据的命令,RC522向卡片发送寻卡命令,卡片返回卡的型号代码到ucComMF522Buf中 */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;	 
}/*** @brief  :防冲突* @param  :Snr:卡片序列,4字节,会返回选中卡片的序列* @retval :状态值MI_OK,成功
*/
char PcdAnticoll ( uint8_t * pSnr )
{char cStatus;uint8_t uc, ucSnr_check = 0;uint8_t ucComMF522Buf [ MAXRLEN ]; uint32_t ulLen;RC522_ClearBit_Register ( Status2Reg, 0x08 );		//清MFCryptol On位 只有成功执行MFAuthent命令后,该位才能置位RC522_Write_Register ( BitFramingReg, 0x00);		//清理寄存器 停止收发RC522_ClearBit_Register ( CollReg, 0x80 );			//清ValuesAfterColl所有接收的位在冲突后被清除ucComMF522Buf [ 0 ] = 0x93;	//卡片防冲突命令ucComMF522Buf [ 1 ] = 0x20;/* 将卡片防冲突命令通过RC522传到卡片中,返回的是被选中卡片的序列 */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;    	 }    RC522_SetBit_Register ( CollReg, 0x80 );		return cStatus;		
}/*** @brief   :用RC522计算CRC16(循环冗余校验)* @param  :pIndata:计算CRC16的数组*            ucLen:计算CRC16的数组字节长度*            pOutData:存放计算结果存放的首地址* @retval :状态值MI_OK,成功
*/
void CalulateCRC ( uint8_t * pIndata, u8 ucLen, uint8_t * pOutData )
{uint8_t uc, ucN;RC522_ClearBit_Register(DivIrqReg,0x04);	RC522_Write_Register(CommandReg,PCD_IDLE);	RC522_SetBit_Register(FIFOLevelReg,0x80);for ( uc = 0; uc < ucLen; uc ++)RC522_Write_Register ( FIFODataReg, * ( pIndata + uc ) );   RC522_Write_Register ( CommandReg, PCD_CALCCRC );uc = 0xFF;do {ucN = RC522_Read_Register ( DivIrqReg );uc --;} while ( ( uc != 0 ) && ! ( ucN & 0x04 ) );pOutData [ 0 ] = RC522_Read_Register ( CRCResultRegL );pOutData [ 1 ] = RC522_Read_Register ( CRCResultRegM );}/*** @brief   :选定卡片* @param  :pSnr:卡片序列号,4字节* @retval :状态值MI_OK,成功
*/
char PcdSelect ( uint8_t * pSnr )
{char ucN;uint8_t uc;uint8_t ucComMF522Buf [ MAXRLEN ]; uint32_t  ulLen;/* PICC_ANTICOLL1:防冲突命令 */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 ] );RC522_ClearBit_Register ( Status2Reg, 0x08 );ucN = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 9, ucComMF522Buf, & ulLen );if ( ( ucN == MI_OK ) && ( ulLen == 0x18 ) )ucN = MI_OK;  elseucN = MI_ERR;    return ucN;}/*** @brief   :校验卡片密码* @param  :ucAuth_mode:密码验证模式*                     = 0x60,验证A密钥*                     = 0x61,验证B密钥*           ucAddr:块地址*           pKey:密码*           pSnr:卡片序列号,4字节* @retval :状态值MI_OK,成功
*/
char PcdAuthState ( uint8_t ucAuth_mode, uint8_t ucAddr, uint8_t * pKey, uint8_t * pSnr )
{char cStatus;uint8_t uc, ucComMF522Buf [ MAXRLEN ];uint32_t ulLen; ucComMF522Buf [ 0 ] = ucAuth_mode;ucComMF522Buf [ 1 ] = ucAddr;/* 前俩字节存储验证模式和块地址,2~8字节存储密码(6个字节),8~14字节存储序列号 */for ( uc = 0; uc < 6; uc ++ )ucComMF522Buf [ uc + 2 ] = * ( pKey + uc );   for ( uc = 0; uc < 6; uc ++ )ucComMF522Buf [ uc + 8 ] = * ( pSnr + uc );   /* 进行冗余校验,14~16俩个字节存储校验结果 */cStatus = PcdComMF522 ( PCD_AUTHENT, ucComMF522Buf, 12, ucComMF522Buf, & ulLen );/* 判断验证是否成功 */if ( ( cStatus != MI_OK ) || ( ! ( RC522_Read_Register ( Status2Reg ) & 0x08 ) ) )cStatus = MI_ERR;   return cStatus;}/*** @brief   :在M1卡的指定块地址写入指定数据* @param  :ucAddr:块地址*           pData:写入的数据,16字节* @retval :状态值MI_OK,成功
*/
char PcdWrite ( uint8_t ucAddr, uint8_t * pData )
{char cStatus;uint8_t uc, ucComMF522Buf [ MAXRLEN ];uint32_t ulLen;ucComMF522Buf [ 0 ] = PICC_WRITE;//写块命令ucComMF522Buf [ 1 ] = ucAddr;//写块地址/* 进行循环冗余校验,将结果存储在& ucComMF522Buf [ 2 ] */CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );/* PCD_TRANSCEIVE:发送并接收数据命令,通过RC522向卡片发送写块命令 */cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );/* 通过卡片返回的信息判断,RC522是否与卡片正常通信 */if ( ( cStatus != MI_OK ) || ( ulLen != 4 ) || ( ( ucComMF522Buf [ 0 ] & 0x0F ) != 0x0A ) )cStatus = MI_ERR;   if ( cStatus == MI_OK ){//memcpy(ucComMF522Buf, pData, 16);/* 将要写入的16字节的数据,传入ucComMF522Buf数组中 */for ( uc = 0; uc < 16; uc ++ )ucComMF522Buf [ uc ] = * ( pData + uc );  /* 冗余校验 */CalulateCRC ( ucComMF522Buf, 16, & ucComMF522Buf [ 16 ] );/* 通过RC522,将16字节数据包括2字节校验结果写入卡片中 */cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 18, ucComMF522Buf, & ulLen );/* 判断写地址是否成功 */if ( ( cStatus != MI_OK ) || ( ulLen != 4 ) || ( ( ucComMF522Buf [ 0 ] & 0x0F ) != 0x0A ) )cStatus = MI_ERR;   			} return cStatus;	
}/*** @brief   :读取M1卡的指定块地址的数据* @param  :ucAddr:块地址*           pData:读出的数据,16字节* @retval :状态值MI_OK,成功
*/
char PcdRead ( uint8_t ucAddr, uint8_t * pData )
{char cStatus;uint8_t uc, ucComMF522Buf [ MAXRLEN ]; uint32_t ulLen;ucComMF522Buf [ 0 ] = PICC_READ;ucComMF522Buf [ 1 ] = ucAddr;/* 冗余校验 */CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );/* 通过RC522将命令传给卡片 */cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );/* 如果传输正常,将读取到的数据传入pData中 */if ( ( cStatus == MI_OK ) && ( ulLen == 0x90 ) ){for ( uc = 0; uc < 16; uc ++ )* ( pData + uc ) = ucComMF522Buf [ uc ];   }elsecStatus = MI_ERR;   return cStatus;}/*** @brief   :让卡片进入休眠模式* @param  :无* @retval :状态值MI_OK,成功
*/
char PcdHalt( void )
{uint8_t ucComMF522Buf [ MAXRLEN ]; uint32_t  ulLen;ucComMF522Buf [ 0 ] = PICC_HALT;ucComMF522Buf [ 1 ] = 0;CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );return MI_OK;}

详情请看代码。

5.测试函数

通过测试函数来试一下对M1卡的识别,读取数据等。

在这里插入代码片char cStr [ 30 ];
/* 卡的ID存储,32位,4字节 */
u8 ucArray_ID [ 4 ];
/*** @brief  : 测试代码,读取卡片ID* @param  :无* @retval :无
*/
void IC_test ( void )
{                                                                                       uint8_t ucStatusReturn;    //返回状态                                       while ( 1 ){ /* 寻卡(方式:范围内全部),第一次寻卡失败后再进行一次,寻卡成功时卡片序列传入数组ucArray_ID中 */if ( ( ucStatusReturn = PcdRequest ( PICC_REQALL, ucArray_ID ) ) != MI_OK )ucStatusReturn = PcdRequest ( PICC_REQALL, ucArray_ID );		   if ( ucStatusReturn == MI_OK  ){/* 防冲突操作,被选中的卡片序列传入数组ucArray_ID中 */if ( PcdAnticoll ( ucArray_ID ) == MI_OK ){sprintf ( cStr, "The Card ID is: %02X%02X%02X%02X", ucArray_ID [ 0 ], ucArray_ID [ 1 ], ucArray_ID [ 2 ], ucArray_ID [ 3 ] );				printf ("%s\r\n",cStr ); }}}
}

代码亲测可以用,有什么疑问可以多交流。
q:2723808286


http://chatgpt.dhexx.cn/article/bdpo94ob.shtml

相关文章

STM32F103+RFID-RC522模块 实现简单读卡写卡demo

目录 前言特别声明:代码下载&#xff1a;功能介绍&#xff1a; 接线STM32STM32F1开发指南(精英版)-库函数版本_V1.2STM32中文参考手册 RFID-RC522RFID射频模块电路原理图 使用图效果图测试程序0 RC522_Handle()最终效果一、先用手机软件NFC Writer读取空卡看看内容1、打开软件和…

使用stm32驱动RC522读取IC卡

stm32驱动RC522 RC522与PN532简介关于STM32驱动方式接线说明程序烧录查看卡号总结 原文链接&#xff1a;https://www.yourcee.com/newsinfo/2924379.html 点击图片购买 RC522与PN532简介 在写这篇文章之前有写过一篇有关于PN532的文章&#xff0c;RC522与PN532在使用上都可以用…

RC522(RFID模块)实践总结

此次使用RC522模块和S50卡实现近场通讯功能&#xff08;开发板与RC522通讯方式为硬件SPI&#xff09;&#xff0c;就实践过程中的一些知识点进行总结&#xff1a; RC522模块和M1卡要点介绍&#xff1b;驱动代码&#xff1b;出现问题及解决方法&#xff1b; 1. RC522模块和M1卡…

RC522应用总结

公司需要做刷卡模块&#xff0c;因此选了RC522做demo程序。下面就RC522知识做简要的总结。 本人使用stm32的硬件spi接口搭建工程&#xff0c;相关的配置如下&#xff1a; spi配置&#xff1a; 引脚配置 SDA -------PA4 SCLK ----PB13 MOSI -------PB15 MISO ------PB14 IRQ —没…

RC522 - NFC刷卡模块

RC522 - NFC刷卡模块 芯片介绍/引脚介绍 MF RC522 是应用于 13.56MHz 非接触式通信中高集成度读写卡系列芯片中的一员。是 NXP 公司针对“三表”应用推出的一款低电压、低成本、体积小的非接触式读写卡芯片&#xff0c;是智能仪表和便携式手持设备研发的较好选择。 非接触式…

STM32--RFID无线射频技术(RC522刷卡模块)

文章目录 1、RFID的概念2、RFID的工作原理&#xff08;1&#xff09;RFID中间件的概念&#xff08;2&#xff09;RFID中间件具有以下特点:&#xff08;3&#xff09;RFID中间件的意义: 3、RFID频率划分&#xff08;1&#xff09;RFID低频特性&#xff08;2&#xff09;RFID高频…

STM32——NFC门禁模块(RC522)

文章目录 1. 前言&#xff08;包括一些个人理解&#xff09;2. RC522门禁工作过程3. CubeMx配置3.1 SPI通讯的配置3.2 SDA和REST引脚的配置 4. 外设代码函数编写4.1 主函数能调用的接口函数4.2 二级内部调用函数4.3 第三级最底层函数4.4 头文件 5. 使用教程 1. 前言&#xff08…

RC522(RFID)读写驱动

文章目录 1、RC522驱动原理2、手机APP查看卡信息3、驱动移植4、读写卡5、源码 1、RC522驱动原理 我们常见的RC522大概如下所示&#xff0c;PCB部分是主机&#xff0c;然后白色的和绿色的都是IC卡&#xff0c;IC卡可以存储信息&#xff0c;通过靠近PCB主机部分就可以被感应到从…

RC522模块学习

目录 1.原理简介 2.SPI通信 3.获取卡号实验 3.驱动函数 参考&#xff1a; https://www.cnblogs.com/ivantang/p/3904025.html https://xiaolong.blog.csdn.net/article/details/117075834?spm1001.2014.3001.5506 https://blog.csdn.net/weixin_47316662/article/detai…

Jmeter下载安装配置---测试小白

一&#xff0c;进入官网&#xff1a;http://jmeter.apache.org/ 1.第一步进入官网如下图 2.选择进行下载&#xff0c;下载下来为一个压缩包&#xff0c;解压即可。 3.我下载的是jmeter4.0版本&#xff0c;对应jdk1.8。然后就进行解压。个人认为要注意3点&#xff1a;1.解压之后…

Windows版JMeter下载安装

进入apache官网https://www.apache.org/dist/jmeter/binaries下载Windows版本JMeter&#xff1b;配置环境变量&#xff08;JMETER_HOMED:\JMeter\apache-jmeter-5.1.1&#xff09;配置环境变量&#xff08;classpath%JMETER_HOME%\lib\ext\ApacheJMeter_core.jar;%JMETER_HOME%…

JMeter下载安装

一、环境准备 1.Jmeter是纯Java开发的&#xff0c; 能够运行Java程序的系统一般都可以运行Jmeter&#xff0c; 如&#xff1a;Windows、 Linux、 mac等。由于是由Java开发&#xff0c;所以自然需要jdk环境。 2.去官方下载JMeter安装包&#xff1a;Apache JMeter - Download Apa…

Mac版本Jmeter下载安装教程

Mac版本Jmeter下载安装教程 1. 下载&安装&启动Jmeter1.1 下载安装包&#xff1a;1.2 解压安装包1.3 启动Jmeter1.4 Jmeter页面 2. Jmeter进一步优化配置2.1 打开&编辑&保存配置文件~/.bash_profile步骤一&#xff1a;打开配置文件~/.bash_profile步骤二&#x…

JMeter-文件下载教程

一、单个下载配置方法 1、先添加线程组 选中测试计划右键添加线程组&#xff1a;测试计划->添加->线程(用户)->线程组 2、添加HTTP请求 ①. 选中线程组右键添加HTTP请求&#xff1a;线程组->添加->取样器->HTTP请求 ②.配置HPPT请求 3、添加BeanShell 取…

jmeter下载与安装教程

jmeter下载与安装教程 下载步骤&#xff1a; 自行百度jmeter下载&#xff0c;找到apache官网&#xff1b;进入网站后&#xff0c;选择左侧Download下的Download Releases&#xff1b;如图所示 2.页面进入新的页面后&#xff0c;点击此页面Binaries标签下的“apache-jmeter-5…

Jmeter下载及安装教程

下载及环境配置 下载 前置条件&#xff0c;电脑得配置Java环境&#xff0c;也就是安装jdk&#xff0c;可百度jdk安装教程。 1、下载地址&#xff1a;Apache JMeter - Download Apache JMeter&#xff0c;&#xff08;Windows版本下载.zip&#xff0c;Linux版本下载.tgz&…

Jmeter下载安装详细步骤(2021)

Jmeter下载安装详细步骤&#xff08;2021&#xff09; 由于项目方有压测需求&#xff0c;因此需要学习Jmeter压测工具的使用&#xff0c;避免遗忘&#xff0c;记录一下Jmeter下载安装的步骤 这里面有个坑就是Jmeter需要依赖JAVA环境&#xff0c;在安装Jmeter之前需要配置好JA…

测试工具:jmeter的下载以及使用

测试不仅要了解项目整体流程&#xff0c;还需要会使用各种测试工具&#xff0c;比如&#xff1a;jmeter压力测试工具。 1.下载地址 https://jmeter.apache.org/download_jmeter.cgi2.下载完直接解压&#xff0c;配置环境 &#xff08;1&#xff09;此电脑-右键属性-高级系统设…

Jmeter安装配置详细教程

目录 1、Jmeter下载1.1、下载地址1.2、选择对应版本 2、JDK安装2.1、java环境检查2.2、JDK安装2.2.1、JDK下载2.2.2、JDK安装2.2.3、JDK环境变量配置2.2.4、验证java环境 3、Jmeter环境部署3.1、环境配置 4、验证jmeter5、修改语言5.1、临时性设置5.2、永久性生效 1、Jmeter下载…

Jmeter常用插件下载

一、jmeter Manager jmeter插件下载地址&#xff1a;https://jmeter-plugins.org/wiki/Start/ 二、PerfMon Metrics Collector&#xff1a;服务器性能监控数据采集器 在性能测试过程中&#xff0c;除了监控TPS和TRT&#xff0c;还需要监控服务器的资源使用情况&#xff0c;比…