你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

基于STM32的驱动RFID模块的经验分享

[复制链接]
攻城狮Melo 发布时间:2023-3-18 12:47
一.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中)。
每个块都有存取条件,存取条件是由密码和存取控制共同决定的。
每个块都有相应的三个控制位,这三个控制位存在于存取控制字节中,相应的控制位决定了该块的访问权限,控制位如图:

20200207114527854.png

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

20200207114554825.png

4.数据块的存取控制
对数据块,与就是块0、1、2的存取控制是由对应块的控制位来决定的:

20200207114614927.png

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

5.控制块的存取控
块3(控制块)的存取操作与数据块不同,如图:

20201116202236794.png


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

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

7.M1与读卡器的通信
通信的流程图如示:

20200207114654203.png

复位应答(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的寄存器和一些相关指令,这些东西厂家都会提供,所以我们只需要复制粘贴到我们的工程中使用即可。下面分享一下相关寄存器的地址和指令:
  1. /
  2. //RC522命令字
  3. /
  4. #define PCD_IDLE              0x00               //取消当前命令
  5. #define PCD_AUTHENT           0x0E               //验证密钥
  6. #define PCD_RECEIVE           0x08               //接收数据
  7. #define PCD_TRANSMIT          0x04               //发送数据
  8. #define PCD_TRANSCEIVE        0x0C               //发送并接收数据
  9. #define PCD_RESETPHASE        0x0F               //复位
  10. #define PCD_CALCCRC           0x03               //CRC计算

  11. /
  12. //Mifare_One卡片命令字
  13. /
  14. #define PICC_REQIDL           0x26               //寻天线区内未进入休眠状态
  15. #define PICC_REQALL           0x52               //寻天线区内全部卡
  16. #define PICC_ANTICOLL1        0x93               //防冲撞
  17. #define PICC_ANTICOLL2        0x95               //防冲撞
  18. #define PICC_AUTHENT1A        0x60               //验证A密钥
  19. #define PICC_AUTHENT1B        0x61               //验证B密钥
  20. #define PICC_READ             0x30               //读块
  21. #define PICC_WRITE            0xA0               //写块
  22. #define PICC_DECREMENT        0xC0               //扣款
  23. #define PICC_INCREMENT        0xC1               //充值
  24. #define PICC_RESTORE          0xC2               //调块数据到缓冲区
  25. #define PICC_TRANSFER         0xB0               //保存缓冲区中数据
  26. #define PICC_HALT             0x50               //休眠

  27. /* RC522  FIFO长度定义 */
  28. #define DEF_FIFO_LENGTH       64                 //FIFO size=64byte
  29. #define MAXRLEN  18


  30. /* RC522寄存器定义 */
  31. // PAGE 0
  32. #define     RFU00                 0x00    //保留
  33. #define     CommandReg            0x01    //启动和停止命令的执行
  34. #define     ComIEnReg             0x02    //中断请求传递的使能(Enable/Disable)
  35. #define     DivlEnReg             0x03    //中断请求传递的使能
  36. #define     ComIrqReg             0x04    //包含中断请求标志
  37. #define     DivIrqReg             0x05    //包含中断请求标志
  38. #define     ErrorReg              0x06    //错误标志,指示执行的上个命令的错误状态
  39. #define     Status1Reg            0x07    //包含通信的状态标识
  40. #define     Status2Reg            0x08    //包含接收器和发送器的状态标志
  41. #define     FIFODataReg           0x09    //64字节FIFO缓冲区的输入和输出
  42. #define     FIFOLevelReg          0x0A    //指示FIFO中存储的字节数
  43. #define     WaterLevelReg         0x0B    //定义FIFO下溢和上溢报警的FIFO深度
  44. #define     ControlReg            0x0C    //不同的控制寄存器
  45. #define     BitFramingReg         0x0D    //面向位的帧的调节
  46. #define     CollReg               0x0E    //RF接口上检测到的第一个位冲突的位的位置
  47. #define     RFU0F                 0x0F    //保留
  48. // PAGE 1     
  49. #define     RFU10                 0x10    //保留
  50. #define     ModeReg               0x11    //定义发送和接收的常用模式
  51. #define     TxModeReg             0x12    //定义发送过程的数据传输速率
  52. #define     RxModeReg             0x13    //定义接收过程中的数据传输速率
  53. #define     TxControlReg          0x14    //控制天线驱动器管教TX1和TX2的逻辑特性
  54. #define     TxAutoReg             0x15    //控制天线驱动器的设置
  55. #define     TxSelReg              0x16    //选择天线驱动器的内部源
  56. #define     RxSelReg              0x17    //选择内部的接收器设置
  57. #define     RxThresholdReg        0x18    //选择位译码器的阈值
  58. #define     DemodReg              0x19    //定义解调器的设置
  59. #define     RFU1A                 0x1A    //保留
  60. #define     RFU1B                 0x1B    //保留
  61. #define     MifareReg             0x1C    //控制ISO 14443/MIFARE模式中106kbit/s的通信
  62. #define     RFU1D                 0x1D    //保留
  63. #define     RFU1E                 0x1E    //保留
  64. #define     SerialSpeedReg        0x1F    //选择串行UART接口的速率
  65. // PAGE 2   
  66. #define     RFU20                 0x20    //保留
  67. #define     CRCResultRegM         0x21    //显示CRC计算的实际MSB值
  68. #define     CRCResultRegL         0x22    //显示CRC计算的实际LSB值
  69. #define     RFU23                 0x23    //保留
  70. #define     ModWidthReg           0x24    //控制ModWidth的设置
  71. #define     RFU25                 0x25    //保留
  72. #define     RFCfgReg              0x26    //配置接收器增益
  73. #define     GsNReg                0x27    //选择天线驱动器管脚(TX1和TX2)的调制电导
  74. #define     CWGsCfgReg            0x28    //选择天线驱动器管脚的调制电导
  75. #define     ModGsCfgReg           0x29    //选择天线驱动器管脚的调制电导
  76. #define     TModeReg              0x2A    //定义内部定时器的设置
  77. #define     TPrescalerReg         0x2B    //定义内部定时器的设置
  78. #define     TReloadRegH           0x2C    //描述16位长的定时器重装值
  79. #define     TReloadRegL           0x2D    //描述16位长的定时器重装值
  80. #define     TCounterValueRegH     0x2E   
  81. #define     TCounterValueRegL     0x2F    //显示16位长的实际定时器值
  82. // PAGE 3      
  83. #define     RFU30                 0x30    //保留
  84. #define     TestSel1Reg           0x31    //常用测试信号配置
  85. #define     TestSel2Reg           0x32    //常用测试信号配置和PRBS控制
  86. #define     TestPinEnReg          0x33    //D1-D7输出驱动器的使能管脚(仅用于串行接口)
  87. #define     TestPinValueReg       0x34    //定义D1-D7用作I/O总线时的值
  88. #define     TestBusReg            0x35    //显示内部测试总线的状态
  89. #define     AutoTestReg           0x36    //控制数字自测试
  90. #define     VersionReg            0x37    //显示版本
  91. #define     AnalogTestReg         0x38    //控制管脚AUX1和AUX2
  92. #define     TestDAC1Reg           0x39    //定义TestDAC1的测试值
  93. #define     TestDAC2Reg           0x3A    //定义TestDAC2的测试值
  94. #define     TestADCReg            0x3B    //显示ADCI和Q通道的实际值
  95. #define     RFU3C                 0x3C    //保留
  96. #define     RFU3D                 0x3D    //保留
  97. #define     RFU3E                 0x3E    //保留
  98. #define     RFU3F                                          0x3F    //保留

  99. /* 和RC522通信时返回的错误代码 */
  100. #define         MI_OK                 0x26
  101. #define         MI_NOTAGERR           0xcc
  102. #define         MI_ERR                0xbb

复制代码

既然RC522是通过SPI与单片机通信的,所以就会有相应的引脚配置,下面给出相关引脚的配置和一些引脚操作宏定义:
  1. /* RC522引脚连接说明(SPI1的引脚) :
  2. CS:PA4( 接的SDA引脚 )
  3. SCK:PA5
  4. MISO:PA6
  5. MOSI:PA7
  6. RST:PB0
  7. */
  8. void RC522_GPIO_Init( void )
  9. {
  10.         GPIO_InitTypeDef GPIO_InitStructure;
  11.        
  12.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE );
  13.        
  14.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  15.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  16.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
  17.         GPIO_Init( GPIOA, &GPIO_InitStructure );
  18.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
  19.         GPIO_Init( GPIOA, &GPIO_InitStructure );
  20.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
  21.         GPIO_Init( GPIOA, &GPIO_InitStructure );
  22.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
  23.         GPIO_Init( GPIOB, &GPIO_InitStructure );
  24.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  25.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  26.         GPIO_Init( GPIOA, &GPIO_InitStructure );
  27. }

  28. /* IO口操作函数 */
  29. #define   RC522_CS_Enable()         GPIO_ResetBits ( GPIOA, GPIO_Pin_4 )
  30. #define   RC522_CS_Disable()        GPIO_SetBits ( GPIOA, GPIO_Pin_4 )

  31. #define   RC522_Reset_Enable()      GPIO_ResetBits( GPIOB, GPIO_Pin_0 )
  32. #define   RC522_Reset_Disable()     GPIO_SetBits( GPIOB, GPIO_Pin_0 )

  33. #define   RC522_SCK_0()             GPIO_ResetBits( GPIOA, GPIO_Pin_5 )
  34. #define   RC522_SCK_1()             GPIO_SetBits( GPIOA, GPIO_Pin_5 )

  35. #define   RC522_MOSI_0()            GPIO_ResetBits( GPIOA, GPIO_Pin_7 )
  36. #define   RC522_MOSI_1()            GPIO_SetBits( GPIOA, GPIO_Pin_7 )

  37. #define   RC522_MISO_GET()          GPIO_ReadInputDataBit( GPIOA, GPIO_Pin_6 )
