1. RC522模块与STM32的硬件连接第一次接触RC522刷卡模块时最让我头疼的就是硬件接线。这个火柴盒大小的模块上有8个引脚但实际只用接7根线。我习惯用STM32F103C8T6这种蓝色小板子做原型开发接线时发现个坑RC522的SPI接口电平是3.3V的千万别接5V具体接线时我推荐这样对应以STM32F1系列为例RC522的SDA接PB12SPI_NSSSCK接PB13SPI_SCKMOSI接PB15SPI_MOSIMISO接PB14SPI_MISORST接PA8任意GPIO都行GND接地VCC接3.3V实测中发现如果SPI时钟速度超过5MHz读卡会不稳定。后来看手册才知道RC522的SPI最高支持10MHz但实际应用建议降到2MHz以下。我在初始化代码里加了这段配置SPI_InitStructure.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_32; // 72MHz/322.25MHz2. SPI通信协议调试技巧刚开始调试SPI通信时我用逻辑分析仪抓波形发现数据全是乱的。后来总结出几个排查要点相位和极性RC522要求SPI模式0CPOL0, CPHA0在STM32CubeMX里要这样设置SPI_InitStructure.SPI_CPOL SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA SPI_CPHA_1Edge;字节顺序RC522是MSB高位在前传输这个容易忽略。有次我调试半天发现数据错位就是因为没注意这个细节。寄存器读写验证建议先用简单的寄存器测试比如读VersionReg版本寄存器它的固定值是0x92。我写了个测试函数uint8_t RC522_Check(void) { uint8_t version ReadRawRC(VersionReg); if(version 0x92) { printf(RC522检测成功\r\n); return 1; } printf(检测失败读到:0x%02X\r\n, version); return 0; }3. 卡片检测流程优化原始代码里的寻卡流程比较基础在实际门禁系统中需要优化。我总结出几个关键点三重检测机制先用PcdRequest()发送0x26命令唤醒卡片防冲撞处理用PcdAnticoll()获取UID最后用PcdSelect()选择卡片这里有个实用技巧在循环寻卡时加入超时判断我通常设置300ms超时uint32_t timeout HAL_GetTick(); do { status PcdRequest(PICC_REQIDL, CT); if(HAL_GetTick() - timeout 300) break; } while(status ! MI_OK);防冲撞处理时要注意同一时间可能有多个卡片进入感应区。我的处理方案是记录首次检测到的卡片UID后续3次检测都匹配才确认有效加入去抖动延时约100ms4. 门禁系统功能实现完整的门禁系统需要这几个功能模块1. 用户卡管理我用结构体数组存储授权卡信息支持增删改查typedef struct { uint8_t UID[4]; char userName[16]; uint32_t validDate; } CardUser; CardUser authUsers[] { {{0x12,0x34,0x56,0x78}, 管理员, 0xFFFFFFFF}, {{0x55,0x66,0x77,0x88}, 员工A, 0x20241231} };2. 权限验证逻辑uint8_t checkPermission(uint8_t *uid) { for(int i0; isizeof(authUsers)/sizeof(CardUser); i) { if(memcmp(uid, authUsers[i].UID, 4) 0) { if(HAL_GetTick() authUsers[i].validDate) { return 1; // 验证通过 } } } return 0; // 验证失败 }3. 状态指示系统LED绿灯长亮等待刷卡LED蓝灯闪烁识别中LED红灯闪烁权限拒绝蜂鸣器提示音成功时滴一声失败时滴滴两声4. 数据存储设计我用STM32的Flash模拟EEPROM存储关键数据扇区20x08008000开始存用户数据每个用户占32字节空间包含UID、权限等级、有效期等写入前要先擦除扇区FLASH_Erase_Sector(FLASH_SECTOR_2, VOLTAGE_RANGE_3);5. 低功耗优化方案门禁系统常需电池供电我通过以下方式降低功耗间歇工作模式void RC522_PowerSave(void) { PcdAntennaOff(); // 关闭天线 HAL_SPI_DeInit(hspi1); // 关闭SPI __HAL_RCC_SPI1_CLK_DISABLE(); // 关闭时钟 }唤醒方式选择外部中断唤醒按键或运动传感器定时唤醒每500ms检测一次动态功耗调整void SystemClock_Config_LowPower(void) { RCC_OscInitTypeDef RCC_OscInitStruct {0}; RCC_OscInitStruct.OscillatorType RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState RCC_PLL_OFF; HAL_RCC_OscConfig(RCC_OscInitStruct); RCC_ClkInitTypeDef RCC_ClkInitStruct {0}; RCC_ClkInitStruct.ClockType RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK; RCC_ClkInitStruct.SYSCLKSource RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider RCC_SYSCLK_DIV8; // 降频到2MHz HAL_RCC_ClockConfig(RCC_ClkInitStruct, FLASH_LATENCY_0); }6. 抗干扰设计经验在工厂环境实测时发现以下干扰问题金属干扰解决方法在RC522背面贴3mm厚度的双面胶隔离天线周围留出5mm以上的净空区多卡冲突采用分层检测策略先检测卡片类型再验证UID增加防冲突重试机制最多3次电磁干扰在VCC和GND之间加0.1uF陶瓷电容SPI信号线串联33Ω电阻使用屏蔽线连接天线调试技巧用以下代码检测环境噪声uint8_t detectNoise(void) { WriteRawRC(CommandReg, PCD_IDLE); WriteRawRC(FIFOLevelReg, 0x80); // 清空FIFO WriteRawRC(CommandReg, PCD_RECEIVE); delay_ms(10); return ReadRawRC(FIFOLevelReg); // 返回噪声数据量 }7. 工程代码架构设计经过多个项目迭代我总结出这样的代码架构├── Drivers │ ├── RC522 │ │ ├── rc522.c // 底层驱动 │ │ └── rc522.h ├── Middlewares │ ├── CardLib // 卡片操作库 │ │ ├── mifare.c │ │ └── mifare.h ├── Application │ ├── UserCards.c // 用户卡管理 │ ├── AccessCtrl.c // 门禁逻辑 │ └── Indicator.c // 状态指示关键设计要点硬件抽象层HAL隔离具体MCU型号采用回调函数处理事件typedef void (*CardDetectedCallback)(uint8_t *UID); void RC522_SetCallback(CardDetectedCallback cb) { userCallback cb; }状态机管理门禁流程typedef enum { STATE_IDLE, STATE_DETECTING, STATE_AUTHING, STATE_GRANTED, STATE_DENIED } SystemState;在main.c中实现主循环while(1) { switch(sysState) { case STATE_IDLE: if(detectCard()) sysState STATE_DETECTING; break; case STATE_DETECTING: if(getCardUID(uid)) sysState STATE_AUTHING; break; // ...其他状态处理 } HAL_Delay(10); }