I2C 讲解:
在 JZ2440 开发板上, I2C 是由两条数据线构成的 SCL,SDA;SCL 作为时钟总线, SDA 作为数据总线; 两条线上可挂载 I2C 设备, 如: AT24C08
两条线连接 ARM9 I2C 控制器, 通过控制来控制 I2C 设备的识别设备地址, 读, 写操作; 如图所示
从中所知: I2C 线上可以挂载很多个 I2C 设备; 挂载简单, 只需要一根数据线和一根时钟线就可以挂载上去, 通过地址来去别每个设备的区别;
I2C 操作:
对 I2C 操作主要思想为: 1, 找到设备 2, 进行读写操作 主要原理为:
1, 发送开始信号 S 然后紧接着发生设备 8 位地址如: 0x50, 然后等待设备发出一个应答 ACK 信号
2, 当控制器接收到 ACK 信号后, 表面找到这条 I2C 总线上确实有这个设备, 然后发出数据, 是进行读还是进行写, 由第 8 位来决定
原理如下图:
JZ2440 对 I2c 驱动框架
正常的设备驱动程序, 大体框架为:
1, 通过应用程序 open,read,write,ioctl 函数去对硬件操作
2, 在内核里, 接收到应用程序发送过来的 open,read,write,ioctl 命令, 出发中断, 进而会跳到一个字符描述符的数组 (open 时返回值) 当中, 查找驱动程序 ID(驱动程序进行注册); 通过该 ID 找到相对应的 drv_open,drv_read,drv_wrte 等操作;
3,drv_open,drv_read,drv_wrte 等操作跟字符设备的硬件驱动程序紧密相关, 会最终会跳到字符设备驱动程序 file_operation 结构体进行硬件的操作过程.
4, 在此之前, 需要编写 I2C 驱动程序, 驱动程序要有主入口, 出口, file_openration 结构体, 进行注册等操作;
具体字符设备驱动程序框架为:
而 I2C 设备驱动采用的是总线 - 设备 - 驱动模型
总线 - 设备 - 驱动模型:
总线 - 设备 - 驱动模型: 其原理为在一条总线上分为两部分, 一部分作为设备链表对象的, 另一部分作为 driver 链表; 设备链表对象就是
注册一个设备 client 对象挂载到这条链表上, 但注册的设备名需要与右边 driver 链表注册的驱动名相比配才有意义, 才能对进入 driver 链表
上与设备名相匹配的驱动程序里面的 probe 函数进行 i2c 驱动操作; 举个例子: 将驱动程序比作学生, 驱动程序的名字比作学生的宿舍号码
管理员相当于总线, 总线知道两个信息: 1, 学生 2, 宿舍号 ; 当有一个学生进行入住, 学生想住 A10-208, 好, 经过管理员同意好, 就
住进 A10-208(注册一个 i2c_driver)了, 当管理员想找到那个学生, 则需要通过 A10-208 这个名字去查找到学生所在住处, 通过管理员
管理的学生信息本 (注册一个设备) 找到名字, 进而去到宿舍找到这个学生, 让他扫地, 打扫卫生什么的; 主要原理图如下:
i2c - 总线 - 驱动模型
应用程序是如何调用的呢? 如图所示
i2c 进行 open,read,write,ioctl 等操作会进入内核太, 找到相关设备驱动函数, 然后就进入了总线控制层, I2c 总线层两部分组成, 核心层
里面已经定义了对 i2c 提供的操作函数, 不需要用户自己定义, 适配器就是具体的对 2440 的 i2c 硬件操作或者定义其它的芯片的硬件操作;
总结: 总得来说, 我们需要进行的操作为
1, 注册一个设备, 里面要有设备名, 以及该设备 id, 并挂载到这条设备链表上
2, 注册一个驱动, 该设备驱动需要有设备名, probe 函数, id_table 设备地址, 然后挂载到驱动链表上
3, 比较两条链表上的设备名字是否相同, 如果有相同的话, 就去到相关的驱动设备上的 probe 函数, 进行
操作
4,probe 函数里面就是我们正常的驱动程序的编程程序了, 主入口, 出口, file_operation 结构体的构造等等操作; 最终应用程序会进入这里操作硬件对象
-----------------------------------------------------------------------------------------------------------------------------------------------
如图代码所示: 新建一个设备有多种方法, 该方法为第二种 i2c_new_probed_device
at24cxx_dev: 注册一个新设备
- #include <Linux/kernel.h>
- #include <Linux/module.h>
- #include <Linux/platform_device.h>
- #include <Linux/i2c.h>
- #include <Linux/err.h>
- #include <Linux/regmap.h>
- #include <Linux/slab.h>
- static struct i2c_client *at24cxx_client;
- static const unsigned short addr_list[] = { 0x60, 0x50, I2C_CLIENT_END };
- static int at24cxx_dev_init(void)
- {
- struct i2c_adapter *i2c_adap;
- struct i2c_board_info at24cxx_info;
- memset(&at24cxx_info, 0, sizeof(struct i2c_board_info));
- strlcpy(at24cxx_info.type, "at24c08", I2C_NAME_SIZE);
- i2c_adap = i2c_get_adapter(0);
- at24cxx_client = i2c_new_probed_device(i2c_adap, &at24cxx_info, addr_list, NULL);
- i2c_put_adapter(i2c_adap);
- if (at24cxx_client)
- return 0;
- else
- return -ENODEV;
- }
- static void at24cxx_dev_exit(void)
- {
- i2c_unregister_device(at24cxx_client);
- }
- module_init(at24cxx_dev_init);
- module_exit(at24cxx_dev_exit);
- MODULE_LICENSE("GPL");
at24cxx_drv: 注册一个新驱动 注: probe 函数并没有进行 i2c 的. 操作, 只是打印一些信息而已
- #include <Linux/kernel.h>
- #include <Linux/module.h>
- #include <Linux/platform_device.h>
- #include <Linux/i2c.h>
- #include <Linux/err.h>
- #include <Linux/regmap.h>
- #include <Linux/slab.h>
- static int __devinit at24cxx_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
- {
- printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
- return 0;
- }
- static int __devexit at24cxx_remove(struct i2c_client *client)
- {
- printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
- return 0;
- }
- static const struct i2c_device_id at24cxx_id_table[] = {
- { "at24c08", 0 },
- {}
- };
- /* 1. 分配 / 设置 i2c_driver */
- static struct i2c_driver at24cxx_driver = {
- .driver = {
- .name = "100ask",
- .owner = THIS_MODULE,
- },
- .probe = at24cxx_probe,
- .remove = __devexit_p(at24cxx_remove),
- .id_table = at24cxx_id_table,
- };
- static int at24cxx_drv_init(void)
- {
- /* 2. 注册 i2c_driver */
- i2c_add_driver(&at24cxx_driver);
- return 0;
- }
- static void at24cxx_drv_exit(void)
- {
- i2c_del_driver(&at24cxx_driver);
- }
- module_init(at24cxx_drv_init);
- module_exit(at24cxx_drv_exit);
- MODULE_LICENSE("GPL");
Makefile:
- KERN_DIR = /work/system/Linux-3.4.2
- all:
- make -C $(KERN_DIR) M=`pwd` modules
- clean:
- make -C $(KERN_DIR) M=`pwd` modules clean
- rm -rf modules.order
- obj-m += at24cxx_dev.o
- obj-m += at24cxx_drv.o
- #obj-m += i2c_bus_s3c2440.o
-----------------------------------------------------------------------------------------------------------------------------------------------
对 I2c 驱动的操作
前面已经讲过对于 I2c 的操作可以调用总线上现成的函数来操作 i2c_smbus_write_byte_data
at24cxx_dev:
- #include <Linux/kernel.h>
- #include <Linux/module.h>
- #include <Linux/platform_device.h>
- #include <Linux/i2c.h>
- #include <Linux/err.h>
- #include <Linux/regmap.h>
- #include <Linux/slab.h>
- static struct i2c_board_info at24cxx_info = {
- I2C_BOARD_INFO("at24c08", 0x50),
- };
- static struct i2c_client *at24cxx_client;
- static int at24cxx_dev_init(void)
- {
- struct i2c_adapter *i2c_adap;
- i2c_adap = i2c_get_adapter(0);
- at24cxx_client = i2c_new_device(i2c_adap, &at24cxx_info);
- i2c_put_adapter(i2c_adap);
- return 0;
- }
- static void at24cxx_dev_exit(void)
- {
- i2c_unregister_device(at24cxx_client);
- }
- module_init(at24cxx_dev_init);
- module_exit(at24cxx_dev_exit);
- MODULE_LICENSE("GPL");
at24cxx_drv: 注册驱动程序, 进行 I2c 操作
- #include <Linux/kernel.h>
- #include <Linux/module.h>
- #include <Linux/platform_device.h>
- #include <Linux/i2c.h>
- #include <Linux/err.h>
- #include <Linux/regmap.h>
- #include <Linux/slab.h>
- #include <Linux/fs.h>
- #include <asm/uaccess.h>
- static int major;
- static struct class *class;
- static struct i2c_client *at24cxx_client;
- /* 传入: buf[0] : addr
- * 输出: buf[0] : data
- */
- static ssize_t at24cxx_read(struct file * file, char __user *buf, size_t count, loff_t *off)
- {
- unsigned char addr, data;
- copy_from_user(&addr, buf, 1);
- data = i2c_smbus_read_byte_data(at24cxx_client, addr);
- copy_to_user(buf, &data, 1);
- return 1;
- }
- /* buf[0] : addr
- * buf[1] : data
- */
- static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t count, loff_t *off)
- {
- unsigned char ker_buf[2];
- unsigned char addr, data;
- copy_from_user(ker_buf, buf, 2);
- addr = ker_buf[0];
- data = ker_buf[1];
- printk("addr = 0x%02x, data = 0x%02x\n", addr, data);
- if (!i2c_smbus_write_byte_data(at24cxx_client, addr, data))
- return 2;
- else
- return -EIO;
- }
- static struct file_operations at24cxx_fops = {
- .owner = THIS_MODULE,
- .read = at24cxx_read,
- .write = at24cxx_write,
- };
- static int __devinit at24cxx_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
- {
- at24cxx_client = client;
- //printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
- major = register_chrdev(0, "at24cxx", &at24cxx_fops);
- class = class_create(THIS_MODULE, "at24cxx");
- device_create(class, NULL, MKDEV(major, 0), NULL, "at24cxx"); /*/dev/at24cxx */
- return 0;
- }
- static int __devexit at24cxx_remove(struct i2c_client *client)
- {
- //printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
- device_destroy(class, MKDEV(major, 0));
- class_destroy(class);
- unregister_chrdev(major, "at24cxx");
- return 0;
- }
- static const struct i2c_device_id at24cxx_id_table[] = {
- { "at24c08", 0 },
- {}
- };
- /* 1. 分配 / 设置 i2c_driver */
- static struct i2c_driver at24cxx_driver = {
- .driver = {
- .name = "100ask",
- .owner = THIS_MODULE,
- },
- .probe = at24cxx_probe,
- .remove = __devexit_p(at24cxx_remove),
- .id_table = at24cxx_id_table,
- };
- static int at24cxx_drv_init(void)
- {
- /* 2. 注册 i2c_driver */
- i2c_add_driver(&at24cxx_driver);
- return 0;
- }
- static void at24cxx_drv_exit(void)
- {
- i2c_del_driver(&at24cxx_driver);
- }
- module_init(at24cxx_drv_init);
- module_exit(at24cxx_drv_exit);
- MODULE_LICENSE("GPL");
应用测试程序 i2c_test:
- #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))
- {
- 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) && (argc == 4))
- {
- buf[0] = strtoul(argv[2], NULL, 0);
- buf[1] = strtoul(argv[3], NULL, 0);
- if (write(fd, buf, 2) != 2)
- printf("write err, addr = 0x%02x, data = 0x%02x\n", buf[0], buf[1]);
- }
- else
- {
- print_usage(argv[0]);
- return -1;
- }
- return 0;
- }
Makefile:
- KERN_DIR = /work/system/Linux-3.4.2
- all:
- make -C $(KERN_DIR) M=`pwd` modules
- clean:
- make -C $(KERN_DIR) M=`pwd` modules clean
- rm -rf modules.order
- obj-m += at24cxx_dev.o
- obj-m += at24cxx_drv.o
- #obj-m += i2c_bus_s3c2440.o
来源: http://www.bubuko.com/infodetail-3010915.html