正点原子i.mx6ullMini开发板用SPI驱动RC522门禁卡模块

正点原子i.mx6ullMini开发板驱动RC522门禁卡模块

前言:两个月前,我学习正点原子的SPI字符设备开发,由于我的是迷你板,没有集成SPI外设,我便自己买了一个RC522门禁模块作做实验,刚开始调试了几天都没寻卡成功,后来放弃了!直到今天下午有空,想起这件事才又重新去调试一下,很奇怪也很幸运,调试成功了。

一、实验目的

通过外接SPI模块,加深对Linux下SPI字符设备驱动的开发以及RC522模块的理解。

二、实验器材与准备工作

硬件与接线部分
3.3V与地就不用说了哈,dddd。

rc522 imx6ull Mini
RST GPIO1_IO01
MISO UART2_RTS
MOSI UART2_CTS
SCK UART2_RXD
SDA(CS) UART2_TXD

三、实验步骤

1.修改设备树
在根节点下配置RST引脚的设备节点

/* 配置SPI外设RC522的RST引脚 */
	spi_rst {
		compatible = "spi_rst";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_spi_rst>;
		rst-gpio = <&gpio1 1 GPIO_ACTIVE_LOW>;
		status = "okay";
	};

在&iomuxc节点下配置RST引脚以及MISO、MOSI、SCK、SDA(CS)的电气属性

pinctrl_spi_rst: rstgrp {
			fsl,pins = <
				MX6UL_PAD_GPIO1_IO01__GPIO1_IO01 0x10b0 /* rc522 rst_pin */
			>;
		};
pinctrl_ecspi3: rc522grp{
			fsl,pins = < 
				MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20 	0x10b0 /* 将 UART2_TX_DATA 复用为普通IO,作为软件片选引脚*/
				MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK	0x10b1 /* SCLK */
				MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO		0x10b1 /* MISO */
				MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI      0x10b1 /* MOSI */
			>;
		};

注意:使用上述引脚的时候,要确保它们没有被别的模块占用

配置spi3节点

