firefly-rk3288j开发板--linux NFC实验之RC522驱动

firefly-rk3288j开发板–linux NFC实验之RC522驱动

1 准备工作

开发板:aio-rk3288j
SDK版本:rk3288_linux_release_20210304
下载工具:Linux_Upgrade_Tool_v2.1
内核版本:4.4.194
文件系统:buildroot
Ubuntu版本:18.04
交叉编译工具:gcc version 6.3.1 20170404

2 硬件原理图

2.1 开发板SPI接口

在这里插入图片描述
在这里插入图片描述

2.2 RC522模块原理图

在这里插入图片描述

3 SPI使用

SPI 是一种高速的,全双工,同步串行通信接口,用于连接微控制器、传感器、存储设备等,本文以W25Q64模块为例简单介绍 SPI 使用。
SPI 以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少 4 根线,分别是:
CS 片选信号
SCLK 时钟信号
MOSI 主设备数据输出、从设备数据输入
MISO 主设备数据输入,从设备数据输出

Linux 内核用 CPOL 和 CPHA 的组合来表示当前 SPI 的四种工作模式:
CPOL=0,CPHA=0 SPI_MODE_0
CPOL=0,CPHA=1 SPI_MODE_1
CPOL=1,CPHA=0 SPI_MODE_2
CPOL=1,CPHA=1 SPI_MODE_3

SPI的CPOL,表示当SCLK空闲idle的时候,其电平的值是低电平0还是高电平1:CPOL=0,时钟空闲idle时候的电平是低电平,所以当SCLK有效的时候,就是高电平,就是所谓的active-high。
CPOL=1,时钟空闲idle时候的电平是高电平,所以当SCLK有效的时候,就是低电平,就是所谓的active-low。

CPHA=0,表示第一个边沿:
对于CPOL=0,idle时候的是低电平,第一个边沿就是从低变到高,所以是上升沿;
对于CPOL=1,idle时候的是高电平,第一个边沿就是从高变到低,所以是下降沿;
CPHA=1,表示第二个边沿:
对于CPOL=0,idle时候的是低电平,第二个边沿就是从高变到低,所以是下降沿;
对于CPOL=1,idle时候的是高电平,第一个边沿就是从低变到高,所以是上升沿;

4 API函数

spi_alloc_master 函数用于申请 spi_master
struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
注册函数
int spi_register_master(struct spi_master *master)
注销函数
void spi_unregister_master(struct spi_master *master)

5 DTS配置

设备树文件位于内核kernel/arch/arm/boot/dts目录下,我们需要打开rk3288.dtsi、rk3288-linux.dtsi、rk3288-firefly-port.dtsi、rk3288-firefly-aio.dtsi.d打开rk3288-firefly-aio.dtsi文件,添加spi设备节点:

&spi2 {
	status = "okay";
	rc522: rc522@0{
		compatible = "firefly,rc522";
		spi-max-frequency = <8000000>;
		reg = <0>;
		rst-gpio = <&gpio7 2 GPIO_ACTIVE_HIGH>;
 		pinctrl-names = "default";
		pinctrl-0 = <&rc522_rst>;
		//spi-cpha;
		//spi-cpol;
	};
};
&pinctrl {
	rc522 {
		rc522_rst: rc522-rst {
			rockchip,pins = <7 2 RK_FUNC_GPIO &pcfg_pull_up>;
		};
	};
}

编译内核,输入如下命令
./build.sh kernel
./build.sh updateimg
在这里插入图片描述

6 RC522驱动编写

6.1 驱动文件

#include <linux/module.h>//模块加载卸载函数
#include <linux/kernel.h>//内核头文件
#include <linux/types.h>//数据类型定义
#include <linux/fs.h>//file_operations结构体
#include <linux/device.h>//class_create等函数
#include <linux/ioctl.h>
#include <linux/kernel.h>/*包含printk等操作函数*/
#include <linux/of.h>/*设备树操作相关的函数*/
#include <linux/gpio.h>/*gpio接口函数*/
#include <linux/of_gpio.h>
#include <linux/platform_device.h>/*platform device*/
#include <linux/spi/spi.h> /*spi相关api*/
#include <linux/delay.h> /*内核延时函数*/
#include <linux/slab.h> /*kmalloc、kfree函数*/
#include <linux/cdev.h>/*cdev_init cdev_add等函数*/
#include <asm/gpio.h>/*gpio接口函数*/
#include <asm/uaccess.h>/*__copy_from_user 接口函数*/
#include "rc522.h"

