文章目录

  • 1.i2c总线
    • 1.1i2c总线驱动硬件知识复习
    • 1.2i2c总线驱动框架结构
    • 1.3i2c总线驱动的API
    • 1.4i2c总线驱动的实例
  • 2.i2c总线驱动
    • 2.1si7006(sht20)设备树的编写
      • 2.1.1画出硬件连接图
      • 2.1.2找到控制器的设备树
      • 2.1.3编写自己的设备树
    • 2.2i2c中相关的结构体
    • 2.3封装数据发送数据的过程
      • 2.3.1消息的封装过程
      • 2.3.2消息的发送过程
    • 2.4读取si7006串号和固件号实例
    • 2.5读取si7006温湿度实例

1.i2c总线

1.1i2c总线驱动硬件知识复习

(1)i2c几根线?

scl:时钟线sda:数据线

(2)几种信号?

start:起始信号:scl为高电平的时候,sda从高到低的跳变stop:结束信号:scl为高电平的时候,sda从低到高的跳变ack:在第九个时钟周期的时候,sda总线是低电平就代表应答(master--->slave   slave ---->master)No ack:在第九个时钟周期的时候,sda总线持续高电平

(3)i2c读写时序

写时序:\*\*start+ (从机地址 7bit + 0(写) 1bit) + ack + 寄存器地址(8bit|16bit)+ack \*\***+data(8bit|16bit) + ack + stop**读时序:**start+ (从机地址 7bit + 0(写) 1bit) + ack + 寄存器地址(8bit|16bit)+ack ****start+ (从机地址 7bit + 1(读) 1bit) + ack + data(8bit 从机给主机) +NO ack+stop**

(4)i2c总线的特点

i2c是一个半双工,同步的,串行,具备应答机制的总线协议。

(5)i2c总线速率(控制器)

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

1.2i2c总线驱动框架结构

1.3i2c总线驱动的API

1.分配并初始化对象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;//const struct i2c_device_id *id_table;}struct device_driver {const char       *name;const struct of_device_id *of_match_table;}//i2c的匹配方式只有两种:1.idtable,2.设备树
2.注册、注销#define i2c_add_driver(driver) i2c_register_driver(THIS_MODULE, driver)  //注册void i2c_del_driver(struct i2c_driver *driver); //注销
3.一键注册的函数module_i2c_driver(变量名);

1.4i2c总线驱动的实例

#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>int myi2c_probe(struct i2c_client* client,const struct i2c_device_id* id)
{printk("%s:%d\n", __func__, __LINE__);return 0;
}
int myi2c_remove(struct i2c_client* client)
{printk("%s:%d\n", __func__, __LINE__);return 0;
}struct of_device_id oftable[] = {{.compatible = "hqyj,myi2c",},{}
};
MODULE_DEVICE_TABLE(of,oftable);struct i2c_driver myi2c = {.probe = myi2c_probe,.remove = myi2c_remove,.driver = {.name = "myi2c123456",.of_match_table = oftable,}
};
module_i2c_driver(myi2c);
MODULE_LICENSE("GPL");

2.i2c总线驱动

2.1si7006(sht20)设备树的编写

2.1.1画出硬件连接图

2.1.2找到控制器的设备树

stm32mp151.dtsi

i2c1: i2c@40012000 {compatible = "st,stm32mp15-i2c";  //和控制器匹配的名字reg = <0x40012000 0x400>;         //控制器的地址interrupt-names = "event", "error";interrupts-extended = <&exti 21 IRQ_TYPE_LEVEL_HIGH>,<&intc GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;clocks = <&rcc I2C1_K>;resets = <&rcc I2C1_R>;#address-cells = <1>;#size-cells = <0>;     //修饰子节点 reg成员个数dmas = <&dmamux1 33 0x400 0x80000001>,<&dmamux1 34 0x400 0x80000001>;dma-names = "rx", "tx";power-domains = <&pd_core>;st,syscfg-fmp = <&syscfg 0x4 0x1>;wakeup-source;i2c-analog-filter;status = "disabled";  //控制器没有使能
};

2.1.3编写自己的设备树

