i.MX283开发板SPI驱动——RC522

一、Linux下SPI驱动介绍

内核版本:2.6.35
Linux下SPI驱动和I2C驱动很类似,他们都是一种总线,且都不支持热拔插,因为一般情况下spi或者i2c设备都是直接焊接在板子上的,不像USB设备那样随时插拔,所以根据总线——设备——驱动模型,spi和i2c设备都可以通过xxx_board_info结构体进行注册,Linux下spi驱动的架构如下:

在这里插入图片描述
spi核心层提供spi master、spi设备和驱动的注册、卸载函数,以及spi通信函数。
spi master是Linux虚拟处理的一个概念,实际上就是spi主机,一般是在芯片内部,芯片有多少个spi接口,就代表有几个master,每个master下面可以挂多个spi设备,但是每个设备都需要一个单独的片选信号。spi master就相当于i2c的adapter,spi master的驱动芯片厂商已经写好,不需要我们去编写,它会操作芯片内部寄存器实现和挂在该master下面的设备进行spi通信。
应用层和挂在某个master下的spi设备通信流程如下:
用户层操作spi设备驱动(open、write、read)——spi设备驱动调用spi核心层提供的通信函数sync或async——调用对应spi master的transfer函数,最终实现和spi设备的通信。

下面就开始编写一个Linux下spi驱动——RC522驱动

二、RC522设备

根据总线——设备——驱动模型,首先需要注册一个spi设备——rc522设备:
首先介绍下几个比较重要的结构体:

1. spi_board_info

struct spi_board_info {
	char		modalias[SPI_NAME_SIZE];//spi设备名 驱动的名字需和设备名保持一致
	const void	*platform_data;
	void		*controller_data;
	int		irq;
	u32		max_speed_hz;//spi最大时钟频率
	u16		bus_num;//spi主机序号,表明该设备是挂在哪一个spi master下
	u16		chip_select;//片选脚 
	u8		mode;//spi模式  SPI_CPHA SPI_CPOL共有四种组合方式 
};

spi_board_info用来描述一个spi板级设备信息,其中包括设备名、要使用哪一个spi主机、spi模式以及使用哪个片选脚,这里的片选是由spi 主机自动控制的,一个设备只能对应一个片选,但是也可以不使用这个片选,可以申请一个普通IO口当作片选,最后再调用spi_new_device注册设备即可。

SPI_CPHA选择对数据线采样的时机,0选择每个时钟周期的第一个沿跳变时采样数据,1选择第二个时钟沿采样数据;SPI_CPOL选择每个时钟周期开始的极性,0表示时钟以低电平开始,1选择高电平开始。这两个比特位有四种组合,对应SPI_MODE_0~SPI_MODE_3。

2. spi_device
spi_device结构体用来描述一个spi设备,可以根据前面板级设备信息注册一个spi设备。

struct spi_device {
	struct device		dev;
	struct spi_master	*master;
	u32			max_speed_hz;//spi最大时钟频率
	u8			chip_select;//片选脚
	u8			mode;//spi 模式
	#define	SPI_CPHA	0x01			/* clock phase */
	#define	SPI_CPOL	0x02			/* clock polarity */
	#define	SPI_MODE_0	(0|0)			/* (original MicroWire) */
	#define	SPI_MODE_1	(0|SPI_CPHA)
	#define	SPI_MODE_2	(SPI_CPOL|0)
	#define	SPI_MODE_3	(SPI_CPOL|SPI_CPHA)
	#define	SPI_CS_HIGH	0x04			/* chipselect active high? */
	#define	SPI_LSB_FIRST	0x08			/* per-word bits-on-wire */
	#define	SPI_3WIRE	0x10			/* SI/SO signals shared */
	#define	SPI_LOOP	0x20			/* loopback mode */
	#define	SPI_NO_CS	0x40			/* 1 dev/bus, no chipselect */
	#define	SPI_READY	0x80			/* slave pulls low to pause */
	u8			bits_per_word;
	int			irq;
	void			*controller_state;
	void			*controller_data;
	char			modalias[SPI_NAME_SIZE];//spi设备名
};