&ecspi3 {	
	fsl,spi-num-chipselects = <1>;
	cs-gpio = <&gpio1 20 GPIO_ACTIVE_LOW>;		/* cs-gpios 要去掉s才是选择软件片选 */
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_ecspi3>;
	status = "okay";

	spi_device: rc522@0 {
	// #address-cells = <1>;
	// #size-cells = <1>;
	compatible = "alientek,rc522";
	spi-max-frequency = <8000000>; 	/* spi datasheet 上有说是10000000 */
	reg = <0>;
	};

编译设备树这些基本操作就不再重复叙述了

2.编写rc522_spi驱动程序,根据原子例程修改的spi驱动

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/input.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/spi/spi.h>
//#include "rc522.h"

#define SPI_NAME    "rc522"
#define SPI_COUNT     1


/* SPI 设备结构体 */
typedef struct rc522_device {
    int major;
    int minor;
    dev_t devid;    /* 设备号 */
    struct cdev cdev;
    struct device *device;
    struct class *class;
    void *private_data;
    struct device_node *node;
    int cs_gpio; /* 片选引脚 */
    int rst_gpio; /* 复位引脚 */
    struct device_node *rst_node;
}Trc522_device, *PTrc522_device;

static Trc522_device TRc522_Device;
// static unsigned char readaddress = 0;

/* 函数声明 */
// void IC_test(void);
static unsigned char Read_One_Reg(PTrc522_device dev, u8 reg);
static void Write_One_Reg(PTrc522_device dev, u8 reg, u8 value);
void RC522_Reset_Disable( void );
void RC522_Reset_Enable(void);
// static void rc522_write_reg(PTrc522_device dev, u8 data, u8 reg);
// static unsigned char rc522_read_reg(PTrc522_device dev, u8 reg);

static int rc522_open(struct inode *inode, struct file *filp)
{
    RC522_Reset_Disable();
    udelay(1);
    RC522_Reset_Enable();
    udelay(1);
    RC522_Reset_Disable();
    udelay(1);

 //   rc522_init();

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

static ssize_t rc522_read(struct file *file, char __user *buf, size_t cnt, loff_t *ppos)
{
    // u8 
    u8 readval, address;
    int err = 0;
    err = copy_from_user(&address, buf, 1);
    if (err < 0)
    {
      printk("copy from user failed!rn");
      return -EFAULT;
    }
    readval = Read_One_Reg(&TRc522_Device, address);
    // // printk("rc522_read ok!rn");
    err = copy_to_user((void *)buf, &readval, 1);
    if (err < 0)
    // {
      printk("copy to user failed!rn");
    //   return -EFAULT;
    // }
//    printk("rc522_read reg :%#X,  readregval :%#Xrn", address, readval);
    return 0;
}

static ssize_t rc522_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    int ret = 0;
    uint8_t buffer[2];
    ret = copy_from_user(buffer, buf, 2);
    if (ret < 0)
    {
      printk("copy from user failed!rn");
    }
    
    Write_One_Reg(&TRc522_Device, buffer[0], buffer[1]);
   
//    printk("write reg: %#X,  writeregval:%#Xn", buffer[0], buffer[1]);
    return 0;
}

static int rc522_release(struct inode *inode, struct file *filp)
{
    int ret = 0;
    RC522_Reset_Enable();
    gpio_free(TRc522_Device.rst_gpio);
    gpio_free(TRc522_Device.cs_gpio);
    printk("rc522_release ok!n");
    return ret;
}

static struct file_operations TRc522_Device_fops = {
    .owner  = THIS_MODULE,
    .open   = rc522_open,
    .read   = rc522_read,
    .write  = rc522_write,
    .release = rc522_release,
};

/* spi read n reg data from slave dev*/
static int spi_read_regs(PTrc522_device dev, u8 reg, u8 *buf, int len)
{
    int ret = 0;
    u8 txdata[len];
    /* 申请一个spi_message变量 */
    struct spi_message message;
    /* 申请并初始化spi_transfer, spi_transfer此结构体用于描述 SPI 传输信息
    这是内核向驱动提供的API做准备*/
    struct spi_transfer *transfer;
    struct spi_device *spi = (struct spi_device *)dev->private_data;
    /* 拉低GPIO_IO20 表示片选选中GPIO_IO20所连接的spi设备 */
    gpio_set_value(dev->cs_gpio, 0);
    
    transfer = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);
    
    // printk("n读寄存器值函数第一次得到形参reg即要读的寄存器地址 is %#xn", reg);
    
    /* 第一次,发送要写入的寄存器地址 */
    txdata[0] = ((reg << 1) & 0x7E) | 0x80;    /* csdn :Addr = ( (Address<<1)&0x7E )|0x80;  原子:txdata[0] =reg | 0x80; */
    /* 根据野火stm32例程,此处要加片选使能    
    gpio_set_value(dev->cs_gpio, 0); */                                        
    transfer->tx_buf = txdata;
    transfer->len = 1;

    // printk("读寄存器值函数将reg变为txdata后 is %#xn", txdata[0]);

    /* 初始化一个spi_message变量 */
    spi_message_init(&message);

    /* 把初始化好的spi_transfer加到message这个队列里来 */
    spi_message_add_tail(transfer, &message);

    /* 使用同步方式传输,同步传输会阻塞的等待 SPI 数据传输完成 */
    ret = spi_sync(spi, &message);

    /* 第二次,读取该寄存器的数据 */
    txdata[0] = 0xff;
    transfer->rx_buf = buf;
    transfer->len = len;

    /* 初始化一个spi_message变量 */
    spi_message_init(&message);

    /* 把初始化好的spi_transfer加到message这个队列里来 */
    spi_message_add_tail(transfer, &message);

    /* 使用同步方式传输,同步传输会阻塞的等待 SPI 数据传输完成 */
    ret = spi_sync(spi, &message);
    kfree(transfer);
    /* 拉高GPIO_IO20,表示通信结束 */
    gpio_set_value(dev->cs_gpio, 1);
    
    // printk("读函数里最终读到的数据是 %#xn", *buf);

    return ret;
}

