i2c驱动--代码
2017-09-25 23:08
441 查看
① 分配一个i2c_driver结构体
②设置i2c_driver结构体
.attach_adapter // 他直接调用i2c_probe(adapter, &addr_data, xxx_detect);
// &addr_data,设备地址;xxx_detect发现这个设备后调用的函数
.detach_client // 卸载这个驱动后,如果之前发现能够支持的设备,则调用它来清理
③注册
i2c_add_driver
(2) 设置i2c_driver结构体
参考 Eeprom.c的static struct i2c_driver eeprom_driver
写:at24cxx_driver
参考
static int __init eeprom_init(void)
static void __exit eeprom_exit(void)
写:at24cxx_init
at24cxx_exit
可以参考 static int eeprom_detect(struct i2c_adapter *adapter, int address, int kind)来写。
at24cxx_detect,这里只是完成简单的打印。
参考 Eeprom.c的static int eeprom_attach_adapter(struct i2c_adapter *adapter) 来写at24cxx_attach
参考 static int eeprom_detach_client(struct i2c_client *client)写at24cxx_detach
通过去at24cxx_attach函数中addr_data的定义找到i2c_client_address_data(driver/i2c/chips/M41t00) 完善代码
这样一个简单的框架完成。
原程序中:
添加修改如下:
测试结果:
可以看到安装驱动后是可以检测到的,知识卸载时没有打印信息,继续修改。
完善这段程序:
此时卸载驱动时,.detach_client = at24cxx_detach才会被调用
at24cxx_detach中添加:
测试结果:
at24cxx_detect中添加:
设备驱动注销时:
测试结果:
完善:at24cxx_read
测试程序:
测试结果:
./i2c_test 查看用法
./i2c_test w 1 7 写地址数据
./i2c_test r 1 读地址数据
②设置i2c_driver结构体
.attach_adapter // 他直接调用i2c_probe(adapter, &addr_data, xxx_detect);
// &addr_data,设备地址;xxx_detect发现这个设备后调用的函数
.detach_client // 卸载这个驱动后,如果之前发现能够支持的设备,则调用它来清理
③注册
i2c_add_driver
第一步
(1) 分配一个i2c_driver结构体(2) 设置i2c_driver结构体
参考 Eeprom.c的static struct i2c_driver eeprom_driver
/* This is the driver that will be inserted */ static struct i2c_driver eeprom_driver = { .driver = { .name = "eeprom", }, .id = I2C_DRIVERID_EEPROM, .attach_adapter = eeprom_attach_adapter, .detach_client = eeprom_detach_client, };
写:at24cxx_driver
static struct i2c_driver at24cxx_driver={ .driver ={ .name="at24cxx", }, .attach_adapter =at24cxx_attach, .detach_client =at24cxx_detach, };
参考
static int __init eeprom_init(void)
static void __exit eeprom_exit(void)
static int __init eeprom_init(void) { return i2c_add_driver(&eeprom_driver); } static void __exit eeprom_exit(void) { i2c_del_driver(&eeprom_driver); }
写:at24cxx_init
at24cxx_exit
static int at24cxx_init(void) { i2c_add_driver(&at24cxx_driver); } static int at24cxx_exit(void) { i2c_del_driver(&at24cxx_driver); }
/*修饰*/ module_init(at24cxx_init); module_exit(at24cxx_exit); MODULE_LICENSE("GPL");
第二步
写at24cxx_attach、at24cxx_detach可以参考 static int eeprom_detect(struct i2c_adapter *adapter, int address, int kind)来写。
at24cxx_detect,这里只是完成简单的打印。
static int at24cxx_detect(struct i2c_adapter *adapter, int address, int kind) { printk("at24cxx_detect\n"); return 0; }
参考 Eeprom.c的static int eeprom_attach_adapter(struct i2c_adapter *adapter) 来写at24cxx_attach
static int at24cxx_attach(struct i2c_adapter *adapter) { return i2c_probe(adapter, &addr_data, at24cxx_detect); }
参考 static int eeprom_detach_client(struct i2c_client *client)写at24cxx_detach
static int at24cxx_detach(struct i2c_client *client)//清理 { printk("at24cxx_detach\n"); return 0; }
通过去at24cxx_attach函数中addr_data的定义找到i2c_client_address_data(driver/i2c/chips/M41t00) 完善代码
static unsigned short ignore[] = { I2C_CLIENT_END }; static unsigned short normal_addr[] = {0x50, I2C_CLIENT_END };//I2C_CLIENT_END,设备地址,地址值是7位 //二进制1010000,转换为16进制为0x50 static struct i2c_client_address_data addr_data = { .normal_i2c = normal_addr,//要发出S信号和设备地址并得到ACK信号才能确定是否存在这个设备 .probe = ignore, .ignore = ignore, /*i2c_probe中的 .forces强制认为这个设备存在*/ };
这样一个简单的框架完成。
三、 .forces :强制认为某地址有设备存在
关于.forces 的使用:在at24cxx.c中去i2c_probe的定义(drivers/i2c/i2c-core.c)原程序中:
static unsigned short normal_addr[] = { 0x50, I2C_CLIENT_END }; /* 地址值是7位 */ /* 改为0x60的话, 由于不存在设备地址为0x60的设备, 所以at24cxx_detect不被调用 */
添加修改如下:
static unsigned short force_addr[] = {ANY_I2C_BUS, 0x60, I2C_CLIENT_END}; static unsigned short * forces[] = {force_addr, NULL}; static struct i2c_client_address_data addr_data = { ------ .forces = forces, /* 强制认为存在这个设备 */ ------ }
测试结果:
可以看到安装驱动后是可以检测到的,知识卸载时没有打印信息,继续修改。
四、修改完善at24cxx_detect
在卸载程序时并没有打印信息,参考(eeprom.c(drivers\i2c\chips))的eeprom_detect函数完善这段程序:
static int at24cxx_detect(struct i2c_adapter *adapter, int address, int kind) { struct i2c_client *new_client; printk("at24cxx_detect\n"); /* 构构一个i2c_client结构体: 以后收改数据时会用到它 */ new_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); new_client->addr = address; new_client->adapter = adapter; new_client->driver = &at24cxx_driver; strcpy(new_client->name, "at24cxx"); i2c_attach_client(new_client); return 0; }
此时卸载驱动时,.detach_client = at24cxx_detach才会被调用
at24cxx_detach中添加:
static int at24cxx_detach(struct i2c_client *client) { ... i2c_detach_client(client); kfree(i2c_get_clientdata(client)); return 0; }
测试结果:
五、字符设备相关的程序:
static int major;/*主设备号*/ static ssize_t at24cxx_read(struct file *file, char __user *buf, size_t size, loff_t * offset) { return 0; } static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t size, loff_t *offset) { return 0; } static struct file_operations at24cxx_fops = { .owner = THIS_MODULE, .read = at24cxx_read, .write = at24cxx_write, };
static struct class *cls;/*系统创建设备节点*/ struct i2c_client *at24cxx_client;
at24cxx_detect中添加:
static int at24cxx_detect(struct i2c_adapter *adapter, int address, int kind) { ... major = register_chrdev(0, "at24cxx", &at24cxx_fops); cls = class_create(THIS_MODULE, "at24cxx"); class_device_create(cls, NULL, MKDEV(major, 0), NULL, "at24cxx"); /* /dev/at24cxx */ return 0; }
设备驱动注销时:
static int at24cxx_detach(struct i2c_client *client) { printk("at24cxx_detach\n"); class_device_destroy(cls, MKDEV(major, 0));/*注销设备*/ class_destroy(cls);/*注销类*/ unregister_chrdev(major, "at24cxx");/*卸载字符设备*/ i2c_detach_client(client); kfree(i2c_get_clientdata(client));/*释放client*/ return 0; }
测试结果:
六、完善: at24cxx_write at24cxx_read
完善: at24cxx_writetatic ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t size, loff_t *offset) { unsigned char val[2];//定义两个子节点的数组 struct i2c_msg msg[1];//构造i2c消息 int ret; /* address = buf[0] * data = buf[1] */ if (size != 2) return -EINVAL; copy_from_user(val, buf, 2);/*从buf里面拷两个字节到val*/ /* 数据传输三要素: 源,目的,长度 */ msg[0].addr = at24cxx_client->addr; /* 目的 */ msg[0].buf = val; /* 源 */ msg[0].len = 2; /* 地址+数据=2 byte */ msg[0].flags = 0; /* 表示写 */ ret = i2c_transfer(at24cxx_client->adapter, msg, 1);/*msg的信息传到适配器*/ if (ret == 1) return 2; else return -EIO; }
完善:at24cxx_read
static ssize_t at24cxx_read(struct file *file, char __user *buf, size_t size, loff_t * offset) { unsigned char address; unsigned char data; struct i2c_msg msg[2];/*地址+数据*/ int ret; /* address = buf[0] * data = buf[1] */ if (size != 1) return -EINVAL; copy_from_user(&address, buf, 1);/*一个字节地址即可*/ /* 数据传输三要素: 源,目的,长度 */ /* 读AT24CXX时,要先把要读的存储空间的地址发给它 */ msg[0].addr = at24cxx_client->addr; /* 目的 */ msg[0].buf = &address; /* 源 */ msg[0].len = 1; /* 地址=1 byte */ msg[0].flags = 0; /* 表示写 */ /* 然后启动读操作 */ msg[1].addr = at24cxx_client->addr; /* 源 */ msg[1].buf = &data; /* 目的 */ msg[1].len = 1; /* 数据=1 byte */ msg[1].flags = I2C_M_RD; /* 表示读 */ ret = i2c_transfer(at24cxx_client->adapter, msg, 2); if (ret == 2) { copy_to_user(buf, &data, 1); return 1; } else return -EIO; }
测试程序:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> /* i2c_test r addr * i2c_test w addr val */ void print_usage(char *file)/*打印用户信息*/ { printf("%s r addr\n", file); printf("%s w addr val\n", file); } int main(int argc, char **argv) { int fd; unsigned char buf[2]; if ((argc != 3) && (argc != 4))/*r w addr val*/ { print_usage(argv[0]); return -1; } fd = open("/dev/at24cxx", O_RDWR);/*打开驱动,可读可写*/ if (fd < 0) { printf("can't open /dev/at24cxx\n"); return -1; } if (strcmp(argv[1], "r") == 0)/*如果是读操作*/ { buf[0] = strtoul(argv[2], NULL, 0); read(fd, buf, 1); printf("data: %c, %d, 0x%2x\n", buf[0], buf[0], buf[0]); } else if (strcmp(argv[1], "w") == 0)/*如果是写操作*/ { buf[0] = strtoul(argv[2], NULL, 0); buf[1] = strtoul(argv[3], NULL, 0); write(fd, buf, 2); } else { print_usage(argv[0]); return -1; } return 0; }
测试结果:
./i2c_test 查看用法
./i2c_test w 1 7 写地址数据
./i2c_test r 1 读地址数据
相关文章推荐
- IIC专题(二)-使用内核原代码提供的at24.c驱动来操作I2C的at24lc04 EEPROM
- I2C-dev.c驱动代码分析
- 展讯智能机平台代码i2c设备驱动解读(上)
- 展讯智能机平台代码 i2c设备驱动解读(下)
- I/O口模拟I2C驱动代码
- MTK I2C驱动代码分析
- I2C的内核驱动测试代码
- i2c设备驱动(ds1337实例:内核代码)
- FPGA音频编解码驱动及I2C写入代码
- Linux I2C Input设备驱动代码的几点理解
- I2C总线驱动代码
- Linux I2C Input设备驱动代码的几点理解
- 虚拟内存设备驱动memdev及实例代码
- 无驱动执行Ring0代码
- 2-驱动_加载函数_卸载函数_注册设备号_led驱动示例代码
- FPGA驱动OLED动态显示(Verilog代码)——工程文件之间关系
- linux驱动学习(八) i2c驱动架构(史上最全) davinc dm368 i2c驱动分析
- Arduino驱动I2C接口12864LCD大屏液晶模块方法及库文件
- 《解决问题1000.1》又是I2C驱动问题,产品应用上一个问题,非常简单,也比较典型,记录一下
- LM75温度芯片I2C驱动