rc522_dev.c:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>

static struct spi_board_info rc522_board_info = 
{
   .modalias = "rc522",
   .max_speed_hz = 8000000,
   .bus_num = 1,
   .chip_select = 0,
   .mode = SPI_MODE_0,
};

static struct spi_device* rc522_dev = NULL;
static int rc522_dev_init(void)
{
   struct spi_master *rc522_master = NULL;
   rc522_master = spi_busnum_to_master(rc522_board_info.bus_num);//根据spi总线编号获取一个spi master
   if(rc522_master != NULL)
   {
      rc522_dev = spi_new_device(rc522_master,&rc522_board_info);//注册spi设备
      if(rc522_dev != NULL)
      {
         printk("module init ok \n");
         return 0;
	  }
	  else
	  {			
		printk("create rc522_dev error \n");
		return -1;
	  }
   }
   else
   {
     	printk("rc522_master not found \n");
		return -1;
   }
}

static void rc522_dev_exit(void)
{
   spi_unregister_device(rc522_dev);
   printk("module exit ok \n"); 
}
module_init(rc522_dev_init);
module_exit(rc522_dev_exit); 

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xzx2020");

三、RC522驱动

rc522驱动有个重要的结构体spi_driver:

struct spi_driver {
	const struct spi_device_id *id_table;
	int			(*probe)(struct spi_device *spi);
	int			(*remove)(struct spi_device *spi);
	void			(*shutdown)(struct spi_device *spi);
	int			(*suspend)(struct spi_device *spi, pm_message_t mesg);
	int			(*resume)(struct spi_device *spi);
	struct device_driver	driver;
};
struct device_driver {
	const char		*name;//驱动名字,需和设备名保持一致
	struct bus_type		*bus;
	struct module		*owner;
	const char		*mod_name;	/* used for built-in modules */
	bool suppress_bind_attrs;	/* disables bind/unbind via sysfs */
#if defined(CONFIG_OF)
	const struct of_device_id	*of_match_table;
#endif
	int (*probe) (struct device *dev);
	int (*remove) (struct device *dev);
	void (*shutdown) (struct device *dev);
	int (*suspend) (struct device *dev, pm_message_t state);
	int (*resume) (struct device *dev);
	const struct attribute_group **groups;
	const struct dev_pm_ops *pm;
	struct driver_private *p;
};

我们需要实现的是spi_driver的probe和remove函数,然后在probe函数里实现字符设备注册,在remove函数里实现字符设备卸载。
spi_write:spi写入若干字节
spi_write_then_read:spi写入若干字节并读取若干字节

rc522_drv.c:
这里的片选脚由spi_master自动控制。

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <asm/uaccess.h> 
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/gpio.h>//gpio_request  gpio_free函数
#include <../arch/arm/mach-mx28/mx28_pins.h>


#define DEVICE_NAME "rc522_drv" //驱动名称
#define RST_PIN  MXS_PIN_TO_GPIO(PINID_SSP0_DATA7)  //p2.7
#define CS_PIN  MXS_PIN_TO_GPIO(PINID_SSP0_DATA6)  //p2.6

#define RC522_RST_Enable()  gpio_direction_output(RST_PIN,0)
#define RC522_RST_Disable() gpio_direction_output(RST_PIN,1)

#define RC522_CS_Enable()  gpio_direction_output(CS_PIN,0)
#define RC522_CS_Disable() gpio_direction_output(CS_PIN,1)

static struct spi_device* rc522_dev = NULL;
static struct cdev *rc522_cdev = NULL;
static struct class *rc522_class = NULL;
static struct device *rc522_device = NULL;
static dev_t device_id;

static int rc522_open(struct inode *inode, struct file *filp)
{
   int ret = -1,ret2 = -1;
   gpio_free(RST_PIN);
   gpio_free(CS_PIN);
   ret = gpio_request(RST_PIN, "RC522_RST");
   ret2 = gpio_request(CS_PIN, "RC522_CS");
   printk("RST_PIN=%d  CS_PIN=%d\n",ret,ret2);
 
   RC522_RST_Disable();
   udelay(1);
   RC522_RST_Enable();
   udelay(1);
   RC522_RST_Disable();
   return 0;
}

