linux-3.4.2 IIC驱动使用介绍

I2C总线驱动源码位置:linux-3.4.2\drivers\i2c\i2c-core.c

 

1. 框架

1.1 硬件协议简介

IIC硬件原理

通信过程:

由主机开始发送S(start)信号后,发送7位设备地址加一位W/R标志,之后将SDA置低,如果IIC线上有对应设备地址的设备时,该设备会将SDA拉低,代表此设备存在。之后如果是写就可以直接发送数据,读就直接接收数据。

 

1.2 驱动框架

驱动框架图

i2c_client 与i2c_driver通过name进行匹配。

 

1.3 bus-drv-dev模型介绍

设备(device)一共有4种构建方法(文档有介绍)

文档位置linux-3.4.2\Documentation\i2c\instantiating-devices

所有构造方法源码地址:https://github.com/yogach/linux-drive/tree/master/i2c

 

a.1 使用i2c_board_info创建i2c设备

定义一个i2c_board_info结构体,里面有设备名字, 设备地址,然后i2c_register_board_info(busnum(总线ID), ...)   (把它们放入__i2c_board_list链表)

   

i2c_register_board_info()
        list_add_tail(&devinfo->list, &__i2c_board_list);

	链表在i2c_scan_static_board_info()函数内被调用。
 调用关系:
	i2c_register_adapter() > i2c_scan_static_board_info() > i2c_new_device()

 

使用限制:必须在 i2c_register_adapter()执行之前 注册 i2c_register_board_info()

所以:不适合我们动态加载insmod

 

a.2 直接使用i2c_new_device(), i2c_new_probed_device()

a.2.1 i2c_new_device() : 认为设备肯定存在(不判断设备是否存在)

 

a.2.2 i2c_new_probed_device() :需要先判断设备是否存在,才会创建新设备

i2c_new_probed_device()
        if (probe(adap, addr_list[i])) /* 确定设备是否真实存在 */
	    break;//如果设备不存在,返回
info->addr = addr_list[i];
	i2c_new_device(adap, info);

 

a.3 从用户空间创建设备

创建设备

echo at24c08 0x50 > /sys/class/i2c-adapter/i2c-0/new_device

 

导致i2c_new_device被调用

 

删除设备

echo 0x50 > /sys/class/i2c-adapter/i2c-0/delete_device

 

导致i2c_unregister_device

 

a.4 如果事先不知道i2c设备接在哪个适配器上可以使用此方法

1、定义一个i2c_driver结构体,结构体中需要实现以下几个参数

 

static struct i2c_driver at24cxx_driver = {
	.class  = I2C_CLASS_HWMON, /* 表示去哪些适配器上找设备 */
	.driver	= {
		.name	= "100ask",
		.owner	= THIS_MODULE,
	},
	.probe		= at24cxx_probe,
	.remove		= __devexit_p(at24cxx_remove),
	.id_table	= at24cxx_id_table,
	.detect     = at24cxx_detect,  /* 用这个函数来检测设备确实存在 */
	.address_list	= addr_list,   /* 这些设备的地址 */
};

 

"class"表示在这一类I2C适配器中找,"detect函数"来分辨设备地址相同的设备哪个才是需要的设备。

 


