IC卡探索记录- MFRC522+STM32F103C8 操作IC卡(M1卡) ---附代码

1 源码

不藏着掖着,直接上代码
链接: https://gitee.com/H0x9DEFA478/ic_mifare1-mfrc522.git.

2 操作对象是什么?

“废话,当然是IC卡了!” ,话可不能怎么说,我们得深入了解,至少的知道操作的IC卡到底是个什么东东,要操作具体内容是什么。

2.1 对象

2.1.1 使用硬件

2.1.1.1 RC522

模块
图是网上随便找的图,这是我使用的模块,硬件电路已经接成SPI模式了

2.1.1.2 主控

主控是STM32F103C8T6,为什么是它,我手里的板子只有它是有排座引出引脚的。

2.1.1.3 IC卡

一张S50白卡。

2.1.2 电路

这玩意还需要电路?直接硬件SPI怼起来。

这是我的硬件连接

VCC----------3.3V--------3.3V

PA9----------------------RST

PA8----------------------IRQ (本例不使用,可不接)

PB14---------------------MISO (注意 不要接错)

PB15---------------------MOSI (注意 不要接错)

PB13---------------------SCK

PB12---------------------SDA(CS)

除了这些,还需要PA11,PA12用作USB功能,单片机用USB虚拟串口与电脑通信

2.1.3 对象说明

2.1.3.1 IC卡S50

M1卡是常见的IC卡类型, 而S50是M1卡的其中一种。

其内部可以存储1KB的数据。显而易见,我们就是要操作这1KB的数据

结构
这是S50卡手册中对这1KB的描述,分为16个扇区,每个扇区4个块,每个块16字节。
可以发现,开头(图片底部的扇区)的扇区比较特殊,根据描述这个块出厂是被固化的,不可修改,用于存放UID之类的。
每个扇区尾部(第四个块)都有一个特殊的块,用来存放密钥A密钥B控制字控制字控制着它所在块的访问条件,这个里面的内容可以不先深究,暂且只需要知道它的默认值是FF 07 80 69 就行,对于这个控制字,代表这这个扇区仅需要验证密钥A就能随便访问整个扇区(读,写,增值减值等),其他控制字类型可以通过S50的说明文档找到。 剩下的就是数据块了,可以被我们自由使用的块。
具体怎么读写这些数据放到后面说。

2.1.3.2 MFRC522

用于与IC卡通信,就是一堆寄存器操作。这个玩意的通信方式有SPI,IIC,UART三种(我的代码给出了SPI和IIC的接口,但IIC的没有测试)

一翻RC522的手册,一看寄存器直接蒙了。这么那么多寄存器,我参考了一些网上的代码,发现都是同源的,操作基本都一致。我多次实验过后发现对于操作M1卡的初始化,只需要初始化一个寄存器就行,没错!就一个。这个后面会提到。

2.1.3.3 STM32F103C8T6

我觉得这个已经不需要说明

3 如何通讯

感觉也是废话,当然是用SPI通讯啊。不过看代码时要分清楚哪些是控制IC卡的通讯,哪些仅仅只是MCU与RC522之间的通讯。

3.1 射频接口

这里并不解释射频的底层原理(而且我也不会),但有一些基本的还是要知道的。

3.1.1 IC卡的能量来源

当然是从射频载波得到,频率为13.56MHz。为什么提到这个?MFRC522是可以控制载波是否开启的,也就是说我可以顺带控制IC卡是否有电。

3.1.2 MFRC522如何与IC卡通信

它们两个之间的通信也是通过载波进行,通过控制载波幅度来通信。(至于怎么控制的,我也不知道)。只需要知道这两者之间的通信是类似于串口的通讯方式,只不过物理层面是通过射频。

3.2 SPI接口

???

4 软件结构

谁不喜欢一层一层的结构呢?
软件分为Mifare1层MFRC522层Mifare1层负责将用户操作转为与IC卡操作的通讯流,在通过MFRC522层传到RC522上,再由RC522与IC卡通讯。
上层:Mifare1层
底层:MFRC522层

4.1 MFRC522层

此层负责将底层(SPI)与IC卡通讯连接起来,向底层调用SPI读写函数,向顶层提供IC卡读写函数。

具体代码见源码。下面为使用时需要包含的头文件。

#ifndef __MFRC522_H_
#define __MFRC522_H_
#include "H_Type.h"

#define vMFRC522_ProtocolType_ISO14443_A  1

#define vMFRC522_LL_Function_Type_SPI     1
#define vMFRC522_LL_Function_Type_IIC     2

