中级会员 
   
	- 积分
 - 327
 
        - 金钱
 - 327 
 
       - 注册时间
 - 2022-4-12
 
      - 在线时间
 - 41 小时
 
 
 
 | 
 
因为我是做工业控制类产品,所以我的GPIO应用场景主要是作开关量控制,如开有源开关量输出,干接点输出,开关量输入等,同时也会有一些简单的LED控制。 
但是在实际的应用中,根据硬件工程师的电路设计,有时候GPIO输出高电平表示“有效”,有时候输出低电平表示“有效”。 
而教程里的大部分驱动的写法好像都是单一的应用——“一个驱动对应一个设备”,并没有体现“Linux下的驱动分离与分层”。 
像我这样的GPIO控制,N种应用就要写N个对应的驱动? 
但是实际上对于MPU而言所有的GPIO控制又都是一样的,无非就是引脚出输出高低电平,或者读取电平。 
 
所以我想跟大家分享下我摸索的GPIO驱动,有不足的地方欢迎指出,(我只测试了写输出,还没有测试读输入) 
 
PS:代码里所有的“fa”只是我的公司名称缩写,并没有特殊含义 
 
1、首先是设备树部分 
大体上跟教程差不多 
 
在根节点下添加自己的设备节点, 
增加了一个fa-polarity = <0>;用于指明该引脚的有效极性,我并没有用GPIO_ACTIVE_LOW这个值(主要是懒,没有深度摸索Linux设备树方面的代码,所以直接新建了一个自己的属性) 
增加了一个fa-name = "fa_gpio_active_output1";用于给这个GPIO设备取个名字,(实际上没什么用,完全可以用节点的名字代替。。。) 
fa_gpio_output1 { 
        #address-cells = <1>; 
        #size-cells = <1>; 
        compatible = "fa,iWaterX-fa_gpio"; 
        pinctrl-names = "default"; 
        pinctrl-0 = <&pinctrl_fa_gpio_output1>; 
        fa-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>; 
        fa-polarity = <0>; 
        fa-name = "fa_gpio_active_output1"; 
        status = "okay"; 
    }; 
 
    fa_gpio_output2 { 
        #address-cells = <1>; 
        #size-cells = <1>; 
        compatible = "fa,iWaterX-fa_gpio"; 
        pinctrl-names = "default"; 
        pinctrl-0 = <&pinctrl_fa_gpio_output2>; 
        fa-gpio = <&gpio1 2 GPIO_ACTIVE_LOW>; 
        fa-polarity = <1>; 
        fa-name = "fa_gpio_passive_output1"; 
        status = "okay"; 
    }; 
 
引脚复用部分根据教程和自己实际情况修改即可 
pinctrl_fa_gpio_output1:gpio_output1_grp { 
            fsl,pins = < 
                MX6UL_PAD_GPIO1_IO03__GPIO1_IO03        0x10b0 
            >; 
        }; 
 
        pinctrl_fa_gpio_output2:gpio_output2_grp { 
            fsl,pins = < 
                MX6UL_PAD_GPIO1_IO02__GPIO1_IO02        0x10b0 
            >; 
        }; 
 
 
2、驱动.c文件 
大体上也是跟教程差不多,主要是增加了设备信息结构体数组,和区别到底是哪个设备被使用的查找函数,先直接贴代码了,重点部分我加粗显示 
 
#include "include.h" 
 
/* 
linux gpio demo 
各个有返回值的函数一定要写返回值,否则可能出现段错误 
头文件根据教程自己添加就好了 
*/ 
 
#define DEVICE_NAME     "fa_gpio" 
#define DEVICE_CNT      1 
#define DEVICE_MAX      5            //我是静态分配的设备信息结构体数组,所以宏定义了允许的最大设备数量为5 
 
/*  */ 
 
/* 设备结构体 */ 
typedef struct 
{ 
    dev_t dev_id;                   /* 设备号 */ 
    struct cdev dev;                /* cdev */ 
    struct class *pclass;           /* 类 */ 
    struct device *pdevice;         /* 设备 */ 
 
    int major;                      /* 主设备号 */ 
    int minor;                      /* 次设备号 */ 
 
    struct device_node *node;       /* 设备节点 */ 
 
    int gpio_cnt;                   /* gpio引脚数量 */ 
    int gpio[10];                   /* GPIO编号 */ 
    int polarity;                   /* GPIO极性 */ 
 
    const char *pname;                    /* 设备名称 */ 
}fa_gpio_t; 
 
static fa_gpio_t fa_gpio[DEVICE_MAX];           //设备信息结构体数组,用于保存各个设备的信息 
static int fa_gpio_index = 0;                              //当前设备总数 
 
/* 
搜索符合要求的设备信息结构体,并返回序号 
pname是目标设备名称 
*/ 
static int driver_fa_gpio_find(const char *pname) 
{ 
    int i; 
    int ret = -1; 
 
    for(i=0;i<fa_gpio_index;i++) 
    { 
        if(strcmp(pname,fa_gpio.pname) == 0) 
        { 
            ret = i; 
            break; 
        } 
    } 
 
    return ret; 
} 
 
