STM32
直播中

一说就是错

13年用户 532经验值
私信 关注
[问答]

S50(M1)卡是怎样驱动RFID-RC522模块的呢

什么是S50(M1)卡呢?其工作原理是什么?

S50(M1)卡是怎样驱动RFID-RC522模块的呢?



回帖(3)

衡辉

2021-11-15 10:02:53
  一.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命令字
  /
  #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();
  else
  RC522_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的基本操作。
  
举报

乔婧

2021-11-15 10:04:02
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 允许空闲中断IdleIEn
  ucWaitFor = 0x10; //认证寻卡等待时候 查询空闲中断标志位
  break;
  case PCD_TRANSCEIVE: //接收发送 发送接收
  ucIrqEn = 0x77; //允许TxIEn RxIEn IdleIEn LoAlertIEn ErrIEn TimerIEn
  ucWaitFor = 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 ] ); //写数据进FIFOdata
  RC522_Write_Register ( CommandReg, ucCommand ); //写命令
  if ( ucCommand == PCD_TRANSCEIVE )
  RC522_SetBit_Register(BitFramingReg,0x80); //StartSend置位启动数据发送 该位与收发命令使用时才有效
  ul = 1000;//根据时钟频率调整,操作M1卡最大等待时间25ms
  do //认证 与寻卡等待时间
  {
  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 );
  }
  }
  else
  cStatus = MI_ERR;
  }
  RC522_SetBit_Register ( ControlReg, 0x80 ); // stop timer now
  RC522_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 ];
  }
  else
  cStatus = 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 ]; //读出UID
  ucSnr_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
  {
  
举报

莫联雪

2021-11-15 10:04:17
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;
  else
  ucN = 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 ];
  }
  else
  cStatus = 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 (“%srn”,cStr );
  }
  }
  }
  }
举报

更多回帖

发帖
×
20
完善资料,
赚取积分