#define DEVICE_NAME     "nfc" 
 

typedef struct
{
      struct device_node *node;//设备树节点
      struct cdev cdev;       //定义一个cdev结构体
      struct class *class;    //创建一个rc522类
      struct device *device;  //创建一个rc522设备 该设备是需要挂在rc522类下面的
      int major;              //主设备号
      dev_t  dev_id;
      struct spi_device *spi; /*spi设备*/
//     int cspin;              /*片选脚*/
      int rstpin;
      struct mutex lock;
      void *private_data;
}rc522_typdef;

static rc522_typdef rc522_dev;//定义一个rc522设备

void spi_rst_enable(void)
{
    gpio_set_value(rc522_dev.rstpin, 0); 
}

void spi_rst_disable(void)
{
   gpio_set_value(rc522_dev.rstpin, 1); 
}

void spi_cs_enable(void)
{
    //gpio_set_value(rc522_dev.cspin, 1);
}

void spi_cs_disable(void)
{
    //gpio_set_value(rc522_dev.cspin, 0);
}

static int rc522_read_regs(rc522_typdef *dev, unsigned char reg, unsigned char *dat, unsigned char len)
{
    int ret = -1;
    unsigned char txdata[len];
    unsigned char * rxdata;
    struct spi_message m;
    struct spi_transfer *t;
    struct spi_device *spi = dev->spi;
    
    t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);   /* 申请内存 */
    if(!t) {
        return -ENOMEM;
    }

    rxdata = kzalloc((sizeof(char) * len), GFP_KERNEL); /* 申请内存 */
    if(!rxdata) {
        goto out1;
    }
    spi_cs_enable();
    /* 一共发送len+1个字节的数据,第一个字节为
    寄存器首地址,一共要读取len个字节长度的数据,*/
    txdata[0] = ((reg << 1) & 0x7e) | 0x80;             
    t->tx_buf = txdata;         /* 要发送的数据 */
    t->rx_buf = rxdata;         /* 要读取的数据 */
    t->len = len + 1;           /* t->len=发送的长度+读取的长度 */
    spi_message_init(&m);       /* 初始化spi_message */
    spi_message_add_tail(t, &m);/* 将spi_transfer添加到spi_message队列 */
    ret = spi_sync(spi, &m);    /* 同步发送 */
    if(ret) {
        goto out2;
    }
    /* 只需要读取的数据 */
    memcpy(dat , rxdata + 1, len); /* 只需要读取的数据 */

out2:
    kfree(rxdata);                  /* 释放内存 */
out1:   
    kfree(t);                       /* 释放内存 */
    spi_cs_disable();
    return ret;
}

static int rc522_write_regs(rc522_typdef *dev, unsigned char reg, unsigned char *dat, unsigned char len)
{
    int ret = -1;
    unsigned char *txdata;
    struct spi_message m;
    struct spi_transfer *t;
    struct spi_device *spi = dev->spi;
    
    t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);   /* 申请内存 */
    if(!t) {
        return -ENOMEM;
    }
    
    txdata = kzalloc(sizeof(char) + len, GFP_KERNEL);
    if(!txdata) {
        goto out1;
    }
    spi_cs_enable();
    /* 一共发送len+1个字节的数据,第一个字节为
    寄存器首地址,len为要写入的寄存器的集合,*/
    *txdata = ((reg << 1) & 0x7e);  /* 写数据的时候首寄存器地址bit8要清零 */
    memcpy(txdata + 1, dat, len);

    t->tx_buf = txdata;         /* 要发送的数据 */
    t->len = len + 1;                   /* t->len=发送的长度+读取的长度 */
    spi_message_init(&m);       /* 初始化spi_message */
    spi_message_add_tail(t, &m);/* 将spi_transfer添加到spi_message队列 */
    ret = spi_sync(spi, &m);    /* 同步发送 */
    if(ret) {
        goto out2;
    }
    
out2:
    kfree(txdata);      /* 释放内存 */
out1:
    kfree(t);                   /* 释放内存 */
    spi_cs_disable();
    return ret; 
}

static unsigned char read_one_reg(rc522_typdef *dev, unsigned char reg)
{
    unsigned char data = 0;

    rc522_read_regs(dev, reg, &data, 1);

    return data;    
}

static void write_one_reg(rc522_typdef *dev,unsigned char reg, unsigned char value)
{
    rc522_write_regs(dev, reg, &value, 1);
}

