嵌入式开发记录 day42 设备树GPIO驱动例程

1、在Linux中引入设备树,方便管理设备、并减少了代码量,在原始的平台文件platform下,管理GPIO管脚很混乱;

  设备树为了方便管理GPIO管脚,添加了pinctrl子系统,用于管理GPIO;

  另外,pinctrl的引入只是方便了GPIO的管理,但是还无法完全代替原来的GPIO子系统;因此原来基于platform下的GPIO子系统与pinctrl是共同存在的;

2、GPIO参考文档

  linux的GPIO系统官方文档:Documentation/devicetree/bindings/gpio/gpio.txt

Example of a node using GPIOs:
 node {
                enable-gpios = <&qe_pio_e 18 GPIO_ACTIVE_HIGH>;
        };  

    设备树的节点,可以包含,互相也可以引用--看到“&”,表示做了引用(也可以理解面向对象编程的重写);
    设备树的节点,“xxx :xxx”表示这个节点可以引用
    &bank gpioa1 gpiob1
    18表示gpio在bank中编号
    GPIO_ACTIVE_HIGH 表示高电平(测试无效,是设备树中强制规定必须有2个cells)

  三星的GPIO系统官方文档:Documentation/devicetree/bindings/gpio/gpio-samsung.txt

3、pinctrl文档

  linux的pinctrl系统官方文档:Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt

  三星的pinctrl系统官方文档:Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt

  源码文档:

    dt-bindings/pinctrl/samsung.h
    arch/arm/boot/dts/exynos4412-pinctrl.dtsi

4、硬件连接信息

  硬件:4412的两个led灯

    gpl2-0对应<&gpl2 0 GPIO_ACTIVE_HIGH>
    gpk1-1对应<&gpk1 1 GPIO_ACTIVE_HIGH>
  vim arch/arm/boot/dts/exynos4412-itop-elite.dts,设备树增加属性两个属性:
    gpios1 = <&gpl2 0 GPIO_ACTIVE_HIGH>;
    gpios2 = <&gpk1 1 GPIO_ACTIVE_HIGH>;

leds_test_node:leds_test_node {
                compatible = "leds_test";
                status = "disabled";
                gpios1 = <&gpl2 0 GPIO_ACTIVE_HIGH>;  // 在前面实验基础上,添加下面这两行代码
                gpios2 = <&gpk1 1 GPIO_ACTIVE_HIGH>;
                // status = "okay";   // 在下面引用使能
        };

  屏蔽掉其它代码对它们的引用

 led3 {
                        label = "red:user";
                        // gpios = <&gpk1 1 GPIO_ACTIVE_HIGH>;   // 屏蔽掉这行
                        default-state = "off";
                };

5、驱动代码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>    // 获取GPIO管脚属性信息

#include <linux/miscdevice.h>
#include <linux/fs.h>

#define DRIVER_NAME "leds_test"

int gpio_pin[2] = {-1};

static int leds_probe(struct platform_device * pdev)
{
    struct device_node *node = pdev->dev.of_node;
    int ret;
    
    printk("led init\n");
    /*
    1、设备树这样写    gpios1 = <&gpl2 0 GPIO_ACTIVE_HIGH>;
        gpios2 = <&gpk1 1 GPIO_ACTIVE_HIGH>;  
        这样获取gpio_pin[0] = of_get_named_gpio(node, "gpios1", 0);
    2、设备树这样写 gpios = <&gpl2 0 GPIO_ACTIVE_HIGH; &gpk1 1 GPIO_ACTIVE_HIGH>;
        这样获取gpio_pin[0] = of_get_named_gpio(node, "gpios", 0);  --> &gpl2 0 GPIO_ACTIVE_HIGH
        这样获取gpio_pin[1] = of_get_named_gpio(node, "gpios", 1);  --> &gpk1 1 GPIO_ACTIVE_HIGH
        
    */    
    gpio_pin[0] = of_get_named_gpio(node, "gpios1", 0);   // 获取GPIO管脚的宏定义 类似于之前EXYNOS_GPIO...
    gpio_pin[1] = of_get_named_gpio(node, "gpios2", 0);
    // 获取到管脚可以申请GPIO
    ret = gpio_request(gpio_pin[0], "led1");
    if(ret!=0){
        printk("gpio_pin[0] request %d failed.", gpio_pin[0]);
        return ret;
    }
    if (gpio_pin[1] < 0)
        printk("gpio_pin[1] is not available \n");
    ret = gpio_request(gpio_pin[1], "led2");
    if(ret!=0){
        printk("gpio_pin[1] request %d failed.", gpio_pin[1]);
        return ret;
    }
    printk("gpio_pin[0] is %d\n",gpio_pin[0]);
    printk("gpio_pin[1] is %d\n",gpio_pin[1]);
    
    
    gpio_free(gpio_pin[0]);
    gpio_free(gpio_pin[1]);
    
    gpio_direction_output(gpio_pin[0],0);   // 设置直接输出模式
    gpio_set_value(gpio_pin[0], 1);            // 设置高电平输出
    
    gpio_direction_output(gpio_pin[1],0);
    gpio_set_value(gpio_pin[1], 1);
    
     
    return 0;
}