参考:linux-5.10.61/Documentation/devicetree/bindings/i2c
&i2c1{pinctrl-names = "default", "sleep";pinctrl-0 = <&i2c1_pins_b>;       //管脚复用,工作状态                                                      pinctrl-1 = <&i2c1_sleep_pins_b>; //管脚复用,休眠状态i2c-scl-rising-time-ns = <100>;   //scl上升沿和下降沿的时间                                              i2c-scl-falling-time-ns = <7>;status = "okay";                  //使能控制器/delete-property/dmas;            //删除dma属性                                          /delete-property/dma-names;si7006@40{compatible = "hqyj,si7006";  //和设备驱动匹配的名字reg = <0x40>;                //从机地址};
};

2.2i2c中相关的结构体

i2c_client结构体:当驱动进入到probe函数之前,内核会创建i2c_client,

这个i2c_client是用来记录数据的。比如i2c_adapter(控制器驱动的对象)就

被存放到这个结构体中。后面在发消息的时候会用到这个i2c_adapter

https://www.totalphase.com/support/articles/200349176-7-bit-8-bit-and-10-bit-I2C-Slave-Addressing

struct i2c_client {unsigned short flags;     //i2c的标志位  I2C_M_TENunsigned short addr;        //从机地址char name[I2C_NAME_SIZE];  //设备的名struct i2c_adapter *adapter;//控制器的对象
};

i2c_msg结构体:i2c设备驱动和控制器驱动传输数据的时候是通过i2c_msg

结构体完成的。

//消息结构体
struct i2c_msg {__u16 addr;     //从机地址__u16 flags;    //读写标志位 0 写  1读__u16 len;     //消息的长度__u8 *buf;       //消息的首地址
};

2.3封装数据发送数据的过程

2.3.1消息的封装过程

有多少起始位就有多少消息,消息的长度是以字节表示的。

写的消息:

start+(slave addr 7bit 0 1bit)+ack+reg+ack+data+ack+stop

(slave addr 7bit 0 1bit)+reg+data

char wbuf[] = {reg,data};
struct i2c_msg w_msg = {.addr = client->addr,.flags = 0,.len = 2,.buf = wbuf,
};

读的消息:

start+(slave addr 7bit 0 1bit)+ack+reg+ack+

start+(slave addr 7bit 1 1bit)+ack+data+NO ack+stop

char rbuf[] = {reg};
unsigned char val;
struct i2c_msg r_msg[] = {[0] = {.addr = client->addr,.flags = 0,.len = 1,.buf = rbuf,},[1] = {.addr = client->addr,.flags = 1,.len = 1,.buf = &val,},
};

2.3.2消息的发送过程

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
功能:发送消息
参数:@adap:控制器驱动对象的指针@msgs:消息的首地址@num:消息的个数
返回值:成功返回值等于num,否则就是失败

2.4读取si7006串号和固件号实例


si7006.h

#ifndef __SI7006_H__
#define __SI7006_H__#define GET_SERIAL  _IOR('j',0,int)
#define GET_FIRWARE  _IOR('j',1,int)#define SERIAL_ADDR 0xfcc9
#define FIRWARE_ADDR 0x84b8#endif

si7006.c

