嵌入式Linux驅動筆記(十三)------spi裝置之RFID-rc522驅動

風箏丶發表於2017-09-13

###你好!這裡是風箏的部落格,

###歡迎和我一起交流。

上一節講了spi框架:通俗易懂式分析瞭解spi框架
現在我們寫一下spi的裝置驅動程式, rc522是一款刷卡模組,類似於學校食堂的刷卡機。

以kernel4.8.17為例:
之前我們給mach-smdk2440.c檔案新增了:

&s3c_device_spi0,

現在我要把rc522裝置接在2440的spi1介面上,所以我們應該修改為:

&s3c_device_spi1,

然後我們看下spi-s3c24xx.c的Makefile,裡面:

obj-$(CONFIG_SPI_S3C24XX)               += spi-s3c24xx-hw.o
spi-s3c24xx-hw-y                        := spi-s3c24xx.o

所以我們在menuconfig裡,選上:Device Drivers —>[*] SPI support —> < M> Samsung S3C24XX series SPI
之前是作為M模組的,現在 選上編進核心。
make uImage,啟動核心之後,發現:

s3c2410-spi s3c2410-spi.1: No platform data supplied
s3c2410-spi: probe of s3c2410-spi.1 failed with error -2

發現,沒有platform 資訊,我們看下&s3c_device_spi1,發現裡面確實沒有platform 資訊,所以我們需要自己新增進去,修改為:

#include <linux/spi/s3c24xx.h>/*by kite 2017.9.13*/
#include <linux/gpio.h>/*by kite 2017.9.13*/
#include <mach/gpio-samsung.h>/*by kite 2017.9.13*/
#include <mach/regs-gpio.h>/*by kite 2017.9.13*/
#include <plat/gpio-cfg.h>/*by kite 2017.9.13*/

static void s3c24xx_spi_cs(struct s3c2410_spi_info *spi, int cs, int pol)/*by kite 2017.9.13*/
{
	s3c_gpio_cfgpin(cs, S3C_GPIO_OUTPUT);
	gpio_set_value(cs, pol);
}

static struct s3c2410_spi_info spi1_info={/*by kite 2017.9.13*/
	.num_cs = 0xff ,/*最大片選數*/
	.bus_num = 1,	
	.set_cs = s3c24xx_spi_cs, 
};

struct platform_device s3c_device_spi1 = {
	.name		= "s3c2410-spi",
	.id		= 1,
	.num_resources	= ARRAY_SIZE(s3c_spi1_resource),
	.resource	= s3c_spi1_resource,
	.dev		= {
		.dma_mask		= &samsung_device_dma_mask,
		.coherent_dma_mask	= DMA_BIT_MASK(32),
		.platform_data		= &spi1_info,/*by kite 2017.9.13*/
	}
};

然後啟動Kernel,發現:s3c2410-spi.1:No clock for device
定位錯誤程式碼在drivers/spi/spi-s3c24xx.c檔案裡的s3c24xx_spi_probe函式裡:

hw->clk = devm_clk_get(&pdev->dev, "spi");
	if (IS_ERR(hw->clk)) {
		dev_err(&pdev->dev, "No clock for device\n");
		err = PTR_ERR(hw->clk);
		goto err_no_pdata;
	}

其中devm_clk_get函式是對clk_get函式進行了封裝:

struct clk *devm_clk_get(struct device *dev, const char *id)
{
	struct clk **ptr, *clk;

	ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL);
	if (!ptr)
		return ERR_PTR(-ENOMEM);

	clk = clk_get(dev, id);
	if (!IS_ERR(clk)) {
		*ptr = clk;
		devres_add(dev, ptr);
	} else {
		devres_free(ptr);
	}

	return clk;
}