#define vMFRC522_MFAuthent_AuthentType_A  1
#define vMFRC522_MFAuthent_AuthentType_B  2

//初始化时参数TxSequenceBuffer的长度
#define vMFRC522_TxSequenceBufferLength   19

//初始化时参数RxSequenceBuffer的长度
#define vMFRC522_RxSequenceBufferLength   vMFRC522_TxSequenceBufferLength


typedef struct{

  int Type;//底层传输类型

  void (*Delay)(int);//延时调用

  void (*Reset)();//复位调用

  union{
    void (*Transfer_SPI)(void*,void*,int,int);//传输序列 (发送序列,接收序列,长度,传输速度(0:高速 其他:低速))
    struct{
      int (*Write)(Hbyte,Hbyte*,int);//底层iic写操作 返回 0:成功 其他:失败 (7位器件地址,发送数据,发送数据长度)
      int (*Read)(Hbyte,Hbyte*,int,Hbyte*,int);//底层iic读操作 返回 0:成功 其他:失败 (7位器件地址,发送数据,发送数据长度,接收数据,接收数据长度)
      Hbyte deviceAddr;//器件7位地址
    }Transfer_IIC;
  }TransferSequence;
  
}MFRC522_LL_Function;



typedef struct _MFRC522{
  
  int ProtocolType;

  void* TxSequenceBuffer;//发送缓存
  void* RxSequenceBuffer;//接收缓存

  struct{
    Hbyte (*ReadReg)(struct _MFRC522*,Hbyte);
    void (*ReReadRegToArray)(struct _MFRC522*,Hbyte,Hbyte*,int);
    void (*ReadRegArray)(struct _MFRC522*,Hbyte,Hbyte*,int);
    void (*WriteReg)(struct _MFRC522*,Hbyte,Hbyte);
    void (*ReWriteRegFromArray)(struct _MFRC522*,Hbyte,Hbyte*,int);
    void (*WriteRegArray)(struct _MFRC522*,Hbyte,Hbyte*,int);
  }StaticFunction;

  int LL_Speed;
  MFRC522_LL_Function LL_Function;

  int MFCrypto1On;
  Hbyte Version;

}MFRC522;


//操作返回结果 

#define vMFRC522_Result_RecvTimeOut                                4            //读取超时
#define vMFRC522_Result_RecvTooLong                                3            //接收数据过长
#define vMFRC522_Result_NoCard                                     2            //无响应 未发现卡
#define vMFRC522_Result_ParamError                                 1            //参数错误
#define vMFRC522_Result_Ok                                         0            //操作无错误
#define vMFRC522_Result_Error                                      -1           //出现错误 此情况下IC通信已经无法继续下去且无法恢复,需要复位后再次尝试通信



//============================================================================================================================================
//
// 提供给外部的方法
//
//============================================================================================================================================

/**
 * @brief 初始化RC522
 * @param _this 未使用的句柄
 * @param LL_Function 底层相关回调与信息
 * @param TxSequenceBuffer 底层发送缓存 长度vMFRC522_TxSequenceBufferLength字节
 * @param RxSequenceBuffer 底层接收缓存 长度vMFRC522_RxSequenceBufferLength字节
 * @param Type 协议类型 vMFRC522_ProtocolType_ISO14443_A可选
 * @return 返回执行结果
 */
int MFRC522_Init(MFRC522* _this,MFRC522_LL_Function* LL_Function,void* TxSequenceBuffer,void* RxSequenceBuffer,int ProtocolType);

/**
 * @brief 复位RC522 在通信出现错误时可以使用 复位后开启载波
 * @param _this RC522句柄
 * @return 无
 */
void MFRC522_Reset(MFRC522* _this);

/**
 * @brief 向卡发送数据
 * @param _this RC522句柄
 * @param TxData 发送的数据指针
 * @param TxBitLength 发送的长度 单位Bit 低位在前
 * @param WaitTime 最大等待时间
 * @return 返回执行结果
 */
int MFRC522_Transmit(MFRC522* _this,Hbyte* TxData,Hbyte TxBitLength,int WaitTime);

/**
 * @brief 接收卡发来的数据
 * @param _this RC522句柄
 * @param RxData 容纳接收数据的空间指针
 * @param MaxRxBitLengthPtr 最大接收长度 单位Bit
 * @param RxBitLengthPtr 用于返回接收到的长度 单位Bit 低位在前
 * @param WaitTime 最大等待时间
 * @return 返回执行结果
 */