static int rc522_release(struct inode *inode, struct file *filp)
{
   RC522_RST_Enable();
   gpio_free(RST_PIN);
   //gpio_free(CS_PIN);
   return 0;
}

static ssize_t rc522_read(struct file *filp,  char __user *buf, size_t count,
                loff_t *f_pos)
{
        uint8_t address,ret;
		int status = -1;
		copy_from_user(&address,buf, 1);
		//printk("read: %x = ",buf[0]);
		//RC522_CS_Enable();	
		address = (( address << 1 ) & 0x7E) | 0x80;	
		status = spi_write_then_read(rc522_dev,&address,1,&ret,1);
        //ret = spi_read(rc522_dev,&address,1); 
        // printk("%x\n",ret);
		//RC522_CS_Disable();	
        //printk("spi_write_then_read %d\n",status);
		copy_to_user((void *)buf,&ret,1);
        
		return 0;
}


static ssize_t rc522_write(struct file *filp, const char __user *buf,
		size_t count, loff_t *f_pos)
{
		uint8_t address,value,buffer[2];
        copy_from_user(buffer,buf, 2);
		//printk("write: %x %x \n",buf[0],buf[1]);
		//address = buffer[0];
		//value = buffer[1];
        //RC522_CS_Enable();
		buffer[0] = (( buffer[0] << 1 ) & 0x7E);
        spi_write(rc522_dev,buffer,2);
		//RC522_CS_Disable();
		 
		return 0;
}

static struct file_operations rc522_fops = 
{
   .owner   = THIS_MODULE,
   .open    = rc522_open,
   .release = rc522_release,
   .write   = rc522_write,
   .read    = rc522_read, 
};
		
static int __devinit rc522_probe(struct spi_device *spi)
{
    int ret;
	
	rc522_dev = spi;

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


	/*分配一个cdev结构体*/
	rc522_cdev = cdev_alloc();
    if(rc522_cdev != NULL)
    {
        /*初始化cdev结构体*/
		cdev_init(rc522_cdev, &rc522_fops);
		ret = cdev_add(rc522_cdev,device_id,1);
		if(ret != 0)
		{
			printk("cdev add error %d \n",ret);
		    goto error;
		}
	}
	else
	{
	     printk("cdev_alloc error \n");
	     return -1;
	}

	
    /*创建一个cdev结构体*/
	rc522_class = class_create(THIS_MODULE, "rc522_class");
	if(rc522_class != NULL)
	{		
	    /*创建一个device结构体*/
		rc522_device = device_create(rc522_class, NULL, device_id, NULL, DEVICE_NAME);
		printk("module init ok\n");
		return 0;
	}
	else
	{
         printk("class_create error\n");
	     return -1;
	}
error:
    cdev_del(rc522_cdev);
	unregister_chrdev_region(device_id,1);
	return -1;

}

static int __devexit rc522_remove(struct spi_device *spi)
{
	cdev_del(rc522_cdev);
	unregister_chrdev_region(device_id,1);
	device_del(rc522_device);
	class_destroy(rc522_class);
	printk("module exit ok\n");
	return 0;
}

static struct spi_driver rc522_driver = {
	.driver = {
		.name =		"rc522",
		.owner =	THIS_MODULE,
	},
	.probe =	rc522_probe,
	.remove =	__devexit_p(rc522_remove),

};

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);
	 return ret;
   }
   return 0;
}

static void __exit rc522_exit(void)
{
   spi_unregister_driver(&rc522_driver);  
}

module_init(rc522_init);
module_exit(rc522_exit);

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

四、测试

rc522.c:

#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 "rc522.h"

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_drv",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 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 ();	 
}

rc522.h:

#ifndef RC522_H
#define RC522_H
#include <stdint.h>