复制代码

我是通过软件模拟SPI与RC522通信的,SPI发送接收字节的代码如下(高位先行):
  1. /* 软件模拟SPI发送一个字节数据,高位先行 */
  2. void RC522_SPI_SendByte( uint8_t byte )
  3. {
  4.         uint8_t n;
  5.         for( n=0;n<8;n++ )
  6.         {
  7.                 if( byte&0x80 )
  8.                         RC522_MOSI_1();
  9.                 else
  10.                         RC522_MOSI_0();
  11.                
  12.                 Delay_us(200);
  13.                 RC522_SCK_0();
  14.                 Delay_us(200);
  15.                 RC522_SCK_1();
  16.                 Delay_us(200);
  17.                
  18.                 byte<<=1;
  19.         }
  20. }

  21. /* 软件模拟SPI读取一个字节数据,先读高位 */
  22. uint8_t RC522_SPI_ReadByte( void )
  23. {
  24.         uint8_t n,data;
  25.         for( n=0;n<8;n++ )
  26.         {
  27.                 data<<=1;
  28.                 RC522_SCK_0();
  29.                 Delay_us(200);
  30.                
  31.                 if( RC522_MISO_GET()==1 )
  32.                         data|=0x01;
  33.                
  34.                 Delay_us(200);
  35.                 RC522_SCK_1();
  36.                 Delay_us(200);
  37.                
  38.                
  39.         }
  40.         return data;
  41. }