/* 字符设备框架 */ 
static int driver_fa_gpio_open(struct inode *pinode, struct file *pfile) 
{ 
    int index; 
    int ret = 0; 
 
    index = driver_fa_gpio_find(pfile->f_path.dentry->d_iname);        //pfile->f_path.dentry->d_iname是被打开的设备文件的名称,它与你设备是同名的 
    if(index != -1) 
    { 
        ret = 0; 
        pfile->private_data = &fa_gpio[index]; 
        printk("%s open !! \r\n",pfile->f_path.dentry->d_iname); 
    } 
    else 
    { 
        ret = -1; 
        printk("%s open failed !! \r\n",pfile->f_path.dentry->d_iname); 
    } 
 
    return ret; 
} 
static int driver_fa_gpio_release(struct inode *pinode, struct file *pfile) 
{ 
    printk("%s release !! \r\n",pfile->f_path.dentry->d_iname); 
 
    return 0; 
} 
 
ssize_t driver_fa_gpio_read(struct file *pfile, char __user *p__user, size_t size, loff_t *ploff) 
{ 
    char databuf[size]; 
    uint8_t value = 0; 
    int i; 
    int ret; 
    fa_gpio_t *pdev = (fa_gpio_t *)pfile->private_data; 
 
    for(i=0;i<pdev->gpio_cnt;i++) 
    { 
        value |= (((gpio_direction_input(pdev->gpio) == pdev->polarity) ? 1 : 0) << i); 
    } 
 
    databuf[0] = value; 
    ret = copy_to_user(p__user,databuf,size); 
 
    printk("%s read !! \r\n",pdev->pname); 
 
    return 0; 
} 
 
ssize_t driver_fa_gpio_write(struct file *pfile, const char __user *p__user, size_t size, loff_t *ploff) 
{ 
    char databuf[size]; 
    uint8_t value; 
    int i; 
    int ret; 
    fa_gpio_t *pdev = (fa_gpio_t *)pfile->private_data; 
 
    ret = copy_from_user(databuf,p__user,size); 
    value = databuf[0]; 
 
    for(i=0;i<pdev->gpio_cnt;i++) 
    { 
        if(value & 0x01) 
        { 
            gpio_direction_output(pdev->gpio,pdev->polarity); 
        } 
        else 
        { 
            gpio_direction_output(pdev->gpio,!pdev->polarity); 
        } 
        value >>= 1; 
    } 
 
    printk("%s write !! \r\n",pdev->pname); 
 
    return 0; 
} 
 
static const struct file_operations driver_fa_gpio_fops =  
{ 
    .owner = THIS_MODULE, 
    .open = driver_fa_gpio_open, 
    .release = driver_fa_gpio_release, 
    .read = driver_fa_gpio_read, 
    .write = driver_fa_gpio_write, 
}; 
 
/* platform 框架 */ 
static int driver_fa_gpio_probe(struct platform_device *pdev) 
{ 
    int i; 
 
    if(fa_gpio_index == DEVICE_MAX) 
    { 
        return -1; 
    } 
 
    //get device_node 
    fa_gpio[fa_gpio_index].node = pdev->dev.of_node;       //教程里一般都是用of_find_node_by_path("xxxxx/xxxxx/xxxxx");这样的话驱动只能指定某一个节点表示的设备了,我这样的写法是参考内核的自带的驱动的写法 
    //get gpio_cnt读取fa-gpio属性中的引脚个数 
    fa_gpio[fa_gpio_index].gpio_cnt = of_gpio_named_count(fa_gpio[fa_gpio_index].node,"fa-gpio"); 
    printk("fa_gpio gpio cnt %d !!\r\n",fa_gpio[fa_gpio_index].gpio_cnt); 
    //get gpio polarity读取引脚极性 
    of_property_read_u32_index(fa_gpio[fa_gpio_index].node,"fa-polarity",0,&fa_gpio[fa_gpio_index].polarity); 
    printk("fa_gpio gpio polarity %d !!\r\n",fa_gpio[fa_gpio_index].polarity); 
    //get device name 
    if(of_property_read_string_index(fa_gpio[fa_gpio_index].node,"fa-name",0,&fa_gpio[fa_gpio_index].pname) != 0) 
    { 
        fa_gpio[fa_gpio_index].pname = fa_gpio[fa_gpio_index].node->name; 
    } 
    printk("fa_gpio name %s !!\r\n",fa_gpio[fa_gpio_index].pname); 
    //读取引脚编号,并初始化引脚为非极性电平输出 
    for(i=0;i<fa_gpio[fa_gpio_index].gpio_cnt;i++) 
    { 
        fa_gpio[fa_gpio_index].gpio = of_get_named_gpio(fa_gpio[fa_gpio_index].node,"fa-gpio",i); 
        gpio_direction_output(fa_gpio[fa_gpio_index].gpio,!fa_gpio[fa_gpio_index].polarity); 
 
        printk("fa_gpio gpio %d = %d !!\r\n",i,fa_gpio[fa_gpio_index].gpio); 
    } 
 
    //alloc chrdev dev_id 
    alloc_chrdev_region(&fa_gpio[fa_gpio_index].dev_id,0,DEVICE_CNT,fa_gpio[fa_gpio_index].pname); 
    fa_gpio[fa_gpio_index].major = MAJOR(fa_gpio[fa_gpio_index].dev_id); 
    fa_gpio[fa_gpio_index].minor = MINOR(fa_gpio[fa_gpio_index].dev_id); 
    //printk chrdev dev_id 
    printk("fa_gpio dev_id = %d major = %d minor = %d \r\n",fa_gpio[fa_gpio_index].dev_id,fa_gpio[fa_gpio_index].major,fa_gpio[fa_gpio_index].minor); 
    //init cdev 
    cdev_init(&fa_gpio[fa_gpio_index].dev,&driver_fa_gpio_fops); 
    //add cdev 
    cdev_add(&fa_gpio[fa_gpio_index].dev,fa_gpio[fa_gpio_index].dev_id,DEVICE_CNT); 
    //create class 
    fa_gpio[fa_gpio_index].pclass = class_create(THIS_MODULE,fa_gpio[fa_gpio_index].pname); 
    //create device 
    fa_gpio[fa_gpio_index].pdevice = device_create(fa_gpio[fa_gpio_index].pclass,NULL,fa_gpio[fa_gpio_index].dev_id,NULL,fa_gpio[fa_gpio_index].pname); 
 
    printk("%s probe !!!\r\n\r\n",fa_gpio[fa_gpio_index].pname); 
 
    fa_gpio_index++; 
 
    return 0; 
} 
 
