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初始值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驱动相关推荐
- firefly-rk3288j开发板--linux I2C实验之eeprom驱动
firefly-rk3288j开发板–linux I2C实验之eeprom驱动 1 准备工作 开发板:aio-rk3288j SDK版本:rk3288_linux_release_20210304 下 ...
- OK6410开发板linux系统下的SPI驱动和测试
OK6410下的SPI驱动是可以用的,但是飞凌把它作为其它用途了,我们修改一些代码才能在/dev目录下创建SPI的设备节点文件 Step1:打开arch/arm/mach_s3c64XX/mach_m ...
- 迅为IMX6ULL开发板Linux学习教程
1800+页使用手册(持续更新)+入门视频教程+实战视频教程 关注VX公众号:迅为电子 , 回复 :终结者,免费获取产品资料 让教程更细致,终结入门难! 所有教程由迅为原创,是迅为工作多年的工程师 ...
- NUC980开发板Linux系统EC20模块 移植 串口 PPP拨号
NUC980开发板Linux系统EC20模块 移植 串口 PPP拨号 1. EC20模块连接 2. Linux内核配置 3. 交叉编译PPP 4. 拨号脚本 5. 进行拨号 1. EC20模块连接 在 ...
- 基于全志A33开发板linux系统移植学习记录(Boot0)
基于全志A33开发板linux系统移植学习记录 第一章 Boot0基于ARMGCC的编译与修改 文章目录 基于全志A33开发板linux系统移植学习记录 前言 一.全志A33简介以及上电引导流程 二. ...
- 【迅为iMX6Q】开发板 Linux 5.15.71 RTL8211E 以太网驱动适配
相关参考 [迅为iMX6Q]开发板 u-boot 2022.04 SD卡 启动 [迅为iMX6Q]开发板 u-boot 2020.04 RTL8211E 以太网驱动适配 [迅为iMX6Q]开发板 Li ...
- 【EDA实验一】Quartus II 软件和 DE2-115 开发板 使用入门实验
Quartus II 软件和 DE2-115 开发板 使用入门实验 文章目录 一.实验目的 二.实验任务及要求 三.实验原理与步骤 四.实验结果与分析 一.实验目的 熟悉 Quartus II 开发环 ...
- at91sam9260ek开发板linux移植文档,AT91SAM9260EK开发板 Linux 移植 移植 文档
AT91SAM9260EK开发板 Linux 移植 移植 文档 AT91SAM9260EKAT91SAM9260EKAT91SAM9260EKAT91SAM9260EK 开发板开发板开发板开发板 Li ...
- Linux移植:正点原子阿尔法IMX6ULL开发板Linux内核源码移植详细步骤(4.1.15版本内核)
Linux移植:正点原子阿尔法IMX6ULL开发板Linux内核源码移植详细步骤(4.1.15版本内核) 文章目录 Linux移植:正点原子阿尔法IMX6ULL开发板Linux内核源码移植详细步骤(4 ...
最新文章
- 并查集(压缩路径+按秩排序)
- linux_磁盘配额
- atcoder题目合集(持续更新中)
- python 中super方法的调用
- Python中文分词 jieba 十五分钟入门与进阶
- POJ - 2018 二分+单调子段和
- 【开学季限时免费】下载19880元大数据开发全链路教程(视频+源码)
- HTML中用弹性布局设置位置,HTML的flex弹性布局
- 赢在微点答案专区英语_自考英语二太难?看了墨盒的单词本,保你信心满满去考试!...
- python 中 函数的使用!!!
- 想要成功,你得像剥洋葱一样一层一层地撕开自己
- PS标尺参考线拖不准问题
- 【C# 练习】3个可乐瓶可以换一瓶可乐,现在有364瓶可乐。问一共可以喝多少瓶可乐,剩下几个空瓶?
- !EOF简单说明,常用来结束while循环
- 移动通信网认证协议,安全
- 西门子官网下载Eplan部件库
- 事务的4个特性——ACID(原子性、一致性、隔离性和持久性)、更新丢失问题...
- 分享106个PHP源码,总有一款适合您
- 收藏|Java程序员必看的几本基础书籍和常用工具
- matlab visa转c,将一个m文件转成c /cpp文件并在VC中进行编译。这种方法有个烦人的地方,每次你都需要把matla...