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

张开发
2026/4/16 14:05:01 15 分钟阅读

分享文章

深入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 GPIOA1.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卡的认证过程采用三次握手协议。以下是典型认证流程读卡器发送认证请求命令卡片返回随机数A读卡器用密钥加密随机数A并发送卡片验证加密结果并返回随机数B读卡器加密随机数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 安全读写流程完整的扇区读写应遵循以下步骤寻卡PCD_Request防冲突获取UIDPCD_Anticoll选择卡片PCD_Select认证扇区PCD_AuthState执行读写操作典型写操作代码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/B2钱包备份块交易记录3系统信息密钥A/B3-15预留扩展预留扩展预留扩展密钥A/B4.2 完整交易流程实现消费交易的安全实现需要包含以下步骤读取当前余额和交易计数器检查余额是否充足执行减值操作记录交易日志验证操作结果更新备份数据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*41, txBlock); return HAL_OK; }4.3 异常处理与数据恢复在实际应用中必须考虑事务中断恢复机制余额一致性检查防重放攻击措施典型恢复流程void Wallet_Recovery(uint8_t sector) { ValueBlock main, backup; M1_ReadValueBlock(sector*4, main); M1_ReadValueBlock(sector*44, 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*44, 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); // TAuto1, TPreScaler13 RC522_WriteReg(MFRC_TPrescalerReg, 0x3E); // 62分频 }5.2 密钥安全管理避免在代码中硬编码密钥推荐采用动态密钥分发方案卡片出厂时写入初始密钥首次使用时通过安全通道更新密钥定期轮换业务密钥密钥更新示例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*43, blockData); // 3. 修改密钥区 if(keyType 0x60) { memcpy(blockData, newKey, 6); } else { memcpy(blockData10, newKey, 6); } // 4. 写回控制块 M1_WriteBlock(sector*43, blockData); }在完成基础功能后可以进一步实现多应用隔离、离线交易批处理等高级功能。实际项目中建议在STM32中集成轻量级文件系统管理卡片数据并使用HMAC算法实现交易验证。

更多文章