46 dht11/dht12 linux platform驱动实现

在全志h3平台上,外部中断的时钟源需要通过设置相应的配置寄存器选择高频率的时钟源才可以用中断的方式来获取数据位的时间.
为了保证平台驱动代码的可移值性,设置时钟源的代码应在平台设备这边完成.

在头文件里, 封装出结构体类型mydht12_pdata_t用于平台设备里指定,平台驱动应当对此平台设备所需作的初始化/结束工作, 也就是由平台驱动在probe和remove函数里调用

mydht12.h
#ifndef __MYDHT12_H
#define __MYDHT12_H

typedef struct {
    int (*enable)(void); //指向一个设备需初始化时所做的工作函数,由设备驱动对匹配上的设备初始化(probe)时调用 
    void (*disable)(void); //指向设备退出前所需作的工作函数, 由设备驱动里的remove函数触 发时调用
}mydht12_pdata_t;


#endif /* __MYDHT12_H */

//
平台设备, 通过平台设备提供io口资源,通过platform_data提供设备驱动应调用的函数:
mypdev.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
#include <mach/gpio.h>
#include <asm/io.h>
#include "mydht12.h"

#define BASE 0x01C20800

int mydht12_enable(void)
{
    u8 *vaddr;

    //设置PA组的外部中断时钟源为24MHz
    vaddr = ioremap(BASE, SZ_1M);
    iowrite32(1, vaddr+0x218);  

    iounmap(vaddr); 
    return 0;
}

void mydht12_disable(void)
{

}

mydht12_pdata_t pdata = {
    mydht12_enable, mydht12_disable 
};

struct resource res[] = {
    {
        .start = GPIOA(9),  //dht12数据脚接的io口
        .end = GPIOA(9), //资源类型为地址和io口的,必须设置end成员的值
        .flags = IORESOURCE_IO,
    },
};

struct platform_device pdev = {
    .name = "mydht12",
    .id = -1, //id为-1时, pdev.dev.init_name="mydht12", 只有一个设备时,可以设-1

    .dev = {
        .platform_data = &pdata,    
    },

    .resource = res,
    .num_resources = ARRAY_SIZE(res),
};

module_driver(pdev, platform_device_register, platform_device_unregister);

MODULE_LICENSE("GPL");

/
平台驱动, 因在全志h3平台里,当一个io口被配置为输入或输出后,中断的功能就会失效. 中断的功能会在request_irq函数调用时开启中断功能. 所以驱动代码里在发出开始信号后才request_irq, 当数据接收完成后free_irq.
同时,为了避免多个进程同时的操作,驱动代码里加了一个锁,确保一时刻只有一个进程可操作传感器.

mypdrv.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/ktime.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <asm/uaccess.h>

#include "mydht12.h"

#define MYMA  3344

typedef struct {
    int gpio; //记录dht12的数据脚所接的io口

    struct cdev cdev;
    struct mutex mutex_lock; //用于确保一时刻只能有一个进程访问传感器数据
    struct mutex mutex_data; //用于进程等传感器收集数据完毕

    u64 prev; //记录上一次的计时时间
    u32 times[40]; //记录40位数据的时间
    int num; //记录当前是第几位数据
    u8  data[5]; //记录转换后的温湿度数据
    const char *name; //记录平台设备的名字 
}mydht12_data_t;

irqreturn_t irq_func(int irqno, void *arg);
ssize_t myread(struct file *fl, char __user *buf, size_t len, loff_t *off)
{
    struct cdev *cdev = fl->f_path.dentry->d_inode->i_cdev;
    mydht12_data_t *data = container_of(cdev, mydht12_data_t, cdev);
    int ret;    

    //防止多个进程同时操作    
    ret = mutex_lock_interruptible(&data->mutex_lock);
    if (ret < 0)
        goto err0;

    //发出开始信号, 先配置数据脚为输出
    gpio_direction_output(data->gpio, 0);
    msleep(30); //至少保持低电平18ms
    gpio_set_value(data->gpio, 1);
    udelay(30); //保持高电平30us
    gpio_direction_input(data->gpio);       

    //检查是否有dht12发回的响应信号,如没有则表示没接设备
    if (gpio_get_value(data->gpio) && (gpio_get_value(data->gpio)))
    {
        printk("no dht12 ack\n");
        ret = -ENODEV;
        goto err1;
    }
    data->num = 0; //从第0位数据开始接收
    ret = request_irq(gpio_to_irq(data->gpio), irq_func, IRQF_TRIGGER_FALLING, data->name, data);
    if (ret < 0)
        goto err1;  

    //等接收数据完成
    ret = mutex_lock_interruptible(&data->mutex_data);
    if (ret < 0)
        goto err2;

    //数据接收完毕
    sprintf(buf, "humi: %d.%d, temp: %d.%d, check_sum:%d\n", data->data[0], data->data[1], data->data[2], data->data[3], data->data[4]);    

    free_irq(gpio_to_irq(data->gpio), data); //完成后,释放中断
    mutex_unlock(&data->mutex_lock); //解锁

    return strlen(buf);
err2:
    free_irq(gpio_to_irq(data->gpio), data);
err1:
    mutex_unlock(&data->mutex_lock);
err0:
    return ret;
}

