目录

  • 0.测试环境说明
  • 1.设备树的修改
  • 2.设备驱动框架
  • 3.I2C数据传输过程
    • 3.1 struct i2c_msg
    • 3.2 SHT20的数据收发
  • 4.I2C适配器超时等待时间的修改
  • 本文资源
  • 参考

0.测试环境说明

Linux的I2C子系统资料遍地都是,但是单看资料是不会明白驱动如何写的,所以选择了一个简单的器件,针对该器件有目的的写一个驱动实现最基本的器件设置和数据读取。

SHT20是一个采用标准I2C协议通信的温湿度传感器,其精度和特性在本次学习中无需关注。驱动基于的开发板环境如下表所示:

环境 参数
开发板型号 荣品RP-DV300B
芯片型号 Hi3516DV300
Kernel Version 4.9.37 SMP
SDK Hi3516CV500_SDK_V2.0.1.0

1.设备树的修改

linux4.9.37版本使用设备树描述板级连接,在 /linux-4.9.37-smp/arch/arm/boot/dts 目录下的 hi3516dv300-demb.dts 文件中添加节点。

这里需要注意的是设备I2C地址使用的是不包含读写位的7位地址,I2C的频率应当适配硬件,SHT20支持的最高SCL频率为0.4MHz,这在手册上可以找到。

很多资料都是这样修改设备树的,但是这样修改之后,编译内核时出现如下警告(对设备树不是很了解,所以放任不管),这个警告并不影响器件运作。

2.设备驱动框架

SHT20挂接在I2C总线上,但是从设备类型看,依旧属于字符设备。从整体来看,驱动程序使用 i2c_add_driver()i2c_del_driver() 函数实现设备在I2C总线上的挂载和卸载,使用 cdev_init()cdev_add()cdev_del() 函数实现对字符设备的注册和注销。

I2C核心实现设备的匹配,执行 .probe().remove() ,在 .probe() 函数中进行字符设备注册,将文件操作结构体填充并绑定到注册的字符设备上。这样对字符设备的访问便会从文件读写函数,通过 i2c_transfer() 向I2C适配器提交 struct i2c_msg 结构体报文实现。

多说无益,来看伪代码:

static int sht2x_open(struct inode *inode,struct file *filp)
{filp->private_data=st_dev.client;/*此处初始化SHT2x器件(软复位)*/return 0;
}static int sht2x_read(struct file *filp,char __user *buf,size_t count,loff_t *f_pos)
{struct i2c_client *client = (struct i2c_client *)filp->private_data;/*对传感器数据进行读取*/copy_to_user(...);/*读回调返回值为驱动读到的有效数据长度*/return count;
}static int sht2x_release(struct inode *inode,struct file *filp)
{return 0;
}static struct file_operations sht2x_fops=
{.owner         =THIS_MODULE,.read          =sht2x_read,.open          =sht2x_open,.release       =sht2x_release,
};static int sht2x_probe(struct i2c_client *client, const struct i2c_device_id *id)
{int ret;/*注册字符设备*/cdev_init(&st_dev.cdev,&sht2x_fops);cdev_add(&st_dev.cdev, ...);/*创建设备节点*/class_create(THIS_MODULE,"sht2xclass");device_create(dev_class,NULL,MKDEV(driver_major,0),NULL,"sht2x");st_dev.client=client;return 0;
FAIL:return ret;
}static int sht2x_remove(struct i2c_client *client)
{/*删除字符设备*/cdev_del(&st_dev.cdev);/*删除节点*/device_unregister(dev_class_device);class_destroy(dev_class);return 0;
}/*设备树方式*/
static struct of_device_id sht2x_of_match[] = {{ .compatible = "sensirion,sht2x", .data = NULL },{}
};static struct i2c_driver sht2x_driver =
{.driver ={.name  = "sht2x",.owner = THIS_MODULE,.of_match_table = sht2x_of_match,},.probe     = sht2x_probe,.remove    = sht2x_remove,.id_table  = sht2x_id,
};static int __init sht2x_init(void)
{return i2c_add_driver(&sht2x_driver);
}
module_init(sht2x_init);static void __exit sht2x_exit(void)
{i2c_del_driver(&sht2x_driver);
}
module_exit(sht2x_exit);

