Linux SPI 字符设备 驱动例子

其实,在Linux中,SPI和IIC的注册流程很相似。在这里我还是用iTOP4412做演示。

从原理图可以得知我们要用到的引脚是这几个。用的是SPI_2。记住这个数字,下面设备注册要用到。

首先我们要把iTOP4412中默认关于rfid的驱动代码注释了,这样才能加载我们的驱动代码

打开平台文件

默认是这样的

将它注释了

然后编译,烧进板子上。


首先是注册设备的代码

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>
#include <linux/spi/spi_gpio.h>
#include <linux/gpio.h>
#include <plat/s3c64xx-spi.h>

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("HQU_Orange");
 
#define SPI_Device_Name   	 "SPI_MDN"
#define SPI_Device_Bus_Num   2

static struct s3c64xx_spi_csinfo my_spi2_csi = 
{	 
	.line = EXYNOS4_GPC1(2),
	.set_level = gpio_set_value,
	.fb_delay = 0x2,								
};


static struct spi_board_info SPI_Device_INFO =
{
	.modalias = SPI_Device_Name,	//初始化设备的名称
	.platform_data = NULL,			//平台文件信息
	.max_speed_hz = 10*1000*1000,	//初始化传输速率
	.bus_num = SPI_Device_Bus_Num,	//控制器编号
	.chip_select = 0,				//控制器片选的编号
	.mode = SPI_MODE_0,				//spi的模式
	.controller_data = &my_spi2_csi,//片选IO的信息	
};
static struct spi_device* MySPI_Device; 
  
static int __init Mini_Linux_Driver_Init(void)
{
	struct spi_master* MySPI_Mawster;
	MySPI_Mawster=spi_busnum_to_master(SPI_Device_Bus_Num);
	MySPI_Device=spi_new_device(MySPI_Mawster,&SPI_Device_INFO);
    return 0;
}
 
static void __exit Mini_Linux_Driver_Exit(void)
{ 
    spi_unregister_device(MySPI_Device);
}
 
module_init(Mini_Linux_Driver_Init);
module_exit(Mini_Linux_Driver_Exit);

注意这个片选引脚是上面我们注释了的片选引脚,这里要对应的上。

这里的SPI_Device_Bus_Num就是上面我们在板子上看到的2,表示这个设备搭载在SPI2上。

当然和IIC一样,也同样可以在仿写上面注释的部分注册SPI设备。

下面贴驱动代码

MySPI_driver.h

#ifndef _MYSPI_DRIVER_H_
#define _MYSPI_DRIVER_H_

#define SPI_Device_Name   	 	"SPI_MDN"
#define SPI_DeviceNode_Name		"SPI_NodeName"

#define RC522_RESET_PIN	EXYNOS4_GPK1(0)

#define DEVICE_NUM 1
#define DEV_MAJOR 0
#define DEV_MINOR 0
#define REGDEV_SIZE 3000

struct reg_dev
{
	char *data;
	unsigned long size;
	
	struct cdev cdev;
};
#endif

MySPI_driver.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/fs.h>
#include <linux/moduleparam.h>
#include <linux/stat.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <asm/uaccess.h> 
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/compat.h>

#include "MySPI_driver.h"

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("HQU_Orange"); 



static struct spi_device* MySPI;
static int device_major_num;
static int device_minor_num;
static struct reg_dev *my_devices;
static struct class *myclass;

void rc522_reset(void);

int SPI_Driver_probe(struct spi_device *spi);
int SPI_Driver_remove(struct spi_device *spi);

int SPI_driver_fops_open(struct inode* p_inode,struct file* p_file);
int SPI_driver_fops_release(struct inode* p_inode,struct file* p_file);
ssize_t SPI_driver_fops_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos);
ssize_t SPI_driver_fops_write(struct file *filp,const char __user *buffer, size_t count, loff_t *ppos);


static struct spi_driver SPI_Driver = 
{
	.driver = 
	{
	.name  = SPI_Device_Name,		
    .owner = THIS_MODULE,
	},
	.probe		= SPI_Driver_probe,
	.remove		= SPI_Driver_remove,
};
 