static int rc522_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &rc522_dev;
   
    spi_rst_disable();
    udelay(10);
    spi_rst_enable();
    udelay(10);
    spi_rst_disable();

    printk("rc522_open ok!\n");
    return 0;
}

static int rc522_release(struct inode* inode ,struct file *filp)
{
    spi_rst_enable();
    gpio_free(rc522_dev.rstpin);
    printk("rc522_release ok!\n");   
    return 0;
}

// loff_t rc522_llseek(struct file *file, loff_t offset, int whence)
// {
//  return 0;
// }

static int rc522_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
{
    unsigned char *write_buf;/*数据缓冲区*/
    int ret;

    write_buf = (unsigned char*)kzalloc(count, GFP_KERNEL);
    if(!write_buf )
       return -ENOMEM;

    ret = copy_from_user(write_buf, buf, count);
    if (ret < 0)
    {
        kfree(write_buf);
        printk("copy from user failed!\r\n");
        return ret;
    }

    write_one_reg(&rc522_dev, write_buf[0], write_buf[1]);

    return 0;
}

static ssize_t rc522_read(struct file *filp,char __user *buf, size_t count,loff_t *f_pos)
{
    int ret; 
    unsigned char adr,value; 

    ret = copy_from_user(&adr, buf, 1);
    if(ret < 0)
    {
        printk("copy from user failed!\r\n");
        return ret;
    }
    value = read_one_reg(&rc522_dev, adr);
    ret = copy_to_user(buf, &value, count);
    if (ret < 0)
    {
        printk("copy to user failed!\r\n");
        return ret;
    }

    return ret;
}

static struct file_operations rc522_fops={
    .owner      = THIS_MODULE,
    .open       = rc522_open,
    .write      = rc522_write,
    .read       = rc522_read,
    .release    = rc522_release,
    // .llseek     = rc522_llseek,
};
        
static int rc522_probe(struct spi_device *spi)
{
    int ret;
    const char *string = NULL;
    
    printk("rc522 probe!\n"); 

   /*获取设备节点*/
   rc522_dev.node = of_find_node_by_path("/spi@ff130000/rc522@0");
   if(rc522_dev.node == NULL)
   {
      printk("device-tree:not found rc522!\r\n"); 
      return -1;
   }

   /*读取rc522设备节点的compatible属性值*/
   ret = of_property_read_string(rc522_dev.node, "compatible", &string);
   if(ret == 0)
   {
       printk("%s\n",string);
   }   

   rc522_dev.rstpin = of_get_named_gpio(rc522_dev.node,"rst-gpio",0);
   if(!gpio_is_valid(rc522_dev.rstpin))
   {
        printk("get gpio error\n");
        ret = -EINVAL;
        return ret;
   }
   
   printk("gpio = %d\n",rc522_dev.rstpin);

   ret = gpio_request(rc522_dev.rstpin,"spi-rst");
   if(ret < 0) 
   {
      printk("gpio_request %d failed\n", rc522_dev.rstpin);
      return ret;
   }
   gpio_direction_output(rc522_dev.rstpin, 1);

   gpio_export(rc522_dev.rstpin, 1);
//
    /*申请设备号*/
    ret = alloc_chrdev_region(&rc522_dev.dev_id, 0, 1, DEVICE_NAME);
    if(ret < 0)
    {
         printk("alloc dev_id error %d\n", ret);
         return ret;
    }

   /*初始化一个cdev*/
   cdev_init(&rc522_dev.cdev, &rc522_fops);

   /*向cdev中添加一个设备*/
   ret = cdev_add(&rc522_dev.cdev, rc522_dev.dev_id, 1);
   if(ret != 0)
   {
        printk("cdev add error %d \n",ret);
        // goto Error;
   }

   /*创建一个nfc_class类*/
   rc522_dev.class = class_create(THIS_MODULE, "nfc_class");
   if(rc522_dev.class == NULL)
   {
      printk("class_create failed\r\n");
      return -1;
   }
   /*在nfc_class类下创建一个NFC_class设备*/
   rc522_dev.device = device_create(rc522_dev.class, NULL, rc522_dev.dev_id, NULL, DEVICE_NAME);   

   /*获取与本驱动匹配的spi设备*/
   rc522_dev.spi = spi;
   spi_setup(rc522_dev.spi);   
   
// Error:
//  cdev_del(&rc522_dev.cdev);
//  unregister_chrdev_region(rc522_dev.dev_id, 1);
    return 0;
}

