当前位置:   article > 正文

Linux驱动开发—内核I2C驱动详解_linux中i2c总线驱动程序的开发

linux中i2c总线驱动程序的开发

I2C驱动文件结构

  • i2c-core.c I2C 核心的功能以及/proc/bus/i2c*接口;

  • i2c-dev.c 并没有针对特定的设备而设计,只是提供了通用的read()、write()和ioctl()等接口,但是不负责具体时序或者业务,具体的时序和业务由上层业务去实现;

  • i2c.h 对i2c_adapter、i2c_algorithm、i2c_driver和i2c_client这4个数据结构进行了定义。

  • busses文件夹 各大厂商的一些I2C适配器的驱动,由厂家编写;如S3C2410的驱动i2c-s3c2410.c;

  • algos文件夹 实现了一些I2C总线适配器的通信方法;

归纳起来就是一个内核驱动和几个数据结构体:

  • I2C适配器内核驱动:负责初始化和使能I2C,然后生成I2C适配器结构体,并加载进I2C核心。

  • I2C适配器结构体(struct i2c_adapter):提供统一的I2C数据接口,描述一个实际的 IIC 物理硬件。

  • I2C算法结构体(struct i2c_algorithm):提供I2C的读写操作接口,用于底层对接实际的控制器,产生特定 SOC 硬件(在i2c_adapter结构体中使用)的 IIC 模块产生通信波形的方法。

  • I2C传参结构体(struct i2c_msg):提供I2C收发通信的传参接口。

  • I2C初始化结构体(struct i2c_driver):用于描述一个 I2C 设备的驱动,需要我们自己编写。

  • i2c_client:对应一个实际硬件上的 I2C device,不需要我们编写,该数据结构是由内核根据设备注册信息自动生成,设备驱动根据硬件实际情况填充。
    在这里插入图片描述

【driver 驱动层】由普通驱动工程师负责,【i2c 核心层】由 Linux 提供,【i2c 核心层】以下由芯片原厂负责。

I2C数据传输过程

在I2C通讯过程中有两个重要的结构体(函数),一个是i2c_msgi2c_transfer函数。
在这里插入图片描述
使用统一的接口i2c_transfer,不关使用哪个芯片,他最终都会调用i2c_transfer,来选择某一款I2C控制器,把数据发送出去,或者从I2c设备读到数据,对于每一次传输的数据都可以用一个i2c_msg结构体来表示。
在这里插入图片描述

i2c_transfer

在I2C驱动中,使用i2c_transfer来传输I2C数据,此函数通过I2C适配器的算法master_xfer进行操作:

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
    adap->algo->master_xfer(adap, msgs, num); //调用适配器的算法
}
  • 1
  • 2
  • 3
  • 4

i2c_msg

i2c_msg结构体的成员变量如下所示:

struct i2c_msg {
    __u16 addr;                 /* IIC设备的基地址,7位 */
    __u16 flags;               /*操作标志位*/
    __u16 len;                /* 读写数据的长度 */
    __u8 *buf;               /* 装有数据的缓冲区 */
    #define I2C_M_RD	0x0001	/* 设置了这个标志位表示本次通信i2c控制器是处于接收方,否则就是发送方 */
    #define I2C_M_TEN	0x0010	/* 设置了这个标志位表示从设备的地址是10bit */
    #define I2C_M_DMA_SAFE	0x0200	/* the buffer of this message is DMA safe */
    /* makes only sense in kernelspace */
    /* userspace buffers are copied anyway */
    #define I2C_M_RECV_LEN	0x0400	/* length will be first received byte */
    #define I2C_M_NO_RD_ACK		0x0800	/* 设置这个标志位表示在读操作中主机不用ACK */
    #define I2C_M_IGNORE_NAK	0x1000	/* 设置这个标志意味当前i2c_msg忽略I2C器件的ack和nack信号 */
    #define I2C_M_REV_DIR_ADDR	0x2000        /* if I2C_FUNC_PROTOCOL_MANGLING */
    #define I2C_M_NOSTART	0x4000        /* if I2C_FUNC_NOSTART */
    #define I2C_M_STOP	0x8000        /* if I2C_FUNC_PROTOCOL_MANGLING */
};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