int MFRC522_Receive(MFRC522* _this,Hbyte* RxData,Hbyte MaxRxBitLengthPtr,Hbyte* RxBitLengthPtr,int WaitTime);

/**
 * @brief 发送后接收数据
 * @param _this RC522句柄
 * @param TxData 发送的数据指针 *这个空间可与RxData为同一个
 * @param TxBitLength 发送的长度 单位Bit 低位在前
 * @param RxData 容纳接收数据的空间指针 *这个空间可与RxData为同一个
 * @param MaxRxBitLengthPtr 最大接收长度 单位Bit
 * @param RxBitLengthPtr 用于返回接收到的长度 单位Bit 低位在前
 * @param WaitTime 最大等待时间
 * @return 返回执行结果
 */
int MFRC522_Transceive(MFRC522* _this,Hbyte* TxData,Hbyte TxBitLength,Hbyte* RxData,Hbyte MaxRxBitLengthPtr,Hbyte* RxBitLengthPtr,int WaitTime);

/**
 * @brief 进行三轮认证
 * @param _this RC522句柄
 * @param SerialNumber 卡ID
 * @param BlockAddr 要验证的块地址
 * @param AuthentType 要验证的密码类型 vMFRC522_MFAuthent_AuthentType_A与vMFRC522_MFAuthent_AuthentType_B可选
 * @param Password 密码序列
 * @return 返回执行结果
 */
int MFRC522_MFAuthent(MFRC522* _this,Hbyte* SerialNumber,Hbyte BlockAddr,int AuthentType,Hbyte* Password);

/**
 * @brief 清除MFCrypto1On位 对一张卡处理完成后调用一次(清除加密状态)
 * @param _this RC522句柄
 * @return 无
 */
void MFRC522_ClearMFCrypto1On(MFRC522* _this);

//============================================================================================================================================
//
// 提供给外部的方法(不一定是顶层必要的方法)
//
//============================================================================================================================================

/**
 * @brief 获取RC522版本号 可使用这个方法判定RC522是否存在
 * @param _this RC522句柄
 * @return 版本号
 */
Hbyte MFRC522_Version(MFRC522* _this);

/**
 * @brief 验证RC522是否存在
 * @param _this RC522句柄
 * @return 返回执行结果
 */
int MFRC522_IsExist(MFRC522* _this);

/**
 * @brief 设置RC522载波状态 可在空闲时关闭载波 节省能源消耗
 * @param _this RC522句柄
 * @param IsEn 是否使能载波
 * @return 无
 */
void MFRC522_SetTxStatus(MFRC522* _this,int IsEn);



#endif //__MFRC522_H_

MFRC522_Init(MFRC522* _this,MFRC522_LL_Function* LL_Function,void* TxSequenceBuffer,void* RxSequenceBuffer,int ProtocolType)中,会将句柄内部的一些函数指针指向对应的函数,以适应SPI或IIC方式,所有的底层调用通过LL_Function传入,这样即使有多个RC522需要控制,只需要创建多个句柄,分别在初始化的时候传入不同的LL_Function就行了。

//初始化寄存器
static void RegInit(MFRC522* _this){

  //ISO14443A
  if(_this->ProtocolType==vMFRC522_ProtocolType_ISO14443_A){

    RegModify(_this,vMFRC522_RFCfgReg,vMFRC522_RFCfgReg_RxGain_Msk,0x7U<<vMFRC522_RFCfgReg_RxGain_Pos);//接收48dB

    //发送接收之间冷却延时设置
    RegModify(_this,vMFRC522_RxSelReg,vMFRC522_RxSelReg_RxWait_Msk,0x01U<<vMFRC522_RxSelReg_RxWait_Pos);

    //调制发送信号为100%ASK
    _this->StaticFunction.WriteReg(_this,vMFRC522_TxASKReg,vMFRC522_TxASKReg_Force100ASK);
  }
  
}

在初始化方法中,void RegInit(MFRC522* _this)被调用。里面负责寄存器初始化。可以发现,里面有三个寄存器被修改。但只有一个寄存器是必须的,其他是可选的(一些无关紧要的设置),必须的寄存器是vMFRC522_TxASKReg,要将它的bit6置为1。