這裡可以看下這篇文章:詳解clock時鐘(CCF)框架及clk_get函式
這裡我們clk_get失敗了,我們知道,clk_get函式是從一個時鐘list連結串列中以字元id名稱來查詢一個時鐘clk結構體並且返回的,所以我們時鐘連結串列裡還未新增spi時鐘:
在drivers/clk/samsung/clk-s3c2410.c檔案裡:

struct samsung_clock_alias s3c2410_common_aliases[] __initdata = {
	ALIAS(PCLK_I2C, "s3c2410-i2c.0", "i2c"),
	ALIAS(PCLK_ADC, NULL, "adc"),
	ALIAS(PCLK_RTC, NULL, "rtc"),
	ALIAS(PCLK_PWM, NULL, "timers"),
	ALIAS(HCLK_LCD, NULL, "lcd"),
	ALIAS(HCLK_USBD, NULL, "usb-device"),
	ALIAS(HCLK_USBH, NULL, "usb-host"),
	ALIAS(UCLK, NULL, "usb-bus-host"),
	ALIAS(UCLK, NULL, "usb-bus-gadget"),
	ALIAS(ARMCLK, NULL, "armclk"),
	ALIAS(UCLK, NULL, "uclk"),
	ALIAS(HCLK, NULL, "hclk"),
	ALIAS(MPLL, NULL, "mpll"),
	ALIAS(FCLK, NULL, "fclk"),
	ALIAS(PCLK, NULL, "watchdog"),
	ALIAS(PCLK_SDI, NULL, "sdi"),
	ALIAS(HCLK_NAND, NULL, "nand"),
	ALIAS(PCLK_I2S, NULL, "iis"),
	ALIAS(PCLK_I2C, NULL, "i2c"),
};

在陣列最後一行新增:

	ALIAS(PCLK_SPI, NULL, "spi"),/*add by kite*/

然後編譯好核心,啟動後還發現:

WARNING: CPU: 0 PID: 1 at drivers/clk/clk.c:652 clk_core_enable+0x98/0xa4

有個警告,我們需要在drivers/spi/spi-s3c24xx.c檔案裡的s3c24xx_spi_probe函式裡,在:

hw->clk = devm_clk_get(&pdev->dev, "spi");
	if (IS_ERR(hw->clk)) {
		dev_err(&pdev->dev, "No clock for device\n");
		err = PTR_ERR(hw->clk);
		goto err_no_pdata;
	}

後面加上兩句:

if (clk_prepare_enable(hw->clk)) {
		printk("spi: clock failed :\n");
	}

因為:“名稱中含有prepare、unprepare字串的API是核心後來才加入的,過去只有clk_enable和clk_disable。只有clk_enable和clk_disable帶來的問題是,有時候,某些硬體的enable/disable clk可能引起睡眠使得enable/disable不能在原子上下文進行。加上prepare後,把過去的clk_enable分解成不可在原子上下文呼叫的clk_prepare(該函式可能睡眠)和可以在原子上下文呼叫的clk_enable。而clk_prepare_enable則同時完成prepare和enable的工作,當然也只能在可能睡眠的上下文呼叫該API。”

這樣,改好後編譯,就能成功註冊spi-master,順利啟動核心了!

因為還沒弄好裝置樹,現在只好老老實實寫platform_device裝置了:
rc522_dev.c檔案:

#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio-samsung.h>

static struct spi_board_info spi_info_rc522[] = {
	{
    	 .modalias = "rc522",  /* 對應的spi_driver名字也是"rc522" */
    	 .max_speed_hz = 8000000,	/* max spi clock (SCK) speed in HZ */
    	 .bus_num = 1,     /* 接在SPI CONTROLLER 1 */
    	 .mode    = SPI_MODE_0,
    	 .chip_select   = S3C2410_GPF(2), /* oled_cs, 它的含義由spi_master確定 */   	 
	 },
};

static int spi_info_rc522_init(void)
{
	spi_register_board_info(spi_info_rc522, ARRAY_SIZE(spi_info_rc522));
	return 0;
}