复制代码

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


2.STM32对RC522寄存器的操作
上面说了,单片机是向RC522的寄存器操作来驱动RC522的,所以会有这几种基本操作:
读取RC522指定寄存器的值
向RC522指定寄存器中写入指定的数据
置位RC522指定寄存器的指定位
清位RC522指定寄存器的指定位

下面给出这些操作的函数实现:
  1. /**
  2.   * @brief  :读取RC522指定寄存器的值
  3.         * @param  :Address:寄存器的地址
  4.   * @retval :寄存器的值
  5. */
  6. uint8_t RC522_Read_Register( uint8_t Address )
  7. {
  8.         uint8_t data,Addr;
  9.        
  10.         Addr = ( (Address<<1)&0x7E )|0x80;
  11.        
  12.         RC522_CS_Enable();
  13.         RC522_SPI_SendByte( Addr );
  14.         data = RC522_SPI_ReadByte();//读取寄存器中的值
  15.         RC522_CS_Disable();
  16.        
  17.         return data;
  18. }

  19. /**
  20.   * @brief  :向RC522指定寄存器中写入指定的数据
  21.   * @param  :Address:寄存器地址
  22.                                       data:要写入寄存器的数据
  23.   * @retval :无
  24. */
  25. void RC522_Write_Register( uint8_t Address, uint8_t data )
  26. {
  27.         uint8_t Addr;
  28.        
  29.         Addr = ( Address<<1 )&0x7E;
  30.        
  31.         RC522_CS_Enable();
  32.         RC522_SPI_SendByte( Addr );
  33.         RC522_SPI_SendByte( data );
  34.         RC522_CS_Disable();
  35.        
  36. }

  37. /**
  38.   * @brief  :置位RC522指定寄存器的指定位
  39.   * @param  :Address:寄存器地址
  40.                                       mask:置位值
  41.   * @retval :无
  42. */
  43. void RC522_SetBit_Register( uint8_t Address, uint8_t mask )
  44. {
  45.         uint8_t temp;
  46.         /* 获取寄存器当前值 */
  47.         temp = RC522_Read_Register( Address );
  48.         /* 对指定位进行置位操作后,再将值写入寄存器 */
  49.         RC522_Write_Register( Address, temp|mask );
  50. }

  51. /**
  52.   * @brief  :清位RC522指定寄存器的指定位
  53.   * @param  :Address:寄存器地址
  54.                       mask:清位值
  55.   * @retval :无
  56. */
  57. void RC522_ClearBit_Register( uint8_t Address, uint8_t mask )
  58. {
  59.         uint8_t temp;
  60.         /* 获取寄存器当前值 */
  61.         temp = RC522_Read_Register( Address );
  62.         /* 对指定位进行清位操作后,再将值写入寄存器 */
  63.         RC522_Write_Register( Address, temp&(~mask) );
  64. }
复制代码

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

3.STM32对RC522的基础通信
上面说了寄存器、指令、对寄存器的操作,这里介绍一些对RC522的基本操作,包括:
开启天线
关闭天线
复位RC522
设置RC522工作方式

