嵌入式学习 day61 DHT11、I2C
一、DHT111、介绍DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温湿度传感技术。传感器包括一 个电容式感湿元件和一个NTC 测温元件并与一个8位单片机相连接。2、产品参数3、串行通信说明单线双向1单总线说明DHT11器件采用简化的单总线通信。单总线即只有一根数据线系统中的数据交换、控制均由单总线 完成。设备主机或从机通过一个漏枀开路或三态端口连至该数据线以允许设备在不发送数据时能够 释放总线而让其它设备使用总线单总线通常要求外接一个约 4.7kΩ 的上拉电阻这样当总线闲置时 其状态为高电平。由于它们是主从结极只有主机呼叫从机时从机才能应答因此主机访问器件都必须 严格遵循单总线序列如果出现序列混乱器件将不响应主机。2单总线传送数据位8bit 湿度整数数据 8bit湿度小数数据 8bit温度整数数据 8bit温度小数数据 8bit校验位3通信时序①DHT11上电后DHT11上电后要等待 1S 以越过不稳定状态在此期间不能发送任何指令测试环境 温湿度数据并记录数据同时 DHT11的DATA数据线由上拉电阻拉高一直保持高电平此时 DHT11的 DATA 引脚处于输入状态时刻检测外部信号。②微处理器的I/O设置为输出同时输出低电平且低电平保持时间不能小于18ms最大不得超过30ms 然后微处理器的I/O设置为输入状态由于上拉电阻微处理器的I/O即DHT11的DATA数据线也随之变 高等待DHT11作出回答信号发送信号如图所示③DHT11的DATA引脚检测到外部信号有低电平时等待外部信号低电平结束延迟后DHT11的DATA 引脚处于输出状态输出 83微秒的低电平作为应答信号紧接着输出 87 微秒的高电平通知外设准备接 收数据微处理器的 I/O 此时处于输入状态检测到 I/O 有低电平DHT11回应信号后等待87微秒 的高电平后的数据接收发送信号如图所示④由DHT11的DATA引脚输出40位数据微处理器根据I/O电平的变化接收40位数据位数据“0” 的格式为 54 微秒的低电平和 23-27 微秒的高电平位数据“1”的格式为 54 微秒的低电平加68-74 微秒的高电平。位数据“0”、“1”格式信号如图所示⑤发送结束信号4代码①设备树节点②驱动代码#include linux/init.h #include linux/fs.h #include linux/cdev.h #include linux/module.h #include asm/io.h #include asm/uaccess.h #include linux/device.h #include linux/mutex.h #include linux/miscdevice.h #include linux/of.h #include linux/of_gpio.h #include linux/gpio.h #include linux/platform_device.h #include linux/delay.h static int dht11stat 0; static int dht11gpio 0; static struct mutex lock; static ssize_t dht11_read(struct file *fp, char __user *puser, size_t n, loff_t *off); static ssize_t dht11_write(struct file *fp, const char __user *puser, size_t n, loff_t *off); static int dht11_open(struct inode *node, struct file *fp); static int dht11_release(struct inode *node, struct file *fp); static ssize_t dht11_show(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t dht11_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); extern void __iomem *of_iomap(struct device_node *np, int index); struct device_attribute attr __ATTR(attr, 0664, dht11_show, dht11_store); static struct file_operations fops { .owner THIS_MODULE, .open dht11_open, .release dht11_release, .read dht11_read, .write dht11_write, }; static struct miscdevice misc { .name misc_dht11, .minor MISC_DYNAMIC_MINOR, .fops fops, }; //从DHT11获取一位数据的函数 static int dht11_get_bit(void) { int num 0; while(0 gpio_get_value(dht11gpio)); num 0; while(gpio_get_value(dht11gpio)) { udelay(10); num; } if(num 5) { return 0; } else if(num 5) { return 1; } } //获取一个字节的函数 static char dht11_get_char(void) { char dat 0; int bit 0; int i 0; for(i0;i 8;i) { bit dht11_get_bit(); dat (dat 1) | bit; } return dat; } //将获得的数据校验成功后发向应用层 static ssize_t dht11_read(struct file *fp, char __user *puser, size_t n, loff_t *off) { int i 0; int nret 0; char dat[5] {0}; int num 0; gpio_set_value(dht11gpio,0); msleep(22); gpio_set_value(dht11gpio,1); udelay(40); gpio_direction_input(dht11gpio); while(0 gpio_get_value(dht11gpio)) { if(num 20){ pr_info(dht11gpio_get_bit 1 failed\n); return -1; } udelay(10); num; } num 0; while(gpio_get_value(dht11gpio)) { if(num 20){ pr_info(dht11gpio_get_bit 2 failed\n); return -1; } udelay(10); num; } for(i 0;i 5;i) { dat[i] dht11_get_char(); } gpio_direction_output(dht11gpio,1); gpio_set_value(dht11gpio,1); pr_info(dht11 read success!\n); if(((dat[0] dat[1] dat[2] dat[3]) 0xff) dat[4]){ nret copy_to_user(puser,dat,sizeof(dat)); if (nret) { pr_info(copy_to_user failed\n); return -1; } return sizeof(dat); } else{ pr_info(data failed\n); return 0; } return 0; } static ssize_t dht11_write(struct file *fp, const char __user *puser, size_t n, loff_t *off) { pr_info(dht11 write success!\n); return 0; } static int dht11_open(struct inode *node, struct file *fp) { pr_info(dht11 open success!\n); return 0; } static int dht11_release(struct inode *node, struct file *fp) { pr_info(dht11 close success!\n); return 0; } static ssize_t dht11_show(struct device *dev, struct device_attribute *attr, char *buf) { return 0; } static ssize_t dht11_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { return 0; } //platform总线匹配成功后的操作 static int dht11_probe(struct platform_device *pdevice) { int ret 0; struct device_node *pdht11node NULL; //注册混杂设备 ret misc_register(misc); if (ret) { pr_info(misc_register failed\n); return -1; } //通过路径查找设备树节点 pdht11node of_find_node_by_path(/putedht11); if (NULL pdht11node) { pr_info(of_find_node_by_path failed\n); return -1; } //从设备树节点中解析并获取指定GPIO硬件编号 dht11gpio of_get_named_gpio(pdht11node, gpio-dht11, 0); if (dht11gpio 0) { pr_info(of_get_named_gpio failed\n); return -1; } //申请GPIO资源 ret gpio_request(dht11gpio, pute_dht11_drv); if (ret) { pr_info(gpio_request failed\n); return -1; } //将GPIO引脚设置为输出模式 ret gpio_direction_output(dht11gpio, 1); if (ret) { pr_info(gpio_direction_output failed\n); return -1; } //将该gpio引脚拉高 gpio_set_value(dht11gpio,1); dht11stat 0; mutex_init(lock); //在sys系统设备目录下创建文件 ret device_create_file(misc.this_device, attr); if (ret) { pr_info(device_create_file failed); return -1; } pr_info(probe success!\n); return 0; } static int dht11_remove(struct platform_device *pdevice) { int ret 0; //释放GPIO资源 gpio_free(dht11gpio); //在sys系统设备目录下删除文件 device_remove_file(misc.this_device, attr); mutex_destroy(lock); //注销混杂设备 ret misc_deregister(misc); if (ret) { pr_info(misc_register failed\n); return -1; } pr_info(remove success\n); return 0; } static struct platform_device_id id_table[] { {.name putedht11}, {}, }; static struct of_device_id dht11_of_match_table[] { {.compatible pute,putedht11}, {}, }; static struct platform_driver dht11_driver { .probe dht11_probe, .remove dht11_remove, .driver { .name putedht11, .of_match_table dht11_of_match_table, }, .id_table id_table, }; module_platform_driver(dht11_driver); MODULE_LICENSE(GPL); MODULE_AUTHOR(pute);③应用层#include stdio.h #include string.h #include stdlib.h #include sys/types.h #include sys/stat.h #include fcntl.h #include unistd.h int main() { int fd; char buf[5] {0}; int ret; fd open(/dev/misc_dht11, O_RDONLY); if (fd 0) { perror(open /dev/misc_dht11 failed); return -1; } while(1) { sleep(3); ret read(fd, buf, sizeof(buf)); if (ret sizeof(buf)) { printf(RH:%d.%d \n, buf[0], buf[1]); printf(temp:%d.%d\n, buf[2], buf[3]); } else { printf(read failed (ret: %d)\n, ret); } } close(fd); return 0; }二、I2C1、通信特点通信方式串行、同步、半双工2、应用场景同一硬件平台不同外设之间的通信注1i2c与spi的区别①12c半双工、SPI全双工②I2c硬件设计更加简洁、SPI因为有CS线所以占用更过硬件资源③I2c通信速率100k、400k、3.4MSPI通信速率1M-十几M之间④I2c一般用于传感器采集接口、SPI屏幕TFT-LCD、存储设备FLASH接口3、I2C组成4、i2c硬件接线需要注意事项:需要接入上拉电阻5、I2C分类1软件I2C用gpio模拟I2C时序2硬件I2C用I2C控制器来生成I2C时序6、I2C总线结构7、I2C典型通信8、I2C结构9、I2C框架流程10、I2C驱动编写LM751配置设备树2编写驱动①驱动层#include linux/init.h #include linux/cdev.h #include linux/fs.h #include linux/module.h #include linux/miscdevice.h #include asm/uaccess.h #include linux/of.h #include asm/io.h #include linux/gpio.h #include linux/of_gpio.h #include linux/interrupt.h #include linux/irqreturn.h #include linux/sched.h #include linux/workqueue.h #include linux/timer.h #include linux/poll.h #include linux/input.h #include linux/of_irq.h #include linux/platform_device.h #include linux/i2c.h static struct i2c_client *plm75client NULL; static ssize_t lm75_read(struct file *fp, char __user *puser, size_t n, loff_t *off) { struct i2c_msg msg; int ret 0; long nret 0; unsigned char data[2] {0}; /*发送0x00到lm75中*/ data[0] 0x00; msg.addr plm75client-addr; msg.flags 0; msg.len 1; msg.buf data; ret plm75client-adapter-algo-master_xfer(plm75client-adapter,msg,1); if(ret 0){ pr_info(master_xfer failed\n); return -1; } /*从lm75读两个字节*/ msg.addr plm75client-addr; msg.flags I2C_M_RD; msg.len 2; msg.buf data; ret plm75client-adapter-algo-master_xfer(plm75client-adapter,msg,1); if(ret 0){ pr_info(master_xfer failed\n); return -1; } nret copy_to_user(puser,data,2); if(nret){ pr_info(copy_to_user failed); return -1; } return 2; } static struct file_operations fops { .owner THIS_MODULE, .read lm75_read, }; static struct miscdevice misc_lm75 { .minor MISC_DYNAMIC_MINOR, .name misc_lm75, .fops fops, }; int lm75_probe(struct i2c_client *pclient, const struct i2c_device_id *devid) { int ret 0; plm75client pclient; //注册混杂设备 ret misc_register(misc_lm75); if(ret){ pr_info(misc_register failed\n); return -1; } pr_info(lm75 probe ok\n); return 0; } int lm75_remove(struct i2c_client *pclient) { int ret 0; ret misc_deregister(misc_lm75); if(ret){ pr_info(misc_deregister failed\n); return -1; } pr_info(lm75 remove ok\n); return 0; } static struct i2c_device_id lm75_id_table[] { {.name putelm75}, }; static struct of_device_id lm75_of_match_table[] { {.compatible pute,putelm75}, {}, }; struct i2c_driver lm75_diver { .probe lm75_probe, .remove lm75_remove, .driver { .name putelm75, .of_match_table lm75_of_match_table, }, .id_table lm75_id_table, }; static int __init lm75_init(void) { //注册I2C设备 int ret 0; ret i2c_register_driver(THIS_MODULE,lm75_diver); if(ret){ pr_info(i2c_register_driver failed\n); return -1; } return 0; } static void __exit lm75_exit(void) { i2c_del_driver(lm75_diver); return; } module_init(lm75_init); module_exit(lm75_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(pute);②应用层#include stdio.h #include sys/types.h #include sys/stat.h #include fcntl.h #include unistd.h #include signal.h #include sys/select.h #include linux/input.h int main(void) { int fd 0; unsigned char data[2] {0}; fd open(/dev/misc_lm75, O_RDWR); if (-1 fd) { perror(fail to open); return -1; } while (1) { read(fd, data, sizeof(data)); printf(temp:%.2lf\n, (((data[0] 8) | data[1]) 7) * 0.5); sleep(1); } close(fd); return 0; }