struct file_operations fops = {
    .read = myread,
};

irqreturn_t irq_func(int irqno, void *arg)
{
    mydht12_data_t *data = (mydht12_data_t *)arg;
    u64 now = ktime_to_us(ktime_get());
    int i, j;

    if (0 == data->num)
        data->prev = now;
    else
    {
        data->times[data->num - 1] = now - data->prev;
        data->prev = now;
    }
    data->num++;

    if (data->num > 40) //收完40位数据的时间
    {
        //把时间转成字节数据
        for (i = 0; i < 5; i++)
        {
            data->data[i] = 0;
            for (j = 0; j < 8; j++)
            {
                if (data->times[i*8+j] > 100)
                    data->data[i] |= 1<<(7-j);
            }
        }
        mutex_unlock(&data->mutex_data); 
    }


    return IRQ_HANDLED;
}

static struct class *mycls; //所有设备共用一个class对象,用于创建设备文件
int myprobe(struct platform_device *pdev)
{
    static int mi = 0; //记录次设备号
    mydht12_pdata_t *pdata = pdev->dev.platform_data;
    struct resource *res;
    int gpio, ret;
    mydht12_data_t *data;
    dev_t devid;

    if (NULL == pdata)
        return -EINVAL;

    //获取第0个IO口资源    
    res = platform_get_resource(pdev, IORESOURCE_IO, 0);
    if (NULL == res)
        return -EINVAL;

    gpio = res->start;
    ret = gpio_request(gpio, pdev->name); //请求IOif (ret < 0)
        goto err0;

    pdata->enable(); //调用设备的enable函数    

初始设备驱动对匹配上的设备的专用数据
    data = kzalloc(sizeof(*data), GFP_KERNEL);
    data->gpio = gpio;
    data->name = pdev->name;    

    //初始化设备专用的锁
    mutex_init(&data->mutex_lock);  
    mutex_init(&data->mutex_data);  
    mutex_lock(&data->mutex_data);

    //申请设备号
    devid = MKDEV(MYMA, mi);
    ret = register_chrdev_region(devid, 1, pdev->name);
    if (ret < 0)
        goto err1;  

    // cdev对象初始化,并加入内核
    cdev_init(&data->cdev, &fops);
    data->cdev.owner = THIS_MODULE;
    ret = cdev_add(&data->cdev, devid, 1);
    if (ret < 0)
        goto err2;

    //创建设备文件
    if (-1 != pdev->id)
        device_create(mycls, NULL, devid, NULL, "%s.%d", pdev->name, pdev->id);
    else
        device_create(mycls, NULL, devid, NULL, pdev->name);

    platform_set_drvdata(pdev, data);//绑定设备驱动对此设备专用的数据
    mi++;   



    printk("myprobe : %s\n", pdev->name);
    return 0;
err2:
    unregister_chrdev_region(devid, 1);
err1:
    kfree(data);
    free_irq(gpio_to_irq(gpio), pdev);
    gpio_free(gpio);
err0:
    return ret;
}

int myremove(struct platform_device *pdev)
{
    mydht12_pdata_t *pdata = pdev->dev.platform_data;
    struct resource *res = platform_get_resource(pdev, IORESOURCE_IO, 0);
    mydht12_data_t *data = platform_get_drvdata(pdev);


    pdata->disable(); //调用设备的disable函数
    gpio_free(res->start);

    unregister_chrdev_region(data->cdev.dev, 1);
    cdev_del(&data->cdev);
    device_destroy(mycls, data->cdev.dev);
    kfree(data);


    printk("myremove : %s\n", pdev->name);
    return 0;
}


struct platform_device_id ids[] = {
    {"mydht12"},
    {}, 
};

struct platform_driver pdrv = {
    .probe = myprobe,
    .remove = myremove,

    .driver = {
        .owner = THIS_MODULE,
        .name = "mydrv",
    },

    .id_table = ids,
};

static int __init test_init(void)
{
    mycls = class_create(THIS_MODULE, "mydht12");
    return platform_driver_register(&pdrv);
}

static void __exit test_exit(void)
{
    platform_driver_unregister(&pdrv);
    class_destroy(mycls);
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值