static int rc522_remove(struct spi_device *spi)
{
    printk("w25qxx remove!\n"); 

    /*删除rc522类*/
    cdev_del(&rc522_dev.cdev);

    /*释放rc522设备号*/
    unregister_chrdev_region(rc522_dev.dev_id, 1);

    /*注销rc522设备*/
    device_destroy(rc522_dev.class, rc522_dev.dev_id);

    /*注销rc522类*/
    class_destroy(rc522_dev.class);

    gpio_free(rc522_dev.rstpin);

    return 0;   
}

static const struct of_device_id rc522_of_match[] = {
   {.compatible = "firefly,rc522"},
   {},
};

static const struct spi_device_id rc522_id[] = {
    { "xxxx", 0 },
    {},
};

static struct spi_driver rc522_driver = {
    .driver = {
        .owner = THIS_MODULE,       
        .name =  "rc522",
        .of_match_table = rc522_of_match,
    },
    .probe = rc522_probe,
    .remove = rc522_remove,
    .id_table = rc522_id,
};

static int __init rc522_init(void)
{
   int ret;

   ret = spi_register_driver(&rc522_driver);
   if(ret < 0)
   {
        printk("spi_register_driver error= %d \n",ret);
   }
   else
   {
        printk("module init ok\n");
   }
   return ret;
}

static void rc522_exit(void)
{
    spi_unregister_driver(&rc522_driver);  
    printk("module exit ok\n");
}