#include "si7006.h"
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/device.h>#define CNAME "si7006"
struct i2c_client* gclient;
int major;
const int count = 1;
struct class* cls;
struct device* dev;int i2c_read_serial_firware(unsigned short reg)
{int ret;unsigned char val;char r_buf[] = { reg >> 8, reg & 0xff };// 1.封装消息结构体struct i2c_msg r_msg[] = {[0] = {.addr = gclient->addr,.flags = 0,.len = 2,.buf = r_buf,},[1] = {.addr = gclient->addr,.flags = 1,.len = 1,.buf = &val,},};// 2.发送消息ret = i2c_transfer(gclient->adapter, r_msg, ARRAY_SIZE(r_msg));if (ret != ARRAY_SIZE(r_msg)) {printk("i2c read error\n");return -EAGAIN;}return val;
}
int si7006_open(struct inode* inode, struct file* file)
{return 0;
}
long si7006_ioctl(struct file* filp,unsigned int cmd, unsigned long arg)
{int data, ret;switch (cmd) {case GET_SERIAL:data = i2c_read_serial_firware(SERIAL_ADDR);if (data < 0) {printk("i2c read serial error\n");return data;}ret = copy_to_user((void*)arg, &data, sizeof(int));if (ret) {printk("copy data serial to user error\n");return -EIO;}break;case GET_FIRWARE:data = i2c_read_serial_firware(FIRWARE_ADDR);if (data < 0) {printk("i2c read firware error\n");return data;}ret = copy_to_user((void*)arg, &data, sizeof(int));if (ret) {printk("copy data firware to user error\n");return -EIO;}break;}return 0;
}
int si7006_close(struct inode* inode, struct file* file)
{return 0;
}
static 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;printk("%s:%d\n", __func__, __LINE__);// 1.注册字符设备驱动major = register_chrdev(0, CNAME, &fops);if (major < 0) {printk("register chrdev error\n");return -EAGAIN;}// 2.创建设备节点cls = class_create(THIS_MODULE, CNAME);if (IS_ERR(cls)) {printk("class create error\n");return PTR_ERR(cls);}dev = device_create(cls, NULL, MKDEV(major, 0), NULL, CNAME);if (IS_ERR(dev)) {printk("device create error\n");return PTR_ERR(dev);}return 0;
}
int si7006_remove(struct i2c_client* client)
{printk("%s:%d\n", __func__, __LINE__);device_destroy(cls, MKDEV(major, 0));class_destroy(cls);unregister_chrdev(major, CNAME);return 0;
}struct of_device_id oftable[] = {{.compatible = "hqyj,si7006",},{}
};
MODULE_DEVICE_TABLE(of, oftable);struct i2c_driver si7006 = {.probe = si7006_probe,.remove = si7006_remove,.driver = {.name = "si7006123456",.of_match_table = oftable,}
};
module_i2c_driver(si7006);
MODULE_LICENSE("GPL");

test.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include "si7006.h"int main(int argc, const char *argv[])
{int fd,serial,firware;if((fd = open("/dev/si7006",O_RDWR)) < 0){perror("open error");exit(EXIT_FAILURE);}ioctl(fd,GET_SERIAL,&serial);ioctl(fd,GET_FIRWARE,&firware);printf("serial = %#x,firware = %#x\n",serial,firware);close(fd);return 0;
}

2.5读取si7006温湿度实例


si7006.h

#ifndef __SI7006_H__
#define __SI7006_H__#define GET_SERIAL  _IOR('j',0,int)
#define GET_FIRWARE  _IOR('j',1,int)
#define GET_TMP  _IOR('j',2,int)
#define GET_HUM  _IOR('j',3,int)
#define SERIAL_ADDR 0xfcc9
#define FIRWARE_ADDR 0x84b8
#define TMP_ADDR 0xe3
#define HUM_ADDR 0xe5
#endif

si7006.c

