RK3568 平台 RC522 RFID 驱动开发全解析
一、引言
二、硬件连接
1. RC522 模块引脚说明
2. RK3568 与 RC522 的连接
3. 硬件注意事项
三、设备树配置
四、内核驱动开发
五、调试与验证
六、参考资料
一、引言
RFID 技术在物联网、门禁系统、支付终端等领域有着广泛应用。RC522 作为一款高集成度的 RFID 读写模块,支持 ISO/IEC 14443A/MIFARE 协议,可轻松实现 13.56MHz 频段的近场通信。本文将详细介绍如何在 RK3568 开发板上开发 RC522 的驱动程序,实现 RFID 卡片的读写功能。
二、硬件连接
1. RC522 模块引脚说明
RC522 模块共有 8 个引脚,常用的 6 个引脚功能如下:
引脚名称 | 功能描述 |
---|---|
SDA | SPI 片选信号 |
SCK | SPI 时钟信号 |
MOSI | SPI 主出从入信号 |
MISO | SPI 主入从出信号 |
IRQ | 中断引脚 |
GND | 接地 |
RST | 复位引脚 |
3.3V | 电源(3.3V) |
用于检测不同接口类型的连接方案
2. RK3568 与 RC522 的连接
RK3568共接6个引脚对应如下:
RC522引脚 | RK3568引脚 | RK3568GPIO引脚复用 |
---|---|---|
SDA | SPI0_CS0_M1 | GPIO2_D2 |
SCK | SPI0_CLK_M1 | GPIO2_D3 |
MOSI | SPI0_MOSI_M1 | GPIO2_D1 |
MISO | SPI0_MISO_M1 | GPIO2_D0 |
IRQ | ||
GND | 接地 | |
RST | ||
3.3V | 电源(3.3V) |
3. 硬件注意事项
RC522 工作电压为 3.3V,不可直接连接 5V 电源,否则会损坏模块。
之前用的杜邦线过长导致通信异常,后来通过将杜邦线裁剪到3-5cm才通信成功。
RST 引脚可连接到 GPIO,以便软件控制 RC522 的复位操作。
三、设备树配置
在 Linux 系统中,设备树(Device Tree)是描述硬件设备信息的关键文件。我们需要在 RK3568 的设备树中添加 RC522 的节点配置。
/home/chenmy/rk356x/RK356X_Android11.0/kernel/arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi
&spi0 {
status = "okay";
rc522:rc522@0{
compatible = "rc522";
reg = <0>;
spi-max-frequency = <1000000>;
};
};
设置一些引脚复用功能
/home/chenmy/rk356x/RK356X_Android11.0/kernel/arch/arm64/boot/dts/rockchip/rk3568.dtsi
spi0: spi@fe610000 {
compatible = "rockchip,rk3066-spi";
reg = <0x0 0xfe610000 0x0 0x1000>;
interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&cru CLK_SPI0>, <&cru PCLK_SPI0>;
clock-names = "spiclk", "apb_pclk";
dmas = <&dmac0 20>, <&dmac0 21>;
dma-names = "tx", "rx";
pinctrl-names = "default", "high_speed";
//pinctrl-0 = <&spi0m0_cs0 &spi0m0_cs1 &spi0m0_pins>;
pinctrl-0 = <&spi0m1_cs0 &spi0m1_pins>;
//pinctrl-1 = <&spi0m0_cs0 &spi0m0_cs1 &spi0m0_pins_hs>;
pinctrl-1 = <&spi0m1_cs0 &spi0m1_pins_hs>;
status = "disabled";
};
下面这个是系统自带的引脚复用直接使用
/home/chenmy/rk356x/RK356X_Android11.0/kernel/arch/arm64/boot/dts/rockchip/rk3568-pinctrl.dtsi
修改完成后需要编译kernel,烧录boot.img
查看引脚复用
cd /sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl
cat pinmux-pins
四、内核驱动开发
RC522一些寄存器设置,可以参考RC522手册
数据格式
rc522.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/of_gpio.h>
#include <linux/vmalloc.h>
#include <linux/cdev.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/workqueue.h>
//#include <linux/spinlock.h>
#include <linux/mutex.h>
#include "mfrc522.h"
#define MAXRLEN 18
/* 认证参数结构 */
struct rc522_auth {
uint8_t code; /* 认证模式(PICC_AUTHENT1A/B) */
uint8_t block_addr; /* 块地址 */
uint8_t key[6]; /* 密钥 */
uint8_t sid[4]; /* 卡片序列号 */
};
/* RC522设备结构 */
struct rc522_dev {
struct spi_device *spi;
struct mutex mutex;
int irq;
int reset_gpio;
//struct gpio_desc *cs_gpio; /* 片选GPIO描述符 */
dev_t devt;
struct cdev cdev;
struct class *class;
struct device *device;
bool card_present;
uint8_t card_uid[5];
struct timer_list detect_timer;
struct work_struct detect_work; // 工作队列
};
/* 向RC522写寄存器 */
static void rc522_write_reg(struct rc522_dev *dev, u8 reg, u8 value)
{
/* 拉低片选,选中设备 */
//gpiod_set_value(dev->cs_gpio, 0);
u8 buf[2];
struct spi_message msg;
struct spi_transfer xfer = {
.tx_buf = buf,
.len = 2,
.cs_change = 0,
};
/* 寄存器地址格式: 0XXXXXX0 */
buf[0] = (reg << 1) & 0x7E;
buf[1] = value;
//spi_write(dev->spi, buf, 2);
spi_message_init(&msg);
spi_message_add_tail(&xfer, &msg);
spi_sync(dev->spi, &msg);
/* 拉高片选,释放设备 */
//gpiod_set_value(dev->cs_gpio, 1);
}
/* 从RC522读寄存器 */
static u8 rc522_read_reg(struct rc522_dev *dev, u8 reg)
{
/* 拉低片选,选中设备 */
//gpiod_set_value(dev->cs_gpio, 0);
u8 tx_buf[2];
u8 rx_buf[2];
struct spi_message msg;
struct spi_transfer xfer = {
.tx_buf = tx_buf,
.rx_buf = rx_buf,
.len = 2,
.cs_change = 0,
};
/* 寄存器地址格式: 1XXXXXX0 */
tx_buf[0] = ((reg << 1) & 0x7E) | 0x80;
tx_buf[1] = 0;
//spi_write_then_read(dev->spi, tx_buf, 2, rx_buf, 2);
spi_message_init(&msg);
spi_message_add_tail(&xfer, &msg);
spi_sync(dev->spi, &msg);
/* 拉高片选,释放设备 */
//gpiod_set_value(dev->cs_gpio, 1);
return rx_buf[1];
}
/* 设置RC522寄存器的多个位 */
static void rc522_set_bit_mask(struct rc522_dev *dev, u8 reg, u8 mask)
{
u8 tmp;
tmp = rc522_read_reg(dev, reg);
rc522_write_reg(dev, reg, tmp | mask);
}
/* 清除RC522寄存器的多个位 */
static void rc522_clear_bit_mask(struct rc522_dev *dev, u8 reg, u8 mask)
{
u8 tmp;
tmp = rc522_read_reg(dev, reg);
rc522_write_reg(dev, reg, tmp & (~mask));
}
/* 计算CRC校验值 */
static int rc522_calculate_crc(struct rc522_dev *dev, u8 *data, u8 length, u8 *crc)
{
u8 i;
/* 清空CRC寄存器 */
rc522_clear_bit_mask(dev, DivIrqReg, 0x04); /* 清除CRC中断标志 */
rc522_write_reg(dev, CommandReg, PCD_IDLE);
rc522_set_bit_mask(dev, FIFOLevelReg, 0x80); /* 清空FIFO */
/* 写入数据 */
for (i = 0; i < length; i++) {
rc522_write_reg(dev, FIFODataReg, data[i]);
}
/* 启动CRC计算 */
rc522_write_reg(dev, CommandReg, PCD_CALCCRC);
/* 等待计算完成 */
i = 0;
while (!(rc522_read_reg(dev, DivIrqReg) & 0x04)) {
i++;
if (i > 0xFF) return MI_ERR;
udelay(10);
}
/* 读取CRC结果 */
crc[0] = rc522_read_reg(dev, CRCResultRegL);
crc[1] = rc522_read_reg(dev, CRCResultRegM);
return MI_OK;
}
/
//功 能:通过RC522和ISO14443卡通讯
/
static int rc522_transceive(struct rc522_dev *dev, u8 command, u8 *send_data, u8 send_len,
u8 *recv_data, u8 *recv_len)
{
int status = MI_ERR;
u8 irq_en = 0x00;
u8 irq_wait = 0x00;
u8 last_bits;
u8 n;
u16 i;
switch (command)
{
case PCD_AUTHENT:
irq_en = 0x12;
irq_wait = 0x10;
break;
case PCD_TRANSCEIVE:
irq_en = 0x77;
irq_wait = 0x30;
break;
default:
break;
}
rc522_write_reg(dev, ComIEnReg, irq_en | 0x80);
rc522_clear_bit_mask(dev, ComIrqReg, 0x80);
rc522_write_reg(dev, CommandReg, PCD_IDLE);
rc522_set_bit_mask(dev, FIFOLevelReg, 0x80);
for (i = 0; i < send_len; i++)
{
rc522_write_reg(dev, FIFODataReg, send_data[i]);
}
rc522_write_reg(dev, CommandReg, command);
if (command == PCD_TRANSCEIVE)
{
rc522_set_bit_mask(dev, BitFramingReg, 0x80);
}
i = 600;//根据时钟频率调整,操作M1卡最大等待时间25ms
do
{
n = rc522_read_reg(dev, ComIrqReg);
i--;
}
while ((i != 0) && !(n & 0x01) && !(n & irq_wait));
rc522_clear_bit_mask(dev, BitFramingReg, 0x80);
if (i != 0)
{
if(!(rc522_read_reg(dev, ErrorReg) & 0x1B))
{
status = MI_OK;
if (n & irq_en & 0x01)
{
status = MI_NOTAGERR;
}
if (command == PCD_TRANSCEIVE)
{
n = rc522_read_reg(dev, FIFOLevelReg);
last_bits = rc522_read_reg(dev, ControlReg) & 0x07;
if (last_bits)
{
*recv_len = (n - 1) * 8 + last_bits;
} else {
*recv_len = n * 8;
}
if (n == 0)
{
n = 1;
}
if (n > MAXRLEN)
{
n = MAXRLEN;
}
for (i = 0; i < n; i++) {
recv_data[i] = rc522_read_reg(dev, FIFODataReg);
}
}
}
else
{
status = MI_ERR;
}
}
rc522_set_bit_mask(dev, ControlReg, 0x80); // stop timer now
rc522_write_reg(dev, CommandReg, PCD_IDLE);
return status;
}
/
//功 能:命令卡片进入休眠状态
//返 回: 成功返回MI_OK
/
static char rc522_halt(struct rc522_dev *dev)
{
int status;
u8 unLen;
u8 buffer[MAXRLEN];
buffer[0] = PICC_HALT;
buffer[1] = 0;
rc522_calculate_crc(dev, buffer, 2, &buffer[2]);
status = rc522_transceive(dev, PCD_TRANSCEIVE, buffer, 4, buffer, &unLen);
return MI_OK;
}
/* 寻卡函数
//参数说明: req_mode:寻卡方式
// 0x52 = 寻感应区内所有符合14443A标准的卡
// 0x26 = 寻未进入休眠状态的卡
// tag_type:卡片类型代码
// 0x4400 = Mifare_UltraLight
// 0x0400 = Mifare_One(S50)
// 0x0200 = Mifare_One(S70)
// 0x0800 = Mifare_Pro(X)
// 0x4403 = Mifare_DESFire
*/
static int rc522_request(struct rc522_dev *dev, u8 req_mode, u8 *tag_type)
{
int status;
u8 unLen;
u8 buffer[MAXRLEN];
/* 清空RC522中断标志 */
rc522_clear_bit_mask(dev, Status2Reg, 0x08);
rc522_write_reg(dev, BitFramingReg, 0x07);
rc522_set_bit_mask(dev, TxControlReg, 0x03);
/* 配置发送数据 */
buffer[0] = req_mode;
/* 发送寻卡命令 */
status = rc522_transceive(dev, PCD_TRANSCEIVE, buffer, 1, buffer, &unLen);
if ((status == MI_OK) && (unLen == 0x10)) {
/* 成功接收到卡类型 */
tag_type[0] = buffer[0];
tag_type[1] = buffer[1];
return MI_OK;
} else {
return MI_NOTAGERR;
}
}
/* 防冲突检测,获取卡片UID
//参数说明: uid:卡片序列号,4字节
*/
static int rc522_anticoll(struct rc522_dev *dev, u8 *uid)
{
int status;
unsigned char i, snr_check = 0;
u8 unLen;
u8 buffer[MAXRLEN];
rc522_clear_bit_mask(dev, Status2Reg, 0x08);
rc522_write_reg(dev, BitFramingReg, 0x00);
rc522_clear_bit_mask(dev, CollReg,0x80);
buffer[0] = PICC_ANTICOLL1;
buffer[1] = 0x20;
status = rc522_transceive(dev, PCD_TRANSCEIVE, buffer, 2, buffer, &unLen);
if (status == MI_OK)
{
for (i=0; i < 4; i++)
{
*(uid + i) = buffer[i];
snr_check ^= buffer[i];
}
if (snr_check != buffer[i])
{
status = MI_ERR; }
}
rc522_set_bit_mask(dev, CollReg, 0x80);
return status;
}
/* 选择卡片
//参数说明: uid:卡片序列号,4字节
*/
static int rc522_select_tag(struct rc522_dev *dev, u8 *uid)
{
int status;
unsigned char i;
u8 unLen;
u8 buffer[MAXRLEN];
buffer[0] = PICC_ANTICOLL1;
buffer[1] = 0x70;
buffer[6] = 0;
for (i=0; i < 4; i++)
{
buffer[i + 2] = *(uid + i);
buffer[6] ^= *(uid + i);
}
rc522_calculate_crc(dev, buffer, 7, &buffer[7]);
rc522_clear_bit_mask(dev, Status2Reg, 0x08);
status = rc522_transceive(dev, PCD_TRANSCEIVE ,buffer, 9, buffer, &unLen);
if ((status == MI_OK) && (unLen == 0x18))
{
status = MI_OK; }
else
{
status = MI_ERR; }
return status;
}
/* 验证卡片密钥
//参数说明: auth_mode: 密码验证模式
// 0x60 = 验证A密钥
// 0x61 = 验证B密钥
// block_addr:块地址
// key:密码
// uid:卡片序列号,4字节
*/
static int rc522_auth(struct rc522_dev *dev, u8 auth_mode, u8 block_addr,
u8 *key, u8 *uid)
{
int status;
u8 unLen;
u8 i, buffer[MAXRLEN];
buffer[0] = auth_mode;
buffer[1] = block_addr;
for (i = 0; i < 6; i++)
{
buffer[i + 2] = *(key + i); }
for (i = 0; i < 6; i++)
{
buffer[i + 8] = *(uid + i); }
status = rc522_transceive(dev, PCD_AUTHENT, buffer, 12, buffer, &unLen);
if ((status != MI_OK) || (!(rc522_read_reg(dev, Status2Reg) & 0x08)))
{
status = MI_ERR; }
return status;
}
/* 读取M1卡一块数据
//参数说明: block_addr:块地址
// data:读出的数据,16字节
*/
static int rc522_read_block(struct rc522_dev *dev, u8 block_addr, u8 *data)
{
int status;
u8 unLen;
u8 i, buffer[MAXRLEN];
buffer[0] = PICC_READ;
buffer[1] = block_addr;
rc522_calculate_crc(dev, buffer, 2, &buffer[2]);
status = rc522_transceive(dev, PCD_TRANSCEIVE, buffer, 4, buffer, &unLen);
if ((status == MI_OK) && (unLen == 0x90))
{
for (i = 0; i < 16; i++)
{
*(data + i) = buffer[i]; }
}
else
{
status = MI_ERR; }
return status;
}
/* 写数据到M1卡一块
//参数说明: block_addr:块地址
// data:写入的数据,16字节
*/
static int rc522_write_block(struct rc522_dev *dev, u8 block_addr, u8 *data)
{
int status;
u8 unLen;
u8 i, buffer[MAXRLEN];
buffer[0] = PICC_WRITE;
buffer[1] = block_addr;
rc522_calculate_crc(dev, buffer, 2, &buffer[2]);
status = rc522_transceive(dev, PCD_TRANSCEIVE, buffer, 4, buffer, &unLen);
if ((status != MI_OK) || (unLen != 4) || ((buffer[0] & 0x0F) != 0x0A))
{
status = MI_ERR; }
if (status == MI_OK)
{
//memcpy(ucComMF522Buf, pData, 16);
for (i = 0; i < 16; i++)
{
buffer[i] = *(data + i); }
rc522_calculate_crc(dev, buffer, 16, &buffer[16]);
status = rc522_transceive(dev, PCD_TRANSCEIVE, buffer, 18, buffer, &unLen);
if ((status != MI_OK) || (unLen != 4) || ((buffer[0] & 0x0F) != 0x0A))
{
status = MI_ERR; }
}
return status;
}
/
//功 能:扣款和充值
//参数说明: dd_mode:命令字
// 0xC0 = 扣款
// 0xC1 = 充值
// block_addr:钱包地址
// value:4字节增(减)值,低位在前
//返 回: 成功返回MI_OK
/
static int rc522_decrement_or_increment(struct rc522_dev *dev, u8 dd_mode, u8 block_addr, u8 *value)
{
int status;
u8 unLen;
u8 i, buffer[MAXRLEN];
buffer[0] = dd_mode;
buffer[1] = block_addr;
rc522_calculate_crc(dev, buffer, 2, &buffer[2]);
status = rc522_transceive(dev, PCD_TRANSCEIVE, buffer, 4, buffer, &unLen);
if ((status != MI_OK) || (unLen != 4) || ((buffer[0] & 0x0F) != 0x0A))
{
status = MI_ERR; }
if (status == MI_OK)
{
for (i = 0; i < 16; i++)
{
buffer[i] = *(value + i); }
rc522_calculate_crc(dev, buffer, 4, &buffer[4]);
unLen = 0;
status = rc522_transceive(dev, PCD_TRANSCEIVE, buffer, 6, buffer, &unLen);
if (status != MI_ERR)
{
status = MI_OK; }
}
if (status == MI_OK)
{
buffer[0] = PICC_TRANSFER;
buffer[1] = block_addr;
rc522_calculate_crc(dev, buffer, 2, &buffer[2]);
status = rc522_transceive(dev, PCD_TRANSCEIVE, buffer, 4, buffer, &unLen);
if ((status != MI_OK) || (unLen != 4) || ((buffer[0] & 0x0F) != 0x0A))
{
status = MI_ERR; }
}
return status;
}
/
//功 能:备份钱包
//参数说明: src_addr:源地址
// dst_addr:目标地址
//返 回: 成功返回MI_OK
/
static int rc522_bak(struct rc522_dev *dev, unsigned char src_addr, unsigned char dst_addr)
{
int status;
u8 unLen;
u8 buffer[MAXRLEN];
buffer[0] = PICC_RESTORE;
buffer[1] = src_addr;
rc522_calculate_crc(dev, buffer, 2, &buffer[2]);
status = rc522_transceive(dev, PCD_TRANSCEIVE, buffer, 4, buffer, &unLen);
if ((status != MI_OK) || (unLen != 4) || ((buffer[0] & 0x0F) != 0x0A))
{
status = MI_ERR; }
if (status == MI_OK)
{
buffer[0] = 0;
buffer[1] = 0;
buffer[2] = 0;
buffer[3] = 0;
rc522_calculate_crc(dev, buffer, 4, &buffer[4]);
status = rc522_transceive(dev, PCD_TRANSCEIVE, buffer, 6, buffer, &unLen);
if (status != MI_ERR)
{
status = MI_OK; }
}
if (status != MI_OK)
{
return MI_ERR; }
buffer[0] = PICC_TRANSFER;
buffer[1] = dst_addr;
rc522_calculate_crc(dev, buffer, 2, &buffer[2]);
status = rc522_transceive(dev, PCD_TRANSCEIVE, buffer, 4, buffer, &unLen);
if ((status != MI_OK) || (unLen != 4) || ((buffer[0] & 0x0F) != 0x0A))
{
status = MI_ERR; }
return status;
}
/* 复位RC522 */
static void rc522_reset(struct rc522_dev *dev)
{
/* 硬件复位 */
if (dev->reset_gpio >= 0) {
gpio_set_value(dev->reset_gpio, 0);
msleep(10);
gpio_set_value(dev->reset_gpio, 1);
msleep(10);
}
/* 软件复位 */
rc522_write_reg(dev, CommandReg, PCD_RESETPHASE);
msleep(5);
/* 配置RC522 */
rc522_write_reg(dev, ModeReg, 0x3D); /* 和Mifare卡通讯,CRC初始值0x6363 */
rc522_write_reg(dev, TReloadRegL, 30);
rc522_write_reg(dev, TReloadRegH, 0);
rc522_write_reg(dev, TModeReg, 0x8D);
rc522_write_reg(dev, TPrescalerReg, 0x3E);
rc522_write_reg(dev, TxAutoReg, 0x40);
u8 value = rc522_read_reg(dev, TxAutoReg);
printk(KERN_INFO " rc522_reset TxAutoReg value = %d
", value);
}
/
//开启天线
//每次启动或关闭天险发射之间应至少有1ms的间隔
/
static void rc522_antenna_on(struct rc522_dev *dev)
{
unsigned char i;
i = rc522_read_reg(dev, TxControlReg);
if (!(i & 0x03))
{
rc522_set_bit_mask(dev, TxControlReg, 0x03);
}
printk(KERN_INFO " rc522_antenna_on i = %d
", i);
}
/
//关闭天线
/
static void rc522_antenna_off(struct rc522_dev *dev)
{
rc522_clear_bit_mask(dev, TxControlReg, 0x03);
printk(KERN_INFO " rc522_antenna_off
");
}
/* 文件操作:打开设备 */
static int rc522_open(struct inode *inode, struct file *filp)
{
struct rc522_dev *dev = container_of(inode->i_cdev, struct rc522_dev, cdev);
filp->private_data = dev;
return 0;
}
/* 文件操作:释放设备 */
static int rc522_release(struct inode *inode, struct file *filp)
{
return 0;
}
/* 文件操作:IOCTL控制 */
static long rc522_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
return 0;
}
/* 文件操作表 */
static const struct file_operations rc522_fops = {
.owner = THIS_MODULE,
.open = rc522_open,
.release = rc522_release,
.unlocked_ioctl = rc522_ioctl,
};
static unsigned char g_ucTempbuf[20];
unsigned char data1[16] = {
0x12,0x34,0x56,0x78,0xED,0xCB,0xA9,0x87,0x12,0x34,0x56,0x78,0x01,0xFE,0x01,0xFE};
//M1卡的某一块写为如下格式,则该块为钱包,可接收扣款和充值命令
//4字节金额(低字节在前)+4字节金额取反+4字节金额+1字节块地址+1字节块地址取反+1字节块地址+1字节块地址取反
unsigned char data2[4] = {
0x12,0,0,0};
unsigned char DefaultKey[6] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
/* 工作队列处理函数 - 可以安全休眠 */
static void rc522_detect_work(struct work_struct *work)
{
struct rc522_dev *dev = container_of(work, struct rc522_dev, detect_work);
u8 tag_type[2];
int status, i;
memset(g_ucTempbuf, 0, 20);
// 使用mutex保护共享资源
mutex_lock(&dev->mutex);
status = rc522_request(dev, PICC_REQALL, tag_type);
if (status == MI_OK) {
dev_info(&dev->spi->dev, "rc522_detect_work Card detected");
status = rc522_anticoll(dev, g_ucTempbuf);
if (status != MI_OK)
{
printk(KERN_INFO " rc522_detect_work Failed to rc522_anticoll status = %d
", status);
rc522_halt(dev);
goto end;
}
status = rc522_select_tag(dev, g_ucTempbuf);
if (status != MI_OK)
{
printk(KERN_INFO " rc522_detect_work Failed to rc522_select_tag status = %d
", status);
goto end;
}
status = rc522_auth(dev, PICC_AUTHENT1A, 1, DefaultKey, g_ucTempbuf);
if (status != MI_OK)
{
printk(KERN_INFO " rc522_detect_work Failed to rc522_auth status = %d
", status);
goto end;
}
printk(KERN_INFO "rc522_detect_work The Card ID is: %02X%02X%02X%02X",g_ucTempbuf[0], g_ucTempbuf[1], g_ucTempbuf[2],g_ucTempbuf[3]);
status = rc522_write_block(dev, 1, data1);
if (status != MI_OK)
{
printk(KERN_INFO " rc522_detect_work Failed to rc522_write_block status = %d
", status);
goto end;
}
/*
status = rc522_decrement_or_increment(dev, PICC_DECREMENT, 1, data2);
if (status != MI_OK)
{
printk(KERN_INFO " rc522_detect_work Failed to rc522_decrement_or_increment status = %d
", status);
goto end;
}
*/
status = rc522_read_block(dev, 2, g_ucTempbuf);
if (status != MI_OK)
{
printk(KERN_INFO " rc522_detect_work Failed to rc522_read_block status = %d
", status);
goto end;
}
} else {
dev_info(&dev->spi->dev, "rc522_detect_work Card removed");
}
end:
mutex_unlock(&dev->mutex);
}
/* 定时器回调 - 原子上下文,快速返回 */
static void rc522_detect_timer(struct timer_list *t)
{
struct rc522_dev *dev = from_timer(dev, t, detect_timer);
// 调度工作队列处理耗时操作
schedule_work(&dev->detect_work);
// 重新启动定时器,继续周期性检测
mod_timer(&dev->detect_timer, jiffies + msecs_to_jiffies(1000)); // 1000ms后再次触发
}
/* 初始化定时器 */
static int rc522_init_timer(struct rc522_dev *dev)
{
rc522_reset(dev);
rc522_antenna_off(dev);
/*rc522_clear_bit_mask(dev, Status2Reg, 0x08);
rc522_write_reg(dev, ModeReg, 0x3D); //3F
rc522_write_reg(dev, RxSelReg, 0x86); //84
rc522_write_reg(dev, RFCfgReg, 0x7F); //4F
rc522_write_reg(dev, TReloadRegL, 30);
rc522_write_reg(dev, TReloadRegH, 0);
rc522_write_reg(dev, TModeReg, 0x8D);
rc522_write_reg(dev, TPrescalerReg, 0x3E); */
msleep(100);
rc522_antenna_on(dev);
int ret = rc522_read_reg(dev, ModeReg);
printk(KERN_INFO " rc522_init_timer ModeReg = %d
", ret);
// 初始化工作队列
INIT_WORK(&dev->detect_work, rc522_detect_work);
// 初始化定时器
timer_setup(&dev->detect_timer, rc522_detect_timer, 0);
mod_timer(&dev->detect_timer, jiffies + msecs_to_jiffies(500));
return 0;
}
/* 释放定时器 */
static void rc522_release_timer(struct rc522_dev *dev)
{
del_timer_sync(&dev->detect_timer);
cancel_work_sync(&dev->detect_work);
}
/* 初始化RC522设备 */
static int rc522_probe(struct spi_device *spi)
{
struct rc522_dev *dev;
int ret;
/* 分配设备结构体 */
dev = devm_kzalloc(&spi->dev, sizeof(*dev), GFP_KERNEL);
if (!dev) {
dev_err(&spi->dev, "rc522_probe Failed to allocate memory for device
");
return -ENOMEM;
}
dev->spi = spi;
mutex_init(&dev->mutex);
dev->card_present = false;
/* 设置SPI模式 */
spi->mode = /*SPI_MODE_0 | SPI_CS_HIGH*/SPI_MODE_0;
printk(KERN_INFO " rc522_init_timer spi->mode = %d
", spi->mode);
spi->bits_per_word = 8;
spi->max_speed_hz = 1000000; /* 1MHz */
ret = spi_setup(spi);
if (ret) {
dev_err(&spi->dev, "rc522_probe Failed to setup SPI device: %d
", ret);
return ret;
}
/* 分配字符设备号 */
ret = alloc_chrdev_region(&dev->devt, 0, 1, "rc522");
if (ret < 0) {
dev_err(&spi->dev, "rc522_probe Failed to allocate char device region
");
return ret;
}
/* 初始化字符设备 */
cdev_init(&dev->cdev, &rc522_fops);
dev->cdev.owner = THIS_MODULE;
ret = cdev_add(&dev->cdev, dev->devt, 1);
if (ret) {
dev_err(&spi->dev, "rc522_probe Failed to add char device
");
unregister_chrdev_region(dev->devt, 1);
return ret;
}
dev->reset_gpio = -1;
/* 获取片选GPIO */
/*dev->cs_gpio = devm_gpiod_get(&spi->dev, "cs", GPIOD_OUT_HIGH);
if (IS_ERR(dev->cs_gpio)) {
dev_err(&spi->dev, "Failed to get CS GPIO
");
return PTR_ERR(dev->cs_gpio);
}*/
/* 创建类 */
dev->class = class_create(THIS_MODULE, "rc522");
if (IS_ERR(dev->class)) {
dev_err(&spi->dev, "rc522_probe Failed to create class
");
cdev_del(&dev->cdev);
unregister_chrdev_region(dev->devt, 1);
return PTR_ERR(dev->class);
}
/* 创建设备节点 */
dev->device = device_create(dev->class, NULL, dev->devt, NULL, "rc522");
if (IS_ERR(dev->device)) {
dev_err(&spi->dev, "rc522_probe Failed to create device
");
class_destroy(dev->class);
cdev_del(&dev->cdev);
unregister_chrdev_region(dev->devt, 1);
return PTR_ERR(dev->device);
}
/* 保存设备结构体指针 */
spi_set_drvdata(spi, dev);
rc522_init_timer(dev);
dev_info(&spi->dev, "rc522_probe RC522 RFID device initialized
");
return 0;
}
/* 移除RC522设备 */
static int rc522_remove(struct spi_device *spi)
{
struct rc522_dev *dev = spi_get_drvdata(spi);
// 释放定时器
rc522_release_timer(dev);
device_destroy(dev->class, dev->devt);
class_destroy(dev->class);
cdev_del(&dev->cdev);
unregister_chrdev_region(dev->devt, 1);
dev_info(&spi->dev, "RC522 RFID device removed
");
return 0;
}
/* 设备树匹配表 */
static const struct of_device_id rc522_of_match[] = {
{
.compatible = "rc522" },
{
}
};
MODULE_DEVICE_TABLE(of, rc522_of_match);
/* SPI驱动结构体 */
static struct spi_driver rc522_driver = {
.driver = {
.name = "rc522",
.owner = THIS_MODULE,
.of_match_table = rc522_of_match,
},
.probe = rc522_probe,
.remove = rc522_remove,
};
/* 模块初始化函数 */
static int __init rc522_init_module(void)
{
int ret;
ret = spi_register_driver(&rc522_driver);
if (ret) {
printk(KERN_ERR "rc522_init_module Failed to register RC522 SPI driver
");
return ret;
}
printk(KERN_INFO "rc522_init_module RC522 SPI driver registered
");
return 0;
}
/* 模块退出函数 */
static void __exit rc522_exit_module(void)
{
spi_unregister_driver(&rc522_driver);
printk(KERN_INFO "rc522_exit_module RC522 SPI driver unregistered
");
}
module_init(rc522_init_module);
module_exit(rc522_exit_module);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("RC522 RFID Reader Driver");
MODULE_AUTHOR("cmy");
rc522.h
/
//MF522命令字
/
#define PCD_IDLE 0x00 //取消当前命令
#define PCD_AUTHENT 0x0E //验证密钥
#define PCD_RECEIVE 0x08 //接收数据
#define PCD_TRANSMIT 0x04 //发送数据
#define PCD_TRANSCEIVE 0x0C //发送并接收数据
#define PCD_RESETPHASE 0x0F //复位
#define PCD_CALCCRC 0x03 //CRC计算
/
//Mifare_One卡片命令字
/
#define PICC_REQIDL 0x26 //寻天线区内未进入休眠状态
#define PICC_REQALL 0x52 //寻天线区内全部卡
#define PICC_ANTICOLL1 0x93 //防冲撞
#define PICC_ANTICOLL2 0x95 //防冲撞
#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 //休眠
/
//MF522 FIFO长度定义
/
#define DEF_FIFO_LENGTH 64 //FIFO size=64byte
/
//MF522寄存器定义
/
// PAGE 0
#define RFU00 0x00
#define CommandReg 0x01
#define ComIEnReg 0x02
#define DivlEnReg 0x03
#define ComIrqReg 0x04
#define DivIrqReg 0x05
#define ErrorReg 0x06
#define Status1Reg 0x07
#define Status2Reg 0x08
#define FIFODataReg 0x09
#define FIFOLevelReg 0x0A
#define WaterLevelReg 0x0B
#define ControlReg 0x0C
#define BitFramingReg 0x0D
#define CollReg 0x0E
#define RFU0F 0x0F
// PAGE 1
#define RFU10 0x10
#define ModeReg 0x11
#define TxModeReg 0x12
#define RxModeReg 0x13
#define TxControlReg 0x14
#define TxAutoReg 0x15
#define TxSelReg 0x16
#define RxSelReg 0x17
#define RxThresholdReg 0x18
#define DemodReg 0x19
#define RFU1A 0x1A
#define RFU1B 0x1B
#define MifareReg 0x1C
#define RFU1D 0x1D
#define RFU1E 0x1E
#define SerialSpeedReg 0x1F
// PAGE 2
#define RFU20 0x20
#define CRCResultRegM 0x21
#define CRCResultRegL 0x22
#define RFU23 0x23
#define ModWidthReg 0x24
#define RFU25 0x25
#define RFCfgReg 0x26
#define GsNReg 0x27
#define CWGsCfgReg 0x28
#define ModGsCfgReg 0x29
#define TModeReg 0x2A
#define TPrescalerReg 0x2B
#define TReloadRegH 0x2C
#define TReloadRegL 0x2D
#define TCounterValueRegH 0x2E
#define TCounterValueRegL 0x2F
// PAGE 3
#define RFU30 0x30
#define TestSel1Reg 0x31
#define TestSel2Reg 0x32
#define TestPinEnReg 0x33
#define TestPinValueReg 0x34
#define TestBusReg 0x35
#define AutoTestReg 0x36
#define VersionReg 0x37
#define AnalogTestReg 0x38
#define TestDAC1Reg 0x39
#define TestDAC2Reg 0x3A
#define TestADCReg 0x3B
#define RFU3C 0x3C
#define RFU3D 0x3D
#define RFU3E 0x3E
#define RFU3F 0x3F
/
//和MF522通讯时返回的错误代码
/
#define MI_OK 0
#define MI_NOTAGERR (-1)
#define MI_ERR (-2)
Makefile
export ARCH=arm64
export CROSS_COMPILE=/home/chenmy/rk356x/RK356X_Android11.0/prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
obj-m += rc522.o
KERNEL_DIR:=/home/chenmy/rk356x/RK356X_Android11.0/kernel
all:
make -C $(KERNEL_DIR) M=$(PWD) modules
clean:
make -C $(KERNEL_DIR) M=$(PWD) clean
五、调试与验证
将编译好的rc522.ko拷贝到开发板
可以看到能够成功的读取的ic卡的信息了。
通过读取寄存器的取值来判断是否写入成功。
六、参考资料
1.RC522 数据手册
2.RK3568 芯片手册
3.Linux 内核文档
4.SPI 子系统文档
5.Linux 设备树参考指南
6.RC522芯片提供的51单片机程序
暂无评论内容