module_init(rc522_init);
module_exit(rc522_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("rc522 driver");
MODULE_AUTHOR("lsjml2022");

#6.2 RC522.h

#ifndef RC522_H
#define RC522_H

#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               

#define PICC_REQIDL           0x26              
#define PICC_REQALL           0x52               
#define PICC_ANTICOLL1        0x93               
#define PICC_ANTICOLL2        0x95               
#define PICC_AUTHENT1A        0x60              
#define PICC_AUTHENT1B        0x61             
#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 DEF_FIFO_LENGTH       64                 

// 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


#define     REQ_ALL               0x52
#define     KEYA                  0x60

#define MI_OK                          (char)0
#define MI_NOTAGERR                    (char)(-1)
#define MI_ERR                         (char)(-2)

#endif

7 编写测试App

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <limits.h>
#include <asm/ioctls.h>
#include <time.h>
#include <pthread.h>
#include <string.h>
#include "rc522.h"

#define  MAXRLEN 18 

int fd = 0;//文件句柄

void print_data(const char *title, char *dat, int count)
{
   int i = 0; 

   printf(title);

   for(i = 0; i < count; i++) 
   {
      printf(" 0x%x", dat[i]);
   }
   printf("\n");
}

unsigned char ReadRawRC(unsigned char Address)
{
  unsigned char buf[1]; 

  buf[0] = Address;
   read(fd,buf,1);  
  return buf[0];  
}

void WriteRawRC(unsigned char Address, unsigned char Value)
{  
  unsigned char buf[2];

  buf[0] = Address;
  buf[1] = Value;
  write(fd,buf,2);  
}

void SetBitMask(unsigned char ucReg, unsigned char ucMask)  
{
  unsigned char ucTemp;

  ucTemp = ReadRawRC ( ucReg );
  WriteRawRC (ucReg, ucTemp | ucMask); // set bit mask
}

void ClearBitMask(unsigned char ucReg, unsigned char ucMask)  
{
  unsigned char ucTemp;

  ucTemp = ReadRawRC (ucReg);
  WriteRawRC (ucReg, ucTemp & ( ~ ucMask)); // clear bit mask
}

void PcdAntennaOn(void)
{
  unsigned char uc;

  uc = ReadRawRC (TxControlReg);
  if (! ( uc & 0x03 ))
  {
      SetBitMask(TxControlReg, 0x03);   
  }
}

static void PcdAntennaOff(void)
{
    ClearBitMask(TxControlReg, 0x03);
}

int PcdReset(void)
{
  fd = open("/dev/nfc", O_RDWR);

    if(fd < 0)
    {
        printf("open rc522_drv error %d\n",fd);
      return fd;
  }

  WriteRawRC ( CommandReg, 0x0f );
  
  while ( ReadRawRC ( CommandReg ) & 0x10 );
 
   //定义发送和接收常用模式 和Mifare卡通讯,CRC初始值0x6363
   WriteRawRC ( ModeReg, 0x3D );          
   WriteRawRC ( TReloadRegL, 30 );      //16位定时器低位   
   WriteRawRC ( TReloadRegH, 0 );      //16位定时器高位 
   WriteRawRC ( TModeReg, 0x8D );      //定义内部定时器的设置 
   WriteRawRC ( TPrescalerReg, 0x3E );   //设置定时器分频系数  
   WriteRawRC ( TxAutoReg, 0x40 );       //调制发送信号为100%ASK 
  return 1;
}

char M500PcdConfigISOType ( unsigned char ucType )
{
  if ( ucType == 'A')                     //ISO14443_A
   {
      ClearBitMask ( Status2Reg, 0x08 );    
      WriteRawRC ( ModeReg, 0x3D );         //3F    
      WriteRawRC ( RxSelReg, 0x86 );        //84    
      WriteRawRC( RFCfgReg, 0x7F );         //4F    
      WriteRawRC( TReloadRegL, 30 );            
      WriteRawRC ( TReloadRegH, 0 );    
      WriteRawRC ( TModeReg, 0x8D );    
      WriteRawRC ( TPrescalerReg, 0x3E );   
      usleep(10000);    
      PcdAntennaOn ();//开天线   
   } 
   else
   {
      return MI_ERR;
   }
   return MI_OK;   
}

char PcdComMF522 ( unsigned char ucCommand, unsigned char * pInData, unsigned char ucInLenByte, unsigned char * pOutData,unsigned int * pOutLenBit )    
{
  char cStatus = MI_ERR;
  unsigned char ucIrqEn   = 0x00;
  unsigned char ucWaitFor = 0x00;
  unsigned char ucLastBits;
  unsigned char ucN;
  unsigned int ul;

  switch ( ucCommand )
  {
     case PCD_AUTHENT:      //Mifare认证
        ucIrqEn   = 0x12;   //允许错误中断请求ErrIEn  允许空闲中断IdleIEn
        ucWaitFor = 0x10;   //认证寻卡等待时候 查询空闲中断标志位
        break;
     
     case PCD_TRANSCEIVE:   //接收发送 发送接收
        ucIrqEn   = 0x77;   //允许TxIEn RxIEn IdleIEn LoAlertIEn ErrIEn TimerIEn
        ucWaitFor = 0x30;   //寻卡等待时候 查询接收中断标志位与 空闲中断标志位
        break;
     
     default:
       break;     
  }
  //IRqInv置位管脚IRQ与Status1Reg的IRq位的值相反 
  WriteRawRC ( ComIEnReg, ucIrqEn | 0x80 );
  //Set1该位清零时,CommIRqReg的屏蔽位清零
  ClearBitMask ( ComIrqReg, 0x80 );  
  //写空闲命令
  WriteRawRC ( CommandReg, PCD_IDLE );     
  
  //置位FlushBuffer清除内部FIFO的读和写指针以及ErrReg的BufferOvfl标志位被清除
  SetBitMask ( FIFOLevelReg, 0x80 );      

  for ( ul = 0; ul < ucInLenByte; ul ++ )
  {
      WriteRawRC ( FIFODataReg, pInData [ ul ] ); //写数据进FIFOdata
  }
    
  WriteRawRC ( CommandReg, ucCommand );         //写命令

  if ( ucCommand == PCD_TRANSCEIVE )
  {  
      //StartSend置位启动数据发送 该位与收发命令使用时才有效
      SetBitMask(BitFramingReg,0x80);           
  }

  ul = 1000;                             //根据时钟频率调整,操作M1卡最大等待时间25ms

  do                                     //认证 与寻卡等待时间 
  {
       ucN = ReadRawRC ( ComIrqReg );    //查询事件中断
       ul --;
  } while ( ( ul != 0 ) && ( ! ( ucN & 0x01 ) ) && ( ! ( ucN & ucWaitFor ) ) ); 

  ClearBitMask ( BitFramingReg, 0x80 );  //清理允许StartSend位

  if ( ul != 0 )
  {
    //读错误标志寄存器BufferOfI CollErr ParityErr ProtocolErr
    if ( ! ( ReadRawRC ( ErrorReg ) & 0x1B ) )  
    {
      cStatus = MI_OK;
      
      if ( ucN & ucIrqEn & 0x01 )       //是否发生定时器中断
        cStatus = MI_NOTAGERR;   
        
      if ( ucCommand == PCD_TRANSCEIVE )
      {
        //读FIFO中保存的字节数
        ucN = ReadRawRC ( FIFOLevelReg );             
        
        //最后接收到得字节的有效位数
        ucLastBits = ReadRawRC ( ControlReg ) & 0x07; 
        
        if ( ucLastBits )
          
          //N个字节数减去1(最后一个字节)+最后一位的位数 读取到的数据总位数
          * pOutLenBit = ( ucN - 1 ) * 8 + ucLastBits;    
        else
          * pOutLenBit = ucN * 8;      //最后接收到的字节整个字节有效
        
        if ( ucN == 0 )   
          ucN = 1;    
        
        if ( ucN > MAXRLEN )
          ucN = MAXRLEN;   
        
        for ( ul = 0; ul < ucN; ul ++ )
          pOutData [ ul ] = ReadRawRC ( FIFODataReg );   
        
        }        
    }   
    else
      cStatus = MI_ERR;       
  }

  SetBitMask ( ControlReg, 0x80 );           // stop timer now
  WriteRawRC ( CommandReg, PCD_IDLE ); 
   
  return cStatus;
}

char PcdRequest (unsigned char ucReq_code, unsigned char * pTagType)
{
  char cStatus;  
  unsigned char ucComMF522Buf [ MAXRLEN ]; 
  unsigned int ulLen;

  //清理指示MIFARECyptol单元接通以及所有卡的数据通信被加密的情况
  ClearBitMask ( Status2Reg, 0x08 );
  //发送的最后一个字节的 七位
  WriteRawRC ( BitFramingReg, 0x07 );

  //ClearBitMask ( TxControlReg, 0x03 );  
  //TX1,TX2管脚的输出信号传递经发送调制的13.56的能量载波信号
  //usleep(10000); 
  SetBitMask ( TxControlReg, 0x03 );  

  ucComMF522Buf [ 0 ] = ucReq_code;   //存入 卡片命令字

  cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 1, ucComMF522Buf,& ulLen );  //寻卡  

  if ( ( cStatus == MI_OK ) && ( ulLen == 0x10 ) )  //寻卡成功返回卡类型 
  {    
     * pTagType = ucComMF522Buf[0];
     * ( pTagType + 1 ) = ucComMF522Buf[1];
  }
  else
  {
      cStatus = MI_ERR;
  }

  return cStatus;  
}

