锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

firefly-rk3288j开发板--linux NFC实验之RC522驱动

时间:2023-11-23 05:07:01 w25传感器

firefly-rk3288j开发板–linux NFC实验之RC522驱动

1 准备工作

开发板:aio-rk3288j
SDK版本:rk3288_linux_release_20210304
下载工具:Linux_Upgrade_Tool_v2.1
内核版本:4.4.194
文件系统:buildroot
Ubuntu版本:18.04
交叉编译工具:gcc version 6.3.1 20170404

2 硬件原理图

2.1 开发板SPI接口

在这里插入图片描述

2.2 RC522模块原理图

3 SPI使用

SPI 是一种高速的,全双工,同步串行通信接口,用于连接微控制器、传感器、存储设备等,本文以W25Q例如,简要介绍64模块 SPI 使用。
SPI 这种模式通常有一个主设备和一个或多个从设备,至少需要 4 根线分别为:
CS 片选信号
SCLK 时钟信号
MOSI 主设备数据输出、从设备数据输入
MISO 输入主设备数据,输出设备数据

Linux 内核用 CPOL 和 CPHA 组合表示当前 SPI 四种工作模式:
CPOL=0,CPHA=0 SPI_MODE_0
CPOL=0,CPHA=1 SPI_MODE_1
CPOL=1,CPHA=0 SPI_MODE_2
CPOL=1,CPHA=1 SPI_MODE_3

SPI的CPOL,表示当SCLK空闲idle电平值为低电平0或高电平1:CPOL=0,时钟空闲idle当时的电平是低电平,所以当SCLK有效时,即高电平,即所谓active-high。
CPOL=1,时钟空闲idle时候的电平是高电平,所以当SCLK有效时,即低电平,即所谓active-low。

CPHA=0,表示第一个边缘:
对于CPOL=0,idle时候的是低电平,第一个边沿就是从低变到高,所以是上升沿;
对于CPOL=1,idle当时是高电平,第一个边缘是从高到低,所以是下降边缘;
CPHA=1.表示第二个边缘:
对于CPOL=0,idle当时是低电平,第二个边缘是从高到低,所以是下降边缘;
对于CPOL=1,idle当时是高电平,第一边是从低到高,所以是上升边;

4 API函数

spi_alloc_master 函数用于申请 spi_master
struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
注册函数
int spi_register_master(struct spi_master *master)
注销函数
void spi_unregister_master(struct spi_master *master)

5 DTS配置

设备树文件位于核心kernel/arch/arm/boot/dts目录下,我们需要打开rk3288.dtsi、rk3288-linux.dtsi、rk3288-firefly-port.dtsi、rk3288-firefly-aio.dtsi.d打开rk3288-firefly-aio.dtsi文件,添加spi设备节点:

&spi2 { 
          status = "okay";  rc522: rc522@0{ 
           compatible = "firefly,rc522";   spi-max-frequency = <8000000>;   reg = <0>;   rst-gpio = <&gpio7 2 GPIO_ACTIVE_HIGH>;    pinctrl-names = "default";   pinctrl-0 = <&rc522_rst>;   //spi-cpha;   //spi-cpol;  }; }; &pinctrl { 
          rc522 { 
           rc522_rst: rc522-rst { 
            rockchip,pins = <7 2 RK_FUNC_GPIO &pcfg_pull_up>;   };
	};
}

编译内核,输入如下命令
./build.sh kernel
./build.sh updateimg

6 RC522驱动编写

6.1 驱动文件

#include //模块加载卸载函数
#include //内核头文件
#include //数据类型定义
#include //file_operations结构体
#include //class_create等函数
#include 
#include /*包含printk等操作函数*/
#include /*设备树操作相关的函数*/
#include /*gpio接口函数*/
#include 
#include /*platform device*/
#include  /*spi相关api*/
#include  /*内核延时函数*/
#include  /*kmalloc、kfree函数*/
#include /*cdev_init cdev_add等函数*/
#include /*gpio接口函数*/
#include /*__copy_from_user 接口函数*/
#include "rc522.h"

#define DEVICE_NAME "nfc" 
 

typedef struct
{ 
        
      struct device_node *node;//设备树节点
      struct cdev cdev;       //定义一个cdev结构体
      struct class *class;    //创建一个rc522类
      struct device *device;  //创建一个rc522设备 该设备是需要挂在rc522类下面的
      int major;              //主设备号
      dev_t  dev_id;
      struct spi_device *spi; /*spi设备*/
// int cspin; /*片选脚*/
      int rstpin;
      struct mutex lock;
      void *private_data;
}rc522_typdef;

static rc522_typdef rc522_dev;//定义一个rc522设备

void spi_rst_enable(void)
{ 
        
    gpio_set_value(rc522_dev.rstpin, 0); 
}

void spi_rst_disable(void)
{ 
        
   gpio_set_value(rc522_dev.rstpin, 1); 
}

void spi_cs_enable(void)
{ 
        
    //gpio_set_value(rc522_dev.cspin, 1);
}

void spi_cs_disable(void)
{ 
        
    //gpio_set_value(rc522_dev.cspin, 0);
}

static int rc522_read_regs(rc522_typdef *dev, unsigned char reg, unsigned char *dat, unsigned char len)
{ 
        
    int ret = -1;
    unsigned char txdata[len];
    unsigned char * rxdata;
    struct spi_message m;
    struct spi_transfer *t;
    struct spi_device *spi = dev->spi;
    
    t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);   /* 申请内存 */
    if(!t) { 
        
        return -ENOMEM;
    }

    rxdata = kzalloc((sizeof(char) * len), GFP_KERNEL); /* 申请内存 */
    if(!rxdata) { 
        
        goto out1;
    }
    spi_cs_enable();
    /* 一共发送len+1个字节的数据,第一个字节为 寄存器首地址,一共要读取len个字节长度的数据,*/
    txdata[0] = ((reg << 1) & 0x7e) | 0x80;             
    t->tx_buf = txdata;         /* 要发送的数据 */
    t->rx_buf = rxdata;         /* 要读取的数据 */
    t->len = len + 1;           /* t->len=发送的长度+读取的长度 */
    spi_message_init(&m);       /* 初始化spi_message */
    spi_message_add_tail(t, &m);/* 将spi_transfer添加到spi_message队列 */
    ret = spi_sync(spi, &m);    /* 同步发送 */
    if(ret) { 
        
        goto out2;
    }
    /* 只需要读取的数据 */
    memcpy(dat , rxdata + 1, len); /* 只需要读取的数据 */

out2:
    kfree(rxdata);                  /* 释放内存 */
out1:   
    kfree(t);                       /* 释放内存 */
    spi_cs_disable();
    return ret;
}

static int rc522_write_regs(rc522_typdef *dev, unsigned char reg, unsigned char *dat, unsigned char len)
{ 
        
    int ret = -1;
    unsigned char *txdata;
    struct spi_message m;
    struct spi_transfer *t;
    struct spi_device *spi = dev->spi;
    
    t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);   /* 申请内存 */
    if(!t) { 
        
        return -ENOMEM;
    }
    
    txdata = kzalloc(sizeof(char) + len, GFP_KERNEL);
    if(!txdata) { 
        
        goto out1;
    }
    spi_cs_enable();
    /* 一共发送len+1个字节的数据,第一个字节为 寄存器首地址,len为要写入的寄存器的集合,*/
    *txdata = ((reg << 1) & 0x7e);  /* 写数据的时候首寄存器地址bit8要清零 */
    memcpy(txdata + 1, dat, len);

    t->tx_buf = txdata;         /* 要发送的数据 */
    t->len = len + 1;                   /* t->len=发送的长度+读取的长度 */
    spi_message_init(&m);       /* 初始化spi_message */
    spi_message_add_tail(t, &m);/* 将spi_transfer添加到spi_message队列 */
    ret = spi_sync(spi, &m);    /* 同步发送 */
    if(ret) { 
        
        goto out2;
    }
    
out2:
    kfree(txdata);      /* 释放内存 */
out1:
    kfree(t);                   /* 释放内存 */
    spi_cs_disable();
    return ret; 
}

static unsigned char read_one_reg(rc522_typdef *dev, unsigned char reg)
{ 
        
    unsigned char data = 0;

    rc522_read_regs(dev, reg, &data, 1);

    return data;    
}

static void write_one_reg(rc522_typdef *dev,unsigned char reg, unsigned char value)
{ 
        
    rc522_write_regs(dev, reg, &value, 1);
}