特别提醒: 一个i2c_msg结构的变量,代表着一次单方向的传输,故我们如果需要进行读写操作就需要两个i2c_msg结构的变量,分别通过对结构体成员变量flag的赋值来确定传输方向,主要赋值函数如下:

static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,unsigned short flags,char read_write, u8 command, int size,union i2c_smbus_data *data)
{

	unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];
	unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];
	int num = read_write == I2C_SMBUS_READ ? 2 : 1;
	int i;
	u8 partial_pec = 0;
	int status;
	struct i2c_msg msg[2] = {
		{
			/* msg[0]为发送要读取的首地址 */
			.addr = addr,
			.flags = flags,
			.len = 1,	//表示寄存器地址字节长度,是以byte为单位
			.buf = msgbuf0,
		}, {
			/* msg[1]读取数据 */
			.addr = addr,
			.flags = flags | I2C_M_RD,
			.len = 0,	//表示期望读到数据的字节长度(寄存器长度),是以byte为单位
			.buf = msgbuf1,	//将读取到的数据保存在buffer中
		},
	};

	/*····省略一些switch循环,*/
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

由于读操作分为了写和读两个方向,因此可以采用两个i2c_msg结构体变量,并使用结构体数组进行保存。

I2C通讯常用的接口函数(老版本)

快速读写接口函数:(连续读写)

i2c_smbus_read_byte();
i2c_smbus_write_byte();
  • 1
  • 2

常用的读操作接口函数:

i2c_smbus_read_byte_data();

i2c_smbus_read_word_data();

i2c_smbus_read_block_data();
  • 1
  • 2
  • 3
  • 4
  • 5

常用的写操作接口函数:

i2c_smbus_write_byte_data();

i2c_smbus_write_word_data();

i2c_smbus_write_block_data();
  • 1
  • 2
  • 3
  • 4
  • 5

i2c_smbus_read_byte_data

s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command);
  • 1

函数功能: 从指定的i2c从设备的指定地址空间中读取一个字节的数据

形参列表:

client: i2c客户端 -- 指定的要读取数据的i2c从设备

command:i2c从设备内部的地址空间 -- 指定要从设备的哪个地址空间中读取数据
  • 1
  • 2
  • 3

返回值:

成功:读取到的数据(一个字节数据)

失败:负数
  • 1
  • 2
  • 3

i2c_smbus_write_byte_data

s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, u8 value);
  • 1

函数功能: 向指定的i2c从设备中的指定地址空间写入一个字节的数据

形参列表:

client:i2c客户端 -- 指定要写入数据的i2c从设备

command:i2c从设备内部的地址空间 -- 把数据写入到哪个地址空间中

value:要写入的数据(一个字节数据)
  • 1
  • 2
  • 3
  • 4
  • 5

返回值:

成功:0

失败:负数
  • 1
  • 2
  • 3

I2C驱动框架

/* i2c 驱动的 probe 函数 */
static int xxx_probe(struct i2c_client *client,const struct i2c_device_id *id) {
	/* 函数具体程序 */
	return 0;
}
/* i2c 驱动的 remove 函数 */
static int xxx_remove(struct i2c_client *client) {
	/* 函数具体程序 */
	return 0;
}
/* 传统匹配方式 ID 列表 */
static const struct i2c_device_id xxx_id[] = {
	{"xxx", 0},
	{}
};
/* 设备树匹配列表 */
static const struct of_device_id xxx_of_match[] = {
	{ .compatible = "xxx" },
	{ /* Sentinel */ }
};
/* i2c 驱动结构体 */
static struct i2c_driver xxx_driver = {
	.probe = xxx_probe,
	.remove = xxx_remove,
	.driver = {
		.owner = THIS_MODULE,
		.name = "xxx",
		.of_match_table = xxx_of_match,
	},
	.id_table = xxx_id,
};
/* 驱动入口函数 */
static int __init xxx_init(void) {
	int ret = 0;
	ret = i2c_add_driver(&xxx_driver);
	return ret;
}
/* 驱动出口函数 */
static void __exit xxx_exit(void) {
	i2c_del_driver(&xxx_driver);
}