static int spi_info_rc522_exit(void)
{
	//spi_unregister_device();
	return 0;
   
}

module_init(spi_info_rc522_init);
module_exit(spi_info_rc522_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("kite");/*modinfo rc522.ko*/
MODULE_DESCRIPTION("A spi-rc522 Module for testing module ");
MODULE_VERSION("V1.0");

裡面就是註冊spi_board_info板級裝置資訊,不過這裡編寫好後我發現會出個問題:

rc522_dev: Unknown symbol spi_register_board_info (err 0)
insmod: can’t insert ‘rc522_dev.ko’: unknown symbol in module or invalid parameter

也就是spi_register_board_info 函式沒有匯出核心符號到公共核心符號表,詳情可以看這篇blog:http://blog.csdn.net/wuyongpeng0912/article/details/46739233。
也可以檢視System.map檔案有沒有這個函式,參考:System.map檔案的作用
所以我們在drivers/spi/spi.c檔案裡找到spi_register_board_info函式實現的地方,在後面加上一句:EXPORT_SYMBOL_GPL(spi_register_board_info);
重新編譯即可。

然後繼續看下platform_driver驅動:
rc522_drv.c檔案:

編譯好後又發現個問題,真是一波三折啊…

WARNING: CPU: 0 PID: 1037 at drivers/base/dd.c:347 driver_probe_device+0x114/0x318

同樣是個警告,定位到drivers/base/dd.c檔案裡的really_probe裡的一句:

WARN_ON(!list_empty(&dev->devres_head));

就是這裡出了警告,WARN_ON是呼叫dump_stack,列印堆疊資訊,只要list_empty返回0,也就是連結串列非空時,會warning列印堆疊。
我們看下s3c24xx_spi_probe函式有句:

hw->master->setup = s3c24xx_spi_setup;

s3c24xx_spi_setup函式裡面有一句:

cs = devm_kzalloc(&spi->dev,
				  sizeof(struct s3c24xx_spi_devstate),
				  GFP_KERNEL);

devm_kzalloc函式裡會加入 dev->devres_head的:

list_add_tail(&node->entry, &dev->devres_head);

所以這裡,我們把devm_kzalloc修改為kzalloc即可:

		/*cs = devm_kzalloc(&spi->dev,
				  sizeof(struct s3c24xx_spi_devstate),
				  GFP_KERNEL);*/
		cs = kzalloc(sizeof(struct s3c24xx_spi_devstate), GFP_KERNEL); /*by kite 2017.9.16*/

這裡說下devm_kzalloc和kzalloc: devm_kzalloc()函式 和kzalloc()函式一樣都是核心記憶體分配函式,但是devm_kzalloc()是跟裝置(device)有關的,當裝置(device)被detached或者驅動(driver)解除安裝(unloaded)時,記憶體會被自動釋放。另外,當記憶體不在使用時,可以使用函式devm_kfree()釋放。
而kzalloc()則需要手動釋放(使用kfree()),但如果工程師檢查不仔細,則有可能造成記憶體洩漏。

所以這裡我們最好還要有kfree函式,如果你不打算釋放spi_master的話,不寫也沒事。
在s3c24xx_spi_probe函式裡,

hw->master->setup = s3c24xx_spi_setup;

下面新增一句:

hw->master->cleanup = s3c24xx_spi_cleanup;

同時實現s3c24xx_spi_cleanup函式:

static void s3c24xx_spi_cleanup(struct spi_device *spi)/*by kite 2017.9.16*/
{
    struct chip_data *chip = spi_get_ctldata(spi);
    kfree(chip);
    spi_set_ctldata(spi, NULL);
    printk("spi_master has been rmmove ! \n");
}

接下來就是rc522_drv.c檔案:

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <sound/core.h>
#include <linux/spi/spi.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>

#include <mach/hardware.h>
#include <mach/regs-gpio.h>

#include <linux/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/delay.h>

#include "rc522.h"
#include <linux/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio-samsung.h>

static volatile unsigned long *gpfcon;
static volatile unsigned long *gpfdat;//f1
#define SET_RC522RST *gpfdat |= (0x01<<(1));
#define CLR_RC522RST *gpfdat &= ~(0x01<<(1));

static struct spi_device *spi_dev;

/* 1. 確定主裝置號 */
static int major;
static struct cdev rc522_cdev;
static struct class *cls;

u8 ReadRawRC(u8   Address )
{
	unsigned char tx_buf[1];
    	unsigned char rx_buf[1];
    
	tx_buf[0] = ((Address<<1)&0x7E)|0x80;//RC522 set

	spi_write_then_read(spi_dev, tx_buf, 1, rx_buf, 1);

	return rx_buf[0];
}

void WriteRawRC(u8   Address, u8   value)
{
	u8   ucAddr[2];

	ucAddr[0] = ((Address<<1)&0x7E);//RC522 set
	ucAddr[1] = value;
	
	spi_write(spi_dev, ucAddr, 2);
}

void ClearBitMask(u8   reg,u8   mask)  
{
    char   tmp = 0x0;
    tmp = ReadRawRC(reg);
    WriteRawRC(reg, tmp & ~mask);  // clear bit mask
}

void SetBitMask(u8   reg,u8   mask)  
{
	char   tmp = 0x0;
	tmp = ReadRawRC(reg);
	WriteRawRC(reg,tmp | mask);  // set bit mask
}

static int rc522_open(struct inode *inode, struct file *file)   
{
	u8   i;u8  Re=0;
	printk("this is open \n");
	
	/*復位*/
	SET_RC522RST;
	ndelay(10);
	CLR_RC522RST;
	ndelay(10);
	SET_RC522RST;
	ndelay(10);
    	WriteRawRC(CommandReg,PCD_RESETPHASE);
	WriteRawRC(CommandReg,PCD_RESETPHASE);
    	ndelay(10);

	WriteRawRC(ModeReg,0x3D);            //和Mifare卡通訊,CRC初始值0x6363
	WriteRawRC(TReloadRegL,30);           
    	WriteRawRC(TReloadRegH,0);
    	WriteRawRC(TModeReg,0x8D);
    	WriteRawRC(TPrescalerReg,0x3E);	
	WriteRawRC(TxAutoReg,0x40);//必須要

	/*關閉天線*/
	ClearBitMask(TxControlReg, 0x03);//

	mdelay(2);
	
	/*開啟天線*/
	i = ReadRawRC(TxControlReg);
   	 if (!(i & 0x03))
    	{
        	SetBitMask(TxControlReg, 0x03);
    	}

	/*設定RC632的工作方式 */
	ClearBitMask(Status2Reg,0x08);
       WriteRawRC(ModeReg,0x3D);//3F
       WriteRawRC(RxSelReg,0x86);//84
       WriteRawRC(RFCfgReg,0x7F);   //4F
	WriteRawRC(TReloadRegL,30);//tmoLength);// TReloadVal = 'h6a =tmoLength(dec) 
	WriteRawRC(TReloadRegH,0);
       WriteRawRC(TModeReg,0x8D);
	WriteRawRC(TPrescalerReg,0x3E);
	ndelay(1000);
       /*開啟天線*/
	i = ReadRawRC(TxControlReg);
   	 if (!(i & 0x03))
    	{
        	SetBitMask(TxControlReg, 0x03);
    	}
	
	return 0;
}

char PcdComMF522(u8   Command, 
                 u8 *pIn , 
                 u8   InLenByte,
                 u8 *pOut , 
                 u8 *pOutLenBit)
{
    char   status = MI_ERR;
    u8   irqEn   = 0x00;
    u8   waitFor = 0x00;
    u8   lastBits;
    u8   n;
    u16   i;
    switch (Command)
    {
        case PCD_AUTHENT:
			irqEn   = 0x12;
			waitFor = 0x10;
			break;
		case PCD_TRANSCEIVE:
			irqEn   = 0x77;
			waitFor = 0x30;
			break;
		default:
			break;
    }
   
    WriteRawRC(ComIEnReg,irqEn|0x80);
    ClearBitMask(ComIrqReg,0x80);	//清所有中斷位
    WriteRawRC(CommandReg,PCD_IDLE);
    SetBitMask(FIFOLevelReg,0x80);	 	//清FIFO快取
    
    for (i=0; i<InLenByte; i++)
    {   WriteRawRC(FIFODataReg, pIn [i]);    }
    WriteRawRC(CommandReg, Command);	  
    
    if (Command == PCD_TRANSCEIVE)
    {    SetBitMask(BitFramingReg,0x80);  }	 //開始傳送
    										 
    //i = 600;//根據時脈頻率調整,操作M1卡最大等待時間25ms
	i = 60;
    do 
    {
        n = ReadRawRC(ComIrqReg);
        i--;mdelay(50);
    }
    while ((i!=0) && !(n&0x01) && !(n&waitFor));
    ClearBitMask(BitFramingReg,0x80);

    if (i!=0)
    {    
        if(!(ReadRawRC(ErrorReg)&0x1B))
        {
            status = MI_OK;
            if (n & irqEn & 0x01)
            {   status = MI_NOTAGERR;   }
            if (Command == PCD_TRANSCEIVE)
            {
               	n = ReadRawRC(FIFOLevelReg);
              	lastBits = ReadRawRC(ControlReg) & 0x07;
                if (lastBits)
                {   *pOutLenBit = (n-1)*8 + lastBits;   }
                else
                {   *pOutLenBit = n*8;   }
                if (n == 0)
                {   n = 1;    }
                if (n > MAXRLEN)
                {   n = MAXRLEN;   }
                for (i=0; i<n; i++)
                {   pOut [i] = ReadRawRC(FIFODataReg);    }
            }
        }
        else
        {   status = MI_ERR;   }
        
    }
   

    SetBitMask(ControlReg,0x80);           // stop timer now
    WriteRawRC(CommandReg,PCD_IDLE); 
    return status;
}


char PcdRequest(u8   req_code,u8 *pTagType)
{
	char   status;  
	u8   unLen;
	u8   ucComMF522Buf[MAXRLEN]; 

	ClearBitMask(Status2Reg,0x08);
	WriteRawRC(BitFramingReg,0x07);
	SetBitMask(TxControlReg,0x03);
 
	ucComMF522Buf[0] = req_code;

	status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,1,ucComMF522Buf,&unLen);

	if ((status == MI_OK) && (unLen == 0x10))
	{    
		*pTagType     = ucComMF522Buf[0];
		*(pTagType+1) = ucComMF522Buf[1];
	}
	else
	{  
		status = MI_ERR;  
	}
   
	return status;
}