/* spi向从设备的多个寄存器写入数据 */
static s32 spi_write_regs(PTrc522_device dev, u8 reg, u8 *buf, int len)
{
    int ret = 0;
    u8 txdata[len];
    /* 申请一个spi_message变量 */
    struct spi_message message;
    /* 申请并初始化spi_transfer, spi_transfer此结构体用于描述 SPI 传输信息
    这是内核向驱动提供的API做准备*/
    struct spi_transfer *transfer;
    struct spi_device *spi = (struct spi_device *)dev->private_data;

    transfer = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);
    /* 拉低GPIO_IO20 表示片选选中GPIO_IO20所连接的spi设备 */
    gpio_set_value(dev->cs_gpio, 0);
    // printk("n写寄存器值函数第一次得到形参reg即要写的寄存器地址 is %#xn", reg);

    /* 第一次,发送要写入的寄存器地址 */
    txdata[0] = (reg << 1) & 0x7E;    /* 原子:txdata[0] = reg & ~0x80; */
                                    /* csdn:( Address<<1 )&0x7E */

    // printk("写寄存器值函数将reg变为txdata后 is %#xn", txdata[0]);
    /* 根据野火stm32例程,此处要加片选使能    
    gpio_set_value(dev->cs_gpio, 0); */   
    transfer->tx_buf = txdata;
    transfer->len = 1;
    /* 初始化一个spi_message变量 */
    spi_message_init(&message);

    /* 把初始化好的spi_transfer加到message这个队列里来 */
    spi_message_add_tail(transfer, &message);

    /* 使用同步方式传输,同步传输会阻塞的等待 SPI 数据传输完成 */
    ret = spi_sync(spi, &message);

    /* 第二次,发送要写入的寄存器数据 */
    transfer->tx_buf = buf;
    transfer->len = len;

    // printk("n写寄存器值函数要发送buf is %#xn", *buf);

    /* 初始化一个spi_message变量 */
    spi_message_init(&message);

    /* 把初始化好的spi_transfer加到message这个队列里来 */
    spi_message_add_tail(transfer, &message);

    /* 使用同步方式传输,同步传输会阻塞的等待 SPI 数据传输完成 */
    ret = spi_sync(spi, &message);
    kfree(transfer);
    /* 拉高GPIO_IO20,表示通信结束 */
    gpio_set_value(dev->cs_gpio, 1);

    // printk("n最后写寄存器值函数要发送buf is %#xn", *buf);

    return ret;
}

/* 读一个寄存器的值 */
static unsigned char Read_One_Reg(PTrc522_device dev, u8 reg)
{
    // u8 ret = 0;
    // unsigned char tx_buf[1];
    // unsigned char rx_buf[1];
    // tx_buf[0] = reg; //RC522 set ((reg << 1) & 0x7E) | 0x80
    // /* 拉低GPIO_IO20 表示片选选中GPIO_IO20所连接的spi设备 */
    // ret = gpio_direction_output(dev->cs_gpio, 0);
    // if (ret < 0)
    // {
    //     printk("cs_gpio set 0 failed!rn");
    // }
    // spi_write_then_read(dev->private_data, tx_buf, 1, rx_buf, 1);
    // /* 拉高GPIO_IO20,表示通信结束 */
    // gpio_direction_output(dev->cs_gpio, 1);

    // return rx_buf[0];

    u8 data = 0;
	spi_read_regs(dev, reg, &data, 1);
	return data;
}

/* 写一个寄存器的值 */
static void Write_One_Reg(PTrc522_device dev, u8 reg, u8 value)
{ 
    u8 buf = value;
	spi_write_regs(dev, reg, &buf, 1);
}

void RC522_Reset_Disable( void )
{
  // gpio_set_value(TRc522_Device.rst_gpio, 1);
//  int ret = 0;
  gpio_set_value(TRc522_Device.rst_gpio, 1);
    // if(ret < 0) {
    //   printk(" set rst_gpio disable failed !rn");
    // }
//    return ret;
}

void RC522_Reset_Enable( void )
{
  //gpio_set_value(TRc522_Device.rst_gpio, 0);
//    int ret = 0;
    gpio_set_value(TRc522_Device.rst_gpio, 0);
    // if(ret < 0) {
    //   printk("set rst_gpio enable failed!rn");
    // }
    // return ret;
}

void rc522_init(void)
{
    RC522_Reset_Disable();
    udelay(10);
    /* disable cs pin */
    gpio_direction_output(TRc522_Device.cs_gpio, 1);
    udelay(10);
}