char PcdAnticoll ( unsigned char * pSnr )
{
  char cStatus;
  unsigned char uc, ucSnr_check = 0;
  unsigned char ucComMF522Buf [ MAXRLEN ]; 
  unsigned int ulLen;
  
  //清MFCryptol On位 只有成功执行MFAuthent命令后,该位才能置位
  ClearBitMask ( Status2Reg, 0x08 );
  //清理寄存器 停止收发
  WriteRawRC ( BitFramingReg, 0x00);  
  //清ValuesAfterColl所有接收的位在冲突后被清除
  ClearBitMask ( CollReg, 0x80 );       
 
  ucComMF522Buf [ 0 ] = 0x93;           //卡片防冲突命令
  ucComMF522Buf [ 1 ] = 0x20;
 
  cStatus = PcdComMF522 ( PCD_TRANSCEIVE, 
                          ucComMF522Buf,
                          2, 
                          ucComMF522Buf,
                          & ulLen);      //与卡片通信

  if ( cStatus == MI_OK)                //通信成功
  {
    for ( uc = 0; uc < 4; uc ++ )
    {
       * ( pSnr + uc )  = ucComMF522Buf [ uc ]; //读出UID
       ucSnr_check ^= ucComMF522Buf [ uc ];
    }
    
    if ( ucSnr_check != ucComMF522Buf [ uc ] )
      cStatus = MI_ERR;            
  }
  
  SetBitMask ( CollReg, 0x80 );
      
  return cStatus;   
}

void CalulateCRC ( unsigned char * pIndata, unsigned char ucLen, unsigned char * pOutData )
{
  unsigned char uc, ucN;

  ClearBitMask(DivIrqReg,0x04);

  WriteRawRC(CommandReg,PCD_IDLE);

  SetBitMask(FIFOLevelReg,0x80);

  for ( uc = 0; uc < ucLen; uc ++)
    WriteRawRC ( FIFODataReg, * ( pIndata + uc ) );   

  WriteRawRC ( CommandReg, PCD_CALCCRC );

  uc = 0xFF;

  do 
  {
      ucN = ReadRawRC ( DivIrqReg );
      uc --;
  } while ( ( uc != 0 ) && ! ( ucN & 0x04 ) );
  
  pOutData [ 0 ] = ReadRawRC ( CRCResultRegL );
  pOutData [ 1 ] = ReadRawRC ( CRCResultRegM );   
}

