Linux IIC 字符设备 驱动例子

 

如果购买了iTOP4412的7寸或10寸屏的(非金属框屏幕)可以用搭载在LVDS上的IIC做测试。

    

查看原理图可得知用于测试的I2C是搭载在I2C_3主线上的。这个后面会用的。

首先我们要将内核里面的触摸屏的驱动关了,来编写我们自己的驱动程序。

menuconfig中去掉触摸的驱动
        Device Drivers  ---> 
        Input device support  --->
        Touchscreens  ---> 
        FT5X0X based touchscreens(去掉)

编译生成zImage文件。烧入进板子。

接下来就是向I2C主线注册设备了。下面贴注册设备的代码。

G
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/slab.h>
 
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("HQU_Orange");
 
#define IIC_Device_Name   "IIC_MDN"
 
static struct i2c_board_info IIC_Device_INFO = 
{
    I2C_BOARD_INFO(IIC_Device_Name, 0x70>>1), /*I2C bus 匹配用的设备名字和设备I2C从机地址*/
};
 
static struct i2c_client *my_i2c_client;
 
static int __init Mini_Linux_Driver_Init(void)
{
    struct i2c_adapter *i2c_adap;
	
    i2c_adap = i2c_get_adapter(3); /* 得到i2c设备器,i2c设备在哪条总线上 I2C3*/
    my_i2c_client = i2c_new_device(i2c_adap, &IIC_Device_INFO);
    i2c_put_adapter(i2c_adap);
    return 0;
}
 
static void __exit Mini_Linux_Driver_Exit(void)
{ 
    i2c_unregister_device(my_i2c_client);
}
 
module_init(Mini_Linux_Driver_Init);
module_exit(Mini_Linux_Driver_Exit);

这就是我们从原理图中找到的iic3。除了这种向iic主线注册设备的方法外还有一种就是直接在平台文件中写入要注册的设备信息

在平台文件中修改

找到注册的iic主线,然后仿照格式在对应的数组里面加上自己的iic设备和地址。

一般情况下就这两种注册iic设备方法最常用。


接下来是如何向iic总线注册iic驱动,直接上代码

MyIIC_Driver.h

#ifndef _MYIIC_DRIVER_H_
#define _MYIIC_DRIVER_H_


#define IIC_Device_Name   		"IIC_MDN"
#define IIC_Driver_Name   		"IIC_No_Match_Driver_Name" 
#define IIC_DeviceNode_Name		"IIC_NodeName"
#define FT5X0X_TP1_EN_Pin	 	EXYNOS4_GPL0(2)
#define FT5X0X_GPX0_3_Pin	 	EXYNOS4_GPX0(3)

#define DEVICE_NUM 1

#define DEV_MAJOR 0

#define DEV_MINOR 0

#ifndef REGDEV_SIZE
#define REGDEV_SIZE 3000
#endif

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

MyIIC_Driver.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.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 "MyIIC_Driver.h"

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



static struct i2c_client *IIC_Client;
static int device_major_num;
static int device_minor_num;
static struct reg_dev *my_devices;
static struct class *myclass;
static const struct i2c_device_id IIC_Device_id[] = 
{
	{ IIC_Device_Name, 0 },
};


int IIC_Driver_probe(struct i2c_client *client,const struct i2c_device_id *id);
int IIC_Driver_remove(struct i2c_client *client);

int IIC_driver_fops_open(struct inode* p_inode,struct file* p_file);
int IIC_driver_fops_release(struct inode* p_inode,struct file* p_file);
ssize_t IIC_driver_fops_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos);
ssize_t IIC_driver_fops_write(struct file *filp,const char __user *buffer, size_t count, loff_t *ppos);


static struct i2c_driver IIC_Driver = 
{
	.driver = 
	{
	.name	= IIC_Driver_Name,
    .owner = THIS_MODULE,
	},
	.probe		= IIC_Driver_probe,
	.remove		= IIC_Driver_remove,
	.id_table	= IIC_Device_id,
};
 
