您的位置:首页 > 运维架构 > Linux

将DHT11移植到Linux系统上

2016-03-04 19:42 465 查看
由于项目需要,需要将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去做其他事情了,等重新回来读取数据时,有可能错过了时序中的某个片段,从而出现有时读取数据正常,有时错误这种现象。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: