由于项目需要,需要将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! ",__LINE__);
- break;
- }
- }
- i=0;
- while(gpio_get_value(PIN))
- {
- udelay(5);
- i++;
- if(i>20)
- {
- printk("humidity_read_data %d err! ",__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 ");
- printk("humidity=[%d],temp=[%d] ",buf[0],buf[2]);
- }
- else
- {
- check_flag=0x0;
- printk("humidity check fail ");
- }
- }
- }
- 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 ");
- return -EAGAIN;
- }
- else
- return 0;
- }
- else
- return -EAGAIN;
- }
- static int humidiy_open(struct inode *inode, struct file *file)
- {
- printk("open in kernel ");
- return 0;
- }
- static int humidiy_release(struct inode *inode, struct file *file)
- {
- printk("humidity release ");
- 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 ", DEVICE_NAME,PIN , ret);
- return ret;
- }
- gpio_direction_output(PIN, 1);
- ret = misc_register(&humidity_dev);
- printk("humidity_dev_init ");
- 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 ");
- return 0; }
- while(1){
- ret=read(humidityfd,buf,sizeof(buf));
- if(ret<0)
- printf("read err! ");
- else{
- humidiyz =buf[0];
- humidiyx =buf[1];
- tempz =buf[2] ;
- tempx =buf[3];
- printf("humidity = %d.%d%% ", humidiyz, humidiyx);
- printf("temp = %d.%d ",tempz,tempx);
- }
- sleep(2);
- }
- close(humidityfd);
- return 0;
- }
本想,这驱动调试起来应该简单的。但在调试到过程中,发现采集到的数据有时正确,有时错误,成功率约为50%。于是按照手册微调一下时序,并没有解决问题。网上查阅相关资料,发现都是用单片机来编程的。当程序本来就是以裸奔的思想跑的,为什么移植到Linux会出错呢?从dht11出来的信号都正常啊。误打误撞,使用local_irq_disable这个函数后,读出的数据都正常啦。local_irq_disable通过屏蔽中断标志位,从而禁止内核的抢占。我猜测是Linux是个多任务系统,该系统按照一定的算法(每隔一段时间就会去跑另一段程序,时间不固定),调用一次驱动去读取数据的过程中(时间较长相对于时间片),这期间CPU去做其他事情了,等重新回来读取数据时,有可能错过了时序中的某个片段,从而出现有时读取数据正常,有时错误这种现象。
http://blog.csdn.net/mike8825/article/details/50804978