赞
踩
I2C总线驱动源码位置:linux-3.4.2\drivers\i2c\i2c-core.c
1.1 硬件协议简介
IIC硬件原理
通信过程:
由主机开始发送S(start)信号后,发送7位设备地址加一位W/R标志,之后将SDA置低,如果IIC线上有对应设备地址的设备时,该设备会将SDA拉低,代表此设备存在。之后如果是写就可以直接发送数据,读就直接接收数据。
1.2 驱动框架
驱动框架图
i2c_client 与i2c_driver通过name进行匹配。
1.3 bus-drv-dev模型介绍
文档位置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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。