i2c_add_driver()
	i2c_register_driver()
		a. at24cxx_driver放入i2c_bus_type的drv链表
               driver_register()会在总线上查找该设备,如果没有则在总线上注册设备。会从dev链表里取出能匹配的i2c_client并调用probe()
		   res = driver_register(&driver->driver);
	           if (res)
		     return res;
				
		b. 对于每一个适配器,调用__process_new_driver()
		   对于每一个适配器,调用它的函数确定address_list里的设备是否存在
		   如果存在,再调用detect进一步确定、设置,然后i2c_new_device()
          	   /* Walk the adapters that are already present */
              i2c_for_each_dev(driver, __process_new_driver);
			__process_new_driver()
				i2c_do_add_adapter()
					/* 检测该总线上支持的设备,并实例化它们 */
					i2c_detect(adap, driver)
                                   //address_list 等于我们自己定义的address_list 如果不存在直接返回
                                    address_list = driver->address_list;
                                    if (!driver->detect || !address_list)
	                                 return 0;
 
					for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
                                   //对传入的地址表中的地址进行判断
                                   temp_client->addr = address_list[i];       
                                   /* 判断这个设备是否存在:简单的发出S信号确定有ACK */
                                   err = i2c_detect_address(temp_client, driver);
                                   	/* Make sure the address is valid */
                                   	err = i2c_check_addr_validity(addr);
                                           /* Make sure there is something at this address */
                                          	if (!i2c_default_probe(adapter, addr))
						    return 0;
								
						/* Finally call the custom detection function */	
                      	                // 设置info.type										
						memset(&info, 0, sizeof(struct i2c_board_info));
						info.addr = addr;	
						//调用i2c_driver结构体内的detect()进一步判断设备         			
						err = driver->detect(temp_client, &info);
 
						/* Detection succeeded, instantiate the device */
						i2c_new_device();

 

b. 驱动的写法

使用SMBus --- system manager bus对i2c设备进行操作 驱动里面用的是这种 这也是官方文档中推荐使用的

文档位置linux-3.4.2\Documentation\i2c\smbus-protocol

里面有库函数介绍

 

图上标记部分代表使用i2c_smbus_write_byte_data()函数进行I2C通信时发送的数据顺序,中括号内表示设备返回给主机。

对一个外部存储设备的读写顺序是:START信号,设备地址,写操作 [设备回ACK] 写地址 [设备回ACK] 写数据 [设备回ACK] PAUSE信号

 

2. 完善设备驱动程序

实现的源码地址https://github.com/yogach/linux-drive/tree/master/i2c/4th_device_driver_test

 

 

3. 不自己写驱动直接访问

直接使用内核中已经写好的驱动

介绍文档位置linux-3.4.2\Documentation\i2c\dev-interface

实现的源码https://github.com/yogach/linux-drive/tree/master/i2c/5th_user_space_test

 

封装后的i2c-dev.h文件内实际上就是调用i2c-dev.c内的函数

 

使用时需要将I2C device interface编译进内核 也可以编译成模块之后加载

Device Drivers
	 I2C support
		<*>   I2C device interface

使用此方法时,有个特殊点需要注意

当i2c的设备地址被注册(已经有驱动注册了)之后,就无法使用此方法进行访问

具体代码如下:

i2cdev_ioctl()
        case I2C_SLAVE:
        //i2cdev_check_addr()函数会在总线进行查找设备是否存在 如果存在返回失败
     	if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))

 

4. 编写"总线(适配器adapter)"驱动

内核自带i2c总线源码位置linux-3.4.2\drivers\i2c\busses\i2c-s3c2410.c

已实现的源码位置https://github.com/yogach/linux-drive/tree/master/i2c/6th_i2c_bus

 

流程:

1、分配/设置i2c_adapter

2、使用i2c_add_adapter()注册i2c_adapter

3、初始化s3c2440 i2c控制器相关寄存器

4、申请i2c中断函数,并在中断实现数据的发送与读取。

 

 

以读AT24C08的100地址为例,下面划红线部分会产生中断。

 

发出start信号的同时,会紧接着发送设备地址与读写状态位,之后会产生一个i2c中断(判断是否收到ack,无返回错误,收到进入下一阶段)。

如果之后是发送数据,每发送一个字节数据会产生一个中断,每次进入中断后可以判断是否收到ack。

如果是接收数据,每接收一个字节数据会产生一个中断,如果接下来还有数据需要接收,则设置接收完之后产生一个ack信号,反之则不发送ack信号。

在读写结束之后,需要发送一个pause信号。

 

所以在使用request_irq()申请中断时,需要注意中断触发方式选择IRQF_TRIGGER_NONE,只要能产生中断就产生。

 

做实验之前先去除内核配置内的

Device Drivers
	 I2C support
	 	I2C Hardware Bus support
	 		< > S3C2410 I2C Driver

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值