驱动模块使用I2C总线范例
来源:互联网 发布:java hashmap用法 编辑:程序博客网 时间:2024/05/22 06:26
在嵌入式中,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.c3 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://blog.chinaunix.net/space.php?uid=11134731&do=blog&id=33193
下面是正题,附上示范代码,碰到相关场合,直接将其复制过去使用即可。
#define AZURE_I2C_ADDR 0x1a#define AZURE_I2C_BUS_NO 0static struct i2c_client *azure_i2c;static int azure_i2c_probe(struct i2c_client *i2c,const struct i2c_device_id *id){azure_i2c = i2c;i2c->addr = AZURE_I2C_ADDR;return 0;}static int azure_i2c_remove(struct i2c_client *client){return 0;}static const struct i2c_device_id azure_i2c_id[] = {{ "azure", 0 },{ }};MODULE_DEVICE_TABLE(i2c, azure_i2c_id);static struct i2c_driver azure_i2c_driver = {.driver = {.name = "AZURE I2C",.owner = THIS_MODULE,},.probe = azure_i2c_probe,.remove = azure_i2c_remove,.id_table = azure_i2c_id,};static int azure_add_i2c_device(){struct i2c_board_info info;struct i2c_adapter *adapter;struct i2c_client *client;int ret;memset(&info, 0, sizeof(struct i2c_board_info));info.addr = AZURE_I2C_ADDR;strlcpy(info.type, "azure", I2C_NAME_SIZE);adapter = i2c_get_adapter(AZURE_I2C_BUS_NO);if (!adapter) {printk(KERN_ERR "can't get i2c adapter %d/n", AZURE_I2C_BUS_NO);goto err_driver;}client = i2c_new_device(adapter, &info);i2c_put_adapter(adapter);if (!client) {printk(KERN_ERR "can't add i2c device at 0x%x/n", (unsigned int)info.addr);goto err_driver;}ret = i2c_add_driver(&azure_i2c_driver);if (ret != 0) {printk(KERN_ERR "can't add i2c driver/n");return ret;}return 0;err_driver:i2c_del_driver(&azure_i2c_driver);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读写接口函数,在其他模块里就只需要调用这些读写接口函数就可以了。如下是模块代码:
#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/list.h>#include <linux/i2c.h>#include <linux/smp_lock.h>#define I2C_API_FAKE_ADDR 0x7f#define I2C_MINORS 256int i2c_api_attach(struct i2c_adapter *adapter);int i2c_api_detach(struct i2c_adapter *adapter);struct i2c_api {struct list_head list;struct i2c_client *client;};static LIST_HEAD(i2c_api_list);static DEFINE_SPINLOCK(i2c_api_list_lock);static const unsigned short normal_addr[] = { I2C_API_FAKE_ADDR, I2C_CLIENT_END };static const unsigned short ignore[] = { I2C_CLIENT_END };static struct i2c_client_address_data addr_data = {.normal_i2c = normal_addr,.probe = ignore,.ignore = ignore,.forces = NULL,};static const struct i2c_device_id id[] = {{"I2C-API", 0},{} }; MODULE_DEVICE_TABLE(i2c, id);static struct i2c_driver i2c_api_driver = {.id_table = id,.attach_adapter = i2c_api_attach,.detach_adapter= i2c_api_detach, .command = NULL,.driver = {.name = "I2C-API",.owner = THIS_MODULE,},.address_data = &addr_data,};static struct i2c_api *get_i2c_api(int bus_id){struct i2c_api *i2c_api;spin_lock(&i2c_api_list_lock);list_for_each_entry(i2c_api, &i2c_api_list, list) {if (i2c_api->client->adapter->nr == bus_id)goto found;}i2c_api = NULL;found:spin_unlock(&i2c_api_list_lock);return i2c_api;}static struct i2c_api *add_i2c_api(struct i2c_client *client){struct i2c_api *i2c_api;if (client->adapter->nr >= I2C_MINORS) {printk(KERN_ERR "i2c_api: Out of device minors (%d)/n",client->adapter->nr);return NULL;}i2c_api = kzalloc(sizeof(*i2c_api), GFP_KERNEL);if (!i2c_api)return NULL;i2c_api->client = client;spin_lock(&i2c_api_list_lock);list_add_tail(&i2c_api->list, &i2c_api_list);spin_unlock(&i2c_api_list_lock);return i2c_api;}static void del_i2c_api(struct i2c_api *i2c_api){spin_lock(&i2c_api_list_lock);list_del(&i2c_api->list);spin_unlock(&i2c_api_list_lock);kfree(i2c_api);}static int i2c_api_do_xfer(int bus_id, char chip_addr, char sub_addr, int mode, char *buf, unsigned int size){/** you could define more transfer mode here, implement it following. */#define I2C_API_XFER_MODE_SEND 0x0 /* standard send */#define I2C_API_XFER_MODE_RECV 0x1 /* standard receive */#define I2C_API_XFER_MODE_SEND_NO_SUBADDR 0x2 /* send without sub-address */#define I2C_API_XFER_MODE_RECV_NO_SUBADDR 0x3 /* receive without sub-address */int ret = 0;char *tmp;struct i2c_api *i2c_api = get_i2c_api(bus_id);if (!i2c_api)return -ENODEV;i2c_api->client->addr = chip_addr;switch (mode) {case I2C_API_XFER_MODE_SEND:tmp = kmalloc(size + 1,GFP_KERNEL);if (tmp == NULL)return -ENOMEM;tmp[0] = sub_addr;memcpy(&tmp[1], buf, size);ret = i2c_master_send(i2c_api->client, tmp, size + 1);ret = (ret == size + 1) ? size : ret;break;case I2C_API_XFER_MODE_RECV:ret = i2c_master_send(i2c_api->client, &sub_addr, 1);if (ret < 0)return ret;ret = i2c_master_recv(i2c_api->client, buf, size);break;case I2C_API_XFER_MODE_SEND_NO_SUBADDR:ret = i2c_master_send(i2c_api->client, buf, size);break;case I2C_API_XFER_MODE_RECV_NO_SUBADDR:ret = i2c_master_recv(i2c_api->client, buf, size);break;default:return -EINVAL;}return ret;}int i2c_api_do_send(int bus_id, char chip_addr, char sub_addr, char *buf, unsigned int size){return i2c_api_do_xfer(bus_id, chip_addr, sub_addr, I2C_API_XFER_MODE_SEND, buf, size);}int i2c_api_do_recv(int bus_id, char chip_addr, char sub_addr, char *buf, unsigned int size){return i2c_api_do_xfer(bus_id, chip_addr, sub_addr, I2C_API_XFER_MODE_RECV, buf, size);}int i2c_api_attach(struct i2c_adapter *adap){struct i2c_board_info info;struct i2c_client *client;memset(&info, 0, sizeof(struct i2c_board_info));strlcpy(info.type, "i2c_api", I2C_NAME_SIZE);info.addr = I2C_API_FAKE_ADDR;client = i2c_new_device(adap, &info);if (client)add_i2c_api(client);printk(KERN_INFO "i2c_api_attach adap[%d]/n", adap->nr);return 0;}int i2c_api_detach(struct i2c_adapter *adap){struct i2c_api *i2c_api;i2c_api = get_i2c_api(adap->nr);if (i2c_api)del_i2c_api(i2c_api);return 0;}static int __init i2c_api_init(void){int ret = i2c_add_driver(&i2c_api_driver);if (ret) {printk(KERN_ERR "[%s] Driver registration failed, module not inserted./n", __func__);return ret;}return 0 ;}static void __exit i2c_api_exit(void){i2c_del_driver(&i2c_api_driver);}MODULE_AUTHOR("Loon, <sepnic@gmail.com>");MODULE_DESCRIPTION("I2C i2c_api Driver");MODULE_LICENSE("GPL");module_init(i2c_api_init);module_exit(i2c_api_exit);EXPORT_SYMBOL_GPL(i2c_api_do_send);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总线。
- 驱动模块使用I2C总线范例
- 驱动模块使用I2C总线范例
- 驱动模块使用SPI总线范例
- 驱动模块使用SPI总线范例
- I2c总线驱动
- I2C总线驱动
- Linux I2C 总线驱动
- linux i2c总线驱动
- I2C总线驱动代码
- I2C总线驱动
- kernel I2C总线驱动
- 浅析I2C总线驱动
- S3C2410 I2C 总线驱动实例
- S3C2410 I2C 总线驱动实例
- Linux驱动之I2C总线
- Linux I2C子系统分析-I2C总线驱动
- Linux I2C子系统分析-I2C总线驱动
- Linux I2C子系统分析-I2C总线驱动
- 由玩魔兽争霸DOTA游戏想到的
- Brush.style:=bsClear可去掉字体的背景颜色
- sql azure笔记
- Delphi Ole编程,什么是Ole?
- 在stringgrid的drawcell事件处理过程中,给单元格赋值时,要用textout输出,不要像一般给cell[i,j]赋值那样。
- 驱动模块使用I2C总线范例
- Hadoop使用公平调度策略
- Hbase数据模型
- TXMLDocument对XML文件进行读取和写入
- Hive数据操纵语言
- 发散一下思维
- Linux共享内存
- 浏览文件夹
- C语言实现几种常见排序算法