DHT11的通信协议是单总线协议,可以用之前学习的pinctl和gpio子系统完成某IO引脚上数据的读与写。
一.在设备树下添加dht11的设备结点
1.流程图
2.设备树代码
(1).在设备树的 iomuxc结点下添加 pinctl_dht11
(2).在根节点下添加 dht11 结点
(3).在内核源码根目录下重新编译设备树文件
linux@ubuntu:~/IMX6ULL/my_linux_kernel$ make dtbs
(4).将新的 dtb 文件更新到开发板上,检查是否有 dht11 这个结点
启动后在/proc/device-tree/目录中查看是否有 dht11 这个节点。
二.编写 dht11 时序代码
#define DHT11_DelayMs(t) mdelay(t)
#define DHT11_DelayUs(t) udelay(t)
#define DHT11_PIN_HIGH 1
#define DHT11_PIN_LOW 0
#define DHT11_IO_OUT() gpio_direction_output(dht11.dht11_gpio, 1);
#define DHT11_IO_IN() gpio_direction_input(dht11.dht11_gpio)
#define DHT11_WRITE(bit) gpio_set_value(dht11.dht11_gpio, bit)
#define DHT11_READ() gpio_get_value(dht11.dht11_gpio)
/**
* @description: 等待响应
*/
//等待响应
static int dht11_wait_for_ready(void)
{
int timeout;
timeout = 400;
while (DHT11_READ() && timeout) // 等待低电平到来
{
udelay(1);
--timeout;
}
if (!timeout)
{
printk("dht11_wait_for_ready timeout1 %d\n", __LINE__);
return -1; // 超时
}
timeout = 1000; //1000
while (!DHT11_READ() && timeout) // 等待高电平到来
{
udelay(1);
--timeout;
}
if (!timeout)
{
printk("dht11_wait_for_ready timeout2 %d\n", __LINE__);
return -1; // 超时
}
timeout = 1000;
while (DHT11_READ() && timeout) // 等待高电平结束
{
udelay(1);
--timeout;
}
if (!timeout)
{
printk("dht11_wait_for_ready timeout3 %d\n", __LINE__);
return -1; // 超时
}
return 0;
}
/**
* @description: 起始信号
*/
static int dht11_start(void)
{
DHT11_IO_OUT();
DHT11_WRITE(0);
mdelay(25);
DHT11_WRITE(1);
udelay(35);
DHT11_IO_IN(); // 设置为输入
udelay(2);
if (dht11_wait_for_ready()) return -1;
return 0;
}
/**
* @description: 读取一个字节
*/
//读取数据
static int dht11_read_byte(unsigned char *byte)
{
unsigned char i;
unsigned char bit = 0;
unsigned char data = 0;
int timeout = 0;
for (i = 0; i < 8; i++)
{
timeout = 1000;
while (DHT11_READ() && timeout) // 等待变为低电平
{
udelay(1);
--timeout;
}
if (!timeout)
{
printk("dht11_read_byte timeout1 %d\n", __LINE__);
return -1; // 超时
}
timeout = 1000;
while (!DHT11_READ() && timeout) // 等待变为高电平
{
udelay(1);
--timeout;
}
if (!timeout)
{
printk("dht11_read_byte timeout2 %d\n", __LINE__);
return -1; // 超时
}
udelay(40);
bit = DHT11_READ();
data <<= 1;
if (bit)
{
data |= 0x01;
}
}
*byte = data;
return 0;
}
//从DHT11读取一次数据
//temp:温度值(范围:0~50°)
//humi:湿度值(范围:20%~90%)
//返回值:0,正常;1,读取失败
static int dht11_read_data(void)
{
unsigned char data[5] = {0};
int i = 0,ret = 0;
// 启动信号
if (dht11_start() != 0)
{
printk("dht11 start failed\n");
ret = -EFAULT;
}
// 读出5字节数据
for (i = 0; i < 5; i++)
{
if (dht11_read_byte(&data[i]))
{
printk("read data err\n");
ret = -EAGAIN;
}
}
if (data[4] != (data[0]+data[1]+data[2]+data[3]))
{
printk("check data failed\n");
ret = -EAGAIN;
}
dht11.humidity = data[0];
dht11.temperature = data[2];
return 0;
}
三.总的驱动代码
1.流程图
2.代码
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <asm/mach/map.h>
#include <linux/timer.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define DHT11_CNT 1
#define DHT11_NAME "dht11"
#define DHT11_DelayMs(t) mdelay(t)
#define DHT11_DelayUs(t) udelay(t)
#define DHT11_PIN_HIGH 1
#define DHT11_PIN_LOW 0
#define DHT11_IO_OUT() gpio_direction_output(dht11.dht11_gpio, 1);
#define DHT11_IO_IN() gpio_direction_input(dht11.dht11_gpio)
#define DHT11_WRITE(bit) gpio_set_value(dht11.dht11_gpio, bit)
#define DHT11_READ() gpio_get_value(dht11.dht11_gpio)
/* dht11设备结构体 */
struct dht11_dev
{
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
int major;
int minor;
struct device_node *nd;
int dht11_gpio;
uint16_t humidity, temperature; //检测到的温湿度数据
};
struct dht11_dev dht11;
/**
* @description: 等待响应
*/
//等待响应
static int dht11_wait_for_ready(void)
{
int timeout;
timeout = 400;
while (DHT11_READ() && timeout) // 等待低电平到来
{
udelay(1);
--timeout;
}
if (!timeout)
{
printk("dht11_wait_for_ready timeout1 %d\n", __LINE__);
return -1; // 超时
}
timeout = 1000; //1000
while (!DHT11_READ() && timeout) // 等待高电平到来
{
udelay(1);
--timeout;
}
if (!timeout)
{
printk("dht11_wait_for_ready timeout2 %d\n", __LINE__);
return -1; // 超时
}
timeout = 1000;
while (DHT11_READ() && timeout) // 等待高电平结束
{
udelay(1);
--timeout;
}
if (!timeout)
{
printk("dht11_wait_for_ready timeout3 %d\n", __LINE__);
return -1; // 超时
}
return 0;
}
/**
* @description: 起始信号
*/
static int dht11_start(void)
{
DHT11_IO_OUT();
DHT11_WRITE(0);
mdelay(25);
DHT11_WRITE(1);
udelay(35);
DHT11_IO_IN(); // 设置为输入
udelay(2);
if (dht11_wait_for_ready()) return -1;
return 0;
}
/**
* @description: 读取一个字节
*/
//读取数据
static int dht11_read_byte(unsigned char *byte)
{
unsigned char i;
unsigned char bit = 0;
unsigned char data = 0;
int timeout = 0;
for (i = 0; i < 8; i++)
{
timeout = 1000;
while (DHT11_READ() && timeout) // 等待变为低电平
{
udelay(1);
--timeout;
}
if (!timeout)
{
printk("dht11_read_byte timeout1 %d\n", __LINE__);
return -1; // 超时
}
timeout = 1000;
while (!DHT11_READ() && timeout) // 等待变为高电平
{
udelay(1);
--timeout;
}
if (!timeout)
{
printk("dht11_read_byte timeout2 %d\n", __LINE__);
return -1; // 超时
}
udelay(40);
bit = DHT11_READ();
data <<= 1;
if (bit)
{
data |= 0x01;
}
}
*byte = data;
return 0;
}
//从DHT11读取一次数据
//temp:温度值(范围:0~50°)
//humi:湿度值(范围:20%~90%)
//返回值:0,正常;1,读取失败
static int dht11_read_data(void)
{
unsigned char data[5] = {0};
int i = 0,ret = 0;
// 启动信号
if (dht11_start() != 0)
{
printk("dht11 start failed\n");
ret = -EFAULT;
}
// 读出5字节数据
for (i = 0; i < 5; i++)
{
if (dht11_read_byte(&data[i]))
{
printk("read data err\n");
ret = -EAGAIN;
}
}
if (data[4] != (data[0]+data[1]+data[2]+data[3]))
{
printk("check data failed\n");
ret = -EAGAIN;
}
dht11.humidity = data[0];
dht11.temperature = data[2];
return 0;
}
/**
* @description: DHT11初始化函数
*/
static int dht11io_init(void)
{
/* 找到设备树中的结点 */
dht11.nd = of_find_node_by_path("/dht11");
if(NULL == dht11.nd)
{
return -EINVAL;
}
/* 获取io编号 */
dht11.dht11_gpio = of_get_named_gpio(dht11.nd,"dht11-gpio",0);
if(0 > dht11.dht11_gpio)
{
printk("can not get dht11 io\r\n");
return -EINVAL;
}
printk("dht11 gpio num = %d \r\n",dht11.dht11_gpio);
/* 初始化io */
gpio_request(dht11.dht11_gpio,"dht11a");
gpio_direction_output(dht11.dht11_gpio,1); //初始化为输出高电平
return 0;
}
/**
* @description: 打开DHT11设备
* @param - inode : 传递给驱动的inode
* @param - filp : 设备文件
* @return : 0 成功,其他 失败
*/
static int dht11_open(struct inode *inode,struct file *filp)
{
int ret = 0;
filp->private_data = &dht11;
ret = dht11io_init();
if(0 > ret)
{
return ret;
}
return 0;
}
/**
* @description: 读取dht11的数据
* @param - filp : 文件描述符
* @param - buf : 传递给用户空间的缓冲区
* @param - cnt : 要读取的字节数
* @param - offt : 相对于文件首地址的偏移量
* @return : 读取到的字节数
*/
static ssize_t dht11_read(struct file *filp,char __user *buf,size_t cnt,loff_t *offt)
{
int ret = 0;
uint16_t databuf[2] = {0,0};
dht11_read_data();
databuf[0] = dht11.humidity;
databuf[1] = dht11.temperature;
ret = copy_to_user(buf,databuf,sizeof(databuf));
return ret;
}
/**
* @description: 释放设备
* @param - filp : 设备文件
* @return : 0 成功,其他 失败
*/
static int dht11_release(struct inode *inode,struct file *filp)
{
return 0;
}
/* 绑定操作函数 */
struct file_operations dht11_fops =
{
.owner = THIS_MODULE,
.open = dht11_open,
.read = dht11_read,
.release = dht11_release,
};
/**
* @description: 驱动入口函数
* @param - : 无
* @return : 无
*/
static int __init dht11_init(void)
{
/* 注册字符设备驱动 */
/* 1.创建设备号 */
if(dht11.major)
{
dht11.devid = MKDEV(dht11.major,0);
register_chrdev_region(dht11.devid,DHT11_CNT,DHT11_NAME);
}
else
{
alloc_chrdev_region(&dht11.devid,0,DHT11_CNT,DHT11_NAME);
dht11.major = MAJOR(dht11.devid);
dht11.minor = MINOR(dht11.devid);
}
printk("dht11 major = %d,minor = %d\r\n",dht11.major,dht11.minor);
/* 2.初始化cdev */
dht11.cdev.owner = THIS_MODULE;
cdev_init(&dht11.cdev,&dht11_fops);
/* 3.添加一个cdev */
cdev_add(&dht11.cdev,dht11.devid,DHT11_CNT);
/* 4.创建类 */
dht11.class = class_create(THIS_MODULE,DHT11_NAME);
if(IS_ERR(dht11.class))
{
return PTR_ERR(dht11.class);
}
/* 5.创建设备 */
dht11.device = device_create(dht11.class,NULL,dht11.devid,NULL,DHT11_NAME);
if(IS_ERR(dht11.device))
{
return PTR_ERR(dht11.device);
}
return 0;
}
/**
* @description: 驱动出口函数
*/
static void __exit dht11_exit(void)
{
/* 注销字符设备驱动 */
gpio_free(dht11.dht11_gpio);
cdev_del(&dht11.cdev);
unregister_chrdev_region(dht11.devid,DHT11_CNT);
device_destroy(dht11.class,dht11.devid);
class_destroy(dht11.class);
}
module_init(dht11_init);
module_exit(dht11_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("kaneki");
Makefile:
KERNELDIR := /home/linux/IMX6ULL/my_linux_kernel
CURRENT_PATH :=$(shell pwd)
obj-m := dht11.o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean