目录
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;
}
相关文章
发表评论