char PcdAnticoll(u8 *pSnr)
{
	char   status;
	u8   i,snr_check=0;
	u8   unLen;
	u8   ucComMF522Buf[MAXRLEN];   

	ClearBitMask(Status2Reg,0x08);
	WriteRawRC(BitFramingReg,0x00);
	ClearBitMask(CollReg,0x80);
 
	ucComMF522Buf[0] = PICC_ANTICOLL1;
	ucComMF522Buf[1] = 0x20;

	status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,2,ucComMF522Buf,&unLen);

	if (status == MI_OK)
	{
		for (i=0; i<4; i++)
		{   
			*(pSnr+i)  = ucComMF522Buf[i];
			snr_check ^= ucComMF522Buf[i];
		}
		if (snr_check != ucComMF522Buf[i])
		{  
			status = MI_ERR;    
		}
    }
    
    SetBitMask(CollReg,0x80);
    return status;
}

static ssize_t rc522_read(struct file * file, char __user *buf, size_t count, loff_t *off)
{
	u8 status;
	u8 CT[2];//卡型別
	u8 UID[4]; //卡號
	status = PcdRequest(PICC_REQALL,CT);/*尋卡 輸出為卡型別----CT卡型別*/
	if(status==MI_OK)
	{
		printk(" find car ok \n");
		status=MI_ERR;
		status = PcdAnticoll(UID);/*防衝撞*/	
		if (status==MI_OK)//防衝撞成功
		{
			status=MI_ERR;	
			printk("The car id is %d%d%d%d \n" , UID[0],UID[1],UID[2],UID[3]);
		}
		
	}
	else
		printk("find car faid \n");
	
		
	return 0;
}