RC522与M1通信前必须开启天线,进行复位,然后设置RC522的工作方式!下面介绍一下相关代码:
  1. /**
  2.   * @brief  :开启天线
  3.   * @param  :无
  4.   * @retval :无
  5. */
  6. void RC522_Antenna_On( void )
  7. {
  8.         uint8_t k;
  9.         k = RC522_Read_Register( TxControlReg );
  10.         /* 判断天线是否开启 */
  11.         if( !( k&0x03 ) )
  12.                 RC522_SetBit_Register( TxControlReg, 0x03 );
  13. }

  14. /**
  15.   * @brief  :关闭天线
  16.   * @param  :无
  17.   * @retval :无
  18. */
  19. void RC522_Antenna_Off( void )
  20. {
  21.         /* 直接对相应位清零 */
  22.         RC522_ClearBit_Register( TxControlReg, 0x03 );
  23. }


  24. /**
  25.   * @brief  :复位RC522
  26.   * @param  :无
  27.   * @retval :无
  28. */
  29. void RC522_Rese( void )
  30. {
  31.         RC522_Reset_Disable();
  32.         Delay_us ( 1 );
  33.         RC522_Reset_Enable();
  34.         Delay_us ( 1 );
  35.         RC522_Reset_Disable();
  36.         Delay_us ( 1 );
  37.         RC522_Write_Register( CommandReg, 0x0F );
  38.         while( RC522_Read_Register( CommandReg )&0x10 )
  39.                 ;

  40.         /* 缓冲一下 */
  41.         Delay_us ( 1 );
  42.         RC522_Write_Register( ModeReg, 0x3D );       //定义发送和接收常用模式
  43.         RC522_Write_Register( TReloadRegL, 30 );     //16位定时器低位
  44.         RC522_Write_Register( TReloadRegH, 0 );      //16位定时器高位
  45.         RC522_Write_Register( TModeReg, 0x8D );      //内部定时器的设置
  46.         RC522_Write_Register( TPrescalerReg, 0x3E ); //设置定时器分频系数
  47.         RC522_Write_Register( TxAutoReg, 0x40 );     //调制发送信号为100%ASK
  48. }


  49. /**
  50.   * @brief  :设置RC522的工作方式
  51.   * @param  :Type:工作方式
  52.   * @retval :无
  53.   M500PcdConfigISOType
  54. */
  55. void RC522_Config_Type( char Type )
  56. {
  57.         if( Type=='A' )
  58.         {
  59.                 RC522_ClearBit_Register( Status2Reg, 0x08 );
  60.                 RC522_Write_Register( ModeReg, 0x3D );
  61.                 RC522_Write_Register( RxSelReg, 0x86 );
  62.                 RC522_Write_Register( RFCfgReg, 0x7F );
  63.                 RC522_Write_Register( TReloadRegL, 30 );
  64.                 RC522_Write_Register( TReloadRegH, 0 );
  65.                 RC522_Write_Register( TModeReg, 0x8D );
  66.                 RC522_Write_Register( TPrescalerReg, 0x3E );
  67.                 Delay_us(2);
  68.                 /* 开天线 */
  69.                 RC522_Antenna_On();
  70.         }
  71. }
复制代码

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


4.STM32控制RC522与M1的通信
这部分是最重要的步骤,RC522与M1的通信是工程要实现的目的,而且要遵守前面提到的M1卡与RC522通信的步骤以及M1卡的内部构造,包括以下操作:
通过RC522和M1卡通讯(数据的双向传输)
寻卡
防冲突
用RC522计算CRC16(循环冗余校验)
选定卡片
校验卡片密码
在M1卡的指定块地址写入指定数据
读取M1卡的指定块地址的数据
让卡片进入休眠模式

