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

本文详细介绍了在Linux环境下SPI驱动的架构及其实现过程,特别聚焦于RC522 RFID模块的驱动开发。从SPI核心层、设备注册到RC522设备的初始化、通信函数的使用,涵盖了spi_board_info和spi_device结构体解析,以及字符设备的注册与卸载等关键步骤。

一、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
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知否,知否

来一杯冰美式把

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值