目录

1、I2C简介

1.1 两根线

1.2 信号

1.3 写时序

1.4 读时序

1.5 I2C速率

1.6 I2C驱动框架简介

2、I2C设备驱动

2.1 I2C相关API

2.1.1 i2c_driver

2.1.2 注册:i2c_add_driver

2.1.3 注销:i2c_del_driver

2.1.4 module_i2c_driver:一键注册,不需要以上注册注销的过程

2.1.5 i2c_client

2.1.6 i2c_msg

2.1.7 i2c_transfer 

3、驱动程序

3.1 写消息封装

3.2 读消息封装

3.3 驱动程序

3.3.1 修改设备树

3.3.2 驱动程序编写

3.4 应用程序

1、I2C简介

1.1 两根线

I2C总线只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL。

1.2 信号

空闲状态:I2C总线总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。

起始信号:当SCL为高电平期间,SDA由高到低的跳变;

停止信号:当SCL为高电平期间,SDA由低到高的跳变;

应答信号:在第九个时钟周期的时候,sda上低电平就代表应答

非应答信号:在第九个时候周期的时候,sda维持高电平

1.3 写时序

起始信号+七位从机地址+一位写(写是0 读是1)+ ack + 寄存器地址+数据+ ack +停止位

1.4 读时序

起始信号+七位从机地址+一位写(写是0 读是1)+ ack + 寄存器地址+ ack +

起始信号+七位从机地址+一位读(写实0 读是1)+ ack + 数据+NO ack + 停止位

1.5 I2C速率

100K 低速 400K 全速 3.4M 高速

1.6 I2C驱动框架简介

在Linux 内核中 I2C 的体系结构分为3 个部分: 1、I2C 核心:I2C 核心提供了I2C 总线驱动和设备驱动的注册、注销方法等。 2、I2C 总线驱动:I2C 总线驱动是对I2C 硬件体系结构中适配器端的实现,适配器可由 CPU 控制,甚至可以直接集成在CPU 内部。一般SOC 的 I2C 总线驱动都是由半导体厂商编写的,不需要用户去编写。因此我们不用关心 I2C 总线驱动具体是如何实现的,我们只要专注于 I2C 设备驱动即可。 3、I2C 设备驱动:I2C 设备驱动是对I2C 硬件体系结构中设备端的实现,设备一般挂接在受CPU 控制的I2C 适配器上,通过I2C 适配器与CPU 交换数据。

2、I2C设备驱动

2.1 I2C相关API

2.1.1 i2c_driver

struct i2c_driver {

int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);

int (*remove)(struct i2c_client *client);

struct device_driver driver;

};

struct device_driver {

const char *name;

const struct of_device_id *of_match_table;

};

2.1.2 注册:i2c_add_driver

#define i2c_add_driver(driver)

i2c_register_driver(THIS_MODULE, driver)

2.1.3 注销:i2c_del_driver

void i2c_del_driver(struct i2c_driver *driver)

2.1.4 module_i2c_driver:一键注册,不需要以上注册注销的过程

定义在 linux/i2c.h 中

#define module_i2c_driver(__i2c_driver)

module_driver(__i2c_driver, i2c_add_driver,

i2c_del_driver)

#define module_driver(__driver, __register, __unregister, ...)

static int __init __driver##_init(void)

