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)
    {
	   
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章