static int rc522_probe(struct spi_device *spi)
{
    int ret = 0;
    printk("rc522 probe!n");
    /* 字符设备框架,注册字符设备驱动 */
    /* 1. 申请设备号 */
    TRc522_Device.major = 0;
    if (TRc522_Device.major){
        TRc522_Device.devid = MKDEV(TRc522_Device.major,0);
        register_chrdev_region(TRc522_Device.devid,SPI_COUNT,SPI_NAME);
    }else
    {
        ret = alloc_chrdev_region(&TRc522_Device.devid,0,SPI_COUNT,SPI_NAME);
        TRc522_Device.major = MAJOR(TRc522_Device.devid);
        TRc522_Device.minor = MINOR(TRc522_Device.devid);
    }
    if (ret < 0){
        printk("Failed to alloc devid!rn");
        ret = -EINVAL;
    }
    /* 2. cdev 初始化  */
//    TRc522_Device.cdev.owner = THIS_MODULE;
    cdev_init(&TRc522_Device.cdev,&TRc522_Device_fops);
    ret = cdev_add(&TRc522_Device.cdev,TRc522_Device.devid,SPI_COUNT);
    if (ret < 0) {
        printk("Failed to add cdev!rn");
    }

    /* 3. 自动创建节点  */
    TRc522_Device.class = class_create(THIS_MODULE,SPI_NAME);
    if (IS_ERR(TRc522_Device.class))
    {
        printk("class create errnorn");
        return PTR_ERR(TRc522_Device.class);
    }
    /* 4. 创建设备 */
    TRc522_Device.device = device_create(TRc522_Device.class,NULL,TRc522_Device.devid,NULL,SPI_NAME);
    if (IS_ERR(TRc522_Device.device))
    {
        printk("device create errnorn");
        return PTR_ERR(TRc522_Device.device);
    }

    /* 设置RC522所使用的RST GPIO */
	  /* 1、获取设备节点:rst_node */
    TRc522_Device.rst_node = of_find_node_by_path("/spi_rst");
    if(TRc522_Device.rst_node == NULL) {
      printk("TRc522_Device.rst_node not find!rn");
      return -EINVAL;
    } else {
      printk("rst_node find!rn");
    }

    /* 2、 获取设备树中的gpio属性,得到RC522 RST 所使用的编号 */
    TRc522_Device.rst_gpio = of_get_named_gpio(TRc522_Device.rst_node, "rst-gpio", 0);
    if(TRc522_Device.rst_gpio < 0) {
      printk("can't get rst_gpio");
      return -EINVAL;
    }
    printk("rst_gpio num = %drn", TRc522_Device.rst_gpio);

    /* 3、设置GPIO1_IO01为输出,并且输出高电平 */
    ret = gpio_direction_output(TRc522_Device.rst_gpio, 1);
    if(ret < 0) {
      printk("can't set gpio!rn");
    }

    /* 通过spi获取&ecspi3的节点,也就是rc522的父节点 */
    TRc522_Device.node = of_find_node_by_path("/soc/aips-bus@02000000/spba-bus@02000000/ecspi@02010000");
    if (NULL == TRc522_Device.node)
    {
        printk("device node get failedrn");
        return -EINVAL;
    }
    TRc522_Device.cs_gpio = of_get_named_gpio(TRc522_Device.node, "cs-gpio", 0);
    if (TRc522_Device.cs_gpio < 0)
    {
        printk("cs_gpio get failed!rn");
        return -EINVAL;
    }

    gpio_direction_output(TRc522_Device.cs_gpio, 1);
    spi->mode = SPI_MODE_0;
    spi_setup(spi);
    /* 设置TRc522_Device的成员变量 private_data 为 spi_device */
    TRc522_Device.private_data = spi;

    // 初始化rc522 
    rc522_init();   // 这句可要可不要
//    test();

    return 0;
}

static int rc522_remove(struct spi_device *spi)
{
    int ret = 0;
    // gpio_direction_output(TRc522_Device.cs_gpio, 1);
    // if (ret < 0)
    // {
    //     printk("cs_gpio set 0 failed!rn");
    // }
    printk("rc522_remove!rn");
    gpio_free(TRc522_Device.cs_gpio);
    gpio_free(TRc522_Device.rst_gpio);
    cdev_del(&TRc522_Device.cdev);
    unregister_chrdev_region(TRc522_Device.devid,SPI_COUNT);
    device_destroy(TRc522_Device.class,TRc522_Device.devid);
    class_destroy(TRc522_Device.class);
    return ret;
}

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

static const struct of_device_id rc522_of_match[] = {
    {.compatible = "alientek,rc522"},
    {/* Sentinel */}
};

/* spi driver 结构体 */
static struct spi_driver rc522_driver = {
    .driver = {
        .name   = "Rc522_SPI",
        .owner  = THIS_MODULE,
        .of_match_table = rc522_of_match,
    },
    .probe     = rc522_probe,
    .remove    = rc522_remove,
    .id_table  = rc522_id,
};

/* 驱动入口函数 */
static int __init Rc522_Spi_Init(void) 
{
    int ret = 0;
    spi_register_driver(&rc522_driver);
    return ret;
}

/* 驱动出口函数 */
static void __exit Rc522_Spi_Exit(void)
{
    spi_unregister_driver(&rc522_driver);
}

/* 加载驱动 */
module_init(Rc522_Spi_Init);
/* 卸载驱动 */
module_exit(Rc522_Spi_Exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("OSS");

注意:我之前调试不行的原因是我直接根据视频的代码调试,可能是在匹配spi的时候或者在通过设备树of父函数找设备节点的时候出现问题,导致最后一直寻卡不成功,而spi通信是正常的,卡在了一个函数里,应该是哪里配置或者打错了,昨天根据例程源码重新写就成功了。

3.应用层函数的编写
最后的那两个测试读写的函数是我自己加的,其它的函数是借鉴下面这位大佬的
i.MX283开发板SPI驱动——RC522
一开始我是直接在驱动程序里调试的,但后来觉得比较不雅观,就参考上面这位大佬的程序,应用归应用,驱动归驱动。如有侵权,请告知,必删!

#include <stdlib.h>      /* using sleep()         */
#include <fcntl.h>       /* using file operation  */
#include <sys/ioctl.h>   /* using ioctl()         */
#include <asm/ioctls.h>
#include <unistd.h> //sleep  write read close
// #include <stdint.h>
#include <stdio.h>       
#include <string.h>
#include "rc522.h"

#define ReadFlag 	1
#define WriteFlag 	0

int fd = 0;//文件句柄

/**
  * @brief  读RC522寄存器
  * @param  ucAddress,寄存器地址
  * @retval 寄存器的当前值
  */
uint8_t ReadRawRC( uint8_t Address )
{
	uint8_t buf[1];	
	buf[0] = Address;	
    read(fd,buf,1);	
	return buf[0];	
}

/**
  * @brief  写RC522寄存器
  * @param  ucAddress,寄存器地址
  * @param  ucValue,写入寄存器的值
  * @retval 无
  */
void WriteRawRC( uint8_t Address, uint8_t Value )
{  
	uint8_t buf[2];	
	buf[0] = Address;
	buf[1] = Value;
	write(fd,buf,2);	
}

/**
  * @brief  对RC522寄存器置位
  * @param  ucReg,寄存器地址
  * @param   ucMask,置位值
  * @retval 无
  */
void SetBitMask ( uint8_t ucReg, uint8_t ucMask )  
{
  uint8_t ucTemp;
  ucTemp = ReadRawRC ( ucReg );
  WriteRawRC ( ucReg, ucTemp | ucMask ); // set bit mask
}


/**
  * @brief  对RC522寄存器清位
  * @param  ucReg,寄存器地址
  * @param  ucMask,清位值
  * @retval 无
  */
void ClearBitMask ( uint8_t ucReg, uint8_t ucMask )  
{
  uint8_t ucTemp;

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


/**
  * @brief  开启天线 
  * @param  无
  * @retval 无
  */
void PcdAntennaOn ( void )
{
  uint8_t uc;

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


/**
  * @brief  关闭天线
  * @param  无
  * @retval 无
  */
void PcdAntennaOff ( void )
{
  ClearBitMask ( TxControlReg, 0x03 );	
}


/**
  * @brief  复位RC522 
  * @param  无
  * @retval 0:复位成功  
  */
int PcdReset(void)
{
	fd = open("/dev/rc522",O_RDWR);
    if(fd < 0)
    {
        printf("open rc522_drv error %dn",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 0;
}


/**
  * @brief  设置RC522的工作方式
  * @param  ucType,工作方式
  * @retval 无
  */
void M500PcdConfigISOType ( uint8_t 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 ();//开天线		
   }	 
}



/**
  * @brief  通过RC522和ISO14443卡通讯
  * @param  ucCommand,RC522命令字
  * @param  pInData,通过RC522发送到卡片的数据
  * @param  ucInLenByte,发送数据的字节长度
  * @param  pOutData,接收到的卡片返回数据
  * @param  pOutLenBit,返回数据的位长度
  * @retval 状态值= MI_OK,成功
  */
char PcdComMF522 ( uint8_t ucCommand,
                   uint8_t * pInData, 
                   uint8_t ucInLenByte, 
                   uint8_t * pOutData,
                   uint32_t * pOutLenBit )		
{
  char cStatus = MI_ERR;
  uint8_t ucIrqEn   = 0x00;
  uint8_t ucWaitFor = 0x00;
  uint8_t ucLastBits;
  uint8_t ucN;
  uint32_t 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;
}

/**
  * @brief 寻卡
  * @param  ucReq_code,寻卡方式 = 0x52,寻感应区内所有符合14443A标准的卡;
            寻卡方式= 0x26,寻未进入休眠状态的卡
  * @param  pTagType,卡片类型代码
             = 0x4400,Mifare_UltraLight
             = 0x0400,Mifare_One(S50)
             = 0x0200,Mifare_One(S70)
             = 0x0800,Mifare_Pro(X))
             = 0x4403,Mifare_DESFire
  * @retval 状态值= MI_OK,成功
  */
char PcdRequest ( uint8_t ucReq_code, uint8_t * pTagType )
{
  char cStatus;  
  uint8_t ucComMF522Buf [ MAXRLEN ]; 
  uint32_t 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;	 
}

/**
  * @brief  防冲撞
  * @param  pSnr,卡片序列号,4字节
  * @retval 状态值= MI_OK,成功
  */
char PcdAnticoll ( uint8_t * pSnr )
{
  char cStatus;
  uint8_t uc, ucSnr_check = 0;
  uint8_t ucComMF522Buf [ MAXRLEN ]; 
  uint32_t 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;		
}


/**
  * @brief  用RC522计算CRC16
  * @param  pIndata,计算CRC16的数组
  * @param  ucLen,计算CRC16的数组字节长度
  * @param  pOutData,存放计算结果存放的首地址
  * @retval 无
  */
void CalulateCRC ( uint8_t * pIndata, 
                 uint8_t ucLen, 
                 uint8_t * pOutData )
{
  uint8_t 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 );		
}


/**
  * @brief  选定卡片
  * @param  pSnr,卡片序列号,4字节
  * @retval 状态值= MI_OK,成功
  */
char PcdSelect ( uint8_t * pSnr )
{
  char ucN;
  uint8_t uc;
  uint8_t ucComMF522Buf [ MAXRLEN ]; 
  uint32_t  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;		
}
/**
  * @brief  验证卡片密码
  * @param  ucAuth_mode,密码验证模式= 0x60,验证A密钥,
            密码验证模式= 0x61,验证B密钥
  * @param  uint8_t ucAddr,块地址
  * @param  pKey,密码 
  * @param  pSnr,卡片序列号,4字节
  * @retval 状态值= MI_OK,成功
  */
char PcdAuthState ( uint8_t ucAuth_mode, 
                    uint8_t ucAddr, 
                    uint8_t * pKey,
                    uint8_t * pSnr )
{
  char cStatus;
  uint8_t uc, ucComMF522Buf [ MAXRLEN ];
  uint32_t 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;
}


/**
  * @brief  写数据到M1卡一块
  * @param  uint8_t ucAddr,块地址
  * @param  pData,写入的数据,16字节
  * @retval 状态值= MI_OK,成功
  */
char PcdWrite ( uint8_t ucAddr, uint8_t * pData )
{
  char cStatus;
  uint8_t uc, ucComMF522Buf [ MAXRLEN ];
  uint32_t 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;		
}
/**
  * @brief  读取M1卡一块数据
  * @param  ucAddr,块地址
  * @param  pData,读出的数据,16字节
  * @retval 状态值= MI_OK,成功
  */
char PcdRead ( uint8_t ucAddr, uint8_t * pData )
{
  char cStatus;
  uint8_t uc, ucComMF522Buf [ MAXRLEN ]; 
  uint32_t 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;		
}


/**
  * @brief  命令卡片进入休眠状态
  * @param  无
  * @retval 状态值= MI_OK,成功
  */
char PcdHalt( void )
{
	uint8_t ucComMF522Buf [ MAXRLEN ]; 
	uint32_t  ulLen;
  

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

  return MI_OK;	
}
void IC_CMT ( uint8_t * UID,
              uint8_t * KEY,
              uint8_t RW,
              uint8_t * Dat )
{
  uint8_t ucArray_ID [ 4 ] = { 0 }; //先后存放IC卡的类型和UID(IC卡序列号)
  
	
  PcdRequest ( 0x52, ucArray_ID ); //寻卡

  PcdAnticoll ( ucArray_ID );      //防冲撞
  
  PcdSelect ( UID );               //选定卡
  
  PcdAuthState ( 0x60, 0x10, KEY, UID );//校验
	

	if ( RW )                        //读写选择,1是读,0是写
    PcdRead ( 0x10, Dat );
   
   else 
     PcdWrite ( 0x10, Dat );
   	 
   PcdHalt ();	 
}

void IC_Read_Or_Write(uint8_t flag, unsigned char *WriteData, uint8_t *readValue)
{
	uint8_t KeyValue[]={0xFF ,0xFF, 0xFF, 0xFF, 0xFF, 0xFF};   // 卡A密钥
	// u8 status = 0;
	char cStr [ 30 ];
	uint8_t ucArray_ID [ 4 ];    /*先后存放IC卡的类型和UID(IC卡序列号)*/                                                                                         
	uint8_t ucStatusReturn;      /*返回状态*/     
 
		/*寻卡*/
		if ( ( ucStatusReturn = PcdRequest ( PICC_REQIDL, ucArray_ID ) ) != MI_OK )  
			/*若失败再次寻卡*/
			ucStatusReturn = PcdRequest ( PICC_REQIDL, ucArray_ID );		                                                

		if ( ucStatusReturn == MI_OK  )
		{
			/*防冲撞(当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作)*/
			if ( PcdAnticoll ( ucArray_ID ) == MI_OK )                                                                   
			{
				PcdSelect(ucArray_ID);			// 选卡		
				PcdAuthState( PICC_AUTHENT1A, 0x11, KeyValue, ucArray_ID );//校验密码 
				if (flag == WriteFlag)
					PcdWrite(0x11, WriteData);
				if (PcdRead(0x11, readValue) != MI_OK)
				{
					printf("read err!rn");
				}
				else
				{
					sprintf ( cStr, "The Card ID is: %02X%02X%02X%02X",ucArray_ID [0], ucArray_ID [1], ucArray_ID [2],ucArray_ID [3] );
					printf ( "%srn",cStr );  //打印卡片ID							
					printf ("readValue: %srn",readValue);
					PcdHalt();
				}
			}				
		}	
}

void test(void)
{
    uint8_t readdata[30] = { 0 };
    uint32_t times = 0;
     /*RC522模块所需外设的初始化配置*/
	// rc522_init ();            
	
	// printk ( "WF-RC522 Testn" );
	
	// PcdReset ();
  
  // /*设置工作方式*/
	// M500PcdConfigISOType ( 'A' );

    
    while (1)
    {
        IC_Read_Or_Write(ReadFlag, NULL, readdata);
		// printf("%s %s %drn", __FILE__, __FUNCTION__, __LINE__);
		//    printf("main func read is %srn", readdata);
        times++;
        if (times >= 100000)
          memset (readdata, 0, sizeof(readdata));
    }
}

test.c测试函数

#include <stdio.h>	
#include <stdlib.h>      
#include <fcntl.h>      
#include <sys/ioctl.h>   
#include <asm/ioctls.h>
#include <unistd.h> 
#include <stdint.h>
#include "rc522.h"


int main(int argc, const char * argv [ ])
{
    int ret = -1;
	uint8_t buf[2];
	ret = PcdReset();
	if(ret != 0)
	{
       	printf("rc522 rst error %d n",ret);
	   	return 0;
	}
	M500PcdConfigISOType ( 'A' );
	test();
    while(1)
    {
	   
	}
}

至于头文件的那些,可以去野火家下载例程,或者网上找,都一样的,我就不一一贴出来了

四、实验结果

迷你板与RC522通信结果截图
串口调试截图

那些个类似马赛克的东西,其实是我以前在卡里写进了中文字符,我在linux下还没处理好支持中文的地方,有待改进!

总结:多调多试,总会成功的,编写文章是为了记录学习,如果可以帮到正有此需要的你,那就更好了。最后,转发本文章请注明出处,谢谢!

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码

)">
< <上一篇

)">
下一篇>>