static struct file_operations IIC_driver_fops = 
{
	.owner 	= THIS_MODULE,
	.open 	= IIC_driver_fops_open,
	.release= IIC_driver_fops_release,
	.write  = IIC_driver_fops_write,
	.read 	= IIC_driver_fops_read,
}; 
 
int IIC_Driver_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
	int res;
	int i;
	dev_t device_ID;	
	
    IIC_Client = client;    
	printk(KERN_EMERG"The client name is  %s  ,The device name is %s  \r\n",client->name,id->name);
	res=alloc_chrdev_region(&device_ID,device_minor_num,DEVICE_NUM,IIC_Device_Name);	//动态获得主设备号
	if(res<0) 
	{
		printk(KERN_EMERG "%s device major num get fail !\r\n",IIC_Device_Name);
		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",IIC_Device_Name,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,IIC_Device_Name);	//创建设备类
	
	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,&IIC_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 ",IIC_Device_Name,i);
		else 	  printk(KERN_EMERG "char device %s %d add succees \r\n ",IIC_Device_Name,i);
		device_create(myclass,NULL,MKDEV(device_major_num,device_minor_num+i),NULL,IIC_DeviceNode_Name"%d",i); //创建设备节点
	}		
    return 0;
}
 
int IIC_Driver_remove(struct i2c_client *client)
{
	printk(KERN_EMERG "char device %s remove \r\n ",client->name);
    return 0;
}
 
int IIC_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 IIC_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 IIC_driver_fops_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos)
{
	int ret;
	u8 reg_data;
	struct i2c_msg msgs[2];

	ret = copy_from_user(&reg_data,buffer,1);

	msgs[0].addr	= IIC_Client->addr;	//0x38
	msgs[0].flags	= 0;	//写
	msgs[0].len	= 1;	//要写的数据的长度
	msgs[0].buf	= &reg_data;

	msgs[1].addr	= IIC_Client->addr;	//0x38
	msgs[1].flags	= I2C_M_RD;	//读
	msgs[1].len	= 1;	//要写的数据的长度
	msgs[1].buf	= &reg_data;
	ret = i2c_transfer(IIC_Client->adapter, msgs, 2);
	if (ret < 0) {
		pr_err("read reg error! ret is %d \n" ,ret);
	}
	ret = copy_to_user(buffer,&reg_data,1);
	
	return ret;	
	
}


ssize_t IIC_driver_fops_write(struct file *filp, const char __user *buffer, size_t count, loff_t *ppos)
{
	int ret;
	u8 buf[2];
	struct i2c_msg msgs[1];
	
	ret = copy_from_user(&buf,buffer,2);
	
	msgs[0].addr	= IIC_Client->addr;	//0x38
	msgs[0].flags	= 0;	//写
	msgs[0].len	= 2;	//第一个是要写的寄存器地址,第二个是要写的内容
	msgs[0].buf	= buf;

	ret = i2c_transfer(IIC_Client->adapter, msgs, 1);
	if (ret < 0) 
	{
		pr_err("IIC write reg 0x%02x error! ret is  %d \n",buf[0],ret);
	}
	ret = copy_to_user(IIC_Client,buf,1);
	
	return ret;
}	
 
 
 