相关新闻

今日头条互动率严重虚高----只有50%是真实的

今日头条互动率严重虚高----只有50%是真实的

有一半的评价都是豆包在回复:1/32/63/105/127/15看吧只有50%互动是真实的,而且其他人,还有一部分也是机器人。-----------------------------本次互动数量:150,所以也有75个互动

2026/10/18 20:06:34 阅读更多 →
沉降变形观测方案设计:从基准构建到预警体系的工程实践

沉降变形观测方案设计:从基准构建到预警体系的工程实践

1. 项目概述:沉降变形观测,远不止“看个读数”那么简单 在工程建设和运维领域,沉降变形观测是一个听起来专业、做起来更需严谨的活儿。很多人,包括一些刚入行的朋友,可能会觉得这活儿不就是定期拿个水准仪去测几个点&a…

2026/10/18 20:06:33 阅读更多 →
CMake目标链接高级用法:example_exec与JSONUtils库的无缝集成

CMake目标链接高级用法:example_exec与JSONUtils库的无缝集成

CMake目标链接高级用法:example_exec与JSONUtils库的无缝集成 【免费下载链接】modern-cmake-sample Example library that shows best practices and proper usage of CMake by using targets 项目地址: https://gitcode.com/gh_mirrors/mo/modern-cmake-sample …