static int leds_remove(struct platform_device * pdev)
{
    printk(KERN_ALERT "Goodbye, curel world, this is remove\n");
    return 0;
}

static const struct of_device_id of_leds_dt_match[] = {
    {.compatible = DRIVER_NAME},
    {},
};

MODULE_DEVICE_TABLE(of,of_leds_dt_match);

static struct platform_driver leds_driver = {
    .probe     = leds_probe,
    .remove = leds_remove,
    .driver = {
        .name = DRIVER_NAME,
        .owner = THIS_MODULE,
        .of_match_table = of_leds_dt_match,
        },
};

static int leds_init(void)
{
    printk(KERN_ALERT "Hello, world\n");
    return platform_driver_register(&leds_driver);
    return 0;
}

static void leds_exit(void)
{
    printk(KERN_ALERT "Goodbye, curel world\n");
    platform_driver_unregister(&leds_driver);
}

module_init(leds_init);
module_exit(leds_exit);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("rty");
MODULE_DESCRIPTION("topeet4412_regiter_dev_drv");

6、函数说明

int of_get_named_gpio(struct device_node *np, const char *propname, int index);
//    功能:of_get_named_gpio,从设备树中提取gpio 口;
//    参数np:设备节点指针;
//    参数propname:属性名;
//    参数index:gpio口引脚标号;
//    返回值: 成功,得到GPIO 口编号;失败,负数,绝对值是错误码。
//    of_get_named_gpio(node,"gpios1",0)
//    of_get_named_gpio(node,"gpios2",0)

7、测试

  1、必须先编译修改后设备树文件,并烧写;

  2、编译上面源文件为模块,并加载,此时开发板两个灯都是亮的;

  3、打印书输出:

[root@iTOP-4412]# insmod itop4412_of_get_gpios.ko                                                                         
[ 1630.825660] itop4412_of_get_gpios: loading out-of-tree module taints kernel.
[ 1630.832394] Hello, world
[ 1630.834104] led init
[ 1630.836076] gpio_pin[0] is 120   // 将管脚编号处理,得到编号后,便可以对管脚操作
[ 1630.839013] gpio_pin[1] is 91

8、给上面的驱动添加file_operations就可以对外提供接口,就可以读写等操作;

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>

#include <linux/miscdevice.h>
#include <linux/fs.h>   // file_operations

#define DRIVER_NAME "leds_test"

int gpio_pin[2] = {-1};

int leds_open(struct inode *inode,struct file *filp)
{
    printk("Device Opened Success!\n");
    return nonseekable_open(inode,filp);
}

int leds_release(struct inode *inode,struct file *filp)
{
    printk("Device Closed Success!\n");
    return 0;
}

long leds_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
{
    printk("debug: leds_ioctl cmd is %d,arg is %d\n" , cmd,arg);

    switch(cmd)
    {
        case 0:
        case 1:
            gpio_set_value(gpio_pin[arg], cmd);
            break;

        default:
            return -EINVAL;
    }
    return 0;
}

static struct file_operations leds_ops = {
    .owner     = THIS_MODULE,
    .open     = leds_open,
    .release= leds_release,
    .unlocked_ioctl     = leds_ioctl,
};

static struct miscdevice leds_dev = {
    .minor    = MISC_DYNAMIC_MINOR,
    .fops    = &leds_ops,
    .name    = DRIVER_NAME,
};

static int leds_probe(struct platform_device * pdev)
{
    struct device_node *node = pdev->dev.of_node;
    int ret;
    
    printk("led init\n");
    
    gpio_pin[0] = of_get_named_gpio(node, "gpios1", 0);
    gpio_pin[1] = of_get_named_gpio(node, "gpios2", 0);
    
    ret = gpio_request(gpio_pin[0], "led1");
    if(ret!=0){
        printk("gpio_pin[0] request %d failed.", gpio_pin[0]);
        return ret;
    }
    if (gpio_pin[1] < 0)
       printk("gpio_pin[1] is not available \n");
    ret = gpio_request(gpio_pin[1], "led2");
    if(ret!=0){
        printk("gpio_pin[1] request %d failed.", gpio_pin[1]);
        return ret;
    }
    printk("gpio_pin[0] is %d\n",gpio_pin[0]);
    printk("gpio_pin[1] is %d\n",gpio_pin[1]);
    
    gpio_free(gpio_pin[0]);
    gpio_free(gpio_pin[1]);
    
    gpio_direction_output(gpio_pin[0],0);
    gpio_set_value(gpio_pin[0], 1);
    
    gpio_direction_output(gpio_pin[1],0);
    gpio_set_value(gpio_pin[1], 1);
    
     
    ret = misc_register(&leds_dev);
    if(ret<0){
        printk("led:register device failed!\n");
        goto exit;
    }
    
    return 0;
exit:
    misc_deregister(&leds_dev);
    return ret;
}

static int leds_remove(struct platform_device * pdev)
{
    printk(KERN_ALERT "Goodbye, curel world, this is remove\n");
    misc_deregister(&leds_dev);
    return 0;
}

static const struct of_device_id of_leds_dt_match[] = {
    {.compatible = DRIVER_NAME},
    {},
};

MODULE_DEVICE_TABLE(of,of_leds_dt_match);

static struct platform_driver leds_driver = {
    .probe     = leds_probe,
    .remove = leds_remove,
    .driver = {
        .name = DRIVER_NAME,
        .owner = THIS_MODULE,
        .of_match_table = of_leds_dt_match,
        },
};

static int leds_init(void)
{
    printk(KERN_ALERT "Hello, world\n");
    return platform_driver_register(&leds_driver);
    return 0;
}

static void leds_exit(void)
{
    printk(KERN_ALERT "Goodbye, curel world\n");
    platform_driver_unregister(&leds_driver);
}

module_init(leds_init);
module_exit(leds_exit);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("rty");
MODULE_DESCRIPTION("topeet4412_regiter_dev_drv");

9、使用应用读写驱动

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc,char **argv)
{
    int fd;
    
    if(argc!=4){
        printf("arvc is 4:\nargv1 is device_node ,\nargv2 is cmd,\nargv2 is lednum\n");
    }
    
    if((fd = open(argv[1], O_RDWR|O_NOCTTY|O_NDELAY))<0){
        printf("open %s failed\n",argv[1]);   
        return -1;
    }
    else{
        printf("open %s ok\n",argv[1]); 
        ioctl(fd,atoi(argv[2]),atoi(argv[3]));        
    }
    close(fd);
    return 0;
}

10、待测试

   1、将驱动编译成模块,应用编译成可执行的.o文件

  2、加载驱动文件,可查看节点

[root@iTOP-4412]# insmod itop4412_of_get_gpios_1.ko      // 加载驱动                                                                  
[  739.139973] Hello, world
[  739.141536] led init
[  739.143360] gpio_pin[0] is 120
[  739.146260] gpio_pin[1] is 91

[root@iTOP-4412]# ls /dev/leds_test    // 驱动中杂项设备注册,会生成设备节点                                                                                    
/dev/leds_test
[root@iTOP-4412]# ./ledstest.o /dev/leds_test 1 0         // 测试应用,控制led 操作设备需要操作设备节点                                                                
[  347.342746] Device Opened Success!
[  347.344880] debug: leds_ioctl cmd is 1,arg is 0
[  347.349226] Device Closed Success!
open /dev/leds_test ok

11、GPIO子系统:

   GPIO子系统使用struct gpio结构来描述

/**
 * struct gpio - a structure describing a GPIO with configuration
 * @gpio:    the GPIO number
 * @flags:    GPIO configuration as specified by GPIOF_*
 * @label:    a literal description string of this GPIO
 */
struct gpio {
    unsigned    gpio;
    unsigned long    flags;
    const char    *label;
};

  

 

posted @ 2020-09-07 22:47  笑不出花的旦旦  阅读(695)  评论(0)    收藏  举报