由于项目需要,需要将DHT11移植到Linux。驱动程序如下

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <asm-generic/uaccess.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>


#define DEVICE_NAME "dht11"
#define PIN S5PV210_GPH0(0)

typedef unsigned char  U8; 
unsigned char buf[6];
unsigned char check_flag;
//spinlock_t lock=SPIN_LOCK_UNLOCKED;

//spinlock_t lock=SPIN_LOCK_UNLOCKED;

//DEFINE_SPINLOCK(lock);
static  DEFINE_MUTEX(mutex);
int read_one_bit(void)          //从io口中读一个字节
{
    gpio_direction_input(PIN);
    return gpio_get_value(PIN);
}


void gpio_out(int value)   //将io口设置为输出,并设置电平
{
    gpio_direction_output(PIN,value);
}

unsigned char  humidity_read_byte(void)
{    
    int i=0;
    int num; 
    unsigned char flag=0;
	unsigned char data=0;
	

    for(num=0;num<8;num++)
	{              
          i=0;
          while(!gpio_get_value(PIN))
		  {
            udelay(10);
            i++;
            if(i>10)
            break;
    	   }
    	    flag=0x0;
    		udelay(28);            
    	   if(gpio_get_value(PIN))
		   {
    		flag=0x01;			
    	   }
    		i=0;
    	   while(gpio_get_value(PIN))
		   {
    		udelay(10);
    		i++;
    		if(i>12)
    		break;
    	   }
    	   data<<=1;
    	   data|=flag;
    }  
	return data;
}


void humidity_read_data(void)
{
	int i=0;
	gpio_out(0);
	mdelay(30);
	gpio_out(1);
	udelay(20);    
	if(read_one_bit()== 0)
	{ 
		while(!gpio_get_value(PIN))
		{
           udelay(5);
           i++;
         	if(i>20)
         	{
				printk("humidity_read_data %d err!\n",__LINE__);
				break;
        	}
		}
        i=0;
        while(gpio_get_value(PIN))
		{
           udelay(5);
           i++;
           if(i>20)
           {
              printk("humidity_read_data %d err!\n",__LINE__);
              break;
           }
        }
		for(i=0;i<5;i++)
        buf[i]=humidity_read_byte();  
       
        buf[5]=buf[0]+buf[1]+buf[2]+buf[3];

        if(buf[4]==buf[5])
		{
           check_flag=0xff;
           printk("humidity check pass\n");
		   printk("humidity=[%d],temp=[%d]\n",buf[0],buf[2]);
        }
        else
		{
           check_flag=0x0;
           printk("humidity check fail\n");           
        }                   
    }
}


static ssize_t humidiy_read(struct file *file, char __user *buffer, size_t size, loff_t *off)
{
        int ret;
        local_irq_disable();
        humidity_read_data();
        local_irq_enable();
        if(check_flag==0xff)
		{
             ret=copy_to_user(buffer,buf,sizeof(buf));
             if(ret<0){
              printk("copy to user err\n");
              return -EAGAIN;
              }
             else
             return  0;
         }
         else
             return -EAGAIN;    
}

static int humidiy_open(struct inode *inode, struct file *file)
{
    printk("open in kernel\n");
    return 0;
}

static int humidiy_release(struct inode *inode, struct file *file)
{
    printk("humidity release\n");
    return 0;
}

static struct file_operations humidity_dev_fops={
    .owner          = THIS_MODULE,
    .open           = humidiy_open,
    .read           = humidiy_read,
    .release        = humidiy_release,
};

static struct miscdevice humidity_dev = {
    .minor          = MISC_DYNAMIC_MINOR,
    .name           = DEVICE_NAME,
    .fops           = &humidity_dev_fops,
};

static int __init humidity_dev_init(void) 
{
    int ret;
    ret = gpio_request(PIN , "humidity");
    if (ret){
      printk("%s: request GPIO %d for humidity failed, ret = %d\n", DEVICE_NAME,PIN , ret);
      return ret;
    }
    gpio_direction_output(PIN, 1);
    ret = misc_register(&humidity_dev);
    printk("humidity_dev_init\n");
    return ret;
}

static void __exit humidity_dev_exit(void)
{
    gpio_free(PIN);
    misc_deregister(&humidity_dev);
}

module_init(humidity_dev_init);
module_exit(humidity_dev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("WWW")

测试程序如下

#include<stdio.h>
#include<sys/types.h>
int main()
{
	int humidityfd;
	int ret;
	char buf[5];
	unsigned char  tempz = 0;
	unsigned char  tempx = 0;     
	unsigned char  humidiyz = 0;
	unsigned char  humidiyx = 0;	
	humidityfd = open("/dev/humidity",0);
	if(humidityfd<0){
		printf("/dev/humidiy open fail\n");
		return 0;	    }      						
	while(1){		
	ret=read(humidityfd,buf,sizeof(buf));
                if(ret<0)
                printf("read err!\n");
                else{
		humidiyz  =buf[0];	
		humidiyx  =buf[1];	
		tempz     =buf[2] ;		
		tempx     =buf[3];		
		printf("humidity = %d.%d%%\n", humidiyz, humidiyx);
		printf("temp = %d.%d\n",tempz,tempx);	
                }
		sleep(2);
	}
	close(humidityfd);
	return 0;
}
本想,这驱动调试起来应该简单的。但在调试到过程中,发现采集到的数据有时正确,有时错误,成功率约为50%。于是按照手册微调一下时序,并没有解决问题。网上查阅相关资料,发现都是用单片机来编程的。当程序本来就是以裸奔的思想跑的,为什么移植到Linux会出错呢?从dht11出来的信号都正常啊。误打误撞,使用local_irq_disable这个函数后,读出的数据都正常啦。local_irq_disable通过屏蔽中断标志位,从而禁止内核的抢占。我猜测是Linux是个多任务系统,该系统按照一定的算法(每隔一段时间就会去跑另一段程序,时间不固定),调用一次驱动去读取数据的过程中(时间较长相对于时间片),这期间CPU去做其他事情了,等重新回来读取数据时,有可能错过了时序中的某个片段,从而出现有时读取数据正常,有时错误这种现象。


Logo

更多推荐