static struct file_operations SPI_driver_fops = 
{
	.owner 	= THIS_MODULE,
	.open 	= SPI_driver_fops_open,
	.release= SPI_driver_fops_release,
	.write  = SPI_driver_fops_write,
	.read 	= SPI_driver_fops_read,
}; 
 
int SPI_Driver_probe(struct spi_device *spi)
{
	int res;
	int i;
	dev_t device_ID;	
	
    MySPI = spi;    
	rc522_reset();
	res=alloc_chrdev_region(&device_ID,device_minor_num,DEVICE_NUM,MySPI->modalias);	//动态获得主设备号
	if(res<0) 
	{
		printk(KERN_EMERG "%s device major num get fail !\r\n",MySPI->modalias);
		return -1;
	}	
	device_major_num = MAJOR(device_ID);
	printk("%s major num get from kernl is %d  And minor num you input is %d \r\n",MySPI->modalias,device_major_num,device_minor_num);
	
	my_devices = kmalloc(DEVICE_NUM * sizeof(struct reg_dev),GFP_KERNEL);
	if(my_devices==NULL)
	{
		unregister_chrdev_region(device_ID,DEVICE_NUM);
		printk(KERN_EMERG "kmalloc is fail!\n");	
		return -1;		
	}
	memset(my_devices,0,DEVICE_NUM * sizeof(struct reg_dev));
	myclass = class_create(THIS_MODULE,MySPI->modalias);	//创建设备类
	
	for(i=0;i<DEVICE_NUM;i++)
	{
		my_devices[i].data = kmalloc(REGDEV_SIZE,GFP_KERNEL);
		memset(my_devices[i].data,0,REGDEV_SIZE);
		cdev_init(&my_devices[i].cdev,&SPI_driver_fops); //初始化设备结构体与文件搭建联系
		res = cdev_add(&my_devices[i].cdev,MKDEV(device_major_num,device_minor_num+i),1); //注册到系统
		if(res<0) printk(KERN_EMERG "char device %s %d add fail	 \r\n ",MySPI->modalias,i);
		else 	  printk(KERN_EMERG "char device %s %d add succees \r\n ",MySPI->modalias,i);
		device_create(myclass,NULL,MKDEV(device_major_num,device_minor_num+i),NULL,SPI_DeviceNode_Name"%d",i); //创建设备节点
	}		
    return 0;
}
 
int SPI_Driver_remove(struct spi_device *spi)
{
	printk(KERN_EMERG "char device %s remove \r\n ",spi->modalias);
    return 0;
}
 
 
int SPI_driver_fops_open(struct inode* p_inode,struct file* p_file)
{
	printk(KERN_EMERG "%s open \r\n ",p_file->f_path.dentry->d_iname);
	return 0;	
}

int SPI_driver_fops_release(struct inode* p_inode,struct file* p_filp)
{
	printk(KERN_EMERG "%s close \r\n ",p_filp->f_path.dentry->d_iname);
	return 0;	
}


ssize_t SPI_driver_fops_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos)
{
	char* value;
	int status;
	value=kmalloc(count*sizeof(char),GFP_KERNEL);
	status=spi_read(MySPI,value,count);
	status=copy_to_user(buffer,value,count);
	kfree(value);
	return status;
}


ssize_t SPI_driver_fops_write(struct file *filp, const char __user *buffer, size_t count, loff_t *ppos)
{
	char* value;
	int status;
	value=kmalloc(count*sizeof(char),GFP_KERNEL);
	status = copy_from_user(value,buffer,count);	
	status=spi_write(MySPI,value,count);
	kfree(value);
	return status;
}	
 
 
void rc522_reset(void)
{
	if(gpio_request_one(RC522_RESET_PIN, GPIOF_OUT_INIT_HIGH, "RC522_RESET"))
        pr_err("failed to request GPK1_0 for RC522 reset control\n");

	s3c_gpio_setpull(RC522_RESET_PIN, S3C_GPIO_PULL_UP);
	gpio_set_value(RC522_RESET_PIN, 0);

	mdelay(5);

	gpio_set_value(RC522_RESET_PIN, 1);
	gpio_free(RC522_RESET_PIN);
}