module_init(xxx_init);
module_exit(xxx_exit);
MODULE_LICENSE("GPL");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

ap3216c光电传感器驱动

#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/workqueue.h>
#include <linux/errno.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/input/aw9523_cfg.h>

#define AP3216C_CNT	1
#define AP3216C_NAME	"ap3216c"

struct ap3216c_dev {
	dev_t devid;				/* 设备号 	 */
	struct cdev cdev;			/* cdev 	*/
	struct class *class;		/* 类 		*/
	struct device *device;		/* 设备 	 */
	struct device_node	*nd; 	/* 设备节点 */
	int major;					/* 主设备号 */
	void *private_data;			/* 私有数据 */
	unsigned short ir, als, ps;		/* 三个光传感器数据 */
};
static struct ap3216c_dev ap3216cdev;
/* 从ap3216c读取多个寄存器数据 */
static int ap3216c_read_regs(struct ap3216c_dev *dev, u8 reg, void *val, int len){
	int ret;
	struct i2c_msg msg[2];
	struct i2c_client *client = (struct i2c_client *)dev->private_data;

	/* msg[0]为发送要读取的首地址 */
	msg[0].addr = client->addr;			/* ap3216c地址 */
	msg[0].flags = 0;					/* 标记为发送数据 */
	msg[0].buf = &reg;					/* 读取的首地址 */
	msg[0].len = 1;						/* reg长度*/

	/* msg[1]读取数据 */
	msg[1].addr = client->addr;			/* ap3216c地址 */
	msg[1].flags = I2C_M_RD;			/* 标记为读取数据*/
	msg[1].buf = val;					/* 读取数据缓冲区 */
	msg[1].len = len;					/* 要读取的数据长度*/

	ret = i2c_transfer(client->adapter, msg, 2);
	if(ret == 2) {
		ret = 0;
	} else {
		printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len);
		ret = -EREMOTEIO;
	}
	return ret;
}
/* 向ap3216c多个寄存器写入数据 */
static s32 ap3216c_write_regs(struct ap3216c_dev *dev, u8 reg, u8 *buf, u8 len){
	u8 b[256];
	struct i2c_msg msg;
	struct i2c_client *client = (struct i2c_client *)dev->private_data;
	
	b[0] = reg;					/* 寄存器首地址 */
	memcpy(&b[1],buf,len);		/* 将要写入的数据拷贝到数组b里面 */
		
	msg.addr = client->addr;	/* ap3216c地址 */
	msg.flags = 0;				/* 标记为写数据 */

	msg.buf = b;				/* 要写入的数据缓冲区 */
	msg.len = len + 1;			/* 要写入的数据长度 */

	return i2c_transfer(client->adapter, &msg, 1);
}
/* 读取ap3216c指定寄存器值,读取一个寄存器 */
static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev, u8 reg){
	u8 data = 0;
	ap3216c_read_regs(dev, reg, &data, 1);
	return data;

#if 0
	struct i2c_client *client = (struct i2c_client *)dev->private_data;
	return i2c_smbus_read_byte_data(client, reg);
#endif
}
/* 向ap3216c指定寄存器写入指定的值,写一个寄存器 */
static void ap3216c_write_reg(struct ap3216c_dev *dev, u8 reg, u8 data){
	u8 buf = 0;
	buf = data;
	ap3216c_write_regs(dev, reg, &buf, 1);
}
/* 读取AP3216C的数据,读取原始数据 */
void ap3216c_readdata(struct ap3216c_dev *dev){
	unsigned char i =0;
    unsigned char buf[6];
	
	/* 循环读取所有传感器数据 */
    for(i = 0; i < 6; i++){
        buf[i] = ap3216c_read_reg(dev, AP3216C_IRDATALOW + i);	
    }

    if(buf[0] & 0X80) 	/* IR_OF位为1,则数据无效 */
		dev->ir = 0;					
	else 				/* 读取IR传感器的数据   		*/
		dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03); 			
	
	dev->als = ((unsigned short)buf[3] << 8) | buf[2];	/* 读取ALS传感器的数据 			 */  
	
    if(buf[4] & 0x40)	/* IR_OF位为1,则数据无效 			*/
		dev->ps = 0;    													
	else 				/* 读取PS传感器的数据    */
		dev->ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F); 
}
/* 打开设备 */
static int ap3216c_open(struct inode *inode, struct file *filp){
	filp->private_data = &ap3216cdev;
	/* 初始化AP3216C */
	ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0x04);	/* 复位AP3216C 			*/
	mdelay(50);													
	ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0X03);	/* 开启ALS、PS+IR 		*/
	return 0;
}
/* 从设备读取数据 */
static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off){
	short data[3];
	long err = 0;

	struct ap3216c_dev *dev = (struct ap3216c_dev *)filp->private_data;	
	ap3216c_readdata(dev);

	data[0] = dev->ir;
	data[1] = dev->als;
	data[2] = dev->ps;
	err = copy_to_user(buf, data, sizeof(data));
	return 0;
}
/* 关闭/释放设备 */
static int ap3216c_release(struct inode *inode, struct file *filp){
	return 0;
}
/* AP3216C操作函数*/
static const struct file_operations ap3216c_ops = {
	.owner = THIS_MODULE,
	.open = ap3216c_open,
	.read = ap3216c_read,
	.release = ap3216c_release,
};
/* i2c驱动的probe函数,当驱动与设备匹配以后此函数就会执行 */
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id){
	/* 1、构建设备号 */
	if (ap3216cdev.major) {
		ap3216cdev.devid = MKDEV(ap3216cdev.major, 0);
		register_chrdev_region(ap3216cdev.devid, AP3216C_CNT, AP3216C_NAME);
	} else {
		alloc_chrdev_region(&ap3216cdev.devid, 0, AP3216C_CNT, AP3216C_NAME);
		ap3216cdev.major = MAJOR(ap3216cdev.devid);
	}

	/* 2、注册设备 */
	cdev_init(&ap3216cdev.cdev, &ap3216c_ops);
	cdev_add(&ap3216cdev.cdev, ap3216cdev.devid, AP3216C_CNT);

	/* 3、创建类 */
	ap3216cdev.class = class_create(THIS_MODULE, AP3216C_NAME);
	if (IS_ERR(ap3216cdev.class)) {
		return PTR_ERR(ap3216cdev.class);
	}

	/* 4、创建设备 */
	ap3216cdev.device = device_create(ap3216cdev.class, NULL, ap3216cdev.devid, NULL, AP3216C_NAME);
	if (IS_ERR(ap3216cdev.device)) {
		return PTR_ERR(ap3216cdev.device);
	}

	ap3216cdev.private_data = client;

	return 0;
}
/* i2c驱动的remove函数,移除i2c驱动的时候此函数会执行 */
static int ap3216c_remove(struct i2c_client *client){
	/* 删除设备 */
	cdev_del(&ap3216cdev.cdev);
	unregister_chrdev_region(ap3216cdev.devid, AP3216C_CNT);

	/* 注销掉类和设备 */
	device_destroy(ap3216cdev.class, ap3216cdev.devid);
	class_destroy(ap3216cdev.class);
	return 0;
}
/* 传统匹配方式ID列表 */
static const struct i2c_device_id ap3216c_id[] = {
	{"alientek,ap3216c", 0},  
	{}
};
/* 设备树匹配列表 */
static const struct of_device_id ap3216c_of_match[] = {
	{ .compatible = "alientek,ap3216c" },
	{ /* Sentinel */ }
};
/* i2c驱动结构体 */	
static struct i2c_driver ap3216c_driver = {
	.probe = ap3216c_probe, /*通过match方法匹配后会调用probe函数*/
	.remove = ap3216c_remove,/*移除i2c驱动的时候此函数会执行*/
	.driver = {
			.owner = THIS_MODULE,
		   	.name = "ap3216c",
		   	.of_match_table = ap3216c_of_match, 
		   },
	.id_table = ap3216c_id,
};		   
/* 驱动入口函数 */
static int __init ap3216c_init(void){
	int ret = 0;
	ret = i2c_add_driver(&ap3216c_driver);//通过i2c_add_driver调用register向i2c-core注册ap3216c_driver的驱动;
	return ret;
}
/* 驱动出口函数 */
static void __exit ap3216c_exit(void){
	i2c_del_driver(&ap3216c_driver);
}