代码删除了大部分内容,但是依然可以看出上面所说的过程。这个过程对于学习过字符设备驱动的人来说非常简单。

3.I2C数据传输过程

3.1 struct i2c_msg

每一个 struct i2c_msg 结构体有自己的起始信号,在I2C适配器传输过程中,该结构体的数量取决于一次通信需要多少个起始信号。

I2C终止信号在一次 i2c_transfer() 函数调用完成后发送。在数据发送过程中,使用结构体的 .flag 标志位指定读写标志位。

static uint16_t i2c_read_reg(struct i2c_client *client, uint8_t reg, uint8_t *recv, uint32_t len)
{struct i2c_msg msg[2];/*首先使用IIC写模式传出要读取的寄存器地址*/msg[0].addr  = client->addr;msg[0].flags = 0;//0表示写msg[0].buf   = ®msg[0].len   = 1;/*然后使用IIC读模式读取从机传来的数据,具体长度由器件协议决定*/msg[1].addr = client->addr;msg[1].flags = I2C_M_RD;msg[1].buf = recv;//读取的数据存放的缓冲区指针msg[1].len = len;//要读取的长度由操作决定/*提交适配器执行消息动作*/ret = i2c_transfer(client->adapter, msg, 2);
}

3.2 SHT20的数据收发

SHT20的中英文手册网上都有,为了方便在文末提供下载链接。

由于只是实现最简单的温湿度读取功能,所以在驱动中只需要实现以下三个函数:

1.设备打开时进行软复位,软复位之后等待15ms
2.主机模式下的温度读取
3.主机模式下的湿度读取

这是因为用户寄存器复位之后的模式为最高分辨率,本次无需更改即可使用。

上面两个图是从手册上摘出的。软件中只需要按照对应的命令写入数据即可。驱动程序将在文末【本文资源】中给出下载链接,就不在正文中粘贴了。

编写中对数据进行读取时需要注意两个细节。一是对读出的原始温湿度数据进行的处理,虽然上图中标明了Data位的MSB到LSB之间一共占据了14位,但是对高低位进行合并的时候却是高位对其的,手册中这样写道:

在进行物理换算时,后两位状态位应置‘0’

另一点需要注意的是,linux驱动中对浮点计算有问题,所以传感器输出的温湿度将在用户应用程序中进行计算。

4.I2C适配器超时等待时间的修改

在测试中,使用海思自带的I2C总线控制驱动进行数据的传输,在默认情况下,数据传输将出现如下问题:

在测试应用中每一次数据读取会出现这样的提示:

hibvt-i2c 120b2000.i2c: wait rx no empty timeout, RIS: 0x10, SR: 0xa0000

经查,这个提示出现在 linux-4.9.37-smp/drivers/i2c/busses/i2c-hibvt.c 文件中:

这表示在等待50us*1024≈51ms没有收到数据后,对本次I2C读取判定超时,这与上面逻辑分析仪中测的的情况相符。由于SHT20在最高分辨率下温度的转换需要66ms(逻辑分析仪同样证实这一点),

所以修复上面的错误可以采用两种方式进行:降低传感器测量分辨率或者增大I2C读超时时间,本文采取后者。

正常对SHT20的温度读取波形如下所示(其中红点表示停止信号,显示过密起始信号隐去了):

最终读取的温湿度如图所示:

本文资源

百度网盘链接: https://pan.baidu.com/s/1iOG4FrrvNUgLUctqSZn1Ag

提取码: aqwq

参考

1.Linux驱动开发(十八):I2C驱动

————2020-6-13 @燕卫博————