int FT5X0X_GPIO_Init(void) //FT5X0X芯片工作使能引脚
{
	int ret;
	ret = gpio_request(FT5X0X_TP1_EN_Pin, "TP1_EN");
	if (ret) 
	{
		printk(KERN_ERR "failed to request TP1_EN \n\r");
		return ret;
	}
	gpio_direction_output(FT5X0X_TP1_EN_Pin, 1);
	s3c_gpio_cfgpin(FT5X0X_TP1_EN_Pin, S3C_GPIO_OUTPUT);
	gpio_free(FT5X0X_TP1_EN_Pin);
	mdelay(5);
	
	ret = gpio_request(FT5X0X_GPX0_3_Pin, "GPX0_3");
	if (ret) 
	{
		gpio_free(FT5X0X_GPX0_3_Pin);
		ret = gpio_request(FT5X0X_GPX0_3_Pin, "GPX0_3");
		if(ret)
		{
		printk(KERN_ERR "failed to request GPX0_3 \n\r");
		return ret;
		}
	}
	gpio_direction_output(FT5X0X_GPX0_3_Pin, 0);
	mdelay(200);
	gpio_direction_output(FT5X0X_GPX0_3_Pin, 1);
	s3c_gpio_cfgpin(FT5X0X_GPX0_3_Pin, S3C_GPIO_OUTPUT);
	gpio_free(FT5X0X_GPX0_3_Pin);
	msleep(300);	
	
	return 0;
}
 
static int __init Mini_Linux_Driver_Init(void)
{
	if(FT5X0X_GPIO_Init()==0)
	i2c_add_driver(&IIC_Driver);	//注册IIC驱动
	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); 
	i2c_del_driver(&IIC_Driver);	//删除IIC驱动
}
 
module_param(device_minor_num,int,S_IRUSR);	 //输入次设备号 
module_init(Mini_Linux_Driver_Init);
module_exit(Mini_Linux_Driver_Exit);
 

第一个务必确保和之前的写的iic设备名字一致,这样才能识别出来,并且匹配,后面的驱动名字和设备节点名字可以随意

调用i2c_add_driver()函数就可以直接将驱动注册到iic总线上了。

从probe其实就是注册设备了,前面我的博客都讲了如何注册字符设备和杂项设备,如果不理解的朋友可以参考一下。

接下来的重点就是如何IIC发送和接收数据

从指定寄存器中接收一个字节的数据:

往指定寄存器中写入一个字节的数据:

如果IIC从机设备是16位的,这里的读写程序都要做相对应的修改。

i2c_transfer()和i2c_msg是发送和接收的重点。


由于我买的是金属框屏幕,故无法做实验了,但是前面注册驱动设备的部分可以演示一下。

加载两个模块。

可以看到iic总线下面的 3- 0038 即 iic3 连接着 从机地址为 0x38 的设备

这个地址就是我们上面在注册设备时设置的地址

我们可以看看它的名字

IIC_MDN也是我们设置的设备名字,说明这个设备确实注册到了iic总线上了

接下来再看看我们的驱动注册没有

前面加载模块提示的信息也说明probe程序已经自动运行了,那么驱动也是成功注册到iic主线上了

最后看看设备节点是否生成

成功生成了,说明驱动和设备都正常了。最后就是测试是否能通信了,但是我没有购买7、10寸的屏幕,故无法演示了,但是

一般是没有什么大问题的。


总结一下:

往IIC总线上注册设备

    i2c_adap = i2c_get_adapter(3); /* 得到i2c设备器,i2c设备在哪条总线上 I2C3*/
    my_i2c_client = i2c_new_device(i2c_adap, &IIC_Device_INFO);
    i2c_put_adapter(i2c_adap);

往IIC总线上注册驱动

	i2c_add_driver(&IIC_Driver);	//注册IIC驱动

在probe中注册为具体的字符设备或是杂项设备

int IIC_Driver_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
	int res;
	int i;
	dev_t device_ID;	
	
    IIC_Client = client;    
	printk(KERN_EMERG"The client name is  %s  ,The device name is %s  \r\n",client->name,id->name);
     ..........
    //注册为具体设备 

}

使用i2c_transfer()接收和发送数据

	msgs[0].addr	= IIC_Client->addr;	//从机地址
	msgs[0].flags	= 0;	//0写1读
	msgs[0].len	= 2;	//发送的字节长度
	msgs[0].buf	= buf;    //发送的数据

	ret = i2c_transfer(IIC_Client->adapter, msgs, 1); //发送一个msg

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值