文章目录

  • 一.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

STM32—驱动RFID-RC522模块相关推荐

  1. stm32驱动RFID高频读卡器读取IC卡

    stm32驱动RFID读卡器读取IC卡 1.介绍RFID 2.RFID控制指令 2.1 读IC卡号 2.2 读IC卡数据块 2.3 写数据到IC卡数据块 2.4 读取RFID读卡器用户数据 2.5 向 ...

  2. STM32驱动W5100S网口模块实现UDP通讯

    本文博客链接:https://blog.csdn.net/zsj2016o/article/details/88883863 作者:zsj ,转载请注明. STM32驱动W5100S网口模块实现UDP ...

  3. STM32 驱动 ESP8266 WIFI 模块

    目录 1. 模块引脚 2. 测试模块是否正常 3. AT指令 ​4. ESP8266三种工作模式 5. 配置WIFI模块 6. STM32驱动WIFI模块 拿到一个野火的esp8266模块,驱一下试试 ...

  4. STM32驱动HC-SR04超声波模块

    STM32学习笔记--HC-SR04超声波测距模块 碰巧学校老师要求做个HC-SR04超声波的实验,笔者在完成实验报告的同时,也顺带完成一篇STM32驱动超声波模块记录. HC-SR04模块使用 ST ...

  5. ITOP4412 RFID RC522模块

    RC522是高度集成的非接触式读写卡芯片.此发送模块利用调制和解调原理,并将它们完全集成到各种非接触式通信方法和协议中.可通过无线电讯号识别特定目标并读写相关数据,而无需识别系统与特定目标之间建立机械 ...

  6. stm32驱动Lora串口模块

    本文使用的硬件 stm32MCU+亿佰特-Lora串口模块,型号E32-433T20D 定义各配置字节 /*配置报文头部选项*/ #define HEAD_Save 0xC0//配置后掉电保存 #de ...

  7. STM32—驱动BT-06蓝牙模块传输数据

    文章目录 BT-06简介 数据透传 配置串口 USART1初始化函数 USART2初始化函数 USART2的NVIC配置 USART1串口重映射 BT-06简介 BT06蓝牙模块是专为智能无线数据传输 ...

  8. STM32驱动TEA5767收音机模块

    Tea5767是飞利浦公司出的一款集成化的收音机芯片,大四的时候机缘巧合遇到了这个芯片,用了一下,写点资料 主要特性 TEA5767HN是一款低功耗立体声收音IC,广泛应用于手机MP3 .MP 4 播 ...

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

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

  10. ESP32学习笔记(49)——RFID RC522使用

    一.简介 MF RC522 是应用于 13.56MHz 非接触式通信中高集成度读写卡系列芯片中的一员.是 NXP 公司针对"三表"应用推出的一款低电压.低成本.体积小的非接触式读写 ...

最新文章

  1. pythonwhile循环结束语句_Python while循环语句
  2. android popupwindow 自定义背景,android – 无法将PopupWindow背景设置为透明
  3. 人生第一个过 5K Star 的 项目 x-spreadsheet 感谢各位大佬的支持
  4. java 访问线程_java线程简介(共享对数据的访问)
  5. png变成矢量图_[PPT]如何将图片背景变成透明
  6. MongoDB存储基础教程
  7. [css] 如何做图片预览,如何放大一个图片?
  8. ppython_Python pcom包_程序模块 - PyPI - Python中文网
  9. 买了社保,再买农村医保是不是多余?
  10. python窗口大小动态变化_python – 如何让tkinter画布动态调整窗口宽度?
  11. linux ios文件是否存在,Linux如何读取iOS镜像文件
  12. 收到“【有奖话题】虚拟空间“筑梦师”,谈谈微软虚拟化 ”礼物一个
  13. es mapping 设置
  14. 有妇如此,夫复何求!
  15. 常见字读音(粤语)---(1)
  16. 布局区块链数据中心的互联在线,积极筹备精选层
  17. 【单片机毕业设计】【mcuclub-103】智能花盆 | 智能养殖箱 | 多功能花盆 | 多功能养殖箱【仿真设计】
  18. 超过ChatGPT3达到ChatGPT4%90性能的小羊驼来了-Vicuna(校招社招必备,chatgpt风口来了赶紧学起来吧)
  19. 庄周带你练习数据库语句复习常备之【JavaWeb阶段学习】
  20. 2022年儿童写作业的台灯怎么选择?推荐儿童护眼台灯品牌排行

热门文章

  1. Splunk lookup 实践
  2. 淦ORB-SLAM2源码 09--SIM(3)算法
  3. cf计算机丢失msvcp100,Win7丢失MSVCP100.dll导致程序无法启动怎们办
  4. 腾讯魏巍:Eunomia云原生资源编排优化
  5. windows和ubuntu双系统之双硬盘安装
  6. 开启充电计划升级职业生涯,让我们相聚社科院杜兰金融管理硕士项目
  7. opc java_Java OPC 代码
  8. Lattice Radiant烧写程序
  9. AngularJS 模块
  10. 特聘专家朱嘉明:2022,数字经济迈入历史新阶段