开发板: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
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时候的是高电平,第一个边沿就是从低变到高,所以是上升沿;
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)
设备树文件位于内核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
#include //模块加载卸载函数
#include //内核头文件
#include //数据类型定义
#include //file_operations结构体
#include //class_create等函数
#include
#include /*包含printk等操作函数*/
#include /*设备树操作相关的函数*/
#include /*gpio接口函数*/
#include
#include /*platform device*/
#include /*spi相关api*/
#include /*内核延时函数*/
#include /*kmalloc、kfree函数*/
#include /*cdev_init cdev_add等函数*/
#include /*gpio接口函数*/
#include /*__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");
#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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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;
}
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文件拷贝到开发板上并加载
输入如下命令编译测试 nfcApp.c 这个测试程序:
arm-linux-gnueabihf-gcc nfcApp.c -o nfcApp
编译成功以后就会生成 nfcApp 这个应用程序
编译出来的.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 //卸载驱动模块
以上应用程序只针对M1卡,可以在此基础上添加CPU卡。M1卡容易被复制,CPU卡有加密验证,安全等级较高。后续添加应用可参考pboc3.0规范及复旦FM1208技术手册。