十九、I2C驱动及应用

一、概述

1、Linux主机驱动和外设驱动分离思想

        外设驱动→API→主机驱动→板级逻辑--具体的i2c设备(camera,ts,eeprom等等)
        主机驱动:根据控制器硬件手册,操作具体的寄存器,产生波形。(单片机工程师肯定有强烈的冲动去配置i2c寄存器,产生波形!)。
        linux应用工程师:屏蔽了驱动和硬件。
        linux驱动工程师:屏蔽硬件!提供标准的主机驱动,驱动工程师需要完成“外设驱动”
       内核函数接口:(API)。主机驱动提供给外设驱动的函数接口。
       注册i2c设备:i2c_board_info
       驱动注册和卸载函数以及结构体:i2c_del_driver/i2c_add_driver,i2c_driver
       读写函数和结构体:i2c_transfer,i2c_msg
       这几个函数是放之四海而皆准!
       外设驱动:针对具体的外部器件的代码。
       摄像头以及声卡中i2c用来配置外部设备(声卡和摄像头)→地址和配置的内容都不一样!
板级逻辑:描述主机和外部设备是怎么连接的。

教程中介绍:I2C函数接口(API):
    设备注册:i2c_board_info
    驱动注册函数和结构体:i2c_del_driver/i2c_add_driver,i2c_driver
    读写函数和结构体:i2c_transfer,i2c_msg 

2.设备-i2c设备注册以及设备注册之后的查询方法

        查询i2c设备地址:

ls /sys/bus/i2c/devices/

        怎么和原理图以及外部设备对应:3-0038→I2C_3_SCL(addr:datasheet中查0x38)
        查询i2c设备名称:

cat /sys/bus/i2c/devices/3-0038/name

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

        在arch\arm\mach-exynos\mach-itop4412.c
        添加i2c设备:i2c_devs3[]中添加
        {
                I2C_BOARD_INFO("i2c_test", 0x70>>1),
        },
       cat /sys/bus/i2c/devices/3-0038/name结果是i2c_test    

3.驱动-i2c驱动注册和卸载。

        i2c设备驱动初始化完成-进入probe函数。

        i2c_del_driver/i2c_add_driver:i2c_driver

        module_initlate_initcall:module_init先运行,late_initcall后运行

4.驱动-i2c数据的传输

        i2c_transfer,i2c_msg

struct i2c_msg {
    __u16 addr;                       /* slave address  */
    __u16 flags;
    #define I2C_M_RD        0x0001    /* read data, from slave to master */
    __u16 len;                        /* msg length                */
    __u8  *buf;                       /* pointer to msg data            */
};

        要完成i2c的读,必须要先写再读!写的时候,你要通知从机,你要读哪个寄存器!

二、驱动代码

1、late_initcall(i2c_test_init);

模块延后执行i2c_test_init,在函数中调用i2c_add_driver(&i2c_test_driver);

2、i2c_add_driver(&i2c_test_driver);

将i2c_test_driver驱动添加到I2C总线上。开始调用i2c_test_driver中的i2c_test_probe。

3、i2c_test_probe

匹配我们前面注册的设备:i2c_test,i2c_test_driver驱动开始运行,

4、misc_register(&i2c_dev);

创建成杂项设备。设备名"i2c_control", // 可以在/dev目录下看到

5、i2c_ops:

定义设备操作函数

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
#include <linux/regulator/consumer.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <asm/uaccess.h> 
#include <linux/miscdevice.h>

static struct i2c_client *this_client;

static int i2c_tes_read_reg(struct i2c_client *client,u8 addr, u8 *pdata) {
	u8 buf1[4] = { 0 };
	u8 buf2[4] = { 0 };
	struct i2c_msg msgs[] = {
		{
			.addr	= client->addr,	//0x38
			.flags	= 0,	//写
			.len	= 1,	//要写的数据的长度
			.buf	= buf1,
		},
		{
			.addr	= client->addr,
			.flags	= I2C_M_RD,
			.len	= 1,
			.buf	= buf2,
		},
	};
	int ret;
	buf1[0] = addr;
	ret = i2c_transfer(client->adapter, msgs, 2);
	if (ret < 0) {
		pr_err("read reg (0x%02x) error, %d\n", addr, ret);
	} else {
		*pdata = buf2[0];
	}
	return ret;
}
static int i2c_tes_read_fw_reg(struct i2c_client *client,unsigned char *val)
{
	int ret;
	*val = 0xff;
	ret = i2c_tes_read_reg(client,0xa6, val);
	printk("ts reg 0xa6 val is %d\n",*val);
	return ret;
}
static int i2c_open_func(struct file *filp)
{
	return 0;
}

static int i2c_release_func(struct file *filp)
{
	return 0;
}

static ssize_t i2c_read_func(struct file *filp, char __user *buffer, size_t count, loff_t *ppos){
	int ret;
	u8 reg_data;
	
	ret = copy_from_user(&reg_data,buffer,1);
	
	struct i2c_msg msgs[] = {
		{
			.addr	= this_client->addr,	//0x38
			.flags	= 0,	//写
			.len	= 1,	//要写的数据的长度
			.buf	= &reg_data,
		},
		{
			.addr	= this_client->addr,
			.flags	= I2C_M_RD,
			.len	= 1,
			.buf	= &reg_data,
		},
	};
	ret = i2c_transfer(this_client->adapter, msgs, 2);
	if (ret < 0) {
		pr_err("read reg error!\n");
	}
	ret = copy_to_user(buffer,&reg_data,1);
	
	return ret;
}

