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初始值0x6363WriteRawRC ( 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  允许空闲中断IdleIEnucWaitFor = 0x10;   //认证寻卡等待时候 查询空闲中断标志位break;case PCD_TRANSCEIVE:   //接收发送 发送接收ucIrqEn   = 0x77;   //允许TxIEn RxIEn IdleIEn LoAlertIEn ErrIEn TimerIEnucWaitFor = 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卡最大等待时间25msdo                                     //认证 与寻卡等待时间 {ucN = ReadRawRC ( ComIrqReg );    //查询事件中断ul --;} while ( ( ul != 0 ) && ( ! ( ucN & 0x01 ) ) && ( ! ( ucN & ucWaitFor ) ) ); ClearBitMask ( BitFramingReg, 0x80 );  //清理允许StartSend位if ( ul != 0 ){//读错误标志寄存器BufferOfI CollErr ParityErr ProtocolErrif ( ! ( 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 );   }        }   elsecStatus = MI_ERR;       }SetBitMask ( ControlReg, 0x80 );           // stop timer nowWriteRawRC ( 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 ]; //读出UIDucSnr_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;  elseucN = 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 ];   }elsecStatus = 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技术手册。

firefly-rk3288j开发板--linux NFC实验之RC522驱动相关推荐

  1. firefly-rk3288j开发板--linux I2C实验之eeprom驱动

    firefly-rk3288j开发板–linux I2C实验之eeprom驱动 1 准备工作 开发板:aio-rk3288j SDK版本:rk3288_linux_release_20210304 下 ...

  2. OK6410开发板linux系统下的SPI驱动和测试

    OK6410下的SPI驱动是可以用的,但是飞凌把它作为其它用途了,我们修改一些代码才能在/dev目录下创建SPI的设备节点文件 Step1:打开arch/arm/mach_s3c64XX/mach_m ...

  3. 迅为IMX6ULL开发板Linux学习教程

    1800+页使用手册(持续更新)+入门视频教程+实战视频教程 关注VX公众号:迅为电子 ,  回复 :终结者,免费获取产品资料  让教程更细致,终结入门难! 所有教程由迅为原创,是迅为工作多年的工程师 ...

  4. NUC980开发板Linux系统EC20模块 移植 串口 PPP拨号

    NUC980开发板Linux系统EC20模块 移植 串口 PPP拨号 1. EC20模块连接 2. Linux内核配置 3. 交叉编译PPP 4. 拨号脚本 5. 进行拨号 1. EC20模块连接 在 ...

  5. 基于全志A33开发板linux系统移植学习记录(Boot0)

    基于全志A33开发板linux系统移植学习记录 第一章 Boot0基于ARMGCC的编译与修改 文章目录 基于全志A33开发板linux系统移植学习记录 前言 一.全志A33简介以及上电引导流程 二. ...

  6. 【迅为iMX6Q】开发板 Linux 5.15.71 RTL8211E 以太网驱动适配

    相关参考 [迅为iMX6Q]开发板 u-boot 2022.04 SD卡 启动 [迅为iMX6Q]开发板 u-boot 2020.04 RTL8211E 以太网驱动适配 [迅为iMX6Q]开发板 Li ...

  7. 【EDA实验一】Quartus II 软件和 DE2-115 开发板 使用入门实验

    Quartus II 软件和 DE2-115 开发板 使用入门实验 文章目录 一.实验目的 二.实验任务及要求 三.实验原理与步骤 四.实验结果与分析 一.实验目的 熟悉 Quartus II 开发环 ...

  8. at91sam9260ek开发板linux移植文档,AT91SAM9260EK开发板 Linux 移植 移植 文档

    AT91SAM9260EK开发板 Linux 移植 移植 文档 AT91SAM9260EKAT91SAM9260EKAT91SAM9260EKAT91SAM9260EK 开发板开发板开发板开发板 Li ...

  9. Linux移植:正点原子阿尔法IMX6ULL开发板Linux内核源码移植详细步骤(4.1.15版本内核)

    Linux移植:正点原子阿尔法IMX6ULL开发板Linux内核源码移植详细步骤(4.1.15版本内核) 文章目录 Linux移植:正点原子阿尔法IMX6ULL开发板Linux内核源码移植详细步骤(4 ...

最新文章

  1. 并查集(压缩路径+按秩排序)
  2. linux_磁盘配额
  3. atcoder题目合集(持续更新中)
  4. python 中super方法的调用
  5. Python中文分词 jieba 十五分钟入门与进阶
  6. POJ - 2018 二分+单调子段和
  7. 【开学季限时免费】下载19880元大数据开发全链路教程(视频+源码)
  8. HTML中用弹性布局设置位置,HTML的flex弹性布局
  9. 赢在微点答案专区英语_自考英语二太难?看了墨盒的单词本,保你信心满满去考试!...
  10. python 中 函数的使用!!!
  11. 想要成功,你得像剥洋葱一样一层一层地撕开自己
  12. PS标尺参考线拖不准问题
  13. 【C# 练习】3个可乐瓶可以换一瓶可乐,现在有364瓶可乐。问一共可以喝多少瓶可乐,剩下几个空瓶?
  14. !EOF简单说明,常用来结束while循环
  15. 移动通信网认证协议,安全
  16. 西门子官网下载Eplan部件库
  17. 事务的4个特性——ACID(原子性、一致性、隔离性和持久性)、更新丢失问题...
  18. 分享106个PHP源码,总有一款适合您
  19. 收藏|Java程序员必看的几本基础书籍和常用工具
  20. matlab visa转c,将一个m文件转成c /cpp文件并在VC中进行编译。这种方法有个烦人的地方,每次你都需要把matla...

热门文章

  1. CSDN【精品专栏】第24期
  2. Android图片选择器 图片裁剪
  3. 【 实测可用 】ESP32 + AD8232 心电图实验
  4. 运行窗口输入命令 点击确定后弹出“打开方式”对话框的处理方法
  5. 8个动作帮你腰部快速塑形
  6. 显卡超频linux,Linux 5.12内核将支持Radeon RX 6000系列显卡超频
  7. DICOM数据信息解析及Pydicom简单处理
  8. 自己写的实用VBA代码合集√
  9. 2022年陕西最新建筑施工电工(建筑特种作业)模拟考试试题及答案
  10. 驳eva的《有关主动防御》