static ssize_t rc522_write(struct file *file, const char __user *buf,size_t count, loff_t *ppos)
{
	return 0;
}

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

static int spi_rc522_probe(struct spi_device *spi)
{
	int res;
	struct device *rc522_res;
	dev_t devid;
	s3c_gpio_cfgpin(S3C2410_GPG(2), S3C_GPIO_OUTPUT);//
	spi_dev = spi;
	/* 3. 告訴核心 */
#if 0
    major = register_chrdev(0, "hello", &hello_fops); /* (major,  0), (major, 1), ..., (major, 255)都對應hello_fops */
#else /*僅僅是註冊裝置號*/
    if (major) {
        devid = MKDEV(major, 0);
        register_chrdev_region(devid, 1, "rc522");  /* (major,0) 對應 pwm_fops, (major, 1~255)都不對應pwm_fops */
    } else {
        alloc_chrdev_region(&devid, 0, 1, "rc522"); /* (major,0) 對應 pwm_fops, (major, 1~255)都不對應pwm_fops */
        major = MAJOR(devid);                     
    }

    cdev_init(&rc522_cdev, &rc522_fops);
    res=cdev_add(&rc522_cdev, devid, 1);
    if(res)
    {
        printk("cdev_add failed\n");
        unregister_chrdev_region(MKDEV(major, 0), 1);
        return 0;
    }
#endif
    cls = class_create(THIS_MODULE, "rc522");
    rc522_res = device_create(cls, NULL, MKDEV(major, 0), NULL, "rc522"); /* /dev/xxx */
    if (IS_ERR(rc522_res)) 
    {
        printk("device_create failed\n");
        return 0;
    }

	gpfcon 	= ioremap(0x56000050 , 8);
	gpfdat 	= gpfcon+1;
	if (!gpfcon)
	{
		printk("ioremap failed\n");
		return -EIO;
	}

    return 0;
}