/////////////////////////////////////////////////////////////////////
//MF522命令字
/////////////////////////////////////////////////////////////////////
#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               //CRC计算

/////////////////////////////////////////////////////////////////////
//Mifare_One卡片命令字
/////////////////////////////////////////////////////////////////////
#define PICC_REQIDL           0x26               //寻天线区内未进入休眠状态
#define PICC_REQALL           0x52               //寻天线区内全部卡
#define PICC_ANTICOLL1        0x93               //防冲撞
#define PICC_ANTICOLL2        0x95               //防冲撞
#define PICC_AUTHENT1A        0x60               //验证A密钥
#define PICC_AUTHENT1B        0x61               //验证B密钥
#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               //休眠

/////////////////////////////////////////////////////////////////////
//MF522 FIFO长度定义
/////////////////////////////////////////////////////////////////////
#define DEF_FIFO_LENGTH       64                 //FIFO size=64byte
#define MAXRLEN  18

/////////////////////////////////////////////////////////////////////
//MF522寄存器定义
/////////////////////////////////////////////////////////////////////
// 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

/////////////////////////////////////////////////////////////////////
//和MF522通讯时返回的错误代码
/////////////////////////////////////////////////////////////////////
#define 	MI_OK                 0x26
#define 	MI_NOTAGERR           0xcc
#define 	MI_ERR                0xbb

uint8_t ReadRawRC( uint8_t Address );
void WriteRawRC( uint8_t Address, uint8_t Value );


int PcdReset(void);
void M500PcdConfigISOType ( uint8_t ucType );
char PcdComMF522 ( uint8_t ucCommand,
                   uint8_t * pInData, 
                   uint8_t ucInLenByte, 
                   uint8_t * pOutData,
                   uint32_t * pOutLenBit );
char PcdRequest ( uint8_t ucReq_code, uint8_t * pTagType );
char PcdAnticoll(uint8_t * pSnr);
void CalulateCRC ( uint8_t * pIndata, 
                 uint8_t ucLen, 
                 uint8_t * pOutData );
char PcdSelect ( uint8_t * pSnr );
char PcdAuthState ( uint8_t ucAuth_mode, 
                    uint8_t ucAddr, 
                    uint8_t * pKey,
                    uint8_t * pSnr );
char PcdWrite ( uint8_t ucAddr, uint8_t * pData );
char PcdRead ( uint8_t ucAddr, uint8_t * pData );
char PcdHalt( void );
void IC_CMT ( uint8_t * UID,
              uint8_t * KEY,
              uint8_t RW,
              uint8_t * Dat );
#endif

rc522_test.c:

#include "rc522.h"
#include <stdio.h>	/*using printf()*/
#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

/**
  * @brief  测试函数
  * @param  无
  * @retval 无
  */
void IC_test ( void )
{
  uint8_t ucArray_ID [4];   /*先后存放IC卡的类型和UID(IC卡序列号)*/                                                                                          
  uint8_t ucStatusReturn;     /*返回状态 */                                                                                        
  static uint8_t ucLineCount = 0; 
	
  while ( 1 )
  { 
    /*寻卡*/
		if ( MI_OK == (ucStatusReturn = PcdRequest ( PICC_REQALL, ucArray_ID )))  	                                                
		{
            /*防冲撞(当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作)*/
			if ( PcdAnticoll ( ucArray_ID ) == MI_OK )                                                                   
			{
			    if(PcdSelect(ucArray_ID)==MI_OK)
			    {
					  printf ("The Card ID is: %02X%02X%02X%02X \n",
			                  ucArray_ID [ 0 ],
			                  ucArray_ID [ 1 ],
			                  ucArray_ID [ 2 ],
			                  ucArray_ID [ 3 ] );
					  PcdHalt();
			    }
			}		
		}
		else 
		{
             printf ("no card \n");
		}
		sleep(1);
  }		
}

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' );
	IC_test();
    while(1)
    {
	   
	}
}

猜你喜欢

转载自blog.csdn.net/qq_24835087/article/details/104850549