I2C驱动程序

9 篇文章 1 订阅
4 篇文章 0 订阅

I2C驱动程序框架

       I2C的协议流程在I2C协议原理简述一文中写过,这里就不再讲解。为了更好的书写I2C客户驱动程序,我们先理一理I2C驱动程序的框架,才能更好的写出属于我们自己的驱动程序。

       上图中的I2C总线驱动程序(也就是I2C适配器驱动程序)是芯片商为我们实现的,知道如何收发数据;而需要我们自己实现的I2C客户驱动程序,则知道数据的具体含义。

        那么我们书写的客户驱动程序什么时候会被调用呢?这个问题要先从分析I2C适配器驱动程序开始讲起。下面给出一个分析流程:

结合linux-2.6.22.6\drivers\i2c\busses\i2c-s3c2410.c,分析一下由芯片商实现的adapter的驱动程序
i2c_adap_s3c_init
	ret = platform_driver_register(&s3c2440_i2c_driver);
		s3c24xx_i2c_probe
			clk_enable(i2c->clk); //使能I2C
			ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED, pdev->name, i2c); //注册中断
			ret = i2c_add_adapter(&i2c->adap); //添加adapter结构体
				i2c_register_adapter(adapter);
					/* let legacy drivers scan this bus for matching devices */
					list_for_each(item,&drivers) {
						driver = list_entry(item, struct i2c_driver, list);
						if (driver->attach_adapter)
						/* We ignore the return code; if it fails, too bad */
						driver->attach_adapter(adap); //这个driver就是我们I2C客户驱动程序注册时,挂载在drivers链表上的
					}

我们可以看到,最后会调用I2C客户驱动程序的driver->attach_adapter函数,而这个driver就是在初始化是就注册进系统的。这样我们就知道了什么时候会调用我们书写的I2C驱动程序了,那下面讲讲怎么写我们自己的I2C客户驱动程序。

I2C客户驱动程序示例

先给出一个已经写好的I2C驱动程序:

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/fs.h>


static unsigned short ignore[] = { I2C_CLIENT_END }; /*顾名思义就是忽略的意思*/
static unsigned short normal_addr[] = { 0x50, I2C_CLIENT_END }; /*地址值为7位:我们的I2C设备地址为0x50*/

static unsigned short force_addr[] = {ANY_I2C_BUS, 0x60, I2C_CLIENT_END};
static unsigned short *forces[]  = {force_addr, NULL};

static struct i2c_client_address_data addr_data = {
	.normal_i2c	= ignore, /*要发出start信号和设备地址,并且收到ACK信号后,才能确定存在这个设备*/
	.probe		= ignore,
	.ignore		= ignore,
	.forces     = forces, /*强制认为存在设备,不用通过发送start信号和设备地址来确认*/
};

static struct i2c_driver at24cxx_driver;
struct i2c_client *at24cxx_client;
static struct class *at24cxx_class;
static struct class_device *at24cxx_class_dev;
static int major = 0;

static ssize_t at24cxx_read (struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
	int ret = 0;
	unsigned char address;
	unsigned char data;
	struct i2c_msg msg[2];

	if (size != 1)
		return -EINVAL;

	copy_from_user(&address, buf, 1);

	/*数据传输3要素:源,目的,长度*/

	/*在读取AT24CXX的数据前,需要将存储数据的空间的地址告诉它*/
	msg[0].addr  = at24cxx_client->addr;  /*目的*/
	msg[0].buf	 = &address;			  /*源*/
	msg[0].len	 = 1;					  /*地址 = 1bytes*/
	msg[0].flags = 0;					  /*写*/

	/*然后启动读操作*/
	msg[1].addr  = at24cxx_client->addr;  /*源*/
	msg[1].buf	 = &data;			   /*目的*/
	msg[1].len	 = 1;					  /*地址 = 1bytes*/
	msg[1].flags = I2C_M_RD;			  /*读*/
	
	ret = i2c_transfer(at24cxx_client->adapter, msg, 2);
	if (2 == ret) {
		copy_to_user(buf, &data, 1);
		return 1;
	}
	else
		return -EIO;
}


static ssize_t at24cxx_write (struct file *file, const char __user *buf, size_t size, loff_t *ppos)
{
	int ret = 0;
	unsigned char val[2];
	struct i2c_msg msg[1];

	/* address = buf[0]
	 * data    = buf[1]
	 */

	copy_from_user(val, buf, 2);

	/*数据传输3要素:源,目的,长度*/
	msg[0].addr  = at24cxx_client->addr;  /*目的*/
	msg[0].buf   = val;                   /*源*/
	msg[0].len   = 2;                     /*地址+数据 = 2bytes*/
	msg[0].flags = 0;                     /*写*/
	
	ret = i2c_transfer(at24cxx_client->adapter, msg, 1);
	if (1 == ret)
		return 2;
	else
		return -EIO;
}



static struct file_operations at24cxx_fops = {
	.owner = THIS_MODULE,
	.read  = at24cxx_read,
	.write = at24cxx_write,
};

