嵌入式学习 day61 DHT11、I2C

📅 2026/8/7 19:35:14
嵌入式学习 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; }