static int __init Mini_Linux_Driver_Init(void)
{
	spi_register_driver(&SPI_Driver);	//注册SPI驱动
	return 0;
}
 
static void __exit Mini_Linux_Driver_Exit(void)
{
 	int i;

	for(i=0;i<DEVICE_NUM;i++)
	{
		cdev_del(&(my_devices[i].cdev));
		device_destroy(myclass,MKDEV(device_major_num,device_minor_num+i));
		kfree(my_devices[i].data);
	}
	class_destroy(myclass);
	kfree(my_devices);
		
	unregister_chrdev_region(MKDEV(device_major_num,device_minor_num),DEVICE_NUM); 
	spi_unregister_driver(&SPI_Driver);	//删除SPI驱动
}
 
module_param(device_minor_num,int,S_IRUSR);	 //输入次设备号 
module_init(Mini_Linux_Driver_Init);
module_exit(Mini_Linux_Driver_Exit);
 

同样要注意在MySPI_driver.h中的SPI_Devcie_Name 要和前面注册的设备名字一致,这样才能匹配的上。

利用 spi_register_driver() 注册spi驱动,匹配成功后,执行prob

在probe中注册为具体设备

这是SPI的read和write的简易代码,请根据需要修改。


接下来进行实验

加载驱动模块

可以看到生成了在spi2.0这个主线的设备:SPI_MDN

加载驱动模块

成功生成了SPI_MDN驱动。

成功生成了SPI_NodeName0这个设备节点。

说明上面的设备和驱动都工作正常

成功卸载模块。


总结:

注册设备:

MySPI_Mawster=spi_busnum_to_master(SPI_Device_Bus_Num);     //获得SPI控制器
MySPI_Device=spi_new_device(MySPI_Mawster,&SPI_Device_INFO);//将设备相关信息注册到控制器上

注册驱动:

spi_register_driver(&SPI_Driver);   //注册SPI驱动

读写:

spi_write(MySPI,value,count); //写

spi_read(MySPI,value,count);   //读

 

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SPI(Serial Peripheral Interface)是一种常用的串行通信协议,用于在微控制器、传感器和外部设备之间传输数据。在Linux内核中,可以通过字符设备驱动来实现对SPI总线的访问和操作。 字符设备驱动是一种在Linux内核中用于管理字符设备的模块,它允许用户空间程序通过文件I/O操作来访问和控制字符设备。对于SPI设备字符设备驱动通常提供了一组系统调用接口,以便用户程序可以通过打开、读取、写入和关闭设备文件来进行SPI通信。 要实现一个SPI字符设备驱动,一般需要完成以下几个步骤: 1. 注册字符设备: 在内核模块初始化的时候,使用`register_chrdev()`函数注册一个字符设备,获得一个主设备号。 2. 创建设备文件: 使用`class_create()`和`device_create()`函数创建设备类和设备文件。这样,用户空间程序就可以通过访问设备文件来进行SPI通信。 3. 实现系统调用接口: 实现`open()`、`read()`、`write()`、`release()`等系统调用接口,用于打开、读取、写入和关闭设备文件。在这些接口中,可以调用SPI总线相关的函数来实现SPI通信。 4. 初始化SPI总线: 在驱动的初始化函数中,使用`spi_register_driver()`函数注册SPI驱动,并设置相应的配置参数,如SPI设备的模式、速率等。 5. 实现数据传输: 在系统调用接口中,可以使用`spi_sync()`函数进行数据传输。该函数将发送和接收缓冲区的地址以及数据长度作为参数,通过SPI总线进行数据传输。 需要注意的是,SPI字符设备驱动的具体实现可能因不同硬件平台而异,你需要根据自己的硬件和驱动需求进行相应的开发和配置。同时,建议参考Linux内核文档和相关的开发文档来了解更多详细信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值