45 超声波测距模块的linux platform驱动模型实现

//用一个头文件封装结构体表示超声波测距模块所用的io口
distancer.h


#ifndef __DISTANCER_H
#define __DISTANCER_H

typedef struct {
    int trigger_io; //表示trigger的引脚 
    int echo_io;    //捕捉数据的引脚 
}mypdata_t;


#endif  /* __DISTANCER_H */

//

mypdev.c


#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <mach/gpio.h>
#include <linux/platform_device.h>
#include "distancer.h"


mypdata_t pdata = {
    .trigger_io = GPIOA(10), //PA10
    .echo_io = GPIOA(9),  //PA9
};

struct platform_device mydev = {
    .name = "distancer",
    .id = 0,
    .dev = {
        .platform_data = &pdata, 
    },
    .resource = NULL,
};

//生成初始化函数和卸载函数,并用module_init, module_exit指定相应的函数
module_driver(mydev, platform_device_register, platform_device_unregister);

MODULE_LICENSE("GPL");

平台设备驱动:
mypdrv.c


#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/ktime.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include "distancer.h"

#define MYMAJOR 1234

typedef struct {
    u64 prev; //记录超声波测距的上升沿中断时间
    int started; //表示已接收到上升沿中断
    int time; //测量的时间多少us

    int locked;
    struct mutex mutex;

    struct cdev cdev;
    mypdata_t *pdata;

}mydata_t; //在设备驱动里每个超声波设备都用一个这类型的对象来记录时间


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; 
    mydata_t *data = container_of(cdev, mydata_t , cdev);
    mypdata_t *pdata = data->pdata;
    int ret;

     发出开始信号///
    gpio_direction_output(pdata->trigger_io, 0);
    gpio_set_value(pdata->trigger_io, 1);
    msleep(1);
    gpio_set_value(pdata->trigger_io, 0);

    //上锁
    data->locked = 1;
    ret = mutex_lock_interruptible(&data->mutex);
    if (ret < 0)
        return -ERESTART;

    data->locked = 0;
    //复制数据
    sprintf(buf, "last time: %dus\n", data->time);

    return strlen(buf);
}


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


irqreturn_t irq_func(int irqno, void *arg) //多个设备共用这个中断处理函数
{
    struct platform_device *pdev = (struct platform_device *)arg;   
    mydata_t *data = platform_get_drvdata(pdev);
    mypdata_t *pdata = pdev->dev.platform_data;
    u64 now = ktime_to_us(ktime_get()); 

    if (gpio_get_value(pdata->echo_io)) //高电平表示上升沿的中断
    {
        data->started = 1; 
        data->prev = now;
    }
    else if(data->started)  //下降沿中断, 测量结束
    {
        data->started = 0;
//      printk("%lldus\n", now - data->prev);
        data->time = (now - data->prev)>>1;
        if (data->locked)
            mutex_unlock(&data->mutex);
    }
    return IRQ_HANDLED;
}

static struct class *mycls;
int myprobe(struct platform_device *pdev) //参数为则匹配上的设备对象的地址
{
    mypdata_t *pdata = pdev->dev.platform_data;
    int ret, irqno;
    mydata_t *data;
    dev_t devid;
    static int mi = 0;

    if (NULL == pdata)
        return -EINVAL;

    ret = gpio_request(pdata->trigger_io, pdev->name);
    if (ret < 0)
        goto err0;
    ret = gpio_request(pdata->echo_io, pdev->name);
    if (ret < 0)
        goto err1;

    irqno = gpio_to_irq(pdata->echo_io);
    ret = request_irq(irqno, irq_func, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, pdev->name, pdev);
    if (ret < 0)
        goto err2;

    data = kzalloc(sizeof(*data), GFP_KERNEL);//每个设备都分配一个记录时间的对象
    platform_set_drvdata(pdev, data); // dev_set_drvdata(&pdev->dev, data)

    data->pdata = pdata;
    mutex_init(&data->mutex);
    mutex_lock(&data->mutex);

    devid = MKDEV(MYMAJOR, mi++); 
    ret = register_chrdev_region(devid, 1, pdev->name);
    if (ret < 0)
        goto err3;  

    cdev_init(&data->cdev, &fops);
    data->cdev.owner = THIS_MODULE;
    ret = cdev_add(&data->cdev, devid, 1);
    if (ret < 0)
        goto err4;

    device_create(mycls, NULL, data->cdev.dev, NULL, "%s.%d", pdev->name, pdev->id);
    printk("in myprobe ...%s.%d\n", pdev->name, pdev->id);

    return 0; //返回0表示设备驱动对刚匹配上的设备初始化成功,负数表示失败
err4:
    unregister_chrdev_region(devid, 1);
err3:
    kfree(data);
    free_irq(irqno, pdev);
err2:
    gpio_free(pdata->echo_io);
err1:
    gpio_free(pdata->trigger_io);
err0:
    return ret;
}

int myremove(struct platform_device *pdev) //参数为需要结束驱动工作的设备对象的地址
{
    mypdata_t *pdata = pdev->dev.platform_data;
    int irqno = gpio_to_irq(pdata->echo_io);
    mydata_t *data = platform_get_drvdata(pdev);

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

    device_destroy(mycls, data->cdev.dev);

    free_irq(irqno, pdev);
    gpio_free(pdata->trigger_io);
    gpio_free(pdata->echo_io);
    kfree(data);
    printk("in myremove ...%s.%d\n", pdev->name);

    return 0;
}


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

    .driver = {
        .owner = THIS_MODULE,
        .name = "distancer", //按名字匹配
    },
};

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

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

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");

//
当多接入模块时,只需多增加一个platform_device对象来描述硬件接口即可.
当模块接的IO口需改变时,不需要修改设备驱动,只需修改设备部分的代码即可

评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值