2026/10/18 21:15:07 阅读更多 →

最新新闻

用A4纸拼出广告牌:低成本大幅面打印的完整工作流与创意实践

用A4纸拼出广告牌:低成本大幅面打印的完整工作流与创意实践

最近路过街头时看到一台摆在路边的打印机,身边围了一圈人,正一张接一张地往外吐A4纸,旁边贴了半面墙的成品。凑近一看才发现,那根本不是一张普通的"打印演示",而是在现场打印一整个广告牌——用最普通的A4纸…

2026/10/19 20:56:16 阅读更多 →
极小函数依赖集求解算法:拆、删、消三步与手算例题

极小函数依赖集求解算法:拆、删、消三步与手算例题

函数依赖的极小化,大概是数据库原理课里最让人“背得下定义却下不了笔”的考点了。提到极小函数依赖集,多数人都能背出三条:右部单属性、没有冗余依赖、没有冗余属性。可真给你一张函数依赖集F,让你求Fmin,很多人第一步…

2026/10/19 20:55:12 阅读更多 →
数据可视化全流程实操:Python+Seaborn从选图到避坑

数据可视化全流程实操:Python+Seaborn从选图到避坑

某高校的数据能力提升营走到第7天,主题就是数据可视化。这个营我们内部一直叫它“疏锦行”,名字听着文艺,实际任务却很务实:一周时间,从数据采集一路练到统计建模,而第7天这一天,不带任何新模型…