static int at24cxx_detect(struct i2c_adapter *adapter, int address, int kind)
{
	int err = 0;

	if (!(at24cxx_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
		err = -ENOMEM;
		goto exit;
	}

	at24cxx_client->addr    = address;
	at24cxx_client->adapter = adapter;
	at24cxx_client->driver  = &at24cxx_driver;

	major = register_chrdev(0, "at24cxx_drv", &at24cxx_fops);
	at24cxx_class = class_create(THIS_MODULE, "at24cxx_drv");
	at24cxx_class_dev = class_device_create(at24cxx_class, NULL, MKDEV(major, 0), NULL, "at24cxx_dev");
	
	/* Fill in the remaining client fields */
	strlcpy(at24cxx_client->name, "at24cxx", I2C_NAME_SIZE);

	/* Tell the I2C layer a new client has arrived */
	if ((err = i2c_attach_client(at24cxx_client)))
		goto exit_kfree;
	
	printk("at24cxx_detect\n");
	return 0;

exit_kfree:
	kfree(at24cxx_client);

exit:
	return err;

}


static int at24cxx_attach_adapter(struct i2c_adapter *adapter)
{
	printk("in %s\n", __FUNCTION__);
	return i2c_probe(adapter, &addr_data, at24cxx_detect);
}

static int at24cxx_detach_client(struct i2c_client *client)
{
	int err;
	printk("in %s\n", __FUNCTION__);
	err = i2c_detach_client(client);
	if (err)
		return err;

	kfree(i2c_get_clientdata(client));
	class_device_unregister(at24cxx_class_dev);
	class_destroy(at24cxx_class);
	unregister_chrdev(major, "at24cxx_drv");
	return 0;
}


/*1. 分配一个i2c_driver结构体*/
static struct i2c_driver at24cxx_driver = {
	.driver = {
		.name	= "at24cxx",
	},
	.attach_adapter	= at24cxx_attach_adapter,
	.detach_client	= at24cxx_detach_client,
};

static int at24cxx_init(void)
{
	return i2c_add_driver(&at24cxx_driver);
}

static void at24cxx_exit(void)
{
	i2c_del_driver(&at24cxx_driver);	
}


module_init(at24cxx_init);
module_exit(at24cxx_exit);
MODULE_LICENSE("GPL");

由上面的代码,可以得出一个编写I2C驱动程序的步骤:

(1)分配一个i2c_driver结构体

/*1. 分配一个i2c_driver结构体*/
static struct i2c_driver at24cxx_driver = {
	.driver = {
		.name	= "at24cxx",
	},
	.attach_adapter	= at24cxx_attach_adapter,
	.detach_client	= at24cxx_detach_client,
};

(2)有了i2c_driver结构体后,在入口函数将它注册进系统

static int at24cxx_init(void)
{
	return i2c_add_driver(&at24cxx_driver);
}

(3)在分析I2C驱动程序的框架时,我们知道适配器驱动程序会调用我们写的attach_adapter,也就是at24cxx_attach_adapter函数。这个函数里面接着调用了i2c_probe函数。

(4)我们可以看到i2c_probe的第二个参数addr_data结构体其实就是从设备的地址,一般normal_i2c这一项是填入从设备真正的地址,也就是.normal_i2c    = normal_addr。但是我们这里强制设置.forces     = forces,也就是不管设备是否存在,都认为其存在了。

static unsigned short ignore[] = { I2C_CLIENT_END }; /*顾名思义就是忽略的意思*/
static unsigned short normal_addr[] = { 0x50, I2C_CLIENT_END }; /*地址值为7位:我们的I2C设备地址为0x50*/

static unsigned short force_addr[] = {ANY_I2C_BUS, 0x60, I2C_CLIENT_END};
static unsigned short *forces[]  = {force_addr, NULL};

static struct i2c_client_address_data addr_data = {
	.normal_i2c	= ignore, /*要发出start信号和设备地址,并且收到ACK信号后,才能确定存在这个设备*/
	.probe		= ignore,
	.ignore		= ignore,
	.forces     = forces, /*强制认为存在设备,不用通过发送start信号和设备地址来确认*/
};

      如果设置了真正的从设备地址后,i2c_probe调用的过程,也是向从设备发起一个探测命令的过程,如果从设备回应了一个ACK,那就判断从设备存在。接着就调用i2c_probe的第三个参数指向的at24cxx_detect函数。

(5)

我们可以看到在at24cxx_detect函数里面,设置了一下at24cxx_client这个结构体,包括一些从设备的地址,后面会用到

at24cxx_client->addr    = address;

以及创建一个字符设备节点,方便后面和用户进程交互

        major = register_chrdev(0, "at24cxx_drv", &at24cxx_fops);
	at24cxx_class = class_create(THIS_MODULE, "at24cxx_drv");
	at24cxx_class_dev = class_device_create(at24cxx_class, NULL, MKDEV(major, 0), NULL, "at24cxx_dev");

(6)

        后面就是字符设备常见的操作了,用户进程调用驱动程序的write和read函数来和I2C设备进行通信。不过这里要说明一下驱动程序是怎么发送数据的,以及数据的结构是怎么样的。

        比如write函数里面就分配了一个i2c_msg结构体,然后填充里面的内容,再利用i2c_transfer函数进行发送。

        struct i2c_msg msg[1];

	/* address = buf[0]
	 * data    = buf[1]
	 */

	copy_from_user(val, buf, 2);

	/*数据传输3要素:源,目的,长度*/
	msg[0].addr  = at24cxx_client->addr;  /*目的*/
	msg[0].buf   = val;                   /*源*/
	msg[0].len   = 2;                     /*地址+数据 = 2bytes*/
	msg[0].flags = 0;                     /*写*/
	
	ret = i2c_transfer(at24cxx_client->adapter, msg, 1);

这里说明下,为什么传输的内容是:地址+数据,这个是at24cxx这个从设备规定的:

  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值