static ssize_t i2c_write_func(struct file *filp, 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	= this_client->addr;	//0x38
	msgs[0].flags	= 0;	//写
	msgs[0].len	= 2;	//第一个是要写的寄存器地址,第二个是要写的内容
	msgs[0].buf	= buf;

	ret = i2c_transfer(this_client->adapter, msgs, 1);
	if (ret < 0) {
		pr_err("write reg 0x%02x error!\n",buf[0]);
	}
	ret = copy_to_user(buffer,buf,1);
	
	return ret;
}


static struct file_operations i2c_ops = {
	.owner 	= THIS_MODULE,
	.open 	= i2c_open_func,
	.release= i2c_release_func,
	.write  = i2c_write_func,
	.read 	= i2c_read_func,
};


static struct miscdevice i2c_dev = {
	.minor	= MISC_DYNAMIC_MINOR,
	.fops	= &i2c_ops,
	.name	= "i2c_control", // 可以在/dev目录下看到
};

static int i2c_test_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	unsigned char val;
	printk("==%s:\n", __FUNCTION__);
	
	i2c_tes_read_fw_reg(client,&val);
	
	this_client = client;
	
	misc_register(&i2c_dev);
	
	return 0;
}

static int __devexit i2c_test_remove(struct i2c_client *client)
{
	i2c_set_clientdata(client, NULL);
	misc_deregister(&i2c_dev);
	printk("==%s:\n", __FUNCTION__);
	return 0;
}

static const struct i2c_device_id i2c_test_id[] = {
	{ "i2c_test", 0 },
	{ }
};

static struct i2c_driver i2c_test_driver = {
	.probe		= i2c_test_probe,
	.remove		= __devexit_p(i2c_test_remove),
	.id_table	= i2c_test_id,    // 驱动能匹配的设备
	.driver	= {
		.name	= "i2c_test",     // 可以在sys/bus/i2c/drivers/目录下看到驱动名称
		.owner	= THIS_MODULE,
	},
};

static void i2c_io_init(void)
{
	int ret;
	ret = gpio_request(EXYNOS4_GPL0(2), "TP1_EN");
	if (ret) {
		printk(KERN_ERR "failed to request TP1_EN for "
				"I2C control\n");
		//return err;
	}
	gpio_direction_output(EXYNOS4_GPL0(2), 1);
	s3c_gpio_cfgpin(EXYNOS4_GPL0(2), S3C_GPIO_OUTPUT);
	gpio_free(EXYNOS4_GPL0(2));
	mdelay(5);
	
	ret = gpio_request(EXYNOS4_GPX0(3), "GPX0_3");
	if (ret) {
		gpio_free(EXYNOS4_GPX0(3));
		ret = gpio_request(EXYNOS4_GPX0(3), "GPX0_3");
	if(ret)
	{
		printk("ft5xox: Failed to request GPX0_3 \n");
	}
	}
	gpio_direction_output(EXYNOS4_GPX0(3), 0);
	mdelay(200);
	gpio_direction_output(EXYNOS4_GPX0(3), 1);
	s3c_gpio_cfgpin(EXYNOS4_GPX0(3), S3C_GPIO_OUTPUT);
	gpio_free(EXYNOS4_GPX0(3));
	msleep(300);	
}

static int __init i2c_test_init(void)
{
	printk("==%s:\n", __FUNCTION__);
	i2c_io_init();
	printk("==%s:\n", __FUNCTION__);
	return i2c_add_driver(&i2c_test_driver);
}
static void __exit i2c_test_exit(void)
{
	printk("==%s:\n", __FUNCTION__);
	i2c_del_driver(&i2c_test_driver);
}

late_initcall(i2c_test_init);
module_exit(i2c_test_exit);

MODULE_AUTHOR("xunwei_rty");
MODULE_DESCRIPTION("TsI2CTest");
MODULE_LICENSE("GPL");

模块运行结果

 三、应用程序代码

#include <stdio.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

int main(int argc,char **argv)
{
	int fd,ret;
	char *i2c_device = "/dev/i2c_control";
	unsigned char buffer[2];
	
	printf("open %s!\n",i2c_device);
	if((fd = open(i2c_device,O_RDWR|O_NDELAY))<0)
		printf("APP open %s failed",i2c_device);
	else{
		printf("APP open %s success!\n",i2c_device);
	}
	
//读一个数据0xa6
	buffer[0] = 0xa6;
	ret = read(fd,buffer,1);
	if(ret<0)
		printf("i2c read failed!\n");
	else{
		printf("i2c read reg 0xa6 data is 0x%02x!\n",buffer[0]);
	}
	
//01先从0x00读出一个数据,02写一个数据到0x00,03再读出来对比
	//01
	buffer[0] = 0x00;
	read(fd,buffer,1);
	printf("i2c read reg 0x00 data is 0x%02x!\n",buffer[0]);
	//02
	buffer[0] = 0x00;
	buffer[1] = 0x40;
	ret = write(fd,buffer,2);
	if(ret<0){
		printf("i2c write failed!\n");
		goto exit;
	}
	//03
	buffer[0] = 0x00;
	read(fd,buffer,1);
	printf("i2c read reg 0x00 data is 0x%02x!\n",buffer[0]);
	
	close(fd);
	
exit:
	close(fd);
	return -1;
}

运行应用程序结果

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Hello Jason

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值