这个底层文件提供了直接(对于高层来说)与IC卡通讯的函数,还要一个三轮认证密码的数int MFRC522_MFAuthent(MFRC522* _this,Hbyte* SerialNumber,Hbyte BlockAddr,int AuthentType,Hbyte* Password),因为NXP官方并不公开三轮认证的加密算法(不然只需要与IC卡通讯的方法就足够了)。NXP只给射频芯片上加了个密码验证的功能,由RC522来完成认证过程,所以提供了这个函数来使用这个功能(不然这个功能应该是属于上层的)。认证完毕后,通信的数据也不需要我们自己加密,而是由RC522加密发送的数据,解密接收的数据。

4.2 Mifare1层

此层负责将用户想对卡的操作转换成通讯流通过底层的MFRC522层传送到IC卡上,完成操作。

具体代码见源码。下面为使用时需要包含的头文件。

#ifndef __IC_Mifare1_H_
#define __IC_Mifare1_H_
#include "H_Type.h"


typedef struct{
  Hbyte* SerialNumber;
  int SerialNumberLength;
  Hbyte SAK;
  Huint16 ATQA;
}IC_Mifare1_CommunicationCallback_Param;

typedef struct{
  
  void* v;//底层句柄


  //必须实现

  int (*Reset)(void*);//复位底层
  int (*Transmit)(void*,Hbyte*,Hbyte,int);//发送数据 (底层句柄,发送数据,发送位长度,最大等待时间)
  int (*Receive)(void*,Hbyte*,Hbyte,Hbyte*,int);//接收数据 (底层句柄,接收数据,最大接收位长度,接收位长度,最大等待时间)
  int (*Transceive)(void*,Hbyte*,Hbyte,Hbyte*,Hbyte,Hbyte*,int);//发送接收数据 (底层句柄,发送数据,发送位长度,接收数据,最大接收位长度,接收位长度,最大等待时间)
  int (*MFAuthent)(void*,Hbyte*,Hbyte,int,Hbyte*);//三轮认证 密钥认证 (底层句柄,卡ID,块地址,密钥)
  

  //可选实现(如果不需要 固定返回vIC_Mifare1_Result_Ok)

  int (*CallBeforeProbeCard)(void*);//在卡检测之前被调用
  int (*CallAfterHaltCard)(void*);//在卡操作完成之后被调用

  int (*BspIsValid)(void*);//底层有效性检查 返回vIC_Mifare1_Result_Ok表明底层硬件有效 否则底层硬件无效

}IC_Mifare1_LL_Function;


typedef struct _IC_Mifare1{

  int Status;//状态

  IC_Mifare1_CommunicationCallback_Param* Param;//只有在CommunicationCallback中有效

  IC_Mifare1_LL_Function LL_Function;//底层方法

}IC_Mifare1;


#define vIC_Mifare1_Result_NoCard                                  1            //无响应 未发现卡
#define vIC_Mifare1_Result_Ok                                      0            //操作无错误
#define vIC_Mifare1_Result_Error                                   -1           //出现错误 此情况下IC通信已经无法继续下去且无法恢复,需要复位后再次尝试通信
#define vIC_Mifare1_Result_StatusError                             -2           //状态错误 调用了在某些状态下不能调用的方法

#define vIC_Mifare1_Status_Select                                  1            //卡选中状态
#define vIC_Mifare1_Status_Ready                                   0            //卡已准备好识别
#define vIC_Mifare1_Status_CommunicationError                      -1           //通信错误
#define vIC_Mifare1_Status_BspInvaild                              -2           //底层无效

#define vIC_Mifare1_AuthentType_A                                  1
#define vIC_Mifare1_AuthentType_B                                  2

#define vIC_Mifare1_ValueBlockGetValue_Value_Msk                   0x0FU        
#define vIC_Mifare1_ValueBlockGetValue_Value_Ok                    0x00U        //数值完全正确
#define vIC_Mifare1_ValueBlockGetValue_Value_PartOk                0x01U        //数值部分正确, 能解析出数值
#define vIC_Mifare1_ValueBlockGetValue_Value_Error                 0x02U        //数值错误过多, 没有解析出数值
#define vIC_Mifare1_ValueBlockGetValue_Addr_Msk                    0xF0U        
#define vIC_Mifare1_ValueBlockGetValue_Addr_Ok                     0x00U        //地址完全正确
#define vIC_Mifare1_ValueBlockGetValue_Addr_PartOk                 0x10U        //地址部分正确, 能解析出地址
#define vIC_Mifare1_ValueBlockGetValue_Addr_Error                  0x20U        //地址错误过多, 没有解析出地址



//============================================================================================================================================
//
// 提供给外部的方法
//
//============================================================================================================================================

