织梦CMS - 轻松建站从此开始!

罗索

驱动模块使用I2C总线范例

jackyhwei 发布于 2011-09-28 20:31 点击:次 
在嵌入式中,I2C接口的意义非常重大,许多外围芯片控制接口都采用I2C。因此,了解在驱动模块中如何进行I2C总线通信是很有必要的。我们先看看I2C驱动代码的树形结构
TAG:

在嵌入式中,I2C接口的意义非常重大,许多外围芯片控制接口都采用I2C。因此,了解在驱动模块中如何进行I2C总线通信是很有必要的。我们先看看I2C驱动代码的树形结构:

.

|-- Kconfig
|-- Makefile
|-- algos
|   |-- Kconfig
|   |-- Makefile
|   |-- i2c-algo-bit.c
|   |-- i2c-algo-pca.c
|   |-- i2c-algo-pcf.c
|   `-- i2c-algo-pcf.h
|-- busses
|   |-- Kconfig
|   |-- Makefile
|   |-- i2c-acorn.c
|   |-- ...
|   |-- i2c-gpio.c
|   |-- ...
|   |-- i2c-pxa.c
|   |-- i2c-s3c2410.c
|   `-- ...
|-- chips
|   |-- Kconfig
|   |-- Makefile
|   |-- ds1682.c
|   |-- pca963x.c
|   `-- tsl2550.c
|-- i2c-boardinfo.c
|-- i2c-core.c
|-- i2c-core.h
`-- i2c-dev.c

3 directories, 86 files

其中i2c-core.c是I2C核心部分,提供接口函数,i2c_adapter和i2c_driver在模块初始化时分别调用i2c_add_numbered_adapter()和i2c_add_driver()将其注册到I2C Core中。

i2c-dev.c是I2C设备部分,实现字符设备访问接口,对硬件的具体访问是通过i2c_adapter来实现的。它在初始化时,需要向I2C Core注册一个i2c_driver。我认为这部分仅仅是让用户层访问I2C总线的,如果内核态访问I2C总线的话,则没有必要理会它;但是我们需要参 考它来实现我们自己的i2c_driver,并且需要将这个i2c_driver注册到I2C Core。

busses目录下有许多个文件,为各种平台的I2C总线驱动,如s2c2410、pxa、mpc等等,跟硬件spec息息相关。一个总线驱动需要两个模块,用结构体i2c_adapter和i2c_algorithm来描述。

algos目录是对bus中的i2c_algorithm的补充,特别的i2c_algorithm可在这里实现。chips是I2C Slave芯片驱动代码,如I2C控制接口的eeprom、LED段码显示芯片等。

更详细的有关I2C驱动架构介绍的文章见:http://blog168.chinaunix.net/space.php?uid=11134731&do=blog&id=33193

下面是正题,附上示范代码,碰到相关场合,直接将其复制过去使用即可。

  1. #define AZURE_I2C_ADDR   0x1a 
  2. #define AZURE_I2C_BUS_NO 0 
  3.  
  4. static struct i2c_client *azure_i2c; 
  5.  
  6. static int azure_i2c_probe(struct i2c_client *i2c, 
  7.     const struct i2c_device_id *id) 
  8.     azure_i2c = i2c; 
  9.     i2c->addr = AZURE_I2C_ADDR; 
  10.      
  11.     return 0; 
  12.  
  13. static int azure_i2c_remove(struct i2c_client *client) 
  14.     return 0; 
  15.  
  16. static const struct i2c_device_id azure_i2c_id[] = { 
  17.     { "azure", 0 }, 
  18.     { } 
  19. }; 
  20. MODULE_DEVICE_TABLE(i2c, azure_i2c_id); 
  21.  
  22. static struct i2c_driver azure_i2c_driver = { 
  23.     .driver  = { 
  24.         .name  = "AZURE I2C"
  25.         .owner = THIS_MODULE, 
  26.     }, 
  27.     .probe    = azure_i2c_probe, 
  28.     .remove   = azure_i2c_remove, 
  29.     .id_table = azure_i2c_id, 
  30. }; 
  31.  
  32. static int azure_add_i2c_device() 
  33.     struct i2c_board_info info; 
  34.     struct i2c_adapter *adapter; 
  35.     struct i2c_client *client; 
  36.     int ret; 
  37.  
  38.     memset(&info, 0, sizeof(struct i2c_board_info)); 
  39.     info.addr = AZURE_I2C_ADDR; 
  40.     strlcpy(info.type, "azure", I2C_NAME_SIZE); 
  41.      
  42.     adapter = i2c_get_adapter(AZURE_I2C_BUS_NO); 
  43.     if (!adapter) { 
  44.         printk(KERN_ERR "can't get i2c adapter %d/n", AZURE_I2C_BUS_NO); 
  45.         goto err_driver; 
  46.     } 
  47.      
  48.     client = i2c_new_device(adapter, &info); 
  49.     i2c_put_adapter(adapter); 
  50.      
  51.     if (!client) { 
  52.         printk(KERN_ERR "can't add i2c device at 0x%x/n", (unsigned int)info.addr); 
  53.         goto err_driver; 
  54.     } 
  55.      
  56.     ret = i2c_add_driver(&azure_i2c_driver); 
  57.     if (ret != 0) { 
  58.         printk(KERN_ERR "can't add i2c driver/n"); 
  59.         return ret; 
  60.     } 
  61.      
  62.     return 0; 
  63.      
  64. err_driver: 
  65.     i2c_del_driver(&azure_i2c_driver); 
  66.     return -ENODEV; 

然后在模块初始化的时候调用azure_add_i2c_device()即将一个i2c_driver注册到I2C Core,之后便可调用i2c_master_recv()和i2c_master_send()进行I2C总线读写操作。 i2c_master_send()/i2c_master_recv()第一个参数client由azure_i2c传入。

 


以上并不是聪明的做法。如果有N个模块要用到I2C,那么在N个模块就要添加基本一样的代码。更好的解决方法是为内核调用I2C写一个独立的模块,EXPORT_SYMBOL读写接口函数,在其他模块里就只需要调用这些读写接口函数就可以了。如下是模块代码:

  1. #include <linux/kernel.h> 
  2. #include <linux/module.h> 
  3. #include <linux/init.h> 
  4. #include <linux/errno.h> 
  5. #include <linux/init.h> 
  6. #include <linux/list.h> 
  7. #include <linux/i2c.h> 
  8. #include <linux/smp_lock.h> 
  9.  
  10. #define I2C_API_FAKE_ADDR 0x7f 
  11. #define I2C_MINORS        256 
  12.  
  13. int  i2c_api_attach(struct i2c_adapter *adapter); 
  14. int  i2c_api_detach(struct i2c_adapter *adapter); 
  15.  
  16. struct i2c_api { 
  17.     struct list_head list; 
  18.     struct i2c_client *client; 
  19. }; 
  20.  
  21. static LIST_HEAD(i2c_api_list); 
  22. static DEFINE_SPINLOCK(i2c_api_list_lock); 
  23.  
  24. static const unsigned short normal_addr[] = { I2C_API_FAKE_ADDR, I2C_CLIENT_END }; 
  25. static const unsigned short ignore[]      = { I2C_CLIENT_END }; 
  26. static struct i2c_client_address_data addr_data =  
  27.     .normal_i2c = normal_addr, 
  28.     .probe      = ignore, 
  29.     .ignore     = ignore, 
  30.     .forces     = NULL, 
  31. }; 
  32.  
  33. static const struct i2c_device_id id[] = { 
  34.     {"I2C-API", 0}, 
  35.     {}  
  36. };       
  37. MODULE_DEVICE_TABLE(i2c, id); 
  38.  
  39. static struct i2c_driver i2c_api_driver = { 
  40.     .id_table       = id, 
  41.     .attach_adapter = i2c_api_attach, 
  42.     .detach_adapter = i2c_api_detach, 
  43.     .command        = NULL, 
  44.     .driver         = { 
  45.         .name  = "I2C-API"
  46.         .owner = THIS_MODULE, 
  47.     }, 
  48.     .address_data   = &addr_data, 
  49. }; 
  50.  
  51. static struct i2c_api *get_i2c_api(int bus_id) 
  52.     struct i2c_api *i2c_api; 
  53.  
  54.     spin_lock(&i2c_api_list_lock); 
  55.     list_for_each_entry(i2c_api, &i2c_api_list, list) { 
  56.         if (i2c_api->client->adapter->nr == bus_id) 
  57.             goto found; 
  58.     } 
  59.     i2c_api = NULL; 
  60.      
  61. found: 
  62.     spin_unlock(&i2c_api_list_lock); 
  63.     return i2c_api; 
  64.  
  65. static struct i2c_api *add_i2c_api(struct i2c_client *client) 
  66.     struct i2c_api *i2c_api; 
  67.  
  68.     if (client->adapter->nr >= I2C_MINORS) { 
  69.         printk(KERN_ERR "i2c_api: Out of device minors (%d)/n"
  70.             client->adapter->nr); 
  71.         return NULL; 
  72.     } 
  73.  
  74.     i2c_api = kzalloc(sizeof(*i2c_api), GFP_KERNEL); 
  75.     if (!i2c_api) 
  76.         return NULL; 
  77.     i2c_api->client = client; 
  78.  
  79.     spin_lock(&i2c_api_list_lock); 
  80.     list_add_tail(&i2c_api->list, &i2c_api_list); 
  81.     spin_unlock(&i2c_api_list_lock); 
  82.     return i2c_api; 
  83.  
  84. static void del_i2c_api(struct i2c_api *i2c_api) 
  85.     spin_lock(&i2c_api_list_lock); 
  86.     list_del(&i2c_api->list); 
  87.     spin_unlock(&i2c_api_list_lock); 
  88.     kfree(i2c_api); 
  89.  
  90. static int i2c_api_do_xfer(int bus_id, char chip_addr, char sub_addr, int mode,  
  91.     char *buf, unsigned int size) 
  92. /** 
  93.  * you can define more transfer mode here, implement it below. 
  94.  */ 
  95. #define I2C_API_XFER_MODE_SEND            0x0 /* standard send */ 
  96. #define I2C_API_XFER_MODE_RECV            0x1 /* standard receive */ 
  97. #define I2C_API_XFER_MODE_SEND_NO_SUBADDR 0x2 /* send with no sub address */ 
  98. #define I2C_API_XFER_MODE_RECV_NO_SUBADDR 0x3 /* receive with no sub address */ 
  99.  
  100.     int ret = 0; 
  101.     char *tmp; 
  102.     struct i2c_api *i2c_api = get_i2c_api(bus_id); 
  103.  
  104.     if (!i2c_api) 
  105.         return -ENODEV; 
  106.      
  107.     i2c_api->client->addr = chip_addr; 
  108.     switch (mode) { 
  109.     case I2C_API_XFER_MODE_SEND: 
  110.         tmp = kmalloc(size + 1,GFP_KERNEL); 
  111.         if (tmp == NULL) 
  112.             return -ENOMEM; 
  113.         tmp[0] = sub_addr; 
  114.         memcpy(&tmp[1], buf, size); 
  115.         ret = i2c_master_send(i2c_api->client, tmp, size + 1); 
  116.         ret = (ret == size + 1) ? size : ret; 
  117.         break
  118.          
  119.     case I2C_API_XFER_MODE_RECV: 
  120.         ret = i2c_master_send(i2c_api->client, &sub_addr, 1); 
  121.         if (ret < 0) 
  122.             return ret; 
  123.         ret = i2c_master_recv(i2c_api->client, buf, size); 
  124.         break
  125.      
  126.     case I2C_API_XFER_MODE_SEND_NO_SUBADDR: 
  127.         ret = i2c_master_send(i2c_api->client, buf, size); 
  128.         break
  129.          
  130.     case I2C_API_XFER_MODE_RECV_NO_SUBADDR: 
  131.         ret = i2c_master_recv(i2c_api->client, buf, size); 
  132.         break
  133.              
  134.     default
  135.         return -EINVAL; 
  136.     } 
  137.     return ret; 
  138.  
  139. int i2c_api_do_send(int bus_id, char chip_addr, char sub_addr, char *buf, unsigned int size) 
  140.     return i2c_api_do_xfer(bus_id, chip_addr, sub_addr, I2C_API_XFER_MODE_SEND, buf, size); 
  141.  
  142. int i2c_api_do_recv(int bus_id, char chip_addr, char sub_addr, char *buf, unsigned int size) 
  143.     return i2c_api_do_xfer(bus_id, chip_addr, sub_addr, I2C_API_XFER_MODE_RECV, buf, size); 
  144.  
  145. int i2c_api_attach(struct i2c_adapter *adap) 
  146.     struct i2c_board_info info; 
  147.     struct i2c_client *client; 
  148.      
  149.     memset(&info, 0, sizeof(struct i2c_board_info)); 
  150.         strlcpy(info.type, "i2c_api", I2C_NAME_SIZE); 
  151.     info.addr = I2C_API_FAKE_ADDR; 
  152.     client = i2c_new_device(adap, &info); 
  153.     if (client) 
  154.         add_i2c_api(client); 
  155.     printk(KERN_INFO "i2c_api_attach adap[%d]/n", adap->nr); 
  156.     return 0; 
  157.  
  158. int i2c_api_detach(struct i2c_adapter *adap) 
  159. {    
  160.     struct i2c_api *i2c_api; 
  161.  
  162.     i2c_api = get_i2c_api(adap->nr); 
  163.     if (i2c_api) 
  164.         del_i2c_api(i2c_api); 
  165.     return 0; 
  166.  
  167. static int __init i2c_api_init(void
  168. {    
  169.     int ret = i2c_add_driver(&i2c_api_driver); 
  170.      
  171.     if (ret) { 
  172.         printk(KERN_ERR "[%s] Driver registration failed, module not inserted./n"
  173. , __func__); 
  174.         return ret; 
  175.     } 
  176.  
  177.     return 0 ;   
  178.  
  179. static void __exit i2c_api_exit(void
  180. {    
  181.     i2c_del_driver(&i2c_api_driver); 
  182.  
  183. MODULE_AUTHOR("Loon, <sepnic@gmail.com>"); 
  184. MODULE_DESCRIPTION("I2C i2c_api Driver"); 
  185. MODULE_LICENSE("GPL"); 
  186.  
  187. module_init(i2c_api_init); 
  188. module_exit(i2c_api_exit); 
  189.  
  190. EXPORT_SYMBOL_GPL(i2c_api_do_send); 
  191. EXPORT_SYMBOL_GPL(i2c_api_do_recv); 

内核版本是2.6.32,i2c_driver结构体成员attach_adapter和detach_adapter可能以后有增删,届时需要对模块里的i2c_api_attach()和i2c_api_detach()做调整。

加载该模块后,其他模块即可调用i2c_api_do_recv()/i2c_api_do_send()读写I2C总线。

(sepnic)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201109/15073.html]
本文出处:blog.csdn.net/sepnic 作者:sepnic
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容