{

return __register(&(__driver) , ##__VA_ARGS__);

}

module_init(__driver##_init);

static void __exit __driver##_exit(void)

{

__unregister(&(__driver) , ##__VA_ARGS__);

}

module_exit(__driver##_exit);

module_i2c_driver(myi2c);

->

#define module_i2c_driver(myi2c)

module_driver(myi2c, i2c_add_driver, i2c_del_driver)

#define module_driver(myi2c, i2c_add_driver, i2c_del_driver)

static int __init myi2c_init(void)

{

return i2c_add_driver(&myi2c);

}

static void __exit __driver##_exit(void)

{

i2c_del_driver(&myi2c);

}

module_init(myi2c_init);

module_exit(myi2c_exit);

2.1.5 i2c_client

struct i2c_client {

unsigned short flags; // 0写 1读

unsigned short addr; //从机地址

char name[I2C_NAME_SIZE]; //驱动的名字

struct i2c_adapter *adapter;//控制器驱动的对象

struct device dev; //这是设备的对象

};

2.1.6 i2c_msg

有多少起始信号就有多少消息,消息的长度用字节表示

struct i2c_msg {

__u16 addr; //从机地址

__u16 flags; //读写标志位 0写 1度

__u16 len; //消息长度

__u8 *buf; //消息首地址

};

2.1.7 i2c_transfer 

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

/*

功能:消息的发送函数

参数:

@adap:控制器的结构体对象

@msgs:消息结构体的首地址

@num:消息结构体的个数

返回值:成功返回num,否则就是失败

*/

3、驱动程序

3.1 写消息封装

起始信号+七位从机地址+一位写(写是0 读是1)+ ack + 寄存器地址+数据+ ack +停止位

char w_buf[] = {地址,数据};

struct i2c_msg w_msg = {

.addr = client->addr,

.flags = 0,

.len = 2,

.buf = w_buf,

};

3.2 读消息封装

起始信号+七位从机地址+一位写(写是0 读是1)+ ack + 寄存器地址+ ack +

起始信号+七位从机地址+一位读(写实0 读是1)+ ack + 数据+NO ack + 停止位

char r_buf[] = {地址};

char val;

struct i2c_msg r_msg[] = {

[0] = {

.addr = client->addr,

.flags = 0,

.len = 1,

.buf = r_buf,

},

[1] = {

.addr = client->addr,

.flags = 1,

.len = 1,

.buf = &val;

},

};

3.3 驱动程序

3.3.1 修改设备树

&i2c1{

pinctrl-names = "default", "sleep";

pinctrl-0 = <&i2c1_pins_b>;

pinctrl-1 = <&i2c1_sleep_pins_b>;

i2c-scl-rising-time-ns = <100>;

i2c-scl-falling-time-ns = <7>;

status = "okay";

/delete-property/dmas;

/delete-property/dma-names;

si7006@40{

compatible = "aaa,si7006";

reg = <0x40>;

};

};

3.3.2 驱动程序编写

由于 client 在各个函数中都需要用到,所以有两种方法,第一种是直接定义一个全局变量,在probe函数中获取到这个 client ,第二种实际上和第一种几乎一致,就是用一个 private_data 的指针去接住这个client。

#ifndef __SI7006_H__

#define __SI7006_H__

#define GET_HUM _IOR('l',0,int)

#define GET_TMP _IOR('l',1,int)

#define HUM_ADDR 0xe5

#define TMP_ADDR 0xe3

#endif

#define I2CNAME "si7006"

struct i2c_client* gclient;

int major = 0;

struct class* cls;

struct device* dev;

int i2c_read(unsigned char reg)

{

// 1.封装消息

int ret;

unsigned char r_buf[] = { reg };

unsigned short val;

struct i2c_msg r_msg[] = {

[0] = {

.addr = gclient->addr,

.flags = 0,

.len = 1,

.buf = r_buf,

},

[1] = {

.addr = gclient->addr,

.flags = 1,

.len = 2,

.buf = (char *)&val,

},

};

// 2.发送消息

ret = i2c_transfer(gclient->adapter, r_msg, ARRAY_SIZE(r_msg));

if (ret != ARRAY_SIZE(r_msg)) {

printk("i2c read hum or temp error\n");

return -EAGAIN;

}

return val >> 8 | val << 8;

}

int si7006_open(struct inode* inode, struct file* file)

{

printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);

return 0;

}

long si7006_ioctl(struct file* file,

unsigned int cmd, unsigned long arg)

{

int ret, data;

switch (cmd) {

case GET_HUM:

data = i2c_read(HUM_ADDR);

ret = copy_to_user((void*)arg, &data, 4);

break;

case GET_TMP:

data = i2c_read(TMP_ADDR);

ret = copy_to_user((void*)arg, &data, 4);

break;

}

return 0;

}

int si7006_close(struct inode* inode, struct file* file)

{

printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);

return 0;

}

struct file_operations fops = {

.open = si7006_open,

.unlocked_ioctl = si7006_ioctl,

.release = si7006_close,

};

int si7006_probe(struct i2c_client* client, const struct i2c_device_id* id)

{

gclient = client;

// 1.注册字符设备驱动

major = register_chrdev(0, I2CNAME, &fops);

// 2.自动创建设备节点

cls = class_create(THIS_MODULE, I2CNAME);

dev = device_create(cls, &client->dev, MKDEV(major, 0), NULL, I2CNAME);

return 0;

}

int si7006_remove(struct i2c_client* client)

{

device_destroy(cls, MKDEV(major, 0));

class_destroy(cls);

unregister_chrdev(major, I2CNAME);

return 0;

}

struct of_device_id oftable[] = {

{

.compatible = "aaa,si7006",

},

{}

};

struct i2c_driver si7006 = {

.probe = si7006_probe,

.remove = si7006_remove,

.driver = {

.name = "bbb",

.of_match_table = oftable,

}

};

module_i2c_driver(si7006);

3.4 应用程序

#include "si7006.h"

int main(int argc, const char* argv[])

{

int fd;

int data,hum,tmp;

if ((fd = open("/dev/si7006", O_RDWR)) == -1)

PRINT_ERR("open error");

while (1) {

ioctl(fd, GET_HUM, &hum);

ioctl(fd, GET_TMP, &tmp);

usleep(2000);

}

close(fd);

return 0;

}

相关文章

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。