驱动实例 — 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\n",__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\n",__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\n",cmd,arg);
    
    if(cmd > 1){
        printk(KERN_EMERG "rs485 control gpio cmd is 0 or 1\n");
    }
    if(arg > 1){
        printk(KERN_EMERG "rs485 control gpio arg is only 1\n");
    }
    
    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\n",__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\n",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\n", __FUNCTION__,__LINE__);

    return 0;
}
 
 
static int rs485_ctrl_remove(struct platform_device *pdev)
{
    printk(KERN_INFO "rs485 control gpio %s\n", __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\n", __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!\n");
}

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就可以看到相关配置信息

  

 

posted @ 2020-07-25 10:39  一个不知道干嘛的小萌新  阅读(3584)  评论(0编辑  收藏  举报