嵌入式Linux驅動筆記(十三)------spi裝置之RFID-rc522驅動
###你好!這裡是風箏的部落格,
###歡迎和我一起交流。
上一節講了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”
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 */
相關文章
- 嵌入式Linux驅動筆記(十一)------i2c裝置之mpu6050驅動Linux筆記
- 嵌入式Linux驅動筆記(十六)------裝置驅動模型(kobject、kset、ktype)Linux筆記模型Object
- Linux裝置驅動探究第1天----spi驅動(1)Linux
- 嵌入式Linux驅動學習筆記(十六)------裝置驅動模型(kobject、kset、ktype)Linux筆記模型Object
- 嵌入式Linux驅動筆記(十二)------通俗易懂式分析瞭解spi框架Linux筆記框架
- SPI驅動示例
- 嵌入式Linux驅動筆記(十七)------詳解V4L2框架(UVC驅動)Linux筆記框架
- SPI驅動框架一框架
- 嵌入式Linux驅動筆記(九)------dts裝置樹在2440使用Linux筆記
- Linux裝置驅動之字元裝置驅動Linux字元
- 【Linux SPI】RFID RC522 裝置驅動Linux
- Linux晶片驅動之SPI ControllerLinux晶片Controller
- Linux驅動開發筆記(四):裝置驅動介紹、熟悉雜項裝置驅動和ubuntu開發雜項裝置DemoLinux筆記Ubuntu
- HP筆記本驅動安裝教程筆記
- 【linux】驅動-7-平臺裝置驅動Linux
- ArmSoM系列板卡 嵌入式Linux驅動開發實戰指南 之 字元裝置驅動Linux字元
- Linux驅動開發筆記(一):helloworld驅動原始碼編寫、makefile編寫以及驅動編譯Linux筆記原始碼編譯
- linux驅動之LED驅動Linux
- 【linux】驅動-6-匯流排-裝置-驅動Linux
- 深入淺出:Linux裝置驅動之字元裝置驅動Linux字元
- 乾坤合一:Linux裝置驅動之塊裝置驅動Linux
- 框架-SPI四種模式+通用裝置驅動實現框架模式
- SPI轉can晶片CSM300詳解以及Linux驅動移植除錯筆記晶片Linux除錯筆記
- 蛻變成蝶:Linux裝置驅動之字元裝置驅動Linux字元
- 蛻變成蝶~Linux裝置驅動之字元裝置驅動Linux字元
- Linux塊裝置驅動Linux
- 字元裝置驅動 —— 字元裝置驅動框架字元框架
- Flash驅動控制--晶片擦除(SPI協議)晶片協議
- 向嵌入式Linux移植實時裝置驅動程式(轉)Linux
- 嵌入式Linux中的LED驅動控制(裝置樹方式)Linux
- Linux裝置驅動程式學習----1.裝置驅動程式簡介Linux
- 乾坤合一:Linux裝置驅動之USB主機和裝置驅動Linux
- 嵌入式Linux驅動筆記(十八)------淺析V4L2框架之ioctlLinux筆記框架
- Linux裝置驅動程式 (轉)Linux
- Linux驅動實踐:如何編寫【 GPIO 】裝置的驅動程式?Linux
- 嵌入式Linux中的LED驅動控制(續)Linux
- 嵌入式Linux中的LED驅動控制(裝置樹方式)(續)Linux
- 驅動Driver-MISC雜項驅動裝置