话不多说,上代码,代码中都有按照我理解的一些注释:
  1. /**
  2.   * @brief  :通过RC522和ISO14443卡通讯
  3. * @param  :ucCommand:RC522命令字
  4. *          pInData:通过RC522发送到卡片的数据
  5. *          ucInLenByte:发送数据的字节长度
  6. *          pOutData:接收到的卡片返回数据
  7. *          pOutLenBit:返回数据的位长度
  8.   * @retval :状态值MI_OK,成功
  9. */
  10. char PcdComMF522 ( uint8_t ucCommand, uint8_t * pInData, uint8_t ucInLenByte, uint8_t * pOutData, uint32_t * pOutLenBit )               
  11. {
  12.     char cStatus = MI_ERR;
  13.     uint8_t ucIrqEn   = 0x00;
  14.     uint8_t ucWaitFor = 0x00;
  15.     uint8_t ucLastBits;
  16.     uint8_t ucN;
  17.     uint32_t ul;
  18.        
  19.        
  20.     switch ( ucCommand )
  21.     {
  22.        case PCD_AUTHENT:                //Mifare认证
  23.           ucIrqEn   = 0x12;                //允许错误中断请求ErrIEn  允许空闲中断IdleIEn
  24.           ucWaitFor = 0x10;                //认证寻卡等待时候 查询空闲中断标志位
  25.           break;
  26.                          
  27.        case PCD_TRANSCEIVE:                //接收发送 发送接收
  28.           ucIrqEn   = 0x77;                //允许TxIEn RxIEn IdleIEn LoAlertIEn ErrIEn TimerIEn
  29.           ucWaitFor = 0x30;                //寻卡等待时候 查询接收中断标志位与 空闲中断标志位
  30.           break;
  31.                          
  32.        default:
  33.          break;
  34.                          
  35.     }
  36.    
  37.     RC522_Write_Register ( ComIEnReg, ucIrqEn | 0x80 );                //IRqInv置位管脚IRQ与Status1Reg的IRq位的值相反
  38.     RC522_ClearBit_Register ( ComIrqReg, 0x80 );                        //Set1该位清零时,CommIRqReg的屏蔽位清零
  39.     RC522_Write_Register ( CommandReg, PCD_IDLE );                //写空闲命令
  40.     RC522_SetBit_Register ( FIFOLevelReg, 0x80 );                        //置位FlushBuffer清除内部FIFO的读和写指针以及ErrReg的BufferOvfl标志位被清除
  41.    
  42.     for ( ul = 0; ul < ucInLenByte; ul ++ )
  43.                   RC522_Write_Register ( FIFODataReg, pInData [ ul ] );                    //写数据进FIFOdata
  44.                        
  45.     RC522_Write_Register ( CommandReg, ucCommand );                                        //写命令
  46.    
  47.    
  48.     if ( ucCommand == PCD_TRANSCEIVE )
  49.                         RC522_SetBit_Register(BitFramingReg,0x80);                                  //StartSend置位启动数据发送 该位与收发命令使用时才有效
  50.    
  51.     ul = 1000;//根据时钟频率调整,操作M1卡最大等待时间25ms
  52.                
  53.     do                                                                                                                 //认证 与寻卡等待时间       
  54.     {
  55.          ucN = RC522_Read_Register ( ComIrqReg );                                                        //查询事件中断
  56.          ul --;
  57.     } while ( ( ul != 0 ) && ( ! ( ucN & 0x01 ) ) && ( ! ( ucN & ucWaitFor ) ) );                //退出条件i=0,定时器中断,与写空闲命令
  58.                
  59.     RC522_ClearBit_Register ( BitFramingReg, 0x80 );                                        //清理允许StartSend位
  60.                
  61.     if ( ul != 0 )
  62.     {
  63.                         if ( ! ( RC522_Read_Register ( ErrorReg ) & 0x1B ) )                        //读错误标志寄存器BufferOfI CollErr ParityErr ProtocolErr
  64.                         {
  65.                                 cStatus = MI_OK;
  66.                                
  67.                                 if ( ucN & ucIrqEn & 0x01 )                                        //是否发生定时器中断
  68.                                   cStatus = MI_NOTAGERR;   
  69.                                        
  70.                                 if ( ucCommand == PCD_TRANSCEIVE )
  71.                                 {
  72.                                         ucN = RC522_Read_Register ( FIFOLevelReg );                        //读FIFO中保存的字节数
  73.                                        
  74.                                         ucLastBits = RC522_Read_Register ( ControlReg ) & 0x07;        //最后接收到得字节的有效位数
  75.                                        
  76.                                         if ( ucLastBits )
  77.                                                 * pOutLenBit = ( ucN - 1 ) * 8 + ucLastBits;           //N个字节数减去1(最后一个字节)+最后一位的位数 读取到的数据总位数
  78.                                         else
  79.                                                 * pOutLenBit = ucN * 8;                                           //最后接收到的字节整个字节有效
  80.                                        
  81.                                         if ( ucN == 0 )               
  82.             ucN = 1;   
  83.                                        
  84.                                         if ( ucN > MAXRLEN )
  85.                                                 ucN = MAXRLEN;   
  86.                                        
  87.                                         for ( ul = 0; ul < ucN; ul ++ )
  88.                                           pOutData [ ul ] = RC522_Read_Register ( FIFODataReg );                                          
  89.                                         }                                       
  90.       }                       
  91.                         else
  92.                                 cStatus = MI_ERR;                          
  93.     }
  94.    
  95.    RC522_SetBit_Register ( ControlReg, 0x80 );           // stop timer now
  96.    RC522_Write_Register ( CommandReg, PCD_IDLE );
  97.                  
  98.    return cStatus;               
  99. }



  100. /**
  101.   * @brief  :寻卡
  102. * @param  ucReq_code,寻卡方式
  103. *                      = 0x52:寻感应区内所有符合14443A标准的卡
  104. *                     = 0x26:寻未进入休眠状态的卡
  105. *         pTagType,卡片类型代码
  106. *                   = 0x4400:Mifare_UltraLight
  107. *                   = 0x0400:Mifare_One(S50)
  108. *                   = 0x0200:Mifare_One(S70)
  109. *                   = 0x0800:Mifare_Pro(X))
  110. *                   = 0x4403:Mifare_DESFire
  111.   * @retval :状态值MI_OK,成功
  112. */
  113. char PcdRequest ( uint8_t ucReq_code, uint8_t * pTagType )
  114. {
  115.    char cStatus;  
  116.          uint8_t ucComMF522Buf [ MAXRLEN ];
  117.    uint32_t ulLen;
  118.        
  119.    RC522_ClearBit_Register ( Status2Reg, 0x08 );        //清理指示MIFARECyptol单元接通以及所有卡的数据通信被加密的情况
  120.    RC522_Write_Register ( BitFramingReg, 0x07 );        //        发送的最后一个字节的 七位
  121.    RC522_SetBit_Register ( TxControlReg, 0x03 );        //TX1,TX2管脚的输出信号传递经发送调制的13.56的能量载波信号

  122.    ucComMF522Buf [ 0 ] = ucReq_code;                //存入寻卡方式
  123.         /* PCD_TRANSCEIVE:发送并接收数据的命令,RC522向卡片发送寻卡命令,卡片返回卡的型号代码到ucComMF522Buf中 */
  124.    cStatus = PcdComMF522 ( PCD_TRANSCEIVE,        ucComMF522Buf, 1, ucComMF522Buf, & ulLen );        //寻卡  
  125.   
  126.    if ( ( cStatus == MI_OK ) && ( ulLen == 0x10 ) )        //寻卡成功返回卡类型
  127.    {   
  128.                  /* 接收卡片的型号代码 */
  129.        * pTagType = ucComMF522Buf [ 0 ];
  130.        * ( pTagType + 1 ) = ucComMF522Buf [ 1 ];
  131.    }
  132.    else
  133.      cStatus = MI_ERR;   
  134.    return cStatus;         
  135. }



  136. /**
  137.   * @brief  :防冲突
  138.         * @param  :Snr:卡片序列,4字节,会返回选中卡片的序列
  139.   * @retval :状态值MI_OK,成功
  140. */
  141. char PcdAnticoll ( uint8_t * pSnr )
  142. {
  143.     char cStatus;
  144.     uint8_t uc, ucSnr_check = 0;
  145.     uint8_t ucComMF522Buf [ MAXRLEN ];
  146.           uint32_t ulLen;
  147.    
  148.     RC522_ClearBit_Register ( Status2Reg, 0x08 );                //清MFCryptol On位 只有成功执行MFAuthent命令后,该位才能置位
  149.     RC522_Write_Register ( BitFramingReg, 0x00);                //清理寄存器 停止收发
  150.     RC522_ClearBit_Register ( CollReg, 0x80 );                        //清ValuesAfterColl所有接收的位在冲突后被清除
  151.    
  152.     ucComMF522Buf [ 0 ] = 0x93;        //卡片防冲突命令
  153.     ucComMF522Buf [ 1 ] = 0x20;
  154.    
  155.           /* 将卡片防冲突命令通过RC522传到卡片中,返回的是被选中卡片的序列 */
  156.     cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 2, ucComMF522Buf, & ulLen);//与卡片通信
  157.        
  158.     if ( cStatus == MI_OK)                //通信成功
  159.     {
  160.                         for ( uc = 0; uc < 4; uc ++ )
  161.                         {
  162.          * ( pSnr + uc )  = ucComMF522Buf [ uc ];                        //读出UID
  163.          ucSnr_check ^= ucComMF522Buf [ uc ];
  164.       }
  165.                        
  166.       if ( ucSnr_check != ucComMF522Buf [ uc ] )
  167.                                 cStatus = MI_ERR;             
  168.     }   
  169.     RC522_SetBit_Register ( CollReg, 0x80 );               
  170.     return cStatus;               
  171. }



  172. /**
  173. * @brief   :用RC522计算CRC16(循环冗余校验)
  174.         * @param  :pIndata:计算CRC16的数组
  175. *            ucLen:计算CRC16的数组字节长度
  176. *            pOutData:存放计算结果存放的首地址
  177.   * @retval :状态值MI_OK,成功
  178. */
  179. void CalulateCRC ( uint8_t * pIndata, u8 ucLen, uint8_t * pOutData )
  180. {
  181.     uint8_t uc, ucN;
  182.        
  183.        
  184.     RC522_ClearBit_Register(DivIrqReg,0x04);       
  185.     RC522_Write_Register(CommandReg,PCD_IDLE);       
  186.     RC522_SetBit_Register(FIFOLevelReg,0x80);
  187.        
  188.     for ( uc = 0; uc < ucLen; uc ++)
  189.             RC522_Write_Register ( FIFODataReg, * ( pIndata + uc ) );   

  190.     RC522_Write_Register ( CommandReg, PCD_CALCCRC );
  191.        
  192.     uc = 0xFF;
  193.        
  194.     do
  195.     {
  196.         ucN = RC522_Read_Register ( DivIrqReg );
  197.         uc --;
  198.     } while ( ( uc != 0 ) && ! ( ucN & 0x04 ) );
  199.                
  200.     pOutData [ 0 ] = RC522_Read_Register ( CRCResultRegL );
  201.     pOutData [ 1 ] = RC522_Read_Register ( CRCResultRegM );
  202.                
  203. }



  204. /**
  205.   * @brief   :选定卡片
  206.   * @param  :pSnr:卡片序列号,4字节
  207.   * @retval :状态值MI_OK,成功
  208. */
  209. char PcdSelect ( uint8_t * pSnr )
  210. {
  211.     char ucN;
  212.     uint8_t uc;
  213.           uint8_t ucComMF522Buf [ MAXRLEN ];
  214.     uint32_t  ulLen;
  215.     /* PICC_ANTICOLL1:防冲突命令 */
  216.     ucComMF522Buf [ 0 ] = PICC_ANTICOLL1;
  217.     ucComMF522Buf [ 1 ] = 0x70;
  218.     ucComMF522Buf [ 6 ] = 0;
  219.        
  220.     for ( uc = 0; uc < 4; uc ++ )
  221.     {
  222.             ucComMF522Buf [ uc + 2 ] = * ( pSnr + uc );
  223.             ucComMF522Buf [ 6 ] ^= * ( pSnr + uc );
  224.     }
  225.                
  226.     CalulateCRC ( ucComMF522Buf, 7, & ucComMF522Buf [ 7 ] );
  227.   
  228.     RC522_ClearBit_Register ( Status2Reg, 0x08 );

  229.     ucN = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 9, ucComMF522Buf, & ulLen );
  230.    
  231.     if ( ( ucN == MI_OK ) && ( ulLen == 0x18 ) )
  232.       ucN = MI_OK;  
  233.     else
  234.       ucN = MI_ERR;   

  235.     return ucN;
  236.                
  237. }



  238. /**
  239.   * @brief   :校验卡片密码
  240.   * @param  :ucAuth_mode:密码验证模式
  241.   *                     = 0x60,验证A密钥
  242.   *                     = 0x61,验证B密钥
  243.   *           ucAddr:块地址
  244.   *           pKey:密码
  245.   *           pSnr:卡片序列号,4字节
  246.   * @retval :状态值MI_OK,成功
  247. */
  248. char PcdAuthState ( uint8_t ucAuth_mode, uint8_t ucAddr, uint8_t * pKey, uint8_t * pSnr )
  249. {
  250.     char cStatus;
  251.           uint8_t uc, ucComMF522Buf [ MAXRLEN ];
  252.     uint32_t ulLen;
  253.        
  254.     ucComMF522Buf [ 0 ] = ucAuth_mode;
  255.     ucComMF522Buf [ 1 ] = ucAddr;
  256.           /* 前俩字节存储验证模式和块地址,2~8字节存储密码(6个字节),8~14字节存储序列号 */
  257.     for ( uc = 0; uc < 6; uc ++ )
  258.             ucComMF522Buf [ uc + 2 ] = * ( pKey + uc );   
  259.        
  260.     for ( uc = 0; uc < 6; uc ++ )
  261.             ucComMF522Buf [ uc + 8 ] = * ( pSnr + uc );   
  262.     /* 进行冗余校验,14~16俩个字节存储校验结果 */
  263.     cStatus = PcdComMF522 ( PCD_AUTHENT, ucComMF522Buf, 12, ucComMF522Buf, & ulLen );
  264.           /* 判断验证是否成功 */
  265.     if ( ( cStatus != MI_OK ) || ( ! ( RC522_Read_Register ( Status2Reg ) & 0x08 ) ) )
  266.       cStatus = MI_ERR;   
  267.                
  268.     return cStatus;
  269.                
  270. }


  271. /**
  272.   * @brief   :在M1卡的指定块地址写入指定数据
  273.   * @param  :ucAddr:块地址
  274.   *           pData:写入的数据,16字节
  275.   * @retval :状态值MI_OK,成功
  276. */
  277. char PcdWrite ( uint8_t ucAddr, uint8_t * pData )
  278. {
  279.     char cStatus;
  280.           uint8_t uc, ucComMF522Buf [ MAXRLEN ];
  281.     uint32_t ulLen;
  282.    
  283.     ucComMF522Buf [ 0 ] = PICC_WRITE;//写块命令
  284.     ucComMF522Buf [ 1 ] = ucAddr;//写块地址
  285.        
  286.           /* 进行循环冗余校验,将结果存储在& ucComMF522Buf [ 2 ] */
  287.     CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );
  288.    
  289.         /* PCD_TRANSCEIVE:发送并接收数据命令,通过RC522向卡片发送写块命令 */
  290.     cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );

  291.                 /* 通过卡片返回的信息判断,RC522是否与卡片正常通信 */
  292.     if ( ( cStatus != MI_OK ) || ( ulLen != 4 ) || ( ( ucComMF522Buf [ 0 ] & 0x0F ) != 0x0A ) )
  293.       cStatus = MI_ERR;   
  294.         
  295.     if ( cStatus == MI_OK )
  296.     {
  297.                         //memcpy(ucComMF522Buf, pData, 16);
  298.                         /* 将要写入的16字节的数据,传入ucComMF522Buf数组中 */
  299.       for ( uc = 0; uc < 16; uc ++ )
  300.                           ucComMF522Buf [ uc ] = * ( pData + uc );  
  301.                         /* 冗余校验 */
  302.       CalulateCRC ( ucComMF522Buf, 16, & ucComMF522Buf [ 16 ] );
  303.       /* 通过RC522,将16字节数据包括2字节校验结果写入卡片中 */
  304.       cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 18, ucComMF522Buf, & ulLen );
  305.                         /* 判断写地址是否成功 */
  306.                         if ( ( cStatus != MI_OK ) || ( ulLen != 4 ) || ( ( ucComMF522Buf [ 0 ] & 0x0F ) != 0x0A ) )
  307.         cStatus = MI_ERR;                          
  308.     }
  309.     return cStatus;       
  310. }


  311. /**
  312.   * @brief   :读取M1卡的指定块地址的数据
  313.   * @param  :ucAddr:块地址
  314.   *           pData:读出的数据,16字节
  315.   * @retval :状态值MI_OK,成功
  316. */
  317. char PcdRead ( uint8_t ucAddr, uint8_t * pData )
  318. {
  319.     char cStatus;
  320.           uint8_t uc, ucComMF522Buf [ MAXRLEN ];
  321.     uint32_t ulLen;

  322.     ucComMF522Buf [ 0 ] = PICC_READ;
  323.     ucComMF522Buf [ 1 ] = ucAddr;
  324.           /* 冗余校验 */
  325.     CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );
  326.     /* 通过RC522将命令传给卡片 */
  327.     cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );
  328.        
  329.           /* 如果传输正常,将读取到的数据传入pData中 */
  330.     if ( ( cStatus == MI_OK ) && ( ulLen == 0x90 ) )
  331.     {
  332.                         for ( uc = 0; uc < 16; uc ++ )
  333.         * ( pData + uc ) = ucComMF522Buf [ uc ];   
  334.     }
  335.                
  336.     else
  337.       cStatus = MI_ERR;   
  338.        
  339.     return cStatus;

  340. }


  341. /**
  342.   * @brief   :让卡片进入休眠模式
  343.   * @param  :无
  344.   * @retval :状态值MI_OK,成功
  345. */
  346. char PcdHalt( void )
  347. {
  348.         uint8_t ucComMF522Buf [ MAXRLEN ];
  349.         uint32_t  ulLen;

  350.   ucComMF522Buf [ 0 ] = PICC_HALT;
  351.   ucComMF522Buf [ 1 ] = 0;
  352.        
  353.   CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );
  354.         PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );

  355.   return MI_OK;
  356.        
  357. }
