查看: 660|回复: 0

[分享] 【经验分享】i.MX6ULL开发:驱动开发7——按键输入捕获

[复制链接]
  • TA的每日心情
    开心
    2020-12-18 12:56
  • 签到天数: 55 天

    [LV.5]常住居民I

    71

    主题

    221

    帖子

    0

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    1588
    最后登录
    2024-4-22
    发表于 2022-8-23 13:09:30 | 显示全部楼层 |阅读模式
    前面几篇文章,从最基础的寄存器点灯,到设备树点灯,再到GPIO子系统点灯,一步步了解嵌入式Linux开发的各种点灯原理。点灯用到的都是GPIO的输出功能,这篇,通过按键的使用,来学习GPIO输入功能的使用。
    1 硬件介绍
    1.1 板子上按键原理图
    先来看原理图,我板子上有4个按键sw1~sw4:
    1.1.1 SW1
    SW1是板子的系统复位按键,不可编程使用
    图片 29.png
    1.1.2 SW2、SW3
    SW2:SNVS_TAMPER1,GPIO5_1
    平时是低电平,按下去是高电平。
    SW3:ONOFF
    它也是系统级的按键,用于长按进行开关机。
    图片 30.png
    1.1.3 SW4
    SW4是BOOT_MODE1脚,用来进行串行烧录模式切换,需要再与复位键配合使用。
    本篇仅测试按键功能,因此可以该按键。
    图片 31.png
    1.1.4 使用其中2个按键
    板子上这4个按键的功能特性如下表:
    图片 32.png
    本实验使用SW2和SW4这两个按键来进行实验。
    图片 33.png
    2 软件编写
    2.1 修改设备树文件
    2.1.1 修改iomuxc节点
    修改imx6ull-myboard.dts,在iomuxc节点的imx6ull-evk字节点下创建一个名为pinctrl_key的子节点,节点内容如下:
    1. <p class="MsoNormal"><font face="Arial" size="3">pinctrl_key: keygrp {<o:p></o:p></font></p><p class="MsoNormal"><font face="Arial" size="3">   fsl,pins = <<o:p></o:p></font></p><p class="MsoNormal"><font face="Arial" size="3">       MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01    0x3080 /* SW2 */<o:p></o:p></font></p><p class="MsoNormal"><font face="Arial" size="3">       MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11      0xF080 /* SW4 */<o:p></o:p></font></p><p class="MsoNormal"><font face="Arial" size="3">   >;};</font></p>
    复制代码

    这部分是对引脚进行配置,这两个引脚的定义是在imx6ull-pinfunc-snvs.h文件中:
    图片 34.png
    引脚宏定义后面的值,是对引脚功能的配置:
    SW2:0x3080,即0011 0000 1000 0000
    SW4:0xF080,即1000 0000 1000 0000
    对照之前讲解GPIO的PAD寄存器的配置,根据两个按键的实际电路配置上拉或下拉。
    1. <p class="MsoNormal"><font face="Arial" size="3">/*<o:p></o:p></font></p><p class="MsoNormal"><font face="Arial" size="3">*bit 16:0 HYS关闭<o:p></o:p></font></p><p class="MsoNormal"><font face="Arial" size="3">*bit [15:14]: [00]下拉 [01]47k上拉 [10]100k上拉 [11]22k上拉 <---<o:p></o:p></font></p><p class="MsoNormal"><font face="Arial" size="3">*bit [13]: [0]kepper功能 [1]pull功能<o:p></o:p></font></p><p class="MsoNormal"><font face="Arial" size="3">*bit [12]: [0]pull/keeper-disable [1]pull/keeper-enable<o:p></o:p></font></p><p class="MsoNormal"><font face="Arial" size="3">*bit [11]: 0 关闭开路输出<o:p></o:p></font></p><p class="MsoNormal"><font face="Arial" size="3">*bit [10:8]: 00 保留值<o:p></o:p></font></p><p class="MsoNormal"><font face="Arial" size="3">*bit [7:6]: 10 速度100Mhz<o:p></o:p></font></p><p class="MsoNormal"><font face="Arial" size="3">*bit [5:3]: 000 输出disable <---<o:p></o:p></font></p><p class="MsoNormal"><font face="Arial" size="3">*bit [2:1]: 00 保留值<o:p></o:p></font></p><p class="MsoNormal"><font face="Arial" size="3">*bit [0]: 0 低转换率<o:p></o:p></font></p><p class="MsoNormal"><font face="Arial" size="3">*/</font></p>
    复制代码

    注:SW4 (MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11)这个GPIO,在设备中实际已经被其它设备(spi4)使用了。
    在imx6ull-myboard.dts的300多行处,有:
    pinctrl_spi4: spi4grp {
    fsl,pins = <
        MX6ULL_PAD_BOOT_MODE0__GPIO5_IO10        0x70a1
        MX6ULL_PAD_BOOT_MODE1__GPIO5_IO11        0x70a1
        MX6ULL_PAD_SNVS_TAMPER7__GPIO5_IO07      0x70a1
        MX6ULL_PAD_SNVS_TAMPER8__GPIO5_IO08      0x80000000
        >;
    };
    理论上我们应该把这里的配置给注释掉,因为1个IO是不能同时进行2种功能的。由于本次实验不使用spi4,暂且也先不管它,看看会有什么影响,如果影响了本实验,再给把这里的配置给注掉。
    2.1.2 添加key节点
    在根节点下创建名为key的按键节点,内容如下:
    1. <font face="Arial" size="3">key {
    2.    #address-cells = <1>;
    3.    #size-cells = <1>;
    4.    compatible = "myboard-key";
    5.    pinctrl-names = "default";
    6.    pinctrl-0 = <&pinctrl_key>;
    7.    key1-gpio = <&gpio5 1 GPIO_ACTIVE_HIGH>;   /* SW2 */
    8.    key2-gpio = <&gpio5 11 GPIO_ACTIVE_LOW>;   /* SW4 */
    9.    status = "okay";
    10. };</font>
    复制代码

    2.2 编写按键驱动程序
    按键驱动,也属于字符设备驱动,和之前的字符设备驱动的框架一样,主要的修改点在按键的硬件初始化配置已经按键的读取。
    新建一个key-Bsp.c
    2.2.1 按键的硬件初始化
    初始化的流程,就是使用OF函数来从设备树中获取key节点,然后使用GPIO子系统的API函数,将GPIO配置为输入。
    1. <font face="Arial" size="3">static int keyio_init(void)
    2. {
    3.    keydev.nd = of_find_node_by_path("/key");
    4.    if (keydev.nd== NULL)
    5.    {
    6.        return -EINVAL;
    7.    }

    8.    keydev.key1_gpio = of_get_named_gpio(keydev.nd ,"key1-gpio", 0);
    9.    keydev.key2_gpio = of_get_named_gpio(keydev.nd ,"key2-gpio", 0);
    10.    if ((keydev.key1_gpio < 0)||(keydev.key2_gpio < 0))
    11.    {
    12.        printk("can't get key\r\n");
    13.        return -EINVAL;
    14.    }
    15.    printk("key1_gpio=%d, key2_gpio=%d\r\n", keydev.key1_gpio, keydev.key2_gpio);

    16.    /* 初始化key所使用的IO */
    17.    gpio_request(keydev.key1_gpio, "key1");    /* 请求IO */
    18.    gpio_request(keydev.key2_gpio, "key2");    /* 请求IO */
    19.    gpio_direction_input(keydev.key1_gpio);    /* 设置为输入 */
    20.    gpio_direction_input(keydev.key2_gpio);    /* 设置为输入 */
    21.    return 0;
    22. }</font>
    复制代码
    2.2.2 读取按键的值
    读取按键的值,也是GPIO子系统的API函数来读取。读取到按键的值后,将该值传递出来给应用层使用,注意这里使用了原子操作的方式atomic_set和atomic_read实现数据的写入和读取。
    1. <font face="Arial" size="3">/* 定义按键值 */
    2. #define KEY1VALUE      0X01    /* 按键值       */
    3. #define KEY2VALUE      0X02    /* 按键值       */
    4. #define INVAKEY        0X00    /* 无效的按键值 */

    5. static ssize_t key_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
    6. {
    7.    int ret = 0;
    8.    int value;
    9.    struct key_dev *dev = filp->private_data;

    10.    if (gpio_get_value(dev->key1_gpio) == 1)         /* key1按下 */
    11.    {
    12.        printk("get key1: high\r\n");
    13.        while(gpio_get_value(dev->key1_gpio));       /* 等待按键释放 */
    14.        atomic_set(&dev->keyvalue, KEY1VALUE);
    15.    }
    16.    else if (gpio_get_value(dev->key2_gpio) == 0)    /* key2按下 */
    17.    {
    18.        printk("get key2: low\r\n");
    19.        while(!gpio_get_value(dev->key2_gpio));      /* 等待按键释放 */
    20.        atomic_set(&dev->keyvalue, KEY2VALUE);
    21.    }
    22.    else
    23.    {
    24.        atomic_set(&dev->keyvalue, INVAKEY);        /* 无效的按键值 */
    25.    }

    26.    value = atomic_read(&dev->keyvalue);
    27.    ret = copy_to_user(buf, &value, sizeof(value));
    28.    return ret;
    29. }</font>
    复制代码
    2.3 编写按键应用程序
    新建一个key-App.c
    按键的应用层程序,主要就通过驱动程序提供的按键读取接口,来循环读取按键的值,并在按键按下时,将按键的值打印出来。
    1. <font face="Arial" size="3">/* 定义按键值 */
    2. #define KEY1VALUE   0X01
    3. #define KEY2VALUE   0X02
    4. #define INVAKEY     0X00

    5. int main(int argc, char *argv[])
    6. {
    7.    int fd, ret;
    8.    char *filename;
    9.    int keyvalue;

    10.    if(argc != 2)
    11.    {
    12.        printf("Error Usage!\r\n");
    13.        return -1;
    14.    }

    15.    filename = argv[1];

    16.    /* 打开key驱动 */
    17.    fd = open(filename, O_RDWR);
    18.    if(fd < 0)
    19.    {
    20.        printf("file %s open failed!\r\n", argv[1]);
    21.        return -1;
    22.    }

    23.    /* 循环读取按键值数据! */
    24.    while(1)
    25.    {
    26.        read(fd, &keyvalue, sizeof(keyvalue));
    27.        if (keyvalue == KEY1VALUE)
    28.        {
    29.            printf("KEY1 Press, value = %#X\r\n", keyvalue);
    30.        }
    31.        else if (keyvalue == KEY2VALUE)
    32.        {
    33.            printf("KEY2 Press, value = %#X\r\n", keyvalue);
    34.        }
    35.    }

    36.    ret= close(fd); /* 关闭文件 */
    37.    if(ret < 0)
    38.    {
    39.        printf("file %s close failed!\r\n", argv[1]);
    40.        return -1;
    41.    }
    42.    return 0;
    43. }</font>
    复制代码
    3 实验测试3.1 编译程序3.1.1 编译设备树
    编译设备树文件,并将编译出的dtb文件复制到启动文件夹:
    图片 35.png
    网络方式启动开发板,查看key节点:
    图片 36.png
    3.1.2 编译按键驱动程序
    图片 37.png

    3.1.3 编译按键应用程序
    图片 38.png

    3.2 测试
    图片 39.png

    3.3 查看CPU占用率
    先Ctrl+C结束掉此按键进程,然后使用如下指令来后台运行按键程序:
    ./key-App /dev/key &
    然后再使用指令:
    top
    来查看CPU是使用情况。从下图可以看出,此时CPU的使用率是99.8%,全被按键检查程序占用了,因为按键程序中有个while循环在一直读取按键的值。
    图片 40.png
    使用指令:
    ps
    查看按键的进程号,如下图为149,再使用:
    kill -9 149
    来杀掉按键进程,然后再使用top指令查看,可以看到CPU的使用率又变回了0。
    图片 41.png
    实际的按键使用中,一般不会使用本篇的这种持续检测导致CPU占满的方式,本篇只是先来介绍GPIO的输入功能的使用,后续会使用更加高效的按键检测机制来实现按键检测功能。
    4 总结
    本篇主要介绍了i.MX6ULL的按键检测的使用,主要的知识点是设备树的修改,以及GPIO的输入配置与高低电平的读取。
    图片 42.png


    签到
    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-4-24 10:57 , Processed in 0.115062 second(s), 20 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表