嵌入式学习 day61 DHT11、I2C
一、DHT11
1、介绍
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(i=0;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;
}
二、I2C
1、通信特点:
通信方式:串行、同步、半双工
2、应用场景:
同一硬件平台不同外设之间的通信
注:(1)i2c与spi的区别
①12c半双工、SPI全双工
②I2c硬件设计更加简洁、SPI因为有CS线,所以占用更过硬件资源
③I2c通信速率(100k、400k、3.4M)SPI通信速率(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驱动编写(LM75)
(1)配置设备树
(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;
}