DHT11温湿度传感器驱动程序

HT11温湿度模块数据传送逻辑 :只有一根数据线DATA,单片机发送序列指令给DHT11模块,模块一次完整的数据传输为40bit,高位先出。
数据格式:8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据+8bit校验和。1.DHT11通信协议
当主机没有与DHT11通信时,总线处于空闲状态,此时总线电平由于上拉电阻的作用处于高电平。
当主机与DHT11正在通信时,总线处于通信状态,一次完整的通信过程如下:
① 主机将对应的GPIO管脚配置为输出,准备向DHT11发送数据;
② 主机发送一个开始信号:
开始信号 = 一个低脉冲 + 一个高脉冲。低脉冲至少持续18ms,高脉冲持续20-40us。
图1.1 开始信号在这里插入图片描述

图1.1 开始信号

③ 主机将对应的GPIO管脚配置为输入,准备接受DHT11传来的数据,这时信号由上拉电阻拉高;
④ DHT11发出响应信号:
响应信号 = 一个低脉冲 + 一个高脉冲。低脉冲持续80us,高脉冲持续80us。
在这里插入图片描述

图1.2 响应信号

⑤ DHT11发出数据信号:
数据为0的一位信号 = 一个低脉冲 + 一个高脉冲。低脉冲持续50us,高脉冲持续26~28us。
在这里插入图片描述

图1.3 数据为0的信号

数据为1的一位信号 = 一个低脉冲 + 一个高脉冲。低脉冲持续50us,高脉冲持续70us。
在这里插入图片描述

图1.4 数据为1的信号

⑥ DHT11发出结束信号:
最后1bit数据传送完毕后,DHT11拉低总线50us,然后释放总线,总线由上拉电阻拉高进入空闲状态。
在这里插入图片描述

图1.5 空闲状态

数据格式:8bit湿度整数数据+8bit湿度小数数据
+8bi温度整数数据+8bit温度小数数据
+8bit校验和。
数据传送正确时,校验和等于“8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据”所得结果的末8位。

查询方式:触发DHT11转换数据后,就把引脚配置为输入引脚,检测引脚的电平变化,并记录高低电平的时间,最后解析出温湿度。注意关闭中断。

示例程序:

#include <linux/module.h>
#include <linux/poll.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <asm/current.h>
#include <linux/delay.h>
#include <linux/ktime.h>

static int major;
static struct class *dht11_class;
static struct gpio_desc *dht11_data_pin;
static int irq;
static int dht11_data = 0;  
static wait_queue_head_t dht11_wq;

static void dht11_reset(void)
{
	gpiod_direction_output(dht11_data_pin, 1);
	//gpiod_set_value(dht11_data_pin, 1);
}


static void dht11_start(void)
{
	//gpiod_direction_output(dht11_data_pin, GPIOD_OUT_HIGH);
	//schedule_timeout(3*HZ);	
	mdelay(30);
	gpiod_set_value(dht11_data_pin, 0);
	mdelay(20);
	gpiod_set_value(dht11_data_pin, 1);
	udelay(40);	
	gpiod_direction_input(dht11_data_pin);	
	udelay(2);	
}

static int dht11_wait_for_ready(void)
{
	
	int timeout_us = 200;
	/*等待低电平*/
	while(gpiod_get_value(dht11_data_pin)&& --timeout_us)
	{
		udelay(1);
	}
	if(!timeout_us)
	{
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		return -1;
	}

	/*现在是低电平*/
	/*等待高电平*/
	timeout_us = 200;
	while(!gpiod_get_value(dht11_data_pin)&& --timeout_us)
	{
		udelay(1);
	}
	if(!timeout_us)
	{
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		return -1;
	}

	/*现在是高电平*/
	
	/*等待低电平*/
	
	timeout_us = 200;
	while(gpiod_get_value(dht11_data_pin)&& --timeout_us)
	{
		udelay(1);
	}
	if(!timeout_us)
	{
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		return -1;
	}
	return 0;
}

static int dht11_read_byte(unsigned char *buf)
{
	int i;
	int us;
	unsigned char data=0;	
	int timeout_us = 200;
	for(i=0;i<8;i++)
	{
		/*现在是低电平*/
		/*等待高电平*/
		timeout_us = 200;
		while(!gpiod_get_value(dht11_data_pin)&& --timeout_us)
		{
			udelay(1);
		}
		if(!timeout_us)
		{
			return -1;
		}

		/* 现在是高电平 */
		/* 等待低电平,累加高电平的时间 */
		timeout_us = 20000000;
		us = 0;
		
		//last2 = ktime_get_boot_ns();
		//for (j = 0; j < 23; j++)
		udelay(40);

		if (gpiod_get_value(dht11_data_pin))/*如果得到高电平*/
		{
			/* get bit 1 */
			data = (data << 1) | 1;

			/* 当前位的高电平未结束, 等待 */
			timeout_us = 400;
			us = 0;
			while (gpiod_get_value(dht11_data_pin) && --timeout_us)
			{
				udelay(1);
				us++;
			}
			if (!timeout_us)
			{
				printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
				return -1;
			}
			
		}
		else
		{
			/*get bit 0*/
			data = (data<<1)|0;
		}
	}
	*buf = data;
	return 0;
}



/* 实现对应的open/read/write等函数,填入file_operations结构体                   */
static ssize_t dht11_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	int us = 0;
	unsigned long flags;
	int timeout_us = 1000000;
	int i;
	unsigned char data[5];

	if(size != 4)
	{
		return -EINVAL;
	}
	
	local_irq_save(flags);	  // 关中断

	/*1.发送高脉冲,启动DHT11*/
	dht11_reset();
	dht11_start();
	
	/*2.等待DHT11就绪*/
	if(dht11_wait_for_ready())/*设置超时时间等待DHT11*/
	{
		local_irq_restore(flags); // 恢复中断
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		return -EAGAIN;
	}
	/*3.读5字节数据*/
	for(i = 0;i<5;i++)
	{
		if(dht11_read_byte(&data[i]))
		{
			local_irq_restore(flags); // 恢复中断
			return -EAGAIN;
		}
	}

	dht11_reset();
	
	local_irq_restore(flags); // 恢复中断

	/*4.根据校验码验证数据*/
	if(data[4]!=(data[0]+data[1]+data[2]+data[3]))
		return -1;

	/*5.返回数据到应用空间*/
	/*data[0]/data[1]:湿度*/
	/*data[2]/data[3]:温度*/
	copy_to_user(buf, data, 4);
	return 4;
}

static unsigned int dht11_drv_poll(struct file *fp, poll_table * wait)
{
//	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
//	poll_wait(fp, &dht11_wait, wait);
	return 0;
}



/* 定义自己的file_operations结构体                                              */
static struct file_operations dht11_fops = {
	.owner	 = THIS_MODULE,
	.read    = dht11_drv_read,
	.poll    = dht11_drv_poll,
};


static irqreturn_t dht11_isr(int irq, void *dev_id)
{

	/* 2. 唤醒APP:去同一个链表把APP唤醒 */
	wake_up(&dht11_wq);
	
	return IRQ_HANDLED; // IRQ_WAKE_THREAD;
}



/* 1. 从platform_device获得GPIO
 * 2. gpio=>irq
 * 3. request_irq
 */
static int dht11_probe(struct platform_device *pdev)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	/* 1. 获得硬件信息 */
	dht11_data_pin = gpiod_get(&pdev->dev, NULL, GPIOD_OUT_HIGH);
	if (IS_ERR(dht11_data_pin))
	{
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	}

	//irq = gpiod_to_irq(dht11_echo);
	//request_irq(irq, dht11_isr, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, "dht11", NULL);

	/* 2. device_create */
	device_create(dht11_class, NULL, MKDEV(major, 0), NULL, "mydht11");

	return 0;
}

static int dht11_remove(struct platform_device *pdev)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	device_destroy(dht11_class, MKDEV(major, 0));
//	free_irq(irq, NULL);
	gpiod_put(dht11_data_pin);
	return 0;
}


static const struct of_device_id ask100_dht11[] = {
    { .compatible = "100ask,dht11" },
    { },
};

/* 1. 定义platform_driver */
static struct platform_driver dht11_driver = {
    .probe      = dht11_probe,
    .remove     = dht11_remove,
    .driver     = {
        .name   = "100ask_dht11",
        .of_match_table = ask100_dht11,
    },
};

/* 2. 在入口函数注册platform_driver */
static int __init dht11_init(void)
{
    int err;
    
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	/* 注册file_operations 	*/
	major = register_chrdev(0, "dht11", &dht11_fops);  

	dht11_class = class_create(THIS_MODULE, "dht11_class");
	if (IS_ERR(dht11_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "dht11");
		return PTR_ERR(dht11_class);
	}

	init_waitqueue_head(&dht11_wq);

	
    err = platform_driver_register(&dht11_driver); 
	
	return err;
}

/* 3. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
 *     卸载platform_driver
 */
static void __exit dht11_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

    platform_driver_unregister(&dht11_driver);
	class_destroy(dht11_class);
	unregister_chrdev(major, "dht11");
}


/* 7. 其他完善:提供设备信息,自动创建设备节点                                     */

module_init(dht11_init);
module_exit(dht11_exit);

MODULE_LICENSE("GPL");



  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
09-20 399

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值