2026/10/19 20:55:12 阅读更多 →
线性回归从手写梯度下降到sklearn实战:原理、特征工程与过拟合避坑

线性回归从手写梯度下降到sklearn实战:原理、特征工程与过拟合避坑

如果你只会调包,那线性回归等于没学会作为数据分析或者机器学习入门的第一课,线性回归几乎是所有人都会遇到的第一个模型。我见过太多人上来就from sklearn.linear_model import LinearRegression,然后fit、predict、打印个 R 就算完事。真要…

2026/10/19 20:55:12 阅读更多 →
达梦数据库大小写敏感机制详解:从原理到实践避坑

达梦数据库大小写敏感机制详解:从原理到实践避坑

1. 项目概述:达梦大小写敏感到底是个什么事做数据库开发或者运维的朋友,只要是第一次接触达梦数据库,十有八九会遇到一个让人摸不着头脑的现象:明明建表时写的是 "USER_INFO",去查询的时候写 "user_inf…

2026/10/19 20:52:07 阅读更多 →
C++ std::bind实战:占位符、引用陷阱与lambda取舍

C++ std::bind实战:占位符、引用陷阱与lambda取舍

1. 我为什么又回头研究bind:代码里最不起眼的粘连剂搞C时间一长,你会发现真正让代码"绕起来"的往往不是那些宏大设计,而是一堆不起眼的小工具。std::bind就是其中之一。最近接手的某调度模块里,回调参数七零八落&#x…

2026/10/19 20:52:07 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介:本资源为电源技术方向的学术研究文档,聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略,面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址: https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集,共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →