news 2026/4/16 14:04:56

深入RC522:除了读卡号,用STM32 HAL库还能玩转M1卡读写与值操作

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深入RC522:除了读卡号,用STM32 HAL库还能玩转M1卡读写与值操作

RC522与STM32 HAL库实战:从基础读卡到M1卡电子钱包开发

当你第一次用RC522模块读到Mifare卡的UID时,那种成就感就像破解了某种神秘代码。但很快你会发现,这仅仅是射频识别世界的冰山一角。在门禁系统、公交卡、校园一卡通等实际应用中,Mifare卡真正强大的功能在于其扇区管理、数据块读写和电子钱包机制。本文将带你从基础读卡出发,逐步实现Mifare Classic 1K卡(简称M1卡)的完整操作流程,最终构建一个简易电子钱包系统。

1. RC522与Mifare卡技术基础

1.1 硬件架构解析

RC522作为NXP推出的低成本13.56MHz射频读写芯片,其内部结构远比表面看到的复杂。芯片核心由模拟电路、协议处理单元和SPI/I2C/UART接口组成。模拟部分负责载波生成和信号解调,协议处理器则实现了ISO14443A标准的底层通信。

关键性能参数对比

参数RC522规格典型应用要求
工作频率13.56MHz ±7kHz13.56MHz
数据传输速率最高424kbps106kbps典型值
读写距离5-10cm(视天线设计)3-5cm(门禁场景)
功耗13-26mA(工作状态)<30mA

在STM32硬件连接上,除了基本的SPI引脚(MOSI/MISO/SCK)外,特别注意:

// 典型引脚定义(以STM32F103为例) #define RC522_CS_PIN GPIO_PIN_4 #define RC522_CS_PORT GPIOA #define RC522_RST_PIN GPIO_PIN_3 #define RC522_RST_PORT GPIOA

1.2 Mifare卡存储结构详解

Mifare Classic 1K卡的1KB存储空间被划分为16个扇区(Sector 0-15),每个扇区包含4个块(Block 0-3)。其中:

  • 块0-2:数据块(共48字节可用空间)
  • 块3:密钥控制块(存放A/B密钥和访问控制位)

扇区访问控制矩阵(以典型配置为例):

操作密钥A权限密钥B权限
读数据块需要可选
写数据块需要需要
增值/减值操作需要需要
读密钥控制块禁止禁止
写密钥控制块需要需要

注意:扇区0的块0存储了卡的UID和厂商信息,通常为只读状态。修改这些数据可能导致卡片失效。

2. HAL库驱动开发与核心函数实现

2.1 SPI通信底层优化

虽然HAL库提供了SPI通信的基本函数,但直接使用HAL_SPI_TransmitReceive()在RC522场景下效率较低。我们需要封装专用通信函数:

uint8_t RC522_SPI_Exchange(uint8_t data) { uint8_t ret; HAL_GPIO_WritePin(RC522_CS_PORT, RC522_CS_PIN, GPIO_PIN_RESET); HAL_SPI_TransmitReceive(&hspi1, &data, &ret, 1, 100); HAL_GPIO_WritePin(RC522_CS_PORT, RC522_CS_PIN, GPIO_PIN_SET); return ret; } void RC522_WriteReg(uint8_t addr, uint8_t val) { addr = (addr << 1) & 0x7E; // 地址格式转换 RC522_SPI_Exchange(addr); RC522_SPI_Exchange(val); } uint8_t RC522_ReadReg(uint8_t addr) { addr = ((addr << 1) & 0x7E) | 0x80; RC522_SPI_Exchange(addr); return RC522_SPI_Exchange(0x00); }

2.2 认证机制实现

Mifare卡的认证过程采用三次握手协议。以下是典型认证流程:

  1. 读卡器发送认证请求命令
  2. 卡片返回随机数A
  3. 读卡器用密钥加密随机数A并发送
  4. 卡片验证加密结果并返回随机数B
  5. 读卡器加密随机数B完成双向认证

对应代码实现:

HAL_StatusTypeDef M1_Authenticate(uint8_t sector, uint8_t keyType, uint8_t* key) { uint8_t cmdBuf[12]; uint16_t recvLen; // 构造认证命令帧 cmdBuf[0] = keyType; // 0x60 for KeyA, 0x61 for KeyB cmdBuf[1] = sector * 4; // 转换为块地址 // 填充密钥和UID memcpy(&cmdBuf[2], key, 6); memcpy(&cmdBuf[8], cardUID, 4); // 发送认证命令 return RC522_Cmd(MFRC_AUTHENT, cmdBuf, 12, cmdBuf, &recvLen); }

3. 数据块高级操作实战

3.1 安全读写流程

完整的扇区读写应遵循以下步骤:

  1. 寻卡(PCD_Request)
  2. 防冲突获取UID(PCD_Anticoll)
  3. 选择卡片(PCD_Select)
  4. 认证扇区(PCD_AuthState)
  5. 执行读写操作

典型写操作代码

void M1_WriteBlock(uint8_t blockAddr, uint8_t* data) { uint8_t cmdBuf[18]; uint16_t recvLen; cmdBuf[0] = PICC_WRITE; cmdBuf[1] = blockAddr; RC522_CalculateCRC(cmdBuf, 2, &cmdBuf[2]); if(RC522_Cmd(MFRC_TRANSCEIVE, cmdBuf, 4, cmdBuf, &recvLen) == HAL_OK) { memcpy(cmdBuf, data, 16); RC522_CalculateCRC(cmdBuf, 16, &cmdBuf[16]); RC522_Cmd(MFRC_TRANSCEIVE, cmdBuf, 18, cmdBuf, &recvLen); } }

3.2 数值块操作技巧

Mifare卡的特殊数值块支持原子性的增值(Increment)、减值(Decrement)和转存(Transfer)操作。这是实现电子钱包功能的基础:

typedef struct { int32_t value; // 实际数值 uint8_t addr; // 备份块地址 } ValueBlock; void M1_ModifyValue(uint8_t blockAddr, int32_t delta, ValueBlock* backup) { uint8_t cmdBuf[16]; int32_t newValue = backup->value + delta; // 构造4字节数值(小端格式) cmdBuf[0] = newValue & 0xFF; cmdBuf[1] = (newValue >> 8) & 0xFF; cmdBuf[2] = (newValue >> 16) & 0xFF; cmdBuf[3] = (newValue >> 24) & 0xFF; // 执行增值操作 M1_ValueOperation(PICC_INCREMENT, blockAddr, cmdBuf); // 更新备份块 memcpy(&cmdBuf[4], &backup->addr, 4); M1_WriteBlock(backup->addr, cmdBuf); }

4. 电子钱包系统设计与实现

4.1 存储结构设计

一个健壮的电子钱包系统需要:

  • 余额存储(主块+备份块)
  • 交易记录区
  • 密钥管理区
  • 系统信息区

典型扇区分配方案

扇区块0块1块2块3(控制块)
1钱包余额主块交易记录1交易记录2密钥A/B
2钱包备份块交易记录3系统信息密钥A/B
3-15预留扩展预留扩展预留扩展密钥A/B

4.2 完整交易流程实现

消费交易的安全实现需要包含以下步骤:

  1. 读取当前余额和交易计数器
  2. 检查余额是否充足
  3. 执行减值操作
  4. 记录交易日志
  5. 验证操作结果
  6. 更新备份数据
typedef struct { uint32_t timestamp; int32_t amount; uint8_t terminalID[4]; } TransactionRecord; HAL_StatusTypeDef Wallet_Deduct(uint8_t sector, int32_t amount, uint8_t* terminal) { ValueBlock balance; TransactionRecord tx; // 1. 读取当前余额 M1_ReadValueBlock(sector*4, &balance); // 2. 检查余额 if(balance.value < amount) return HAL_ERROR; // 3. 准备交易记录 tx.timestamp = HAL_GetTick(); tx.amount = -amount; memcpy(tx.terminalID, terminal, 4); // 4. 执行减值操作 if(M1_ModifyValue(sector*4, -amount, &balance) != HAL_OK) { return HAL_ERROR; } // 5. 记录交易 uint8_t txBlock[16]; memcpy(txBlock, &tx, sizeof(TransactionRecord)); M1_WriteBlock(sector*4+1, txBlock); return HAL_OK; }

4.3 异常处理与数据恢复

在实际应用中必须考虑:

  • 事务中断恢复机制
  • 余额一致性检查
  • 防重放攻击措施

典型恢复流程

void Wallet_Recovery(uint8_t sector) { ValueBlock main, backup; M1_ReadValueBlock(sector*4, &main); M1_ReadValueBlock(sector*4+4, &backup); if(main.value != backup.value) { // 选择较新的版本恢复 int32_t recovered = (main.timestamp > backup.timestamp) ? main.value : backup.value; M1_WriteValue(sector*4, recovered); M1_WriteValue(sector*4+4, recovered); } }

5. 性能优化与安全增强

5.1 通信时序优化

通过调整RC522的定时器参数可以显著提升通信成功率:

void RC522_TimingOptimize(void) { // 设置定时器重装值(约25ms超时) RC522_WriteReg(MFRC_TReloadRegL, 30); RC522_WriteReg(MFRC_TReloadRegH, 0); // 定时器模式配置 RC522_WriteReg(MFRC_TModeReg, 0x8D); // TAuto=1, TPreScaler=13 RC522_WriteReg(MFRC_TPrescalerReg, 0x3E); // 62分频 }

5.2 密钥安全管理

避免在代码中硬编码密钥,推荐采用动态密钥分发方案:

  1. 卡片出厂时写入初始密钥
  2. 首次使用时通过安全通道更新密钥
  3. 定期轮换业务密钥

密钥更新示例

void M1_ChangeKey(uint8_t sector, uint8_t keyType, uint8_t* oldKey, uint8_t* newKey) { uint8_t blockData[16]; // 1. 使用旧密钥认证 M1_Authenticate(sector, keyType, oldKey); // 2. 读取控制块 M1_ReadBlock(sector*4+3, blockData); // 3. 修改密钥区 if(keyType == 0x60) { memcpy(blockData, newKey, 6); } else { memcpy(blockData+10, newKey, 6); } // 4. 写回控制块 M1_WriteBlock(sector*4+3, blockData); }

在完成基础功能后,可以进一步实现多应用隔离、离线交易批处理等高级功能。实际项目中,建议在STM32中集成轻量级文件系统管理卡片数据,并使用HMAC算法实现交易验证。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 14:03:37

QQ空间历史说说备份终极指南:GetQzonehistory免费开源工具完整教程

QQ空间历史说说备份终极指南&#xff1a;GetQzonehistory免费开源工具完整教程 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 在数字时代&#xff0c;我们的记忆越来越多地存储在云端&…

作者头像 李华
网站建设 2026/4/16 14:02:49

终极二维码修复指南:QrazyBox如何让损坏的二维码重获新生

终极二维码修复指南&#xff1a;QrazyBox如何让损坏的二维码重获新生 【免费下载链接】qrazybox QR Code Analysis and Recovery Toolkit 项目地址: https://gitcode.com/gh_mirrors/qr/qrazybox 二维码作为数字时代的通行证&#xff0c;承载着海量信息。然而物理磨损、…

作者头像 李华
网站建设 2026/4/16 14:00:28

极简实现 YouTube 视频内嵌字幕保存,iOS/Android全适配

很多人做 YouTube 视频资料整理时&#xff0c;表面问题是保存视频&#xff0c;但实际瓶颈是如何稳定保存视频内嵌字幕。尤其在移动端&#xff0c;常见情况是&#xff1a;安卓往往还能找到可行路径&#xff0c;iPhone 却限制更多。本文提供一套低学习成本、跨平台适配、可重复执…

作者头像 李华
网站建设 2026/4/16 13:58:31

深入 Rust 引用计数智能指针:Rc 与 Arc 从入门到实战

文章目录 深入 Rust 引用计数智能指针&#xff1a;Rc 与 Arc 从入门到实战引用计数&#xff1a;共享所有权的底层逻辑Rc&#xff1a;单线程下的轻量共享什么是 Rc基本用法Rc RefCell&#xff1a;实现单线程共享可变数据Rc 的陷阱&#xff1a;循环引用与 Weak 指针 Arc&#xf…

作者头像 李华
网站建设 2026/4/16 13:57:35

从CTF实战到漏洞挖掘:2025年广西网安赛题WriteUP深度解析

1. 从CTF签到题看信息隐藏技巧 2025年广西网安赛的Misc签到题看似简单&#xff0c;却暗藏玄机。题目给出一个加密的ZIP压缩包&#xff0c;提示需要爆破六位数密码。这里就涉及到CTF比赛中常见的信息隐藏三板斧&#xff1a; 弱密码爆破&#xff1a;使用工具如John the Ripper或f…

作者头像 李华