当前位置:   article > 正文

linux-3.4.2 IIC驱动使用介绍_i2c_detect_address

i2c_detect_address

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链表)

   

  1. i2c_register_board_info()
  2. list_add_tail(&devinfo->list, &__i2c_board_list);
  3. 链表在i2c_scan_static_board_info()函数内被调用。
  4. 调用关系:
  5. 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() :需要先判断设备是否存在,才会创建新设备

  1. i2c_new_probed_device()
  2. if (probe(adap, addr_list[i])) /* 确定设备是否真实存在 */
  3. break;//如果设备不存在,返回
  4. info->addr = addr_list[i];
  5. 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结构体,结构体中需要实现以下几个参数

 

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

 

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

 

  1. i2c_add_driver()
  2. i2c_register_driver()
  3. a. at24cxx_driver放入i2c_bus_type的drv链表
  4. driver_register()会在总线上查找该设备,如果没有则在总线上注册设备。会从dev链表里取出能匹配的i2c_client并调用probe()
  5. res = driver_register(&driver->driver);
  6. if (res)
  7. return res;
  8. b. 对于每一个适配器,调用__process_new_driver()
  9. 对于每一个适配器,调用它的函数确定address_list里的设备是否存在
  10. 如果存在,再调用detect进一步确定、设置,然后i2c_new_device()
  11. /* Walk the adapters that are already present */
  12. i2c_for_each_dev(driver, __process_new_driver);
  13. __process_new_driver()
  14. i2c_do_add_adapter()
  15. /* 检测该总线上支持的设备,并实例化它们 */
  16. i2c_detect(adap, driver)
  17. //address_list 等于我们自己定义的address_list 如果不存在直接返回
  18. address_list = driver->address_list;
  19. if (!driver->detect || !address_list)
  20. return 0;
  21. for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
  22. //对传入的地址表中的地址进行判断
  23. temp_client->addr = address_list[i];
  24. /* 判断这个设备是否存在:简单的发出S信号确定有ACK */
  25. err = i2c_detect_address(temp_client, driver);
  26. /* Make sure the address is valid */
  27. err = i2c_check_addr_validity(addr);
  28. /* Make sure there is something at this address */
  29. if (!i2c_default_probe(adapter, addr))
  30. return 0;
  31. /* Finally call the custom detection function */
  32. // 设置info.type
  33. memset(&info, 0, sizeof(struct i2c_board_info));
  34. info.addr = addr;
  35. //调用i2c_driver结构体内的detect()进一步判断设备
  36. err = driver->detect(temp_client, &info);
  37. /* Detection succeeded, instantiate the device */
  38. 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编译进内核 也可以编译成模块之后加载

  1. Device Drivers
  2. I2C support
  3. <*> I2C device interface

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

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

具体代码如下:

  1. i2cdev_ioctl()
  2. case I2C_SLAVE:
  3. //i2cdev_check_addr()函数会在总线进行查找设备是否存在 如果存在返回失败
  4. 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,只要能产生中断就产生。

 

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

  1. Device Drivers
  2. I2C support
  3. I2C Hardware Bus support
  4. < > S3C2410 I2C Driver

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/444495
推荐阅读
相关标签
  

闽ICP备14008679号