/**
 * @brief 初始化IC_Mifare1控制器
 * @param _this 未使用的句柄
 * @param LL_Function 底层方法集合
 * @return 无
 */
void IC_Mifare1_Init(IC_Mifare1* _this,IC_Mifare1_LL_Function* LL_Function);

/**
 * @brief 尝试进行一次卡操作 如果寻卡成功CommunicationCallback被调用 此后卡被halt
 * @param _this IC_Mifare1句柄
 * @param CommunicationCallback IC_Mifare1句柄
 * @param IsAll 0:仅选中空闲的卡 其他:所有卡都在选中范围
 * @return 0: CommunicationCallback成功被调用 其他:CommunicationCallback没有被调用
 */
int IC_Mifare1_Communication(IC_Mifare1* _this,void (*CommunicationCallback)(IC_Mifare1*,IC_Mifare1_CommunicationCallback_Param*),int IsAll);

//============================================================================================================================================
//
// 提供给外部的方法 在IC_Mifare1_Communication()传入的CommunicationCallback()中调用
//
//============================================================================================================================================

/**
 * @brief 密钥验证
 * @param _this IC_Mifare1句柄
 * @param BlockAddr 要验证的块地址
 * @param AuthentType 密码类型 要验证的密码类型 vIC_Mifare1_AuthentType_A与vIC_Mifare1_AuthentType_B可选
 * @param Password 密钥
 * @return 返回执行结果
 */
int IC_Mifare1_Authent(IC_Mifare1* _this,Hbyte BlockAddr,int AuthentType,Hbyte* Password);

/**
 * @brief 读一个块
 * @param _this IC_Mifare1句柄
 * @param BlockAddr 块地址
 * @param Dst 指向用于存放数据的内存 大小16字节
 * @return 返回执行结果
 */
int IC_Mifare1_ReadBlock(IC_Mifare1* _this,Hbyte BlockAddr,Hbyte* Dst);

/**
 * @brief 写一个块
 * @param _this IC_Mifare1句柄
 * @param BlockAddr 块地址
 * @param SrcData 要写入的数据 大小16字节
 * @return 返回执行结果
 */
int IC_Mifare1_WriteBlock(IC_Mifare1* _this,Hbyte BlockAddr,Hbyte* SrcData);

/**
 * @brief 数值块操作 数值块内的值 增加/减小/不加不减 到IC卡的数值缓冲区
 * @param _this IC_Mifare1句柄
 * @param BlockAddr IC_Mifare1句柄
 * @param IsDecrement 如果dValue不为0, 则该段有效: 0:加值 其他:减值
 * @param dValue 变化的值 如果为0 则使用Restore指令, 值不做变化的转到IC卡的数值缓冲区
 * @return 返回执行结果
 */
int IC_Mifare1_ValueOperation(IC_Mifare1* _this,Hbyte BlockAddr,int IsDecrement,Huint32 dValue);

/**
 * @brief 数值块操作 将IC卡的数值缓冲区的数值存储到块中
 * @param _this IC_Mifare1句柄
 * @param BlockAddr 要存储到的块地址
 * @return 返回执行结果
 */
int IC_Mifare1_ValueTransfer(IC_Mifare1* _this,Hbyte BlockAddr);

//============================================================================================================================================
//
// 附加给外部的方法 无调用条件 随便调用
//
//============================================================================================================================================

/**
 * @brief 生成数组块数据
 * @param BlockData 生成的数据存放位置
 * @param BlockAddr 块地址
 * @param Value 数值
 * @return 无
 */
void IC_Mifare1_MakeValueBlock(Hbyte* BlockData,Hbyte BlockAddr,Huint32 Value);

/**
 * @brief 简单解析并获取块的数值和地址域的地址 (有更好的恢复方法(只是恢复概率更高) 但这个函数没有使用 其实也没什么必要)
 * @param BlockData 块数据
 * @param BlockAddr 用于返回地址域的地址
 * @param Value 用于返回数值
 * @return 结果有效性
 */
Hbyte IC_Mifare1_ValueBlockGetValue(Hbyte* BlockData,Hbyte* BlockAddr,Huint32* Value);

#endif //__IC_Mifare1_H_

