rc522.h
/**
* @file rc522.h
* @brief RC522 RFID模块驱动程序头文件
* @details 用于STM32F103C8单片机
* @author Claude
* @date 2025-04-10
*/
#ifndef RC522_H
#define RC522_H
#include "stm32f10x.h"
#include <stdint.h>
/* RC522引脚定义 */
#define RC522_SPI SPI1
#define RC522_SPI_CLK RCC_APB2Periph_SPI1
#define RC522_CS_PIN GPIO_Pin_4
#define RC522_CS_GPIO_PORT GPIOA
#define RC522_CS_GPIO_CLK RCC_APB2Periph_GPIOA
#define RC522_CS_LOW() GPIO_ResetBits(RC522_CS_GPIO_PORT, RC522_CS_PIN)
#define RC522_CS_HIGH() GPIO_SetBits(RC522_CS_GPIO_PORT, RC522_CS_PIN)
#define RC522_SCK_PIN GPIO_Pin_5
#define RC522_SCK_GPIO_PORT GPIOA
#define RC522_SCK_GPIO_CLK RCC_APB2Periph_GPIOA
#define RC522_MISO_PIN GPIO_Pin_6
#define RC522_MISO_GPIO_PORT GPIOA
#define RC522_MISO_GPIO_CLK RCC_APB2Periph_GPIOA
#define RC522_MOSI_PIN GPIO_Pin_7
#define RC522_MOSI_GPIO_PORT GPIOA
#define RC522_MOSI_GPIO_CLK RCC_APB2Periph_GPIOA
#define RC522_RST_PIN GPIO_Pin_0
#define RC522_RST_GPIO_PORT GPIOB
#define RC522_RST_GPIO_CLK RCC_APB2Periph_GPIOB
#define RC522_RST_LOW() GPIO_ResetBits(RC522_RST_GPIO_PORT, RC522_RST_PIN)
#define RC522_RST_HIGH() GPIO_SetBits(RC522_RST_GPIO_PORT, RC522_RST_PIN)
/* RC522寄存器定义 */
#define RC522_REG_COMMAND 0x01
#define RC522_REG_COM_IE_N 0x02
#define RC522_REG_DIV_IE_N 0x03
#define RC522_REG_COM_IRQ 0x04
#define RC522_REG_DIV_IRQ 0x05
#define RC522_REG_ERROR 0x06
#define RC522_REG_STATUS1 0x07
#define RC522_REG_STATUS2 0x08
#define RC522_REG_FIFO_DATA 0x09
#define RC522_REG_FIFO_LEVEL 0x0A
#define RC522_REG_CONTROL 0x0C
#define RC522_REG_BIT_FRAMING 0x0D
#define RC522_REG_COLL 0x0E
#define RC522_REG_MODE 0x11
#define RC522_REG_TX_MODE 0x12
#define RC522_REG_RX_MODE 0x13
#define RC522_REG_TX_CONTROL 0x14
#define RC522_REG_TX_ASK 0x15
#define RC522_REG_CRC_RESULT_H 0x21
#define RC522_REG_CRC_RESULT_L 0x22
#define RC522_REG_MOD_WIDTH 0x24
#define RC522_REG_TYPE_B 0x25
#define RC522_REG_SERIAL_SPEED 0x26
#define RC522_REG_VERSION 0x37
/* RC522命令定义 */
#define RC522_CMD_IDLE 0x00 // 取消当前命令
#define RC522_CMD_MEM 0x01 // 存储数据到FIFO缓冲区
#define RC522_CMD_GENERATE_RANDOM 0x02 // 生成随机数
#define RC522_CMD_CALC_CRC 0x03 // CRC计算
#define RC522_CMD_TRANSMIT 0x04 // 数据传输
#define RC522_CMD_NO_CMD_CHANGE 0x07 // 无命令改变
#define RC522_CMD_RECEIVE 0x08 // 接收数据
#define RC522_CMD_TRANSCEIVE 0x0C // 发送并接收数据
#define RC522_CMD_MF_AUTHENT 0x0E // Mifare认证
#define RC522_CMD_SOFT_RESET 0x0F // 软复位
/* Mifare命令 */
#define PICC_REQIDL 0x26 // 寻找未进入休眠状态的卡
#define PICC_REQALL 0x52 // 寻找所有卡
#define PICC_ANTICOLL 0x93 // 防冲撞
#define PICC_SElECTTAG 0x93 // 选择卡
#define PICC_AUTHENT1A 0x60 // 认证密钥A
#define PICC_AUTHENT1B 0x61 // 认证密钥B
#define PICC_READ 0x30 // 读块
#define PICC_WRITE 0xA0 // 写块
#define PICC_DECREMENT 0xC0 // 扣款
#define PICC_INCREMENT 0xC1 // 充值
#define PICC_RESTORE 0xC2 // 将块中的数据传输到缓冲区
#define PICC_TRANSFER 0xB0 // 保存缓冲区中的数据
#define PICC_HALT 0x50 // 休眠
/* 错误码 */
#define MI_OK 0
#define MI_NOTAGERR 1
#define MI_ERR 2
/* 定义卡类型结构体 */
typedef struct {
uint8_t uid[4]; // 卡片UID
uint8_t sak; // 卡片类型标识
} RC522_Card_t;
/**
* @brief 初始化RC522模块
* @details 配置SPI接口和GPIO引脚,初始化RC522模块
* @return 无
*/
void RC522_Init(void);
/**
* @brief 重置RC522模块
* @details 通过硬件复位引脚和软件复位命令重置RC522
* @return 无
*/
void RC522_Reset(void);
/**
* @brief 向RC522寄存器写入数据
* @param addr RC522寄存器地址
* @param val 要写入的值
* @return 无
*/
void RC522_WriteRegister(uint8_t addr, uint8_t val);
/**
* @brief 从RC522寄存器读取数据
* @param addr RC522寄存器地址
* @return 读取到的寄存器值
*/
uint8_t RC522_ReadRegister(uint8_t addr);
/**
* @brief 设置RC522寄存器位
* @param addr RC522寄存器地址
* @param mask 要设置的位掩码
* @return 无
*/
void RC522_SetRegisterBits(uint8_t addr, uint8_t mask);
/**
* @brief 清除RC522寄存器位
* @param addr RC522寄存器地址
* @param mask 要清除的位掩码
* @return 无
*/
void RC522_ClearRegisterBits(uint8_t addr, uint8_t mask);
/**
* @brief 计算CRC校验码
* @param pInData 输入数据指针
* @param len 数据长度
* @param pOutData 输出CRC结果指针(2字节)
* @return 执行状态码,MI_OK表示成功
*/
uint8_t RC522_CalculateCRC(uint8_t *pInData, uint8_t len, uint8_t *pOutData);
/**
* @brief 与卡片通信
* @details 发送命令并接收卡片响应
* @param cmd 命令
* @param pInData 要发送的数据
* @param inLen 发送数据长度
* @param pOutData 接收数据缓冲区
* @param pOutLen 接收数据长度指针
* @return 执行状态码,MI_OK表示成功
*/
uint8_t RC522_CommunicateWithCard(uint8_t cmd, uint8_t *pInData, uint8_t inLen, uint8_t *pOutData, uint8_t *pOutLen);
/**
* @brief 请求卡片
* @details 寻找卡片并返回卡片类型
* @param reqMode 请求模式(PICC_REQIDL或PICC_REQALL)
* @param pTagType 卡片类型(2字节)
* @return 执行状态码,MI_OK表示成功
*/
uint8_t RC522_Request(uint8_t reqMode, uint8_t *pTagType);
/**
* @brief 防冲撞检测
* @details 读取卡片序列号
* @param pCard RC522_Card_t结构体指针,用于存储卡片信息
* @return 执行状态码,MI_OK表示成功
*/
uint8_t RC522_Anticoll(RC522_Card_t *pCard);
/**
* @brief 选择卡片
* @details 选择指定UID的卡片进行后续操作
* @param pCard RC522_Card_t结构体指针,包含卡片UID信息
* @return 执行状态码,MI_OK表示成功
*/
uint8_t RC522_SelectTag(RC522_Card_t *pCard);
/**
* @brief 验证卡片密钥
* @param authMode 认证模式(PICC_AUTHENT1A或PICC_AUTHENT1B)
* @param blockAddr 块地址
* @param pKey 密钥(6字节)
* @param pCard 卡片信息
* @return 执行状态码,MI_OK表示成功
*/
uint8_t RC522_Auth(uint8_t authMode, uint8_t blockAddr, uint8_t *pKey, RC522_Card_t *pCard);
/**
* @brief 读取数据块
* @param blockAddr 块地址
* @param pData 数据缓冲区(16字节)
* @return 执行状态码,MI_OK表示成功
*/
uint8_t RC522_ReadBlock(uint8_t blockAddr, uint8_t *pData);
/**
* @brief 写入数据块
* @param blockAddr 块地址
* @param pData 要写入的数据(16字节)
* @return 执行状态码,MI_OK表示成功
*/
uint8_t RC522_WriteBlock(uint8_t blockAddr, uint8_t *pData);
/**
* @brief 中止卡片通信
* @details 使卡片进入休眠状态
* @return 执行状态码
*/
uint8_t RC522_Halt(void);
/**
* @brief 关闭RC522天线
* @return 无
*/
void RC522_AntennaOff(void);
/**
* @brief 开启RC522天线
* @return 无
*/
void RC522_AntennaOn(void);
#endif /* RC522_H */
rc522.c
/**
* @file rc522.c
* @brief RC522 RFID模块驱动程序源文件
* @details 用于STM32F103C8单片机
* @author Claude
* @date 2025-04-10
*/
#include "rc522.h"
#include "stm32f10x.h"
#include <stdint.h>
/**
* @brief SPI发送/接收一个字节
* @param data 要发送的字节
* @return 接收到的字节
*/
static uint8_t RC522_SPITransfer(uint8_t data)
{
// 等待发送缓冲区为空
while (SPI_I2S_GetFlagStatus(RC522_SPI, SPI_I2S_FLAG_TXE) == RESET);
// 发送数据
SPI_I2S_SendData(RC522_SPI, data);
// 等待接收完成
while (SPI_I2S_GetFlagStatus(RC522_SPI, SPI_I2S_FLAG_RXNE) == RESET);
// 返回接收到的数据
return SPI_I2S_ReceiveData(RC522_SPI);
}
/**
* @brief 初始化RC522模块
*/
void RC522_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
// 使能SPI和GPIO时钟
RCC_APB2PeriphClockCmd(RC522_SPI_CLK | RC522_CS_GPIO_CLK | RC522_SCK_GPIO_CLK |
RC522_MISO_GPIO_CLK | RC522_MOSI_GPIO_CLK | RC522_RST_GPIO_CLK, ENABLE);
// 配置SPI引脚
GPIO_InitStructure.GPIO_Pin = RC522_SCK_PIN | RC522_MOSI_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(RC522_SCK_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = RC522_MISO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
GPIO_Init(RC522_MISO_GPIO_PORT, &GPIO_InitStructure);
// 配置CS引脚
GPIO_InitStructure.GPIO_Pin = RC522_CS_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(RC522_CS_GPIO_PORT, &GPIO_InitStructure);
// 配置RST引脚
GPIO_InitStructure.GPIO_Pin = RC522_RST_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(RC522_RST_GPIO_PORT, &GPIO_InitStructure);
// 初始电平
RC522_CS_HIGH(); // 片选拉高,不选中
RC522_RST_HIGH(); // 复位引脚拉高
// SPI配置
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // 双线全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; // 主模式
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 8位数据
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; // 时钟极性,空闲低电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; // 第一个边沿采样
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // 软件控制片选
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; // 波特率预分频
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; // 高位先行
SPI_InitStructure.SPI_CRCPolynomial = 7; // CRC多项式
SPI_Init(RC522_SPI, &SPI_InitStructure);
// 使能SPI
SPI_Cmd(RC522_SPI, ENABLE);
// 复位RC522
RC522_Reset();
// 配置RC522模式
RC522_WriteRegister(RC522_REG_TX_MODE, 0x00); // 发送模式:CRC初始值0x6363
RC522_WriteRegister(RC522_REG_RX_MODE, 0x00); // 接收模式:CRC使能,接收数据后CRC校验
RC522_WriteRegister(RC522_REG_MOD_WIDTH, 0x26); // ModWidth设置
// 配置定时器和其他设置
RC522_WriteRegister(RC522_REG_CONTROL, 0x10); // 设置发射强度
RC522_WriteRegister(RC522_REG_TX_ASK, 0x40); // 设置为100%ASK调制
RC522_WriteRegister(RC522_REG_MODE, 0x3D); // CRC初始值为0x6363
// 开启天线
RC522_AntennaOn();
}
/**
* @brief 重置RC522模块
*/
void RC522_Reset(void)
{
// 硬复位
RC522_RST_HIGH();
delay_us(1);
RC522_RST_LOW();
delay_us(1);
RC522_RST_HIGH();
delay_us(1);
// 软复位
RC522_WriteRegister(RC522_REG_COMMAND, RC522_CMD_SOFT_RESET);
// 等待复位完成
delay_ms(50);
// 写入模式配置
RC522_WriteRegister(RC522_REG_MODE, 0x3D);
// 写入定时器配置
RC522_WriteRegister(RC522_REG_CONTROL, 0x10); // T0Control
RC522_WriteRegister(RC522_REG_COM_IRQ, 0x00); // 清除所有中断
RC522_WriteRegister(RC522_REG_DIV_IRQ, 0x00); // 清除所有中断标志
}
/**
* @brief 向RC522寄存器写入数据
* @param addr RC522寄存器地址
* @param val 要写入的值
*/
void RC522_WriteRegister(uint8_t addr, uint8_t val)
{
// 地址格式:0XXXXXX0,写操作最低位为0
addr = (addr << 1) & 0x7E;
RC522_CS_LOW(); // 选中RC522
RC522_SPITransfer(addr); // 发送地址
RC522_SPITransfer(val); // 发送数据
RC522_CS_HIGH(); // 取消选中
}
/**
* @brief 从RC522寄存器读取数据
* @param addr RC522寄存器地址
* @return 读取到的寄存器值
*/
uint8_t RC522_ReadRegister(uint8_t addr)
{
uint8_t val;
// 地址格式:1XXXXXX0,读操作最高位为1
addr = ((addr << 1) & 0x7E) | 0x80;
RC522_CS_LOW(); // 选中RC522
RC522_SPITransfer(addr); // 发送地址
val = RC522_SPITransfer(0x00); // 读取数据
RC522_CS_HIGH(); // 取消选中
return val;
}
/**
* @brief 设置RC522寄存器位
* @param addr RC522寄存器地址
* @param mask 要设置的位掩码
*/
void RC522_SetRegisterBits(uint8_t addr, uint8_t mask)
{
uint8_t tmp;
tmp = RC522_ReadRegister(addr);
RC522_WriteRegister(addr, tmp | mask);
}
/**
* @brief 清除RC522寄存器位
* @param addr RC522寄存器地址
* @param mask 要清除的位掩码
*/
void RC522_ClearRegisterBits(uint8_t addr, uint8_t mask)
{
uint8_t tmp;
tmp = RC522_ReadRegister(addr);
RC522_WriteRegister(addr, tmp & (~mask));
}
/**
* @brief 计算CRC校验码
* @param pInData 输入数据指针
* @param len 数据长度
* @param pOutData 输出CRC结果指针(2字节)
* @return 执行状态码,MI_OK表示成功
*/
uint8_t RC522_CalculateCRC(uint8_t *pInData, uint8_t len, uint8_t *pOutData)
{
uint8_t status, i, n;
// 清除CRC_IRQ中断标志
RC522_ClearRegisterBits(RC522_REG_DIV_IRQ, 0x04);
// 清除FIFO缓冲区
RC522_SetRegisterBits(RC522_REG_FIFO_LEVEL, 0x80);
// 将数据写入FIFO
for (i = 0; i < len; i++) {
RC522_WriteRegister(RC522_REG_FIFO_DATA, *(pInData + i));
}
// 发送CRC计算命令
RC522_WriteRegister(RC522_REG_COMMAND, RC522_CMD_CALC_CRC);
// 等待CRC计算完成
i = 0xFF;
do {
n = RC522_ReadRegister(RC522_REG_DIV_IRQ);
i--;
} while ((i != 0) && !(n & 0x04)); // CRCIrq = 1表示计算完成
// 读取CRC计算结果
if (i != 0) {
pOutData[0] = RC522_ReadRegister(RC522_REG_CRC_RESULT_L);
pOutData[1] = RC522_ReadRegister(RC522_REG_CRC_RESULT_H);
status = MI_OK;
} else {
status = MI_ERR;
}
return status;
}
/**
* @brief 与卡片通信
* @param cmd 命令
* @param pInData 要发送的数据
* @param inLen 发送数据长度
* @param pOutData 接收数据缓冲区
* @param pOutLen 接收数据长度指针
* @return 执行状态码,MI_OK表示成功
*/
uint8_t RC522_CommunicateWithCard(uint8_t cmd, uint8_t *pInData, uint8_t inLen, uint8_t *pOutData, uint8_t *pOutLen)
{
uint8_t status = MI_ERR;
uint8_t irqEn = 0x00;
uint8_t waitIRq = 0x00;
uint8_t lastBits;
uint8_t n;
uint8_t i;
switch (cmd) {
case RC522_CMD_MF_AUTHENT: // Mifare认证
irqEn = 0x12; // 允许错误中断和空闲中断
waitIRq = 0x10; // 等待空闲中断
break;
case RC522_CMD_TRANSCEIVE: // 发送并接收数据
irqEn = 0x77; // 允许发送、接收、空闲、计时器结束中断
waitIRq = 0x30; // 等待接收中断和空闲中断
break;
default:
break;
}
// 设置中断开关
RC522_WriteRegister(RC522_REG_COM_IE_N, irqEn | 0x80); // 允许中断请求
// 清除中断标志
RC522_ClearRegisterBits(RC522_REG_COM_IRQ, 0x80); // 清除所有中断标志
RC522_SetRegisterBits(RC522_REG_FIFO_LEVEL, 0x80); // 清空FIFO
// 发送命令
RC522_WriteRegister(RC522_REG_COMMAND, RC522_CMD_IDLE); // 取消当前命令
// 写入数据到FIFO
for (i = 0; i < inLen; i++) {
RC522_WriteRegister(RC522_REG_FIFO_DATA, pInData[i]);
}
// 执行命令
RC522_WriteRegister(RC522_REG_COMMAND, cmd);
// 如果是发送并接收数据命令,需要启动发送
if (cmd == RC522_CMD_TRANSCEIVE) {
RC522_SetRegisterBits(RC522_REG_BIT_FRAMING, 0x80); // 启动数据发送
}
// 等待命令完成
i = 2000; // 根据时钟频率调整,等待最长时间约25ms
do {
n = RC522_ReadRegister(RC522_REG_COM_IRQ);
i--;
} while ((i != 0) && !(n & 0x01) && !(n & waitIRq)); // 等待命令完成
// 清除启动位
RC522_ClearRegisterBits(RC522_REG_BIT_FRAMING, 0x80);
// 检查命令执行结果
if (i != 0) {
// 读取错误标志寄存器
if (!(RC522_ReadRegister(RC522_REG_ERROR) & 0x1B)) {
status = MI_OK;
// 如果碰撞错误
if (n & 0x08) {
status = MI_ERR;
}
// 读取接收数据长度
if (cmd == RC522_CMD_TRANSCEIVE) {
n = RC522_ReadRegister(RC522_REG_FIFO_LEVEL);
lastBits = RC522_ReadRegister(RC522_REG_CONTROL) & 0x07;
if (lastBits) {
*pOutLen = (n - 1) * 8 + lastBits;
} else {
*pOutLen = n * 8;
}
if (n == 0) {
n = 1;
}
if (n > 16) {
n = 16;
}
// 从FIFO读取接收数据
for (i = 0; i < n; i++) {
pOutData[i] = RC522_ReadRegister(RC522_REG_FIFO_DATA);
}
// 如果没有数据,返回错误
if (n == 0) {
status = MI_ERR;
}
}
} else {
status = MI_ERR;
}
}
return status;
}
/**
* @brief 请求卡片
* @param reqMode 请求模式(PICC_REQIDL或PICC_REQALL)
* @param pTagType 卡片类型(2字节)
* @return 执行状态码,MI_OK表示成功
*/
uint8_t RC522_Request(uint8_t reqMode, uint8_t *pTagType)
{
uint8_t status;
uint8_t data_to_send[1];
uint8_t recv_data[16];
uint8_t recv_bits;
// 清除MIFARECrypto1On位
RC522_ClearRegisterBits(RC522_REG_STATUS2, 0x08);
// 设置发送数据位长度
RC522_WriteRegister(RC522_REG_BIT_FRAMING, 0x07); // 7位
// 准备发送数据
data_to_send[0] = reqMode;
// 发送请求命令
status = RC522_CommunicateWithCard(RC522_CMD_TRANSCEIVE, data_to_send, 1, recv_data, &recv_bits);
// 如果成功接收到数据,返回卡片类型
if ((status == MI_OK) && (recv_bits == 0x10)) {
*pTagType = recv_data[0];
*(pTagType + 1) = recv_data[1];
} else {
status = MI_ERR;
}
return status;
}
/**
* @brief 防冲撞检测
* @param pCard RC522_Card_t结构体指针,用于存储卡片信息
* @return 执行状态码,MI_OK表示成功
*/
uint8_t RC522_Anticoll(RC522_Card_t *pCard)
{
uint8_t status;
uint8_t i, check_sum = 0;
uint8_t serial_in[2];
uint8_t serial_out[16];
uint8_t recv_bits;
// 清除MIFARECrypto1On位
RC522_ClearRegisterBits(RC522_REG_STATUS2, 0x08);
// 设置BitFraming:不使用发送CRC,整字节发送
RC522_WriteRegister(RC522_REG_BIT_FRAMING, 0x00);
// 准备防冲撞命令
serial_in[0] = PICC_ANTICOLL; // 防冲撞命令
serial_in[1] = 0x20; // NVB - 0x20表示没有传输的有效字节
// 发送防冲撞命令
status = RC522_CommunicateWithCard(RC522_CMD_TRANSCEIVE, serial_in, 2, serial_out, &recv_bits);
// 如果接收成功
if (status == MI_OK) {
// 检查是否接收到完整的UID
if (recv_bits == 0x40) {
// 计算校验和
for (i = 0; i < 4; i++) {
pCard->uid[i] = serial_out[i];
check_sum ^= serial_out[i]; // 异或校验
}
// 检查校验和是否正确
if (check_sum != serial_out[4]) {
status = MI_ERR;
}
} else {
status = MI_ERR;
}
}
return status;
}
/**
* @brief 选择卡片
* @param pCard RC522_Card_t结构体指针,包含卡片UID信息
* @return 执行状态码,MI_OK表示成功
*/
uint8_t RC522_SelectTag(RC522_Card_t *pCard)
{
uint8_t status;
uint8_t i;
uint8_t buffer[9];
uint8_t recv_data[16];
uint8_t recv_bits;
// 准备选择卡片命令
buffer[0] = PICC_SElECTTAG; // 选择卡片命令
buffer[1] = 0x70; // NVB - 0x70表示7个有效字节
// 复制UID到发送缓冲区
for (i = 0; i < 4; i++) {
buffer[i+2] = pCard->uid[i];
}
// 计算并添加BCC
buffer[6] = buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]; // BCC校验
// 计算CRC
status = RC522_CalculateCRC(buffer, 7, &buffer[7]);
if (status == MI_OK) {
// 发送选择卡片命令
status = RC522_CommunicateWithCard(RC522_CMD_TRANSCEIVE, buffer, 9, recv_data, &recv_bits);
// 如果接收成功并且接收到SAK
if ((status == MI_OK) && (recv_bits == 0x08)) {
pCard->sak = recv_data[0]; // 保存SAK值
return recv_data[0];
} else {
return 0;
}
}
return 0;
}
/**
* @brief 验证卡片密钥
* @param authMode 认证模式(PICC_AUTHENT1A或PICC_AUTHENT1B)
* @param blockAddr 块地址
* @param pKey 密钥(6字节)
* @param pCard 卡片信息
* @return 执行状态码,MI_OK表示成功
*/
uint8_t RC522_Auth(uint8_t authMode, uint8_t blockAddr, uint8_t *pKey, RC522_Card_t *pCard)
{
uint8_t status;
uint8_t buffer[12];
uint8_t recv_data[16];
uint8_t recv_bits;
// 准备认证命令
buffer[0] = authMode; // 认证命令(A或B)
buffer[1] = blockAddr; // 块地址
// 复制密钥到发送缓冲区
for (uint8_t i = 0; i < 6; i++) {
buffer[i+2] = *(pKey+i);
}
// 复制卡片UID到发送缓冲区
for (uint8_t i = 0; i < 4; i++) {
buffer[i+8] = pCard->uid[i];
}
// 发送认证命令
status = RC522_CommunicateWithCard(RC522_CMD_MF_AUTHENT, buffer, 12, recv_data, &recv_bits);
// 检查MIFARECrypto1On位是否置位
if ((status != MI_OK) || !(RC522_ReadRegister(RC522_REG_STATUS2) & 0x08)) {
status = MI_ERR;
}
return status;
}
/**
* @brief 读取数据块
* @param blockAddr 块地址
* @param pData 数据缓冲区(16字节)
* @return 执行状态码,MI_OK表示成功
*/
uint8_t RC522_ReadBlock(uint8_t blockAddr, uint8_t *pData)
{
uint8_t status;
uint8_t buffer[4];
uint8_t recv_bits;
// 准备读块命令
buffer[0] = PICC_READ; // 读块命令
buffer[1] = blockAddr; // 块地址
// 计算CRC
status = RC522_CalculateCRC(buffer, 2, &buffer[2]);
if (status == MI_OK) {
// 发送读块命令
status = RC522_CommunicateWithCard(RC522_CMD_TRANSCEIVE, buffer, 4, pData, &recv_bits);
// 检查接收到的数据长度
if ((status != MI_OK) || (recv_bits != 0x90)) {
status = MI_ERR;
}
}
return status;
}
/**
* @brief 写入数据块
* @param blockAddr 块地址
* @param pData 要写入的数据(16字节)
* @return 执行状态码,MI_OK表示成功
*/
uint8_t RC522_WriteBlock(uint8_t blockAddr, uint8_t *pData)
{
uint8_t status;
uint8_t buffer[18];
uint8_t recv_data[16];
uint8_t recv_bits;
// 准备写块命令
buffer[0] = PICC_WRITE; // 写块命令
buffer[1] = blockAddr; // 块地址
// 计算CRC
status = RC522_CalculateCRC(buffer, 2, &buffer[2]);
if (status == MI_OK) {
// 发送写块命令
status = RC522_CommunicateWithCard(RC522_CMD_TRANSCEIVE, buffer, 4, recv_data, &recv_bits);
// 检查卡片是否已准备好接收数据(ACK)
if ((status == MI_OK) && (recv_bits == 4) && ((recv_data[0] & 0x0F) == 0x0A)) {
// 准备待写入的16字节数据加CRC
for (uint8_t i = 0; i < 16; i++) {
buffer[i] = *(pData+i);
}
// 计算CRC
status = RC522_CalculateCRC(buffer, 16, &buffer[16]);
if (status == MI_OK) {
// 发送数据
status = RC522_CommunicateWithCard(RC522_CMD_TRANSCEIVE, buffer, 18, recv_data, &recv_bits);
// 检查写入是否成功(ACK)
if ((status != MI_OK) || (recv_bits != 4) || ((recv_data[0] & 0x0F) != 0x0A)) {
status = MI_ERR;
}
}
} else {
status = MI_ERR;
}
}
return status;
}
/**
* @brief 中止卡片通信
* @return 执行状态码
*/
uint8_t RC522_Halt(void)
{
uint8_t status;
uint8_t buffer[4];
uint8_t recv_data[16];
uint8_t recv_bits;
// 准备休眠命令
buffer[0] = PICC_HALT; // 休眠命令
buffer[1] = 0; // 必须为0
// 计算CRC
status = RC522_CalculateCRC(buffer, 2, &buffer[2]);
if (status == MI_OK) {
// 发送休眠命令
status = RC522_CommunicateWithCard(RC522_CMD_TRANSCEIVE, buffer, 4, recv_data, &recv_bits);
}
// 注意:在休眠命令中,卡片不会回应,所以这里不检查接收的数据
return status;
}
/**
* @brief 关闭RC522天线
*/
void RC522_AntennaOff(void)
{
// 清除寄存器中的天线驱动位
RC522_ClearRegisterBits(RC522_REG_TX_CONTROL, 0x03);
}
/**
* @brief 开启RC522天线
*/
void RC522_AntennaOn(void)
{
uint8_t value;
// 读取当前值
value = RC522_ReadRegister(RC522_REG_TX_CONTROL);
// 如果天线未开启,则开启
if (!(value & 0x03)) {
RC522_SetRegisterBits(RC522_REG_TX_CONTROL, 0x03);
}
}
/**
* @brief 延时微秒函数
* @param us 微秒数
*/
static void delay_us(uint32_t us)
{
uint32_t i;
for (i = 0; i < us * 8; i++) {
__NOP(); // 空操作,根据实际时钟频率调整循环次数
}
}
/**
* @brief 延时毫秒函数
* @param ms 毫秒数
*/
static void delay_ms(uint32_t ms)
{
uint32_t i;
for (i = 0; i < ms; i++) {
delay_us(1000); // 调用微秒延时1000次
}
}
使用示例代码
/**
* @file rc522_example.c
* @brief RC522 RFID模块应用实例
* @details 实现卡片检测、读取和写入功能
* @author Claude
* @date 2025-04-10
*/
#include "stm32f10x.h"
#include "rc522.h"
#include <stdio.h>
#include <string.h>
// 根据您的项目配置串口打印函数
void USART_Printf(const char* str);
void PrintHex(uint8_t* data, uint8_t length);
// 默认密钥A(出厂默认)
uint8_t DefaultKeyA[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
// 测试数据(16字节,用于写入扇区)
uint8_t TestData[16] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
};
int main(void)
{
// 系统初始化(时钟配置等)
SystemInit();
// 初始化串口(用于调试输出)
// USART_Config(); // 根据您的项目需要实现
// 初始化RC522模块
RC522_Init();
// 打印初始化信息
USART_Printf("RC522 RFID Module Initialized\r\n");
USART_Printf("Firmware Version: ");
uint8_t version = RC522_ReadRegister(RC522_REG_VERSION);
printf("0x%02X\r\n", version);
// 卡片信息结构体
RC522_Card_t card;
uint8_t cardType[2];
uint8_t blockData[16];
uint8_t status;
// 主循环
while (1) {
// 清除先前的卡片数据
memset(&card, 0, sizeof(RC522_Card_t));
// 检查是否有新卡片
status = RC522_Request(PICC_REQIDL, cardType);
if (status == MI_OK) {
USART_Printf("\r\n");
USART_Printf("Card detected!\r\n");
USART_Printf("Card type: ");
printf("0x%02X%02X\r\n", cardType[0], cardType[1]);
// 执行防冲撞检测,获取卡片UID
status = RC522_Anticoll(&card);
if (status == MI_OK) {
USART_Printf("Card UID: ");
PrintHex(card.uid, 4);
USART_Printf("\r\n");
// 选择检测到的卡片
uint8_t sak = RC522_SelectTag(&card);
USART_Printf("Card SAK: 0x%02X\r\n", sak);
// 演示读取数据块(块1,通常是用户数据区)
USART_Printf("Reading block 1...\r\n");
// 首先需要验证密钥
status = RC522_Auth(PICC_AUTHENT1A, 1, DefaultKeyA, &card);
if (status == MI_OK) {
USART_Printf("Authentication successful\r\n");
// 读取数据块1
status = RC522_ReadBlock(1, blockData);
if (status == MI_OK) {
USART_Printf("Block 1 data: ");
PrintHex(blockData, 16);
USART_Printf("\r\n");
// 写入测试数据到块1(如果需要)
USART_Printf("Writing test data to block 1...\r\n");
status = RC522_WriteBlock(1, TestData);
if (status == MI_OK) {
USART_Printf("Write successful\r\n");
// 再次读取确认写入成功
status = RC522_ReadBlock(1, blockData);
if (status == MI_OK) {
USART_Printf("Block 1 data (after write): ");
PrintHex(blockData, 16);
USART_Printf("\r\n");
}
} else {
USART_Printf("Write failed\r\n");
}
} else {
USART_Printf("Read failed\r\n");
}
} else {
USART_Printf("Authentication failed\r\n");
}
// 使卡片进入休眠状态
RC522_Halt();
}
}
// 延时等待(避免过快循环扫描)
for (uint32_t i = 0; i < 1000000; i++) {
__NOP();
}
}
}
/**
* @brief 以十六进制形式打印数据
* @param data 数据指针
* @param length 数据长度
*/
void PrintHex(uint8_t* data, uint8_t length)
{
char buffer[5];
for (uint8_t i = 0; i < length; i++) {
sprintf(buffer, "%02X ", data[i]);
USART_Printf(buffer);
}
}
/**
* @brief 通过串口发送字符串
* @param str 字符串指针
* @note 这是一个简化版实现,您需要根据实际项目配置来实现此函数
*/
void USART_Printf(const char* str)
{
// 示例实现,根据您的项目配置修改
while (*str) {
// 等待发送缓冲区为空
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
// 发送字符
USART_SendData(USART1, *str++);
}
}
/**
* @brief 初始化USART1用于调试输出
* @note 这是一个简化版实现,您需要根据实际项目配置来实现此函数
*/
void USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 使能GPIO和USART时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
// 配置USART1 Tx引脚(PA9)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置USART1 Rx引脚(PA10)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置USART1参数
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
// 使能USART1
USART_Cmd(USART1, ENABLE);
}