RK3568 平台 RC522 RFID 驱动开发全解析

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>;
	};
};

图片[1] - RK3568 平台 RC522 RFID 驱动开发全解析 - 宋马
设置一些引脚复用功能
/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";
		
	};

图片[2] - RK3568 平台 RC522 RFID 驱动开发全解析 - 宋马
下面这个是系统自带的引脚复用直接使用
/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手册
图片[3] - RK3568 平台 RC522 RFID 驱动开发全解析 - 宋马
数据格式
图片[4] - RK3568 平台 RC522 RFID 驱动开发全解析 - 宋马
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拷贝到开发板

图片[5] - RK3568 平台 RC522 RFID 驱动开发全解析 - 宋马
可以看到能够成功的读取的ic卡的信息了。
通过读取寄存器的取值来判断是否写入成功。

六、参考资料

1.RC522 数据手册
2.RK3568 芯片手册
3.Linux 内核文档
4.SPI 子系统文档
5.Linux 设备树参考指南
6.RC522芯片提供的51单片机程序

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容