char PcdSelect ( unsigned char * pSnr )
{
  char ucN;
  unsigned char uc;
  unsigned char ucComMF522Buf [ MAXRLEN ]; 
  unsigned int  ulLen;
  
  
  ucComMF522Buf [ 0 ] = PICC_ANTICOLL1;
  ucComMF522Buf [ 1 ] = 0x70;
  ucComMF522Buf [ 6 ] = 0;

  for ( uc = 0; uc < 4; uc ++ )
  {
    ucComMF522Buf [ uc + 2 ] = * ( pSnr + uc );
    ucComMF522Buf [ 6 ] ^= * ( pSnr + uc );
  }
  
  CalulateCRC ( ucComMF522Buf, 7, & ucComMF522Buf [ 7 ] );

  ClearBitMask ( Status2Reg, 0x08 );

  ucN = PcdComMF522 ( PCD_TRANSCEIVE,
                     ucComMF522Buf,
                     9,
                     ucComMF522Buf, 
                     & ulLen );
  
  if ( ( ucN == MI_OK ) && ( ulLen == 0x18 ) )
    ucN = MI_OK;  
  else
    ucN = MI_ERR;    
  
  return ucN;   
}

char PcdAuthState ( unsigned char ucAuth_mode, unsigned char ucAddr, unsigned char * pKey, unsigned char * pSnr )
{
  char cStatus;
  unsigned char uc, ucComMF522Buf [ MAXRLEN ];
  unsigned int ulLen;
  
  ucComMF522Buf [ 0 ] = ucAuth_mode;
  ucComMF522Buf [ 1 ] = ucAddr;

  for ( uc = 0; uc < 6; uc ++ )
    ucComMF522Buf [ uc + 2 ] = * ( pKey + uc );   

  for ( uc = 0; uc < 6; uc ++ )
    ucComMF522Buf [ uc + 8 ] = * ( pSnr + uc );   

  cStatus = PcdComMF522 ( PCD_AUTHENT,
                          ucComMF522Buf, 
                          12,
                          ucComMF522Buf,
                          & ulLen );

  if ( ( cStatus != MI_OK ) || ( ! ( ReadRawRC ( Status2Reg ) & 0x08 ) ) )
    cStatus = MI_ERR;   
    
  return cStatus;
}

char PcdWrite ( unsigned char ucAddr, unsigned char * pData )
{
  char cStatus;
  unsigned char uc, ucComMF522Buf [ MAXRLEN ];
  unsigned int ulLen;
   
  
  ucComMF522Buf [ 0 ] = PICC_WRITE;
  ucComMF522Buf [ 1 ] = ucAddr;

  CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );

  cStatus = PcdComMF522 ( PCD_TRANSCEIVE,
                          ucComMF522Buf,
                          4, 
                          ucComMF522Buf,
                          & ulLen );

  if ( ( cStatus != MI_OK ) || ( ulLen != 4 ) || 
         ( ( ucComMF522Buf [ 0 ] & 0x0F ) != 0x0A ) )
    cStatus = MI_ERR;   
      
  if ( cStatus == MI_OK )
  {
    //memcpy(ucComMF522Buf, pData, 16);
    for ( uc = 0; uc < 16; uc ++ )
      ucComMF522Buf [ uc ] = * ( pData + uc );  
    
    CalulateCRC ( ucComMF522Buf, 16, & ucComMF522Buf [ 16 ] );

    cStatus = PcdComMF522 ( PCD_TRANSCEIVE,
                           ucComMF522Buf, 
                           18, 
                           ucComMF522Buf,
                           & ulLen );
    
    if ( ( cStatus != MI_OK ) || ( ulLen != 4 ) || 
         ( ( ucComMF522Buf [ 0 ] & 0x0F ) != 0x0A ) )
      cStatus = MI_ERR;   
    
  }   
  return cStatus;   
}

char PcdRead ( unsigned char ucAddr, unsigned char * pData )
{
  char cStatus;
  unsigned char uc, ucComMF522Buf [ MAXRLEN ]; 
  unsigned int ulLen;
  
  ucComMF522Buf [ 0 ] = PICC_READ;
  ucComMF522Buf [ 1 ] = ucAddr;

  CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );
 
  cStatus = PcdComMF522 ( PCD_TRANSCEIVE,
                          ucComMF522Buf,
                          4, 
                          ucComMF522Buf,
                          & ulLen );

  if ( ( cStatus == MI_OK ) && ( ulLen == 0x90 ) )
  {
    for ( uc = 0; uc < 16; uc ++ )
      * ( pData + uc ) = ucComMF522Buf [ uc ];   
  }
  
  else
    cStatus = MI_ERR;   
   
  return cStatus;   
}