复制代码

详情请看代码。

5.测试函数
通过测试函数来试一下对M1卡的识别,读取数据等。
  1. 在这里插入代码片char cStr [ 30 ];
  2. /* 卡的ID存储,32位,4字节 */
  3. u8 ucArray_ID [ 4 ];
  4. /**
  5.   * @brief  : 测试代码,读取卡片ID
  6.   * @param  :无
  7.   * @retval :无
  8. */
  9. void IC_test ( void )
  10. {                                                                                       
  11.         uint8_t ucStatusReturn;    //返回状态                                       
  12.        
  13.   while ( 1 )
  14.   {
  15.                 /* 寻卡(方式:范围内全部),第一次寻卡失败后再进行一次,寻卡成功时卡片序列传入数组ucArray_ID中 */
  16.                 if ( ( ucStatusReturn = PcdRequest ( PICC_REQALL, ucArray_ID ) ) != MI_OK )
  17.                         ucStatusReturn = PcdRequest ( PICC_REQALL, ucArray_ID );                  

  18.                 if ( ucStatusReturn == MI_OK  )
  19.                 {
  20.                         /* 防冲突操作,被选中的卡片序列传入数组ucArray_ID中 */
  21.                         if ( PcdAnticoll ( ucArray_ID ) == MI_OK )
  22.                         {
  23.                                 sprintf ( cStr, "The Card ID is: %02X%02X%02X%02X", ucArray_ID [ 0 ], ucArray_ID [ 1 ], ucArray_ID [ 2 ], ucArray_ID [ 3 ] );                               
  24.                                 printf ("%s\r\n",cStr );
  25.                         }
  26.                 }

  27.   }
  28. }
复制代码

————————————————
版权声明:Aspirant-GQ


收藏 评论0 发布时间:2023-3-18 12:47

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版