DHT 11をLinuxシステムに移植する
5986 ワード
プロジェクトの必要性のため、DHT 11を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に移植したら間違いがあったのだろうか.dht 11から出る信号はすべて正常ですね.誤打誤撞、local_を使用irq_disableという関数の後、読み出したデータは正常になりました.local_irq_disableは、割り込みフラグビットを遮断することによって、カーネルのプリエンプトを禁止する.Linuxはマルチタスクシステムだと思います.このシステムは一定のアルゴリズム(一定の時間ごとに別のプログラムを走ります.時間が固定されていません)に従って、1回の駆動を呼び出してデータを読み取る過程(時間が長いのはタイムスライスに対して)で、この間CPUは他のことをして、再び戻ってデータを読み取る時、タイミングのあるセグメントを逃した可能性があります.これにより、読み出しデータが正常であったり、エラーであったりする現象が発生します.