目录
IIC简介
相关API函数
通过iic的通道的编号获取iic适配器的结构体
向内核注册iic的设备信息
取消iic注册
释放iic的控制器
向内核注册一个iic驱动层的信息结构体
取消iic的驱动层注册
iic数据的交互
相关例程
例程分析
例程分享
设备层
驱动层
应用层
I2C(Inter-Integrated Circuit)是一种飞利浦(Philips)于1980年推出的同步串行半双工通信协议,用于连接多个从机设备到一个主机,并且支持多主多从的连接。I2C总线由两条线组成:时钟线(SCL)和数据线(SDA)。这两条线由硬件进行传输,传输速率随波特率而定,支持的速率有普通模式(100 kbps)、快速模式(400 kbps)和高速模式(3.4 Mbps)。
在Linux内核中,提供了I2C子系统来支持I2C总线的驱动和操作。以下是I2C总线相关的一些主要组件和概念:
头文件
linux/ic.h
原型
struct i2c_adapter *i2c_get_adapter(int nr)
参数
int nr 0
返回值
适配器的核心结构体
头文件
linux/i2c.h
原型
struct i2c_client * i2c_new_device(struct i2c_adapter * adap, struct i2c_board_info const * info)
参数
struct i2c_adapter * adap: 表示I2C适配器的结构体,用于指定要使用的适配器。
struct i2c_board_info const * info: 表示I2C设备端向内核注册相关资源信息的结构体,包括设备的名字、地址、中断等信息。
返回值
成功:返回一个指向I2C设备层的结构体的指针。
失败:返回NULL。
struct i2c_board_info结构体包含以下字段:
type[I2C_NAME_SIZE]: 用来匹配I2C驱动层的设备名字。
flags: 一些标志位。
addr: 从机地址。
platform_data: 设备数据,通常为NULL。
archdata: 与体系结构相关的数据。
of_node: 设备树节点。
irq: 中断号。
头文件
linux/i2c.h
原型
void i2c_unregister_device(struct i2c_client *client)
参数
struct i2c_client *client i2c的设备端结构体
返回值
无
头文件
linux/i2c.c
原型
void i2c_put_adapter(struct i2c_adapter *adap)
参数
struct i2c_adapter *adap iic的控制结构体
返回值
无
头文件
Linux/i2c.h
原型
#define i2c_add_driver(driver) \
i2c_register_driver(THIS_MODULE, driver)
参数
struct i2c_driver *driver iic的驱动层结构体
返回值
成功 0
失败 负数
struct i2c_driver {
unsigned int class;
int (*attach_adapter)(struct i2c_adapter *) __deprecated;
int (*detach_adapter)(struct i2c_adapter *) __deprecated;
/* Standard driver model interfaces */
int (*probe)(struct i2c_client *, const struct i2c_device_id *); 设备端和驱动端匹配成功后的执行函数
int (*remove)(struct i2c_client *); 设备端和驱动端有一端卸载调用的函数
/* driver model interfaces that don't relate to enumeration */
void (*shutdown)(struct i2c_client *);
int (*suspend)(struct i2c_client *, pm_message_t mesg);
int (*resume)(struct i2c_client *);
void (*alert)(struct i2c_client *, unsigned int data);
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver; 有一个用来匹配设备端的东西
const struct i2c_device_id *id_table; 另外一种匹配设备端的方式
/* Device detection callback for automatic device creation */
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
};
struct device_driver {
const char *name; 用来匹配iic设备层的名字
struct bus_type *bus; THIS_MODULE
struct module *owner;
const char *mod_name; /* used for built-in modules */
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
const struct of_device_id *of_match_table;
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
const struct attribute_group **groups;
const struct dev_pm_ops *pm;
struct driver_private *p;
};
struct i2c_device_id {
char name[I2C_NAME_SIZE]; 用来匹配iic设别层的名字
kernel_ulong_t driver_data /* Data private to the driver */
__attribute__((aligned(sizeof(kernel_ulong_t))));
};
struct i2c_driver 结构体包含了I2C设备驱动的相关信息,包括设备的probe、remove、shutdown、suspend、resume等函数指针,用于在设备端和驱动端匹配成功后的执行函数,以及设备端和驱动端有一端卸载调用的函数。
struct device_driver 结构体包含了设备驱动的相关信息,包括设备的名字、总线类型、probe、remove、shutdown、suspend、resume等函数指针,以及一些其他属性。
struct i2c_device_id 结构体用来匹配I2C设备层的名字,并且可以包含私有数据。
头文件
linux/i2c.h
原型
void i2c_del_driver(struct i2c_driver *driver)
参数
struct i2c_driver *driver iic驱动层的结构体
返回值
无
头文件
linux/i2c.h
原型
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs)
参数
struct i2c_adapter *adap iic的适配器
struct i2c_msg *msgs iicde 消息结构体
int num 消息结构体的个数
返回值
成功 0
失败 非零
struct i2c_msg {
__u16addr; 从机地址
__u16flags; 标志位
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
#define I2C_M_RD 0x0001 /* read data, from slave to master */
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
__u16len; 消息的长度
__u8*buf; 消息的指针
};
第一个程序是关于BH1750光照传感器的I2C设备的初始化。在这个例程中:
myadapter
和myiic
,分别表示I2C适配器和I2C设备。bh1750_device_init
函数中,通过i2c_get_adapter(0)
来申请I2C适配器的结构体,并且向内核注册I2C设备信息。bh1750_device_exit
函数中,取消了I2C设备的注册并释放了I2C适配器的控制器。第二个例程是关于BH1750光照传感器的I2C设备的驱动的注册。在这个例程中:
mydrv
、myclient
、myfops
和mymisc
,分别表示I2C驱动、I2C客户端、文件操作以及杂项设备。myprobe
函数中,当设备端和驱动端匹配成功后,注册了杂项设备。bh1750_driver_init
函数中,向内核注册了I2C的驱动层信息结构体。bh1750_driver_exit
函数中,取消了I2C驱动的注册。#include
#include
#include
// 定义全局变量
struct i2c_adapter *myadapter = NULL; // 定义一个指向I2C适配器的指针
struct i2c_board_info myinfo = { // 定义I2C设备信息结构体
.type = "mybh1750", // 设备类型
.addr = 0x23, // 设备地址
.platform_data = NULL, // 平台数据
};
struct i2c_client *myiic = NULL; // 定义一个指向I2C设备的指针
// 模块初始化函数
static int __init bh1750_device_init(void) {
// 申请iic的适配器的结构体
myadapter = i2c_get_adapter(0); // 通过适配器编号获取I2C适配器结构体
if (myadapter == NULL) {
// 有些iic适配器申请不到可以直接使用
printk("申请iic适配结构体失败\n");
}
// 向内核注册iic设备信息
myiic = i2c_new_device(myadapter, &myinfo); // 向内核注册新的I2C设备
return 0;
}
// 模块退出函数
static void __exit bh1750_device_exit(void) {
// 取消iic注册
i2c_unregister_device(myiic); // 取消I2C设备的注册
// 释放iic的控制器
i2c_put_adapter(myadapter); // 释放I2C适配器的控制器
}
// 指定模块初始化和退出函数
module_init(bh1750_device_init);
module_exit(bh1750_device_exit);
MODULE_LICENSE("GPL"); // 指定模块的许可证
#include
#include
#include
#include
#include
#include
// 函数声明
int myprobe(struct i2c_client *i2c_client, const struct i2c_device_id *i2c_device_id);
int myremove(struct i2c_client *i2c_client);
int myopen(struct inode *inode, struct file *file);
int myclose(struct inode *inode, struct file *file);
ssize_t myread(struct file *file, char __user *buf, size_t size, loff_t *loff);
// 定义I2C设备ID
struct i2c_device_id myinfo = {
.name = "mybh1750"
};
// 定义I2C驱动
struct i2c_driver mydrv = {
.driver = {
.name = "mybh1750"
},
.driver.owner = THIS_MODULE,
.id_table = &myinfo,
.probe = myprobe,
.remove = myremove,
};
// 定义杂项设备和I2C客户端
struct miscdevice mymisc;
struct i2c_client myclient;
struct file_operations myfops;
// 打开函数
int myopen(struct inode *inode, struct file *file) {
struct i2c_msg msg;
__u8 data = 0x10;
printk("iic设备被打开\n");
msg.addr = myclient.addr;
msg.flags = 0; // 写
msg.buf = &data;
msg.len = 1; // 一个字节
i2c_transfer(myclient.adapter, &msg, 1);
return 0;
}
// 关闭函数
int myclose(struct inode *inode, struct file *file) {
printk("iic设备被关闭\n");
return 0;
}
// 读取函数
ssize_t myread(struct file *file, char __user *buf, size_t size, loff_t *loff) {
__u8 data[2];
int ret;
struct i2c_msg msg;
msg.addr = myclient.addr;
msg.flags = 1; // 读
msg.buf = data;
msg.len = 2; // 两个字节
i2c_transfer(myclient.adapter, &msg, 1);
ret = copy_to_user(buf, data, 2);
return 0;
}
// 设备探测函数
int myprobe(struct i2c_client *i2c_client, const struct i2c_device_id *i2c_device_id) {
myclient = *i2c_client;
printk("设备端和驱动端匹配成功\n");
// 注册杂项设备
myfops.owner = THIS_MODULE;
myfops.open = myopen;
myfops.release = myclose;
myfops.read = myread;
mymisc.minor = 255;
mymisc.name = "mybh1750";
mymisc.fops = &myfops;
misc_register(&mymisc);
return 0;
}
// 设备移除函数
int myremove(struct i2c_client *i2c_client) {
// 可以在此处执行设备移除时的清理操作
return 0;
}
// 模块初始化函数
static int __init bh1750_driver_init(void) {
// 向内核注册iic的驱动层信息结构体
i2c_add_driver(&mydrv);
return 0;
}
// 模块退出函数
static void __exit bh1750_driver_exit(void) {
i2c_del_driver(&mydrv);
}
// 指定模块初始化和退出函数
module_init(bh1750_driver_init);
module_exit(bh1750_driver_exit);
MODULE_LICENSE("GPL"); // 指定模块的许可证
#include
#include
#include
#include
#include
int main(){
int fd = 0;
char data[2] = {0};
fd = open("/dev/mybh1750", O_RDWR);
if(fd < 0){
printf("打开失败\n");
perror("open");
}
while(1){
read(fd, data, 2);
printf("光照数值 %f\n", (data[0] << 8 | data[1]) / 1.2);
sleep(1);
}
return 0;
}