#include "si7006.h"
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/uaccess.h>#define CNAME "si7006"
struct i2c_client* gclient;
int major;
const int count = 1;
struct class* cls;
struct device* dev;
int i2c_read_serial_firware(unsigned short reg)
{int ret;unsigned char val;char r_buf[] = { reg >> 8, reg & 0xff };// 1.封装消息结构体struct i2c_msg r_msg[] = {[0] = {.addr = gclient->addr,.flags = 0,.len = 2,.buf = r_buf,},[1] = {.addr = gclient->addr,.flags = 1,.len = 1,.buf = &val,},};// 2.发送消息ret = i2c_transfer(gclient->adapter, r_msg, ARRAY_SIZE(r_msg));if (ret != ARRAY_SIZE(r_msg)) {printk("i2c read error\n");return -EAGAIN;}return val;
}int i2c_read_tmp_hum(unsigned char reg)
{int ret;unsigned short val;char r_buf[] = { reg };// 1.封装消息结构体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 = (__u8*)&val,},};// 2.发送消息ret = i2c_transfer(gclient->adapter, r_msg, ARRAY_SIZE(r_msg));if (ret != ARRAY_SIZE(r_msg)) {printk("i2c read error\n");return -EAGAIN;}return (val >> 8 | val << 8);
}
int si7006_open(struct inode* inode, struct file* file)
{return 0;
}
long si7006_ioctl(struct file* filp,unsigned int cmd, unsigned long arg)
{int data, ret;switch (cmd) {case GET_SERIAL:data = i2c_read_serial_firware(SERIAL_ADDR);if (data < 0) {printk("i2c read serial error\n");return data;}ret = copy_to_user((void*)arg, &data, sizeof(int));if (ret) {printk("copy data serial to user error\n");return -EIO;}break;case GET_FIRWARE:data = i2c_read_serial_firware(FIRWARE_ADDR);if (data < 0) {printk("i2c read firware error\n");return data;}ret = copy_to_user((void*)arg, &data, sizeof(int));if (ret) {printk("copy data firware to user error\n");return -EIO;}break;case GET_TMP:data = i2c_read_tmp_hum(TMP_ADDR);if (data < 0) {printk("i2c read tmp error\n");return data;}data &= 0xffff;ret = copy_to_user((void*)arg, &data, sizeof(int));if (ret) {printk("copy data tmp to user error\n");return -EIO;}break;case GET_HUM:data = i2c_read_tmp_hum(HUM_ADDR);if (data < 0) {printk("i2c read hum error\n");return data;}data &= 0xffff;ret = copy_to_user((void*)arg, &data, sizeof(int));if (ret) {printk("copy data hum to user error\n");return -EIO;}break;}return 0;
}
int si7006_close(struct inode* inode, struct file* file)
{return 0;
}static 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;printk("%s:%d\n", __func__, __LINE__);// 1.注册字符设备驱动major = register_chrdev(0, CNAME, &fops);if (major < 0) {printk("register chrdev error\n");return -EAGAIN;}// 2.创建设备节点cls = class_create(THIS_MODULE, CNAME);if (IS_ERR(cls)) {printk("class create error\n");return PTR_ERR(cls);}dev = device_create(cls, NULL, MKDEV(major, 0), NULL, CNAME);if (IS_ERR(dev)) {printk("device create error\n");return PTR_ERR(dev);}return 0;
}
int si7006_remove(struct i2c_client* client)
{printk("%s:%d\n", __func__, __LINE__);device_destroy(cls, MKDEV(major, 0));class_destroy(cls);unregister_chrdev(major, CNAME);return 0;
}struct of_device_id oftable[] = {{.compatible = "hqyj,si7006",},{}
};
MODULE_DEVICE_TABLE(of, oftable);struct i2c_driver si7006 = {.probe = si7006_probe,.remove = si7006_remove,.driver = {.name = "si7006123456",.of_match_table = oftable,}
};
module_i2c_driver(si7006);
MODULE_LICENSE("GPL");

test.c

#include "si7006.h"
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>int main(int argc, const char* argv[])
{int fd, serial, firware, tmpcode, humcode;float tmp, hum;if ((fd = open("/dev/si7006", O_RDWR)) < 0) {perror("open error");exit(EXIT_FAILURE);}ioctl(fd, GET_SERIAL, &serial);ioctl(fd, GET_FIRWARE, &firware);printf("serial = %#x,firware = %#x\n", serial, firware);while (1) {ioctl(fd, GET_TMP, &tmpcode);ioctl(fd, GET_HUM, &humcode);hum = 125.0 * humcode / 65536 - 6;tmp = 175.72 * tmpcode / 65536 - 46.85;printf("tmp=%.2f,hum=%.2f\n",tmp,hum);sleep(1);}close(fd);return 0;
}

include <sys/types.h>
#include <unistd.h>

int main(int argc, const char* argv[])
{
int fd, serial, firware, tmpcode, humcode;
float tmp, hum;
if ((fd = open(“/dev/si7006”, O_RDWR)) < 0) {
perror(“open error”);
exit(EXIT_FAILURE);
}

ioctl(fd, GET_SERIAL, &serial);
ioctl(fd, GET_FIRWARE, &firware);printf("serial = %#x,firware = %#x\n", serial, firware);
while (1) {ioctl(fd, GET_TMP, &tmpcode);ioctl(fd, GET_HUM, &humcode);hum = 125.0 * humcode / 65536 - 6;tmp = 175.72 * tmpcode / 65536 - 46.85;printf("tmp=%.2f,hum=%.2f\n",tmp,hum);sleep(1);
}
close(fd);
return 0;

}