static struct spi_driver spi_rc522_drv = {
	.driver = {
		.name	= "rc522",
		.owner	= THIS_MODULE,
	},
	.probe		= spi_rc522_probe,
	//.remove		= spi_rc522_remove,
};

static int spi_rc522_init(void)
{
    return spi_register_driver(&spi_rc522_drv);
}

static void spi_rc522_exit(void)
{
    spi_unregister_driver(&spi_rc522_drv);
}

module_init(spi_rc522_init);
module_exit(spi_rc522_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("kite");/*modinfo my_keyboard.ko*/
MODULE_DESCRIPTION("A spi-rc522 Module for testing module ");
MODULE_VERSION("V1.0");

rc522.h:

#ifndef __RC522_H
#define __RC522_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                 0
#define 	MI_NOTAGERR           (1)
#define 	MI_ERR                (2)

#define	SHAQU1	0X01
#define	KUAI4	0X04
#define	KUAI7	0X07
#define	REGCARD	0xa1
#define	CONSUME	0xa2
#define READCARD	0xa3
#define ADDMONEY	0xa4

#endif 

這樣,當我們cat rc522裝置時,就會讀出我們卡的id了

除錯:
在Linux根目錄下,找spidev_test.c檔案,會發現在tools/spi目錄下,
make編譯即可得到可執行檔案:spidev_test
放到板子上短接mosi和miso,就可以測試了。如圖,
當然,別忘記了把spidev.c檔案編進核心,以及在裝置樹裡新增相應的節點,這樣在dev下才會出現spidev的裝置節點。

編譯的時候發現直接make不好使,我就在spidev_test.c檔案裡修改:
註釋兩行:#include <linux/spi/spidev.h>
#include </work/system/nanopi/linux/include/linux/spi/spi.h>
新增: #include “my_spidev.h”

spidev

my_spidev.h:

#ifndef SPIDE_H
#define SPIDE_H

#include <linux/types.h>

 /* User space versions of kernel symbols for SPI clocking modes,
 * matching <linux/spi/spi.h>
 */

#define SPI_CPHA        0x01
#define SPI_CPOL        0x02

#define SPI_MODE_0      (0|0)
#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
#define SPI_LSB_FIRST       0x08
#define SPI_3WIRE       0x10
#define SPI_LOOP        0x20
#define SPI_NO_CS       0x40
#define SPI_READY       0x80
#define SPI_TX_DUAL     0x100
#define SPI_TX_QUAD     0x200
#define SPI_RX_DUAL     0x400
#define SPI_RX_QUAD     0x800

#define SPI_IOC_MAGIC           'k'

struct spi_ioc_transfer {
 __u64       tx_buf;
 __u64       rx_buf;
 
 __u32       len;
 __u32       speed_hz;
 
 __u16       delay_usecs;
 __u8        bits_per_word;
 __u8        cs_change;
 __u8        tx_nbits;
__u8        rx_nbits;
__u16       pad;

/* If the contents of 'struct spi_ioc_transfer' ever change
* incompatibly, then the ioctl number (currently 0) must change;
* ioctls with constant size fields get a bit more in the way of
* error checking than ones (like this) where that field varies.
      *
* NOTE: struct layout is the same in 64bit and 32bit userspace.
*/
};

/* not all platforms use <asm-generic/ioctl.h> or _IOC_TYPECHECK() ... */
#define SPI_MSGSIZE(N) \
((((N)*(sizeof (struct spi_ioc_transfer))) < (1 << _IOC_SIZEBITS)) \
? ((N)*(sizeof (struct spi_ioc_transfer))) : 0)
#define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)])

/* Read / Write of SPI mode (SPI_MODE_0..SPI_MODE_3) (limited to 8 bits) */
#define SPI_IOC_RD_MODE         _IOR(SPI_IOC_MAGIC, 1, __u8)
#define SPI_IOC_WR_MODE         _IOW(SPI_IOC_MAGIC, 1, __u8)

/* Read / Write SPI bit justification */
#define SPI_IOC_RD_LSB_FIRST        _IOR(SPI_IOC_MAGIC, 2, __u8)
#define SPI_IOC_WR_LSB_FIRST        _IOW(SPI_IOC_MAGIC, 2, __u8)

/* Read / Write SPI device word length (1..N) */
#define SPI_IOC_RD_BITS_PER_WORD    _IOR(SPI_IOC_MAGIC, 3, __u8)
#define SPI_IOC_WR_BITS_PER_WORD    _IOW(SPI_IOC_MAGIC, 3, __u8)

/* Read / Write SPI device default max speed hz */
#define SPI_IOC_RD_MAX_SPEED_HZ     _IOR(SPI_IOC_MAGIC, 4, __u32)
#define SPI_IOC_WR_MAX_SPEED_HZ     _IOW(SPI_IOC_MAGIC, 4, __u32)

/* Read / Write of the SPI mode field */
#define SPI_IOC_RD_MODE32       _IOR(SPI_IOC_MAGIC, 5, __u32)
#define SPI_IOC_WR_MODE32       _IOW(SPI_IOC_MAGIC, 5, __u32)


 
#endif /* SPIDEV_H */


相關文章