module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228

bit_xfer()函数

我们使用的接口i2c_transfer会调用bit_xfer函数,bit_xfer函数主要作用是按照I2C协议,通过调用i2c_starti2c_stop等函数产生满足I2C协议的高低电平。

/*该函数已删除判断函数,仅保留了操作语句*/
static int bit_xfer(struct i2c_adapter *i2c_adap,
		    struct i2c_msg msgs[], int num)
{
	
		/*产生开始位*/
		i2c_start(adap);

		/*发送从机地址*/
		ret = bit_doAddress(i2c_adap, pmsg);

		/*判断是读操作还是写操作*/
		if (pmsg->flags & I2C_M_RD) 
		{
			/*读操作*/
			ret = readbytes(i2c_adap, pmsg);
		}
		else 
		{
			/*写操作*/
			ret = sendbytes(i2c_adap, pmsg);
			
		}

		i2c_stop(adap);/*产生停止位*/
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

i2c-algo-bit.c文件

i2c-algo-bit.c文件为发出相应波形的函数,该文件位于 I2C -> algos文件夹下,有最开始的文件结构可知algos文件夹 主要是用来实现了一些I2C总线适配器的通信方法;

//sda拉低电平
static inline void sdalo(struct i2c_algo_bit_data *adap)
{
	setsda(adap, 0);
	udelay((adap->udelay + 1) / 2);
}


//sda拉高电平
static inline void sdahi(struct i2c_algo_bit_data *adap)
{
	setsda(adap, 1);
	udelay((adap->udelay + 1) / 2);
}

//scl拉低电平
static inline void scllo(struct i2c_algo_bit_data *adap)
{
	setscl(adap, 0);
	udelay(adap->udelay / 2);
}

static void i2c_start(struct i2c_algo_bit_data *adap)
{
	/* assert: scl, sda are high */
	setsda(adap, 0);
	udelay(adap->udelay);
	scllo(adap);
}

static void i2c_repstart(struct i2c_algo_bit_data *adap)
{
	/* assert: scl is low */
	sdahi(adap);
	sclhi(adap);
	setsda(adap, 0);
	udelay(adap->udelay);
	scllo(adap);
}


static void i2c_stop(struct i2c_algo_bit_data *adap)
{
	/* assert: scl is low */
	sdalo(adap);
	sclhi(adap);
	setsda(adap, 1);
	udelay(adap->udelay);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

结语

I2C驱动具有中间大、两头小的特点,即向上接口很小,被浓缩成一个接口即i2c_transfer(),向下也很小,最终都是到达i2c-algo-bit.c文件产生波形。中间你无论是哪个厂家,无论你使用什么算法,最终提供给驱动开发人员的接口一定是i2c_transfer()接口,向下使用的文件一定是i2c-algo-bit.c文件中的函数产生波形。

故对于开发者来说,不用关心硬件使用的是什么(理论上,实际还是需要考虑的),都可以很就容易的使用I2C驱动,并且一个好的I2C程序扩展性强,可移植性强!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/694477
推荐阅读
相关标签