char PcdHalt( void )
{
  unsigned char ucComMF522Buf [ MAXRLEN ]; 
  unsigned int  ulLen;
  

  ucComMF522Buf [ 0 ] = PICC_HALT;
  ucComMF522Buf [ 1 ] = 0;
  
   CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );
  PcdComMF522 ( PCD_TRANSCEIVE,
                ucComMF522Buf,
                4, 
                ucComMF522Buf, 
                & ulLen );

  return MI_OK; 
}

int main(int argc, const char * argv [ ])
{
   int ret = -1;
  unsigned char KeyValue[]={0xFF ,0xFF, 0xFF, 0xFF, 0xFF, 0xFF};   // 卡A密钥
  char cStr [ 30 ], writebuf[16], readbuf[16];
  unsigned char ucArray_ID [ 4 ];    /*先后存放IC卡的类型和UID(IC卡序列号)*/                                                                                         
  unsigned char ucStatusReturn, snr;      /*返回状态*/     
    
   snr = 1; //扇区号
  ret = PcdReset();
  if(ret < 0)
  {
        printf("rc522 rst error %d \n",ret);
      return 0;
  }
  ucStatusReturn = M500PcdConfigISOType ( 'A' );
   if(ucStatusReturn == MI_ERR)
   {
      printf("M500PcdConfigISOType error! \n");
   }
   else
   {
      printf("M500PcdConfigISOType normal! \n");
   }

   while(1)
   {
         ucStatusReturn = PcdRequest ( PICC_REQIDL, ucArray_ID ); 
         if ( ucStatusReturn == MI_OK )  
         {
            if ( PcdAnticoll ( ucArray_ID ) == MI_OK )  
            {
                  PcdSelect(ucArray_ID);      // 选卡 
                  ucStatusReturn = PcdAuthState(KEYA, (snr*4 + 3) , KeyValue, ucArray_ID );//校验密码 
                  if(ucStatusReturn !=  MI_OK)
                  {
                     sprintf(cStr, "The Card ID is: %02X%02X%02X%02X",ucArray_ID [0], ucArray_ID [1], ucArray_ID [2],ucArray_ID [3]);
                     printf("%s\r\n",cStr);  //打印卡片ID 
                  }
                  else
                  {
                        printf("rc522 card number err!\r\n");
                  }

                  if(PcdWrite((snr*4 + 3) , writebuf) == MI_OK)
                  {
                     printf("PcdWrite Success! \r\n");
                  }

                  if(PcdRead((snr*4 + 3) , readbuf) == MI_OK)
                  {
                     print_data("read block", readbuf, 16);            
                  }
                  else
                  {
                     printf("PcdRead err! \r\n");
                  }
            } 
            else
            {
                  PcdHalt();
            }                                       
         }
   }

   return 0; 
}

8 编译驱动程序和测试APP

8.1 编译驱动程序

KDIR:=/rk3288_linux/rk3288_linux_release_20220607/kernel
obj-m:=nfc_driver.o
PWD:=$(shell pwd)
all:
    $(MAKE) -C $(KDIR) M=$(PWD) 
clean:
    rm -rf *.ko *.order *.symvers *.cmd *.o *.mod.c *.tmp_versions .*.cmd .tmp_versions

输入如下命令编译出驱动模块文件:
make -j8
在这里插入图片描述
编译成功后会生成一个.ko文件拷贝到开发板上并加载

8.2 编译测试App

输入如下命令编译测试 nfcApp.c 这个测试程序:
arm-linux-gnueabihf-gcc nfcApp.c -o nfcApp
编译成功以后就会生成 nfcApp 这个应用程序

8.3 运行测试

编译出来的.ko 和 nfcApp 这两个文件拷贝到/lib/modules/4.4.194目录中,重启开发板,进入目录/lib/modules/4.4.194中输入加载.ko驱动模块:
insmod nfc_driver.ko
在这里插入图片描述
驱动加载成功以后就可以使用nfcApp 软件来测试驱动是否正常,输入如下命令:
./nfcApp /dev/nfc
在这里插入图片描述
rmmod nfc_driver.ko //卸载驱动模块

8.4 CPU卡

以上应用程序只针对M1卡,可以在此基础上添加CPU卡。M1卡容易被复制,CPU卡有加密验证,安全等级较高。后续添加应用可参考pboc3.0规范及复旦FM1208技术手册。

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林深见麋鹿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值