执行void IC_Mifare1_Init(IC_Mifare1* _this,IC_Mifare1_LL_Function* LL_Function)时,LL_Function包含对MFRC522层的操作函数指针。
Mifare1层封装了对M1卡的操作,因为M1卡都是从寻卡开始,到Halt卡结束。所以这些过程被封装起来了,出了个
int IC_Mifare1_Communication(IC_Mifare1* _this,void (*CommunicationCallback)(IC_Mifare1*,IC_Mifare1_CommunicationCallback_Param*),int IsAll)函数,寻卡操作与Halt卡操作自动完成(其中Halt操作要在所有操作都成功的情况下会被使用,如果有操作失败,则不会被使用)。如果上次的调用这个函数时出现通讯错误,则本次调用时内部会首先复位底层的RC522,然后才开始寻卡操作。如果切顺利,在自动选中卡之后,传入该函数的
void (*CommunicationCallback)(IC_Mifare1*,IC_Mifare1_CommunicationCallback_Param*)被调用。这是用户传入的回调,在里面用户可以按照自己的需要调用验证密码,读、写、加值减值等操作。如果这个函数没有被调用(例如没有找到卡), 这个方法就返回非0值。

因为通信格式相同, Increment加值 \ Decrement减值 \ Restore不变, 这三个操作被int IC_Mifare1_ValueOperation(IC_Mifare1* _this,Hbyte BlockAddr,int IsDecrement,Huint32 dValue)这个函数包揽了。根据值是否为0,是否减少 来判定使用那条指令。这个函数调用成功后,被操作的值存在IC卡的缓冲区中(IC卡块里的数据并没有变化)。如果要保存操作后的数据(在IC卡的缓存里),需要调用int IC_Mifare1_ValueTransfer(IC_Mifare1* _this,Hbyte BlockAddr);来实现保存。该保存函数的成功意味着数据成功保存(啥,这都能失败那就是卡的问题了)。
*IC_Mifare1_ReadBlock()IC_Mifare1_WriteBlock()直接读取/写入块,没有缓冲区的事。

5 源码实现

STM32作为虚拟串口,接收主机的串口助手的指令。默认上电后不进行任何卡操作(仅初始化RC522)。

5.1 串口助手发送的指令

通过串口助手发送字符串进行操作,源码使用UTF8编码,串口助手要注意要设置为正确的编码。

单字符指令(注意大小写)

字符串: i 获取一些运行信息

字符串: v 获取MFRC522版本寄存器的值

字符串: I (i的大写)发送该指令后,进入初始化状态,进入范围的白卡都会被初始化,扇区1写入一些数据,并设置密钥

字符串: D 发送该指令后,进入反初始化状态,进入范围的卡都会被反初始化,密钥恢复为FFFFFFFFFFFF(只有指令I初始化的卡才能被反初始化)

字符串: R 发送该指令后,进入读取状态,可以读取初始化好的卡的内部数据

多字符指令(注意大小写)

字符串: Cxxx 该命令以C开头,xxx为10进制有符号整数,长度不限。对数值块进行加,减操作。成功操作后,自动进入读取状态。

例如: C-100值减100

例如: C50 值加50

串口助手发送C命令不要选上发送回车(后果我也不知道)

5.2 IC块的使用

源码只使用了IC卡的第二个扇区,这个扇区的所有块都被使用了。该扇区通过I指令

5.2.1 作用布局

块0: 存放用户数据(存了一个字符串)
块1: 数值块(备份)
块2: 数值块
块3:控制块(修改密码)

数值块通过写块操作生成,只需要按照特定格式写入。
数值块
数值为整型数,位宽4字节,在块中小端排列,重复拍3次,其中第二次位取反。最后4个字节存放地址,存放备份的地址,但实际上这个地址似乎可以随便选。

5.3 相关操作

5.3.1 初始化卡

init

5.3.2 尝试重复初始化

疯狂恢复操作失败,因为卡已经初始化了,密码已修改(如果操作失败,RC522重新初始化 IC卡被重新上点,导致重复检测)
reinit

5.3.3 读取卡

read

5.3.4 减值

请添加图片描述

5.3.5 加值

请添加图片描述

5.3.6 将卡反初为白卡(恢复默认密码)

请添加图片描述

版权声明:本文为CSDN博主「0x9DEFA478」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_42907191/article/details/120744485

生成海报
点赞 0

0x9DEFA478

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

串口通信之————IIC(软件驱动)

B站账号:小光学嵌入式 ⏩ 大家好哇!我是小光,嵌入式爱好者,一个想要成为系统架构师的大二学生。⏩最近开始系统性补习STM32基础知识,规划有:串口通信&#xf

STM32串口输出字符串

串口 串口全称为串行接口,采用 全双工、异步通信的通信方式,一次只能传输一帧,一帧中包含 起始位、数据位(一般为 8bit )、校验位、停止位。由于采用异步通信&#xff0