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

来源:互联网 发布:易语言发送qq消息源码 编辑:程序博客网 时间:2024/06/05 07:43

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

#ifndef __DISTANCER_H#define __DISTANCER_Htypedef 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 1234typedef 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口需改变时,不需要修改设备驱动,只需修改设备部分的代码即可