static int driver_fa_gpio_remove(struct platform_device *pdev) 
{ 
    int i,j; 
 
    for(j=0;j<fa_gpio_index;j++) 
    { 
        for(i=0;i<fa_gpio[j].gpio_cnt;i++) 
        { 
            gpio_direction_output(fa_gpio[j].gpio,!fa_gpio[j].polarity); 
        } 
 
        //destroy device 
        device_destroy(fa_gpio[j].pclass,fa_gpio[j].dev_id); 
        //destroy class 
        class_destroy(fa_gpio[j].pclass); 
        //del cdev 
        cdev_del(&fa_gpio[j].dev); 
        //del dev_id 
        unregister_chrdev_region(fa_gpio[j].dev_id,DEVICE_CNT); 
 
        printk("%s remove !!!\r\n\r\n",fa_gpio[j].pname); 
    } 
 
    fa_gpio_index = 0; 
 
    return 0; 
} 
 
static const struct of_device_id driver_fa_gpio_of_mach[] = { 
    {.compatible = "fa,iWaterX-fa_gpio"}, 
    {} 
}; 
static const struct platform_device_id driver_fa_gpio_id_mach[] = { 
    {"fa,iWaterX-fa_gpio",0}, 
    {} 
}; 
 
static struct platform_driver platform_driver_fa_gpio = { 
    .driver = { 
        .owner = THIS_MODULE, 
        .name = DEVICE_NAME, 
        .of_match_table = driver_fa_gpio_of_mach, 
    }, 
    .probe = driver_fa_gpio_probe, 
    .remove = driver_fa_gpio_remove, 
    .id_table = driver_fa_gpio_id_mach, 
}; 
 
int __init driver_fa_gpio_init(void) 
{ 
    printk("%s init ok !!\r\n\r\n",DEVICE_NAME); 
 
    return platform_driver_register(&platform_driver_fa_gpio); 
} 
 
void __exit driver_fa_gpio_exit(void) 
{ 
    printk("%s exit ok !!\r\n\r\n",DEVICE_NAME); 
 
    return platform_driver_unregister(&platform_driver_fa_gpio); 
} 
 
module_init(driver_fa_gpio_init); 
module_exit(driver_fa_gpio_exit); 
 
MODULE_LICENSE("GPL"); 
MODULE_AUTHOR("JHW"); 
 
3、应用 
应用软件参考教程即可 
 
根据上述的驱动代码,编译设备树,并加载驱动后, 
ls /dev就可以看到fa_gpio_active_output1和fa_gpio_passive_output1这2个设备了,设备名是我的“fa-name”属性决定的,所以不是现实的节点的名称 
运行测试app,参数使用/dev/fa_gpio_active_output1或者/dev/fa_gpio_passive_output1就可以分别驱动2种GPIO设备了 
 
如果就改变教程的GPIO驱动代码,当有2个设备使用同一个驱动时,驱动中的设备信息结构体只会保存最后一个匹配的设备的信息,只有最后一个设备才能被驱动 
 
 
 
 
 |   
 
 
 
 |