zoukankan      html  css  js  c++  java
  • 驱动实例 — GPIO驱动 485调试 设备树修改

      应用场景:使用的是3399pro,控制GPIO1_B5(RS485)的高低电平。来控制uart0的收发。

      http://wiki.t-firefly.com/AIO-3399C/driver_gpio.html   有关于3399详细GPIO使用说明

      http://www.wowotech.net/device_model/429.html  GPIO调试相关

    1.调试485确认硬件没问题

    查看哪些引脚被占用:
    cat  /sys/kernel/debug/gpio
    

      查看当前开发板哪些引脚被占用。可以看到uart0的引脚被蓝牙占用。(因此到时候要在设备树中将蓝牙disabled了,没用到)

    cd /sys/class/gpio
    echo 45 > export  将gpio 45暴露给用户层
    这样在gpio目录下就有一个gpio45的文件,就可以直接对gpio45进行操作。
    
    echo out > gpio45/direction
    echo 0 > gpio45/value    
    echo 1 > gpio45/value 
    

      这样直接echo 111 > /dev/ttyS0。可以在PC上的串口助手看到有输出。

      但是我用cat /dev/ttyS0发现数据一直不能接收进来。

      后面必须要编写串口程序配置串口才能接收。

    2.修改设备树

    2.1 禁用蓝牙,因为蓝牙占用了uart0 

      

       将status = "okay";修改成disabled。

      以下设备树配置有问题:

      

       

    3.编写GPIO驱动 

    #include <linux/kernel.h>
    #include <linux/gpio.h>
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/delay.h>
    #include <linux/of.h>
    #include <linux/of_gpio.h>
    #include <linux/of_platform.h>
    #include <linux/version.h>
    #include <linux/init.h>
    #include <linux/fs.h>
    #include <linux/sched.h>
    #include <linux/pm.h>
    #include <linux/sysctl.h>
    #include <linux/proc_fs.h>
    #include <asm/uaccess.h>
    #include <asm/io.h>
     
    #define GPIO_LOW 0
    #define GPIO_HIGH 1
    int gpio;
    int major;
    static struct class *cls;
    
    static int rs485_ctrl_open(struct inode *inode, struct file *file)
    {
        printk(KERN_EMERG "%s-%d: enter
    ",__FUNCTION__,__LINE__);
    
        return 0;
    }
     
    static ssize_t rs485_ctrl_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
    {
        int val;
        int ret;
        printk(KERN_EMERG "%s-%d: enter
    ",__FUNCTION__,__LINE__);
         
        ret = copy_from_user(&val, buf, count); //copy_to_user();
         
        if (val == 1)
        {
            gpio_set_value(gpio,GPIO_HIGH);
        }
        else
        {
            gpio_set_value(gpio,GPIO_LOW);
        }
    
        return 0;
    }
    
    static long rs485_ctrl_ioctl( struct file *files, unsigned int cmd, unsigned long arg){
        //printk("cmd is %d,arg is %d
    ",cmd,arg);
        
        if(cmd > 1){
            printk(KERN_EMERG "rs485 control gpio cmd is 0 or 1
    ");
        }
        if(arg > 1){
            printk(KERN_EMERG "rs485 control gpio arg is only 1
    ");
        }
        
        gpio_set_value(gpio,cmd);
        
        return 0;
    }
    
    static struct file_operations rs485_ctrl_fops = {
        .owner =  THIS_MODULE, 
        .open  =  rs485_ctrl_open, 
        .write =  rs485_ctrl_write,
        .unlocked_ioctl = rs485_ctrl_ioctl,
    };
     
    static int rs485_ctrl_probe(struct platform_device *pdev)
    {
        int ret ;
         
        enum of_gpio_flags flag;
        
        //设备节点结构体
        struct device_node *rs485_ctrl_node = pdev->dev.of_node;
         
        printk(KERN_EMERG "rs485 control gpio %s-%d: enter
    ",__FUNCTION__,__LINE__);
    
        //of_get_named_gpio_flags 从设备树中读取 rs485_ctrl_gpio 的 GPIO 配置编号和标志
        gpio = of_get_named_gpio_flags(rs485_ctrl_node,"rs485_ctrl_gpio", 0,&flag);
        //gpio_is_valid 判断该 GPIO 编号是否有效。
        if (!gpio_is_valid(gpio)){
            printk(KERN_INFO "hello: invalid gpio : %d
    ",gpio);
            return -1;
        }
        
        //gpio_request 则申请占用该 GPIO。如果初始化过程出错,需要调用 gpio_free 来释放之前申请过且成功的 GPIO 。
        ret = gpio_request(gpio, "rs485_ctrl-gpio");
        if (ret) {
            gpio_free(gpio);
            return -EIO;
        }
    
        //调用 gpio_direction_output 就可以设置输出高还是低电平
        gpio_direction_output(gpio, GPIO_HIGH);
        
        major = register_chrdev(0, "rs485_ctrl_dev", &rs485_ctrl_fops);
        cls = class_create(THIS_MODULE, "rs485_ctrl_dev");
        device_create(cls, NULL, MKDEV(major, 0), NULL, "rs485_ctrl_drv");
        gpio_set_value(gpio, GPIO_HIGH);
    
    
        printk(KERN_INFO "rs485 control gpio %s-%d: exit
    ", __FUNCTION__,__LINE__);
    
        return 0;
    }
     
     
    static int rs485_ctrl_remove(struct platform_device *pdev)
    {
        printk(KERN_INFO "rs485 control gpio %s
    ", __FUNCTION__);
        gpio_free(gpio);
        device_destroy(cls, MKDEV(major, 0));
        class_destroy(cls);
        unregister_chrdev(major, "rs485_ctrl_dev");
        
        return 0;
    }
    static const struct of_device_id of_rs485_ctrl_match[] = {
        { .compatible = "rs485_ctrl" },
        { /* Sentinel */ }
    };
    static struct platform_driver rs485_ctrl_driver = {
        .probe = rs485_ctrl_probe,
        .remove = rs485_ctrl_remove,
        .driver = {
            .name = "rs485_ctrl_drv",
            .owner = THIS_MODULE,
            .of_match_table = of_rs485_ctrl_match,
        },
    };
     
    static int __init rs485_ctrl_init(void)
    {
        printk(KERN_INFO "rs485 control gpio init %s
    ", __FUNCTION__);
        return platform_driver_register(&rs485_ctrl_driver);
    }
     
    static void __exit rs485_ctrl_exit(void)
    {
        platform_driver_unregister(&rs485_ctrl_driver);
        printk(KERN_INFO "rs485 control gpio exit!
    ");
    }
    
    module_init(rs485_ctrl_init);
    module_exit(rs485_ctrl_exit);
    MODULE_LICENSE("GPL");

    4.将GPIO驱动编译进内核

      要给linux内核添加模块(驱动)有如下两种方式:

    (1)动态方式:采用insmod命令来给运行中的linux加载模块。

    (2)静态方式:修改linux的配置菜单,添加模块相关文件到源码对应目录,然后把模块直接编译进内核。

      内核的配置系统一般由以下几部分组成:

    (1)Makefile:分布在Linux内核源代码中的Makefile,定义Linux内核的编译规则。

    (2)配置文件(Kconfig):给用户提供配置选项,修改该文件来改变配置菜单选项。

    (3)配置工具(make menuconfig):包括配置命令解释器(对配置脚本中使用的配置命令进行解释),配置用户界面(提供字符界面和图形界面)。这些配置工具都是使用脚本语言编写的,如Tcl/TK、Perl等。

      配置工具(make menuconfig)根据kconfig配置脚本产生配置菜单,然后根据配置菜单的配置情况生成顶层目录下的.config(只有一个.config),在.config里定义了配置选择的配置宏定义,如下所示:

      

      可以看到这里配置了很多CONFIG_XXX相关的宏定义。

      打开/drivers/char/Makefile:

      

      所以.config里面的配置项也就是应用到了Makefile里面。

      流程就是:Kconfig(修改该文件来改变配置菜单选项) ----> Make menuconfig(配置菜单) ----> .config(配置生成) ----> Makefile(根据.config里面的配置项确定要编译哪些驱动)

    不创建新的驱动文件夹:

      (1)把我们的驱动源文件(rs485_driver.c)放到对应目录下,具体放到哪里需要根据驱动的类型和特点。这里假设我们放到./driver/char下。

      (2)然后我们修改./driver/char下的Kconfig文件

      

      注意这里的RS485_DRIVER这个名字可以随便写,他并不需要跟驱动源文件保持一致,但最好保持一致,等下我们在修改Makefile时会用到这个名字,他将会变成CONFIG_RS485_DRIVER,那个名字必须与这个名字对应。如上所示,tristate定义了这个配置选项的可选项有几个。(具体查看Kconfig语法规则)

      (3)修改./driver/char下的Makefile文件,如下所示:

      

      Makefile的CONFIG_RS485_DRIVER需要和Kconfig中的RS485_DRIVER对应起来。因为Kconfig中的RS485_DRIVER在经过make menuconfig配置过,会生成.config。在.config中就会变成CONFIG_RS485_DRIVER。

      这样配置就完成了。只需要再顶层目录make menuconfig。

      

       

      然后选择配置。再make一下就可以了。

    创建新的驱动文件夹:

      (1)在源码的对应目录下建立自己的目录(rs485),这里假设为/drivers/char/rs485。

      (2) 把驱动源码放到新建的rs485目录下,并在此目录下新建Kconfig和Makefile文件。然后给新建的Kconfig和Makefile添加内容。

      

      

      (3)在drivers/char/Kconfig中加入:source “drivers/char/rs485/Kconfig”

          在drivers/char/Makefile中加入:obj-$(CONFIG_RS485_DRIVER) += rs485/  (这边直接指定我们创建的目录,Makefile就会去文件rs485下的Makefile找并编译)

      然后make menuconfig选择编译:

      

       在.config就可以看到相关配置信息

      

  • 相关阅读:
    [转]使用NLog记录日志到数据库 自定义日志表的数据格式
    [转]使用C#实现长整型向任意编码的转换和逆转换
    解释一下 P/NP/NPComplete/NPHard 等问题
    在C#中创建进度窗体
    [转载] 关于Winform编译中的属性设置
    [转]使用NLog記錄Exception
    SQL Server数据库,在表上建立唯一性索引的一些问题
    DES文件字符加密解密
    线程运行超时处理类
    使用FFmpeg从视频中截图的命令
  • 原文地址:https://www.cnblogs.com/zhuangquan/p/13375806.html
Copyright © 2011-2022 走看看