Linux总线之I2C相关推荐

  1. linux内核的I2C子系统详解1——I2C总线概览、驱动框架概览

    以下内容源于朱有鹏<物联网大讲堂>课程的学习,如有侵权,请告知删除. 1.I2C总线汇总概览 (1)三根通信线:SCL.SDA.GND: (2)同步.串行.电平.低速(几百k).近距离: ...

  2. linux iic总线驱动程序,linux总线驱动之初识i2c驱动数据传输流程

    吃个晚饭,画个流程图,没想到已经这么晚了.还是速度把这篇文章搞定,收拾回去了. 先看下linux中的i2c的数据流程图吧.这里主要是用gpio模拟的i2c的. 还是具体看下代码吧,流程只是个大概,和i ...

  3. Linux i2c总线(2) - I2C核心层

    1. I2C 核心层初始化 这一部分向系统注册了一个名为i2c的总线类型. static int __init i2c_init(void) {int retval;retval = bus_regi ...

  4. linux i2c子系统看不懂啊,Linux 下的I2C子系统

    Linux 下的I2C子系统 2013.7.16 本文分为两部分,一.设备模型 二.平台相关 . ================================================ 第一 ...

  5. I.MX6 Android Linux shell MMPF0100 i2c 获取数据

    #!/system/bin/busybox ash# # I.MX6 Android Linux shell MMPF0100 i2c 获取数据 # 说明: # 本文主要记录通过shell脚本来获取M ...

  6. Linux总线设备驱动框架的理解(非常棒的文章!)

    以下内容源于微信公众号:嵌入式企鹅圈.有格式内容上的修改,如有侵权,请告知删除. Linux的设备驱动框架,即某类设备对应的驱动的框架. 这里是"Linux总线设备驱动框架",应该 ...

  7. linux 深入理解I2C内核驱动

    系列文章 I.MX6ULL 手册查找使用方法 实战点亮LED(寄存器版) I.MX6ULL 手册查找使用方法 实战点亮LED(固件库版本) linux 字符设备驱动实战 linux LED设备驱动文件 ...

  8. linux自带i2c工具使用

    导读 I2C总线被全球超过50个公司的1000+个ICs所使用,已然是一个世界标准.另外,I2C总线与多种不同的控制总线是兼容的,比如SMBus(系统管理总线),PMBus(电源管理总线),IPMI( ...

  9. linux驱动之I2C子系统

    目录 一.I2C基本原理 二.linux内核的I2C子系统详解 1.linux内核的I2C驱动框架总览 2.I2C子系统的4个关键结构体(kernel/include/linux/i2c.h) 3.关 ...

最新文章

  1. 平板电脑计算机怎么关按键音,平板电脑输入法怎么设置【图解】
  2. 字符转换属性text-tranform改变大小写
  3. python读取json配置文件_Python简单读取json文件功能示例
  4. 初学者不建议月python吗_9.python入门
  5. MySQL联合索引原理_复合索引_组合索引_多列索引
  6. JS正则表达式大全【转】
  7. PMP备考指南之第一章:引论
  8. uva10069-Distinct Subsequences
  9. 《算法导论》为什么经典
  10. 宝塔linux架设手游,守望黎明架-linux手工端设教程
  11. BIOS知识枝桠—— Protocol
  12. 【只推荐一位】木东居士,带着大家一起成长的数据科学大神!
  13. 搜索引擎模块设计与实现——相关度搜索算法模块
  14. Jetson TK1
  15. 很常用的倒计时脚本,可任意设置时…
  16. 以水稻为例教你如何使用BSA方法进行遗传定位(上篇)
  17. 计算机五大部件相关知识
  18. Nginx常见错误代码总结和处理方案
  19. 【第二季】Arcgis地图制图基础|(一)Arcgis地图符号制作
  20. 打印机 树莓派安装cpus_用树莓派搭建网络打印机

热门文章

  1. uniCloud云开发----2、uniCloud云开发之云端云函数和本地云函数的调试
  2. django网页制作(1)虚拟机、python虚拟环境
  3. U盘安装winxp、win7系统最详攻略
  4. 广东培正计算机科学与技术,广东培正学院数据科学与计算机学院
  5. 好消息! 可帮您创业东大门女装。
  6. 拳皇97风云再起汉化版怎么调难度呀
  7. STM32哪些引脚默认高电平
  8. UserWarning: semaphore_tracker: There appear to be 4 leaked semaphores to clean up at shutdown
  9. java加redis锁
  10. 四大网络功能 HP演绎教育PC智能魔方