Linux驱动下的IIC总线的介绍以及相关API与例程

目录

IIC简介

相关API函数

通过iic的通道的编号获取iic适配器的结构体

向内核注册iic的设备信息

取消iic注册

释放iic的控制器

向内核注册一个iic驱动层的信息结构体

取消iic的驱动层注册

iic数据的交互

相关例程

例程分析

例程分享

设备层

驱动层

应用层


IIC简介

I2C(Inter-Integrated Circuit)是一种飞利浦(Philips)于1980年推出的同步串行半双工通信协议,用于连接多个从机设备到一个主机,并且支持多主多从的连接。I2C总线由两条线组成:时钟线(SCL)和数据线(SDA)。这两条线由硬件进行传输,传输速率随波特率而定,支持的速率有普通模式(100 kbps)、快速模式(400 kbps)和高速模式(3.4 Mbps)。

  1. 概念: IIC(Inter-Integrated Circuit)总线也称为I2C,是一种短距离串行总线标准,主要用于同一主板上各个IC或功能模块之间的通信。
  2. 工作原理: IIC总线包含串行时钟线SCL和串行数据线SDA,通过这两根信号线传输数据和时钟信号。它有主机和从机之分,通信总是由主机发起和控制。
  3. 工作模式: IIC总线的工作模式有主机写模式、主机读模式、主机随机读模式等。通过起始信号、地址帧、读写控制位、应答位、数据帧构成一个完整的传输过程。
  4. Linux中的实现: Linux内核提供I2C核心驱动框架和核心算法,上层基于框架实现特定I2C控制器的设备驱动;应用程序通过I2C设备节点访问总线设备。也提供了用户空间I2C工具用于测试和调试。
  5. 应用场景: IIC总线广泛应用于嵌入式系统中的各种小型传感器、驱动模块等的通信连接,具有线路简单、软硬件设计灵活等优点。

在Linux内核中,提供了I2C子系统来支持I2C总线的驱动和操作。以下是I2C总线相关的一些主要组件和概念:

  1. I2C适配器(I2C Adapter):I2C适配器是硬件和内核I2C子系统之间的接口,负责与硬件I2C控制器进行通信。每个I2C适配器对应一个物理I2C总线。
  2. I2C设备(I2C Device):I2C设备是连接到I2C总线上的外设设备,可以是传感器、存储器、显示屏等。每个I2C设备都有一个唯一的7位或10位地址。
  3. I2C驱动(I2C Driver):I2C驱动是用于控制和操作特定类型的I2C设备的内核模块。它提供了与I2C设备通信的接口,包括设备的初始化、读写数据等操作。

相关API函数

通过iic的通道的编号获取iic适配器的结构体

头文件
    linux/ic.h
原型
	struct i2c_adapter *i2c_get_adapter(int nr)
参数
    int nr    0
返回值
    适配器的核心结构体

向内核注册iic的设备信息

头文件
		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: 中断号。

取消iic注册

头文件
    linux/i2c.h
原型
    void i2c_unregister_device(struct i2c_client *client)
参数
    struct i2c_client *client    i2c的设备端结构体
返回值
    无

释放iic的控制器

头文件
    linux/i2c.c
原型
    void i2c_put_adapter(struct i2c_adapter *adap)
参数
    struct i2c_adapter *adap    iic的控制结构体
返回值
    无

向内核注册一个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设备层的名字,并且可以包含私有数据。

取消iic的驱动层注册

头文件
     linux/i2c.h
原型
    void i2c_del_driver(struct i2c_driver *driver)
参数
    struct i2c_driver *driver    iic驱动层的结构体    
返回值
    无

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设备的初始化。在这个例程中:

  1. 首先定义了全局变量​​myadapter​​​和​​myiic​​,分别表示I2C适配器和I2C设备。
  2. 在​​bh1750_device_init​​​函数中,通过​​i2c_get_adapter(0)​​来申请I2C适配器的结构体,并且向内核注册I2C设备信息。
  3. 在​​bh1750_device_exit​​函数中,取消了I2C设备的注册并释放了I2C适配器的控制器。

第二个例程是关于BH1750光照传感器的I2C设备的驱动的注册。在这个例程中:

  1. 定义了全局变量​​mydrv​​​、​​myclient​​​、​​myfops​​​和​​mymisc​​,分别表示I2C驱动、I2C客户端、文件操作以及杂项设备。
  2. 在​​myprobe​​函数中,当设备端和驱动端匹配成功后,注册了杂项设备。
  3. 在​​bh1750_driver_init​​函数中,向内核注册了I2C的驱动层信息结构体。
  4. 在​​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;
}

你可能感兴趣的:(Linux驱动开发,linux,驱动开发,c语言,mcu)