• Linux GPIO键盘驱动开发记录_OMAPL138


    Linux GPIO键盘驱动开发记录_OMAPL138

    Linux基本配置完毕了,这几天开始着手Linux驱动的开发,从一个最简单的键盘驱动开始,逐步的了解开发驱动的过程有哪些。看了一下Linux3.3内核文件下的driver目录,点开里面的C文件,感觉底层的Linux驱动机制还是很复杂的,还需要一段漫长时间的学习。现在开发的也不能说是叫做驱动,也只能说是驱动的应用,我们学习驱动也从应用逐步开始,往里面深入吧。

    0.开发准备

    • 内核源文件(当时我们编译内核时候的目录,很重要,编译驱动的时候需要依赖这些内核源文件)
    • Makefile文件(编译驱动的Makefile文件)
    • 驱动源程序
    • 应用程序(有main函数的)

    1.键盘的接线图

    我们主要使用USER0和USER1 KEY,两个按键,完成Linux GPIO键盘驱动开发。从图中可以看出GPIO0_6和GPIO6_1主要采集键盘按下的信息。

    2. key.c驱动文件

    驱动结构可以看上图,主要是在注册Linux内核设备,我们使用platform_device进行内核注册。我们从思维到图上看,从后往前的顺序进行一个一个定义。

    2.1 gpio_key_buttons结构体

    Linux提供的标准结构体,在#include <linux/gpio_key.h>头文件中。这里面就要定义按键的行为信息和指定GPIO口,这个是和上面的硬件原理图打交道的一个结构体。以下是和这个结构体有关的定义。

    #include <linux/gpio.h>
    #include <linux/gpio_keys.h>
    #include <mach/da8xx.h>						// 板子的头文件
    
    #define             OMAPL138_KEYS_BEBOUNCE          10
    #define             OMAPL138_GPIO_KEYS_POLL_MS      200
    
    #define             OMAPL138_USER_KEY0              GPIO_TO_PIN( 0,6 )
    #define             OMAPL138_USER_KEY1              GPIO_TO_PIN( 6,1 )
    
    static const short  omapl138_user_key_pins[] = {
        DA850_GPIO0_6, DA850_GPIO6_1, -1
    };
    
    static struct   gpio_keys_button omapl138_user_keys[]   =   {
        [0] = {
            .type               =   EV_KEY,
            .active_low         =   1,
            .wakeup             =   0,
            .debounce_interval  =   OMAPL138_KEYS_BEBOUNCE,
            .code               =   KEY_PROG1,
            .desc               =   "user_key0",
            .gpio               =   OMAPL138_USER_KEY0
        },
        [1] = {
            .type               =   EV_KEY,
            .active_low         =   1,
            .wakeup             =   0,
            .debounce_interval  =   OMAPL138_KEYS_BEBOUNCE,
            .code               =   KEY_PROG2,
            .desc               =   "user_key1",
            .gpio               =   OMAPL138_USER_KEY1
        }
    };
    

    在其他的平台可能GPIO口的定义不同,我用的是OMAPL138,使用的内核文件是OMAPL138提供的,他的里面定义了da8xx.h定义了相关GPIO的宏定义,你需要找到你自己平台GPIO结构体的定义,修改这个宏定义即可。这个结构提十分具备可读性,一看就能看懂了,其中的.code就是我们写应用程序的时候,按键读出来的值,就可以判断了。

    2.2 gpio_keys_platform_data结构体

    platform_data结构体顾名思义,基本上就是和我们整个驱动开发数据相关的。

    static struct   gpio_keys_platform_data     omapl138_user_keys_pdata    =   {
        .buttons                =   omapl138_user_keys,
        .nbuttons               =   ARRAY_SIZE( omapl138_user_keys )
    };
    

    里面的成员,.buttons就是刚才我们定义gpio_keys_button数组,.nbuttons就是长度,我们使用宏函数ARRAY_SIZE完成取值。

    2.3 platform_device结构体

    思维导图在向前,我们就需要定义platform_device结构体了,这个结构体就是要和Linux内核打交道的结构提了。里面有设备名称.name,.id,dev

    static void     omapl138_user_keys_release( struct device *dev ) {
    
    }
    static struct   platform_device     omapl138_user_keys_device   =   {
            // you can find mount root by command "dmesg | grep gpio-keys ";
            // cat /proc/bus/input/devices  look the where the device is.
            .name               =   "gpio-keys",
            .id                 =   1,
            .dev                =   {
                                .platform_data  =   &omapl138_user_keys_pdata,
                                .release    =   omapl138_user_keys_release,
            },
    };
    

    .name中当驱动程序向linux注册后通过dmesg | grep gpio-keys命令就可以看到这个名字,在内核调试输出中可以看到。.id设备挂载节点的id,同样的设备id不能重复。.dev给定data和release函数(可以为空)。

    2.4 初始化函数和退出函数

    最后就是初始化函数和退出函数,最终要的就是resigster这个函数了。向内核注册设备。

    static int __init omapl138_user_keys_init( void )
    {
        int reg;
        reg     =   platform_device_register( &omapl138_user_keys_device );
        if( reg ) {
            pr_warning( "Could not register baseboard GPIO tronlong keys!" );
        }else {
            printk( KERN_INFO "User keys register successful!" );
        }
        return reg;
    }
    
    static void __exit omapl138_user_keys_exit( void )
    {
        platform_device_unregister( &omapl138_user_keys_device );
        printk( KERN_INFO   "user keys unregister ! 
    " );
    }
    
    
    module_init( omapl138_user_keys_init );
    module_exit( omapl138_user_keys_exit );
    
    MODULE_DESCRIPTION( "user keys platform driver," );
    MODULE_AUTHOR("Carlos Wei");
    MODULE_LICENSE( "GPL" );
    

    当我们使用insmod name.ko的时候,自动调用module_init()里面写入的函数了,当我们rmmod name.ko的时候,自动调用module_exit()里面写入的函数。

    3 编译内核或者以模块形式加载

    我们可以把这个驱动程序静态编译到内核代码树中也可以以模块的形式加载到内核中,我建议一开始开发的时候使用模块的形式加入到内核中,等着编译成熟之后,在编译到内核里面。

    3.1 编译

    制定内核源码文件的路径,写好Makefile文件,编译后上传到目标板的任意路径。

    3.1.1 Makefile文件

    ifneq ($(KERNELRELEASE),)
    
    obj-m := key.o
    
    else
    
    all:
    	make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-arago-linux-gnueabi-
    
    clean:
    	rm -rf *.ko *.o *.mod.o *.mod.c *.symvers  modul* .button.* .tmp_versions
    
    KDIR=/usr/src/linux-headers-3.3.0-omapl138
    
    endif
    

    这里比较重要的:

    • KDIR=? 这个位置就是你Linux源码文件的路径,(内核事先编译一定要正确
    • CROSS_COMPILE= 这个指定交叉编译器,我的交叉编译器名字比较怪异arm-arago-linux-gnueabi-

    3.1.2 Make一下生成key.ko

    编译完成之后,通过ls命令,查看生成了key.ko文件。

    通过scp或者ftp把key.ko文件传输到目标板子上。

    3.2 加载驱动与查看挂载节点

    1) insmod key.ko

    2) 通过dmesg查看内核输出是否成功

    3)通过cat /proc/bus/input/devices命令查看挂在详情

    重点是event1,一会儿我们编辑应用程序需要打开/dev/input/event1这个设备节点进行对GPIO键盘操作。

    4)测试键盘输入

    在没有编写应用程序的时候,我们也可以通过简单的方法对GPIO键盘进行一个简单的测试。GPIO挂载节点是Handlers = event1 则输入:

    cat /dev/input/event1

    然后我们按键盘,如果当我们点击键盘的时候 终端输出乱码则代表我们的驱动是编写成功的。

    5) 解挂驱动

    如果我们不需要驱动了,则需要进行解挂驱动:

    rmmod key.ko

    也可以通过dmesg命令查看内核输出日志。

    4 应用程序开发

    建立文件:key_app.c

    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/ioctl.h>
    #include <time.h>
    #include <fcntl.h>
    #include <linux/input.h>
    
    int main( int argc, char **argv  )
    {
        int key_state;
        int fd;
        int ret;
        int code;
        struct input_event buf;
    
        fd = open( "/dev/input/event1", O_RDONLY );
        if( fd  < 0 ) {
            printf( "Open GPIO_keys failed!!
    " );
            return -1;
        }
    
        printf( "Open GPIO keys successful! 
    " );
        while( 1 ) {
            ret = read( fd, &buf, sizeof( struct input_event ) );
            if( ret <= 0 ) {
                printf( "read failed! 
    " );
                return -1;
            }
            code = buf.code;
            key_state   =   buf.value;
            printf("wait...... 
    ");
            switch( code ) {
                case KEY_PROG1:
    
                    code = '1';
                    printf( "KEY1 state = %d
    ", key_state );
                    break;
                case KEY_PROG2:
    
                    code = '2';
                    printf( "KEY2 state = %d
    ", key_state );
                    break;
            }
    
    
        }
        printf("key test finished.
    ");
        close(fd);
        return 0;
    }
    

    一个非常简单的程序,就是输出案件之。我们编译它:

    arm-arago-linux-gnueabi-gcc key_app.c -o key_app.o

    生成的key_app.o文件放入目标板的Linux目录,然后运行即可。

    5 调试结果

    通过按键就可以输出这些值,意味着我们的驱动开发成功了。

    附录:源程序

    链接: https://pan.baidu.com/s/1pNcEBEj 密码: xpsq


    版权声明:

    1. 本文为MULTIBEANS团队研发跟随文章,未经允许不得转载。

    2· 文中涉及的内容若有侵权行为,请与本人联系,本人会及时删除。

    3· 尊重成果,本文将用的参考文献全部给出,向无私的工程师,爱好者致敬。

  • 相关阅读:
    jxl读和取Excel文件
    Studio for WPF:定制 C1WPFChart 标记
    为C1Chart for WPF添加自定义标题、坐标轴单位标签以及旋转坐标轴注释
    自定义饼图(PieChart)各个PieSlice的外观
    vue组件
    vue双向绑定
    第一个Vue程序
    vue入门介绍
    js生成随机固定长度字符串的简便方法
    JavaScript 函数式编程读书笔记1
  • 原文地址:https://www.cnblogs.com/sigma0/p/8284147.html
Copyright © 2020-2023  润新知