当前位置: 首页 > news >正文

Linux驱动开发:I2C子系统

目录

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;
}
http://www.lryc.cn/news/66091.html

相关文章:

  • [C++] 动态内存与智能指针
  • 多态的原理
  • RK3588平台开发系列讲解(内存篇)Linux 伙伴系统数据结构
  • Windows(MFC/C++)上进程间通讯的几种简单又实用的方法
  • 嘉兴桐乡会计考证培训-备考中级职称有必要报班吗?
  • java元注解和自定义注解的区别
  • 技术到底是什么
  • 什么CRM客户管理系统最好?
  • 吴军《计算之魂》读后感
  • CSS进阶
  • 金兰组织 | 2023金兰解决方案集经营管理篇正式发布
  • 【python】pytorch包:深度学习(序章)
  • HTML <acronym> 标签
  • python基本数据类型 - 字典集合
  • python数据类型总结
  • TS内置类型总结
  • Spring Cloud Alibaba: Gateway 网关过滤器 GatewayGatewayFilter factory (记录)
  • Windows Server 2016版本说明
  • 车载红外夜视「升温」
  • ext3 文件系统的特点、优缺点以及使用场景
  • rk3568 修改开机logo
  • golang实现关键路径算法
  • Overcoming catastrophic forgetting in neural networks
  • [Linux] Linux文件系统
  • 有仰拍相机和俯拍相机时,俯拍相机中心和吸嘴中心的标定
  • 【Vue学习笔记5】Vue3中的响应式:ref和reactive、watchEffect和watch
  • 自动化测试工具的基本原理以及应用场景
  • 《Java虚拟机学习》 java代码的运行过程
  • 关于Intel处理器架构中AVX2里Gather特性的说明
  • UNIX常用命令(C站最全,一文通关)