13_Linux2.6 IIC驱动

13_Linux2.6 IIC驱动

1、i2c驱动框架

i2c驱动框架模型:
在这里插入图片描述
其中步骤四④为:在i2c_add_adapter和i2c_add_driver的第③步时若发现设备,则构造一个i2c_client结构体来配对。
在这里插入图片描述
分析i2c注册i2c_driver的过程:

i2c_add_driver
	i2c_register_driver
		driver->driver.bus = &i2c_bus_type;
		driver_register(&driver->driver);
		list_for_each_entry(adapter, &adapters, list) {
			driver->attach_adapter(adapter);
						i2c_probe(adapter, &addr_data, eeprom_detect);
							i2c_probe_address // 发出Start信号,发出设备地址(来自addr_data)
								i2c_smbus_xfer
									i2c_smbus_xfer_emulated
										i2c_transfer
											adap->algo->master_xfer // s3c24xx_i2c_xfer

2、编写驱动程序

怎么写I2C设备驱动程序?
① 分配一个i2c_driver结构体
② 设置
attach_adapter // 它直接调用 i2c_probe(adap, 设备地址, 发现这个设备后要调用的函数);
detach_client // 卸载这个驱动后,如果之前发现能够支持的设备,则调用它来清理
③ 注册:i2c_add_driver

2.7、i2c实验的读写函数

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

	/* address  = buf[0] 
	 * data     = buf[1]
	 */
	if(size != 1)
		return -EINVAL;

	copy_from_user(&address, buf, 1);

	/* 数据传输三要素:源、目的、长度 */

	/* 读at24cxx时,先把要读的存储空间的地址告诉它 */
	msg[0].addr	 = at24cxx_client->addr;  /* 目的 */
	msg[0].buf 	 = &address;			  /* 源 */
	msg[0].len   = 1;					  /* 地址 = 1 byte */
	msg[0].flags = 0;					  /* 0表示写 */

	/* 然后启动读操作 */
	msg[1].addr	 = at24cxx_client->addr;  /* 源 */
	msg[1].buf 	 = &data;			 	  /* 目的 */
	msg[1].len   = 1;					  /* 数据 = 1 byte */
	msg[1].flags = I2C_M_RD;			  /* I2C_M_RD表示读 */

	ret = i2c_transfer(at24cxx_client->adapter, msg, 2);

	if(ret == 2)
	{
		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 *offset)
{
	unsigned char val[2];
	struct i2c_msg msg[1];
	int ret;

	/* address  = buf[0] 
	 * data     = buf[1]
	 */
	if(size != 2)
		return -EINVAL;

	copy_from_user(val, buf, 2);

	/* 数据传输三要素:源、目的、长度 */
	msg[0].addr	 = at24cxx_client->addr;  /* 目的 */
	msg[0].buf 	 = val;					  /* 源 */
	msg[0].len   = 2;					  /* 地址+数据 = 2 byte */
	msg[0].flags = 0;					  /* 0表示写 */

	ret = i2c_transfer(at24cxx_client->adapter, msg, 1);

	if(ret == 1)
		return 2;
	else
		return -EIO;
}

2.6、在file_operations结构体at24cxx_fops中:

添加用于i2c实验的读写函数

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

2.5、发现新的adapter或者driver之后调用函数at24cxx_detect:

① 在这里构造一个i2c_client结构体: 以后收发数据时会用到它;
② 构造主设备号,添加file_operations结构体at24cxx_fops;

static int at24cxx_detect(struct i2c_adapter *adapter, int address, int kind)
{
	printk("at24cxx_detect\n");

	/* 构造一个i2c_client结构体: 以后收发数据时会用到 它 */
	at24cxx_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
	at24cxx_client->addr    = address;
	at24cxx_client->adapter = adapter;
	at24cxx_client->driver  = &at24cxx_driver;
	strcpy(at24cxx_client->name, "at24cxx");
	i2c_attach_client(at24cxx_client);	/* Tell the I2C layer a new client has arrived */

	major = register_chrdev(0, "at24cxx", &at24cxx_fops);

	cls = class_create(THIS_MODULE, "at24cxx");
	class_device_create(cls, NULL, MKDEV(major, 0), NULL, "at24cxx"); /* /dev/at24cxx */
	
	return 0;
}

2.4、at24cxx_attach和at24cxx_detach函数:

在这个函数中要用到识别设备所要发出的地址数据addr_data

static unsigned short ignore[]		={I2C_CLIENT_END};

/* i2c设备地址值是前面7位 1 0 1 0 0 0 0 A2 A1 A0
 * 改为0x60的话,由于不存在设备地址为0x60的设备,
 * 所以at24cxx_detect不被调用
 */
static unsigned short normal_addr[] ={0x50, I2C_CLIENT_END};
/* 参数1:总线,表示在哪条总线上查找设备
 * 参数2:设备地址
 * 参数3:查找设备时用于退出的标记
 */
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 = normal_addr,	/* 要发出S信号和设备地址并得到ACK信号,才能确定存在这个设备 */
	.probe		= ignore,
	.ignore		= ignore,
	//.forces		= forces,	/* 强制认为存在这个设备 */
};

static int at24cxx_attach(struct i2c_adapter *adapter)
{
	/* 参数3:发现新的adapter或者driver之后调用这个函数 */
	return i2c_probe(adapter, &addr_data, at24cxx_detect);
}

static int at24cxx_detach(struct i2c_client *client)
{
	printk("at24cxx_detach\n");
	class_device_destroy(cls, MKDEV(major, 0));
	class_destroy(cls);
	unregister_chrdev(major, "at24cxx");

	i2c_detach_client(client);
	kfree(i2c_get_clientdata(client));

	return 0;
}

2.3、在i2c_driver结构体at24cxx_driver中:

/* 1. 分配/定义一个i2c_driver结构体 */
/* 2. 设置i2c_driver结构体 */
static struct i2c_driver at24cxx_driver = {
	.driver = {
			.name = "at24cxx",
		},
	.attach_adapter = at24cxx_attach,
	.detach_client  = at24cxx_detach,
};

2.1、在入口函数at24cxx_init中:

注册i2c_driver:

i2c_add_driver(&at24cxx_driver);

2.2、在出口函数at24cxx_exit中:

卸载i2c_driver:

i2c_del_driver(&at24cxx_driver);

3、测试

1th:

  1. insmod at24cxx.ko
    观察输出信息
    在这里插入图片描述
  2. 修改normal_addr里的0x50为0x60
    编译加载,观察输出信息,没有设备不会调用函数at24cxx_detect
    在这里插入图片描述

2th:

没有真正的i2c设备,使用forces强制指定设备,也可以成功调用at24cxx_detect函数
在这里插入图片描述

3th:

在装载时没有构造i2c_client结构体,所以在卸载时不会调用at24cxx_detach函数,需要加上;
在at24cxx_detect里面添加。
在这里插入图片描述

4th:

完成基本框架,现在来填写i2c的读写函数
在这里插入图片描述
5th:
完整的i2c驱动程序,可以测试读写操作
在这里插入图片描述
总结:
构造i2c_driver结构体,里面有一个函数:.attach_adapter = at24cxx_attach,当注册驱动时,会从总线的adapter链表中拿出来,调用其.attach_adapter = at24cxx_attach函数,会调用i2c_probe,probe函数会使用总线里的传输函数发出信号查看是否有那些IIC设备;当发现有这些设备之后注册i2c_client结构体,对其注册、设置等操作;之后就是注册字符设备,实现IIC的读写函数等等。
以前我们得数据都是读取那些寄存器,现在都是使用IIC总线驱动程序提供的哪些读写函数,到IIC总线上将数据读写进来。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ZhangDaniel_ZD

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

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

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

打赏作者

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

抵扣说明:

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

余额充值