static int rc522_open(struct inode *inode, struct file *filp)
{ 
        
    filp->private_data = &rc522_dev;
   
    spi_rst_disable();
    udelay(10);
    spi_rst_enable();
    udelay(10);
    spi_rst_disable();

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

static int rc522_release(struct inode* inode ,struct file *filp)
{ 
        
    spi_rst_enable();
    gpio_free(rc522_dev.rstpin);
    printk("rc522_release ok!\n");   
    return 0;
}

// loff_t rc522_llseek(struct file *file, loff_t offset, int whence)
// { 
        
// return 0;
// }

static int rc522_write(struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
{ 
        
    unsigned char *write_buf;/*数据缓冲区*/
    int ret;

    write_buf = (unsigned char*)kzalloc(count, GFP_KERNEL);
    if(!write_buf )
       return -ENOMEM;

    ret = copy_from_user(write_buf, buf, count);
    if (ret < 0)
    { 
        
        kfree(write_buf);
        printk("copy from user failed!\r\n");
        return ret;
    }

    write_one_reg(&rc522_dev, write_buf[0], write_buf[1]);

    return 0;
}

static ssize_t rc522_read(struct file *filp,char __user *buf, size_t count,loff_t *f_pos)
{ 
        
    int ret; 
    unsigned char adr,value; 

    ret = copy_from_user(&adr, buf, 1);
    if(ret < 0)
    { 
        
        printk("copy from user failed!\r\n");
        return ret;
    }
    value = read_one_reg(&rc522_dev, adr);
    ret = copy_to_user(buf, &value, count);
    if (ret < 0)
    { 
        
        printk("copy to user failed!\r\n");
        return ret;
    }

    return ret;
}

static struct file_operations rc522_fops={ 
        
    .owner      = THIS_MODULE,
    .open       = rc522_open,
    .write      = rc522_write,
    .read       = rc522_read,
    .release    = rc522_release,
    // .llseek = rc522_llseek,
};
        
static int rc522_probe(struct spi_device *spi)
{ 
        
    int ret;
    const char *string = NULL;
    
    printk("rc522 probe!\n"); 

   /*获取设备节点*/
   rc522_dev.node = of_find_node_by_path("/spi@ff130000/rc522@0");
   if(rc522_dev.node == NULL)
   { 
        
      printk("device-tree:not found rc522!\r\n"); 
      return -1;
   }

   /*读取rc522设备节点的compatible属性值*/
   ret = of_property_read_string(rc522_dev.node, "compatible", &string);
   if(ret == 0)
   { 
        
       printk("%s\n",string);
   }   

   rc522_dev.rstpin = of_get_named_gpio(rc522_dev.node,"rst-gpio",0);
   if(!gpio_is_valid(rc522_dev.rstpin))
   { 
        
        printk("get gpio error\n");
        ret = -EINVAL;
        return ret;
   }
   
   printk("gpio = %d\n",rc522_dev.rstpin);

   ret = gpio_request(rc522_dev.rstpin,"spi-rst");
   if(ret < 0) 
   { 
        
      printk("gpio_request %d failed\n", rc522_dev.rstpin);
      return ret;
   }
   gpio_direction_output(rc522_dev.rstpin, 1);

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

   /*初始化一个cdev*/
   cdev_init(&rc522_dev.cdev, &rc522_fops);

   /*向cdev中添加一个设备*/
   ret = cdev_add(&rc522_dev.cdev, rc522_dev.dev_id, 1);
   if(ret != 0)
   { 
        
        printk("cdev add error %d \n",ret);
        // goto Error;
   }

   /*创建一个nfc_class类*/
   rc522_dev.class = class_create(THIS_MODULE, "nfc_class");
   if(rc522_dev.class == NULL)
   { 
        
      printk("class_create failed\r\n");
      return -1;
   }
   /*在nfc_class类下创建一个NFC_class设备*/
   rc522_dev.device = device_create(rc522_dev.class, NULL, rc522_dev.dev_id, NULL, DEVICE_NAME);   

   /*获取与本驱动匹配的spi设备*/
   rc522_dev.spi = spi;
   spi_setup(rc522_dev.spi);   
   
// Error:
// cdev_del(&rc522_dev.cdev);
// unregister_chrdev_region(rc522_dev.dev_id, 1);
    return 0;
}

static int rc522_remove(struct spi_device *spi)
{ 
        
    printk("w25qxx remove!\n"); 

    /*删除rc522类*/
    cdev_del(&rc522_dev.cdev);

    /*释放rc522设备号*/
    unregister_chrdev_region(rc522_dev.dev_id, 1);

    /*注销rc522设备*/
    device_destroy(rc522_dev.class, rc522_dev.dev_id);

    /*注销rc522类*/
    class_destroy(rc522_dev.class);

    gpio_free(rc522_dev.rstpin);

    return 0;   
}

static const struct of_device_id rc522_of_match[] = { 
        
   { 
        .compatible = "firefly,rc522"},
   { 
        },
};

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

static struct spi_driver rc522_driver = { 
        
    .driver = { 
        
        .owner = THIS_MODULE,       
        .name =  "rc522",
        .of_match_table = rc522_of_match,
    },
    .probe = rc522_probe,
    .remove = rc522_remove,
    .id_table = rc522_id,
};

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);
   }
   else
   { 
        
        printk("module init ok\n");
   }
   return ret;
}

static void rc522_exit(void)
{ 
         

相关文章