从零开始学习linux的I2C设备驱动框架——写一个简单的SHT20驱动相关推荐

  1. 驱动开发之六 --- 一个简单的显示驱动之一 [译文]

    这个系列的文章在网上到处都是 这里也不清楚谁才是原文作者 我这里做个整理,标注一下希望大家能看的更加舒服一点 目录 (一)驱动开发一个简单的显示驱动 (二)驱动开发一个简单的显示驱动 (三)驱动开发一 ...

  2. ##HTML做小米官网,好复杂,以后用框架写一个简单的

    小米官网<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8&q ...

  3. ROS的学习(十六)用C++写一个简单的服务器(service)和客户端(client)

    我们将创建一个服务器节点add_two_ints_server,它将会收到两个整数,并且返回它们的和.切换目录到之前建立的beginner_tutorials包下: cd ~/catkin_ws/sr ...

  4. ROS的学习(十四)用C++写一个简单的接收者

    打开一个终端,进入到beginner_tutorials包下面: cd ~/catkin_ws/src/beginner_tutorials 编辑文件 src/listener.cpp: vim sr ...

  5. ROS的学习(十二)用C++写一个简单的发布者

    节点是一个可执行程序,它连接到了ROS的网络系统中.我们将会创建一个发布者,也就是说话者节点,它将会持续的广播一个信息. 改变目录到之前所建立的那个包下: cd ~/catkin_ws/src/beg ...

  6. linux access源码,从零开始学习Linux:Day04 源码安装Nginx 。acess/status/referer

    从零开始学习Linux:Day04 源码安装Nginx .acess/status/referer 常见web服务器有IIS,apche,nginx,tomcat,tengine等等,现在nginx使 ...

  7. linux的led驱动的实验总结,linux设备驱动归纳总结(五):4.写个简单的LED驱动

    linux设备驱动归纳总结(五):4.写个简单的LED驱动 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  8. 驱动框架5——基于驱动框架写led驱动

    以下内容源于朱有鹏<物联网大讲堂>课程的学习整理,如有侵权,请告知删除. 五.基于驱动框架写led驱动1 1.分析 (1)参考哪里?  drivers/leds/leds-s3c24xx. ...

  9. 实战!手把手教你如何编写一个Linux驱动并写一个支持物联网的LED演示demo

    目录 一.开发环境 二. 准备工作: 1. 创建一个项目工程目录 2. 创建输出与目标目录 3.头文件目录 4. 建立源代码src目录 5. 使用git管理你的项目 三.编写LED驱动 三.一 准备工 ...

最新文章

  1. Link Cut Tree学习笔记
  2. 《Python Cookbook 3rd》笔记(5.19):创建临时文件和文件夹
  3. c 调用java程序_C ++可以调用Java代码吗?
  4. ai项目实施步骤_停止AI产品开发中道德责任的6个步骤
  5. android8 测试,Android 8.0 Oreo 国内可用测试平台上线
  6. JavaScript 你必须了解的主流趋势!
  7. 二种清空数据库的好方法
  8. Android studio for mac
  9. 行到水穷处,坐看云起时-我的2007!
  10. 一种语音识别模型的训练方法及装置与流程
  11. LeetCode(160): Intersection of Two Linked Lists
  12. 天联无法ping通服务器地址_金万维天联标准版无法连接,怎么办?
  13. MV178——我的心灵家园
  14. HPE主机根据磁盘序列号或位置确定Naa号
  15. 浅谈web前端工程师hr面试经典问题20+
  16. 时间转换 秒(s)转 ()天 ()小时() 分钟 ()秒
  17. 直播带货系统,实现一套完整的直播系统应该具备的功能
  18. 数据结构实验——哈夫曼编码
  19. 数据结构极客视频5_DFS的题目
  20. Verilog固定优先级仲裁器——Fixed Priority Arbiter

热门文章

  1. 今年国庆,我选择给自己充电
  2. 计算机字长通常不可能,计算机的字长通常不可能为( )位。
  3. 投资绩效约束下的有限套利(Shleifer,Vishny)
  4. 《gcc五分钟系列》基础部分结束
  5. Python:实现double factorial iterative双阶乘迭代算法(附完整源码)
  6. 第二章 数据采集模块之FlinkCDC实时采集Mysql业务数据(源码资料见文末)
  7. vue 数据大屏使用数字字体
  8. You may use special comments to disable some warnings. 报错解决的三种方式
  9. 蓝桥杯——既约分数(c语言)
  10. 和平精英怎么改到120帧?进来看看你也可以!