• Linux学习第20天:Linux按键输入驱动开发: 大道至简 量入为出


    Linux版本号4.1.15   芯片I.MX6ULL                                    大叔学Linux    品人间百味  思文短情长 


            中国文化博大精深,太极八卦,阴阳交合,变化无穷。在程序的开发中也是这样,数字0和1也是同样的道理。就本节来说,输入和输出的万千变化才是程序驱动开发的基石。所以题目为大道至简,就是要说明这个道理。量入为出,不去纠结到底是先有输入还是先有输出。工作中接触最多的传感器就是输入,之后驱动开发根据采集到的输入进行判断比对后输出,也就是我要说的量入为出。

            本节开始学习第一个输入驱动的开发---按键驱动开发。主要内容包括按键驱动原理、硬件设计原理、驱动开发和测试。其中最重要的是驱动的开发和测试。

            本节的思维导图如下:

    一、按键驱动原理

            在驱动程序中使用一个整形变量来表示按键值,应用程序通过 read 函数来读取按键值,判断按键有没有按下。保存按键值的变量就是个共享资源,驱动程序要向其写入按键值,应用程序要读取按键值。我们使用原子操作对这个按键值【整型数】进行赋值及读取。
     

    二、硬件原理图

            按键 KEY0 是连接到 I.MX6U 的 UART1_CTS 这个 IO 上的, KEY0接了一个 10K 的上拉电阻,因此 KEY0 没有按下的时候 UART1_CTS 应该是高电平,当 KEY0按下以后 UART1_CTS 就是低电平。

    三、驱动开发

    1.修改设备树文件

    1. 1 #ifndef _BSP_GPIO_H
    2. 2 #define _BSP_GPIO_H
    3. 3 #define _BSP_KEY_H
    4. 4 #include "imx6ul.h"
    5. 5 /***************************************************************
    6. 6 Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
    7. 7 文件名 : bsp_gpio.h
    8. 8 作者 : 左忠凯
    9. 9 版本 : V1.0
    10. 10 描述 : GPIO 操作文件头文件。
    11. 11 其他 : 无
    12. 12 论坛 : www.openedv.com
    13. 13 日志 : 初版 V1.0 2019/1/4 左忠凯创建
    14. 14 ***************************************************************/
    15. 15
    16. 16 /* 枚举类型和结构体定义 */
    17. 17 typedef enum _gpio_pin_direction
    18. 18 {
    19. 19 kGPIO_DigitalInput = 0U, /* 输入 */
    20. 20 kGPIO_DigitalOutput = 1U, /* 输出 */
    21. 21 } gpio_pin_direction_t;
    22. 22
    23. 23 /* GPIO 配置结构体 */
    24. 24 typedef struct _gpio_pin_config
    25. 25 {
    26. 26 gpio_pin_direction_t direction; /* GPIO 方向:输入还是输出 */
    27. 27 uint8_t outputLogic; /* 如果是输出的话,默认输出电平 */
    28. 28 } gpio_pin_config_t;

            枚举类型 gpio_pin_direction_t 表示 GPIO 方向,输入或输出。结构体 gpio_pin_config_t 是 GPIO 的配置结构体,里面有 GPIO 的方向和默认输出电平两个成员变量。

    1. 1 #include "bsp_gpio.h"
    2. 2 /***************************************************************
    3. 3 Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
    4. 4 文件名 : bsp_gpio.h
    5. 5 作者 : 左忠凯
    6. 6 版本 : V1.0
    7. 7 描述 : GPIO 操作文件。
    8. 8 其他 : 无
    9. 9 论坛 : www.openedv.com
    10. 10 日志 : 初版 V1.0 2019/1/4 左忠凯创建
    11. 11 ***************************************************************/
    12. 12
    13. 13 /*
    14. 14 * @description : GPIO 初始化。
    15. 15 * @param - base : 要初始化的 GPIO 组。
    16. 16 * @param - pin : 要初始化 GPIO 在组内的编号。
    17. 17 * @param - config : GPIO 配置结构体。
    18. 18 * @return : 无
    19. 19 */
    20. 20 void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config)
    21. 21 {
    22. 22 if(config->direction == kGPIO_DigitalInput) /* 输入 */
    23. 23 {
    24. 24 base->GDIR &= ~( 1 << pin);
    25. 25 }
    26. 26 else /* 输出 */
    27. 27 {
    28. 28 base->GDIR |= 1 << pin;
    29. 29 gpio_pinwrite(base,pin, config->outputLogic);/* 默认输出电平 */
    30. 30 }
    31. 31 }
    32. 32
    33. 33 /*
    34. 34 * @description : 读取指定 GPIO 的电平值 。
    35. 35 * @param – base : 要读取的 GPIO 组。
    36. 36 * @param - pin : 要读取的 GPIO 脚号。
    37. 37 * @return : 无
    38. 38 */
    39. 39 int gpio_pinread(GPIO_Type *base, int pin)
    40. 40 {
    41. 41 return (((base->DR) >> pin) & 0x1);
    42. 42 }
    43. 43
    44. 44 /*
    45. 45 * @description : 指定 GPIO 输出高或者低电平 。
    46. 46 * @param – base : 要输出的的 GPIO 组。
    47. 47 * @param - pin : 要输出的 GPIO 脚号。
    48. 48 * @param – value : 要输出的电平, 1 输出高电平, 0 输出低低电平
    49. 49 * @return : 无
    50. 50 */
    51. 51 void gpio_pinwrite(GPIO_Type *base, int pin, int value)
    52. 52 {
    53. 53 if (value == 0U)
    54. 54 {
    55. 55 base->DR &= ~(1U << pin); /* 输出低电平 */
    56. 56 }
    57. 57 else
    58. 58 {
    59. 59 base->DR |= (1U << pin); /* 输出高电平 */
    60. 60 }
    61. 61 }

            函数 gpio_init 用于初始化指定的 GPIO 引脚,最终配置的是 GDIR 寄存器,此函数有三个参数,这三个参数的含义如下:
    base: 要初始化的 GPIO 所属于的 GPIO 组,比如 GPIO1_IO18 就属于 GPIO1 组。
    pin: 要初始化 GPIO 在组内的标号,比如 GPIO1_IO18 在组内的编号就是 18。
    config: 要初始化的 GPIO 配置结构体,用来指定 GPIO 配置为输出还是输入。


            函数 gpio_pinread 是读取指定的 GPIO 值,也就是读取 DR 寄存器的指定位,此函数有两个
    参数和一个返回值,参数含义如下:
    base: 要读取的 GPIO 所属于的 GPIO 组,比如 GPIO1_IO18 就属于 GPIO1 组。
    pin: 要读取的 GPIO 在组内的标号,比如 GPIO1_IO18 在组内的编号就是 18。
    返回值: 读取到的 GPIO 值,为 0 或者 1。


            函数 gpio_pinwrite 是控制指定的 GPIO 引脚输入高电平(1)或者低电平(0),就是设置 DR 寄
    存器的指定位,此函数有三个参数,参数含义如下:
    base: 要设置的 GPIO 所属于的 GPIO 组,比如 GPIO1_IO18 就属于 GPIO1 组。
    pin: 要设置的 GPIO 在组内的标号,比如 GPIO1_IO18 在组内的编号就是 18。
    value: 要设置的值, 1(高电平)或者 0(低电平)。
            以后就可以使用函数 gpio_init 设置指定 GPIO 为输入还是输出,使用函数 gpio_pinread和 gpio_pinwrite 来读写指定的 GPIO。

    2.按键驱动

    1. 15 /* 定义按键值 */
    2. 16 enum keyvalue{
    3. 17 KEY_NONE = 0,
    4. 18 KEY0_VALUE,
    5. 19 };

            bsp_key.h 文件中定义了一个枚举类型: keyvalue, 此枚举类型表示按键值。

    1. 1 #include "bsp_key.h"
    2. 2 #include "bsp_gpio.h"
    3. 3 #include "bsp_delay.h"
    4. 4 /***************************************************************
    5. 5 Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
    6. 6 文件名 : bsp_key.c
    7. 7 作者 : 左忠凯
    8. 8 版本 : V1.0
    9. 9 描述 : 按键驱动文件。
    10. 10 其他 : 无
    11. 11 论坛 : www.openedv.com
    12. 12 日志 : 初版 V1.0 2019/1/4 左忠凯创建
    13. 13 ***************************************************************/
    14. 14
    15. 15 /*
    16. 16 * @description : 初始化按键
    17. 17 * @param : 无
    18. 18 * @return : 无
    19. 19 */
    20. 20 void key_init(void)
    21. 21 {
    22. 22 gpio_pin_config_t key_config;
    23. 23
    24. 24 /* 1、初始化 IO 复用, 复用为 GPIO1_IO18 */
    25. 25 IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0);
    26. 26
    27. 27 /* 2、、配置 UART1_CTS_B 的 IO 属性
    28. 28 *bit 16:0 HYS 关闭
    29. 29 *bit [15:14]: 11 默认 22K 上拉
    30. 30 *bit [13]: 1 pull 功能
    31. 31 *bit [12]: 1 pull/keeper 使能
    32. 32 *bit [11]: 0 关闭开路输出
    33. 33 *bit [7:6]: 10 速度 100Mhz
    34. 34 *bit [5:3]: 000 关闭输出
    35. 35 *bit [0]: 0 低转换率
    36. 36 */
    37. 37 IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xF080);
    38. 38
    39. 39 /* 3、初始化 GPIO GPIO1_IO18 设置为输入*/
    40. 40 key_config.direction = kGPIO_DigitalInput;
    41. 41 gpio_init(GPIO1,18, &key_config);
    42. 42
    43. 43 }
    44. 44
    45. 45 /*
    46. 46 * @description : 获取按键值
    47. 47 * @param : 无
    48. 48 * @return : 0 没有按键按下,其他值:对应的按键值
    49. 49 */
    50. 50 int key_getvalue(void)
    51. 51 {
    52. 52 int ret = 0;
    53. 53 static unsigned char release = 1; /* 按键松开 */
    54. 54
    55. 55 if((release==1)&&(gpio_pinread(GPIO1, 18) == 0)) /* KEY0 按下 */
    56. 56 {
    57. 57 delay(10); /* 延时消抖 */
    58. 58 release = 0; /* 标记按键按下 */
    59. 59 if(gpio_pinread(GPIO1, 18) == 0)
    60. 60 ret = KEY0_VALUE;
    61. 61 }
    62. 62 else if(gpio_pinread(GPIO1, 18) == 1) /* KEY0 未按下 */
    63. 63 {
    64. 64 ret = 0;
    65. 65 release = 1; /* 标记按键释放 */
    66. 66 }
    67. 67
    68. 68 return ret;
    69. 69 }

            bsp_key.c 中一共有两个函数: key_init key_getvalue, key_init 是按键初始化函数,用来初始化按键所使用的 UART1_CTS 这个 IO。

            函数 key_init 先设置 UART1_CTS 复用为GPIO1_IO18,然后配置 UART1_CTS 这个 IO 为速度为 100MHz,默认 22K 上拉。最后调用函数 gpio_init 来设置 GPIO1_IO18 为输入功能。
            函数 key_getvalue 用于获取按键值,此函数没有参数,只有一个返回值,返回值表示按键
    值,返回值为 0 的话就表示没有按键按下,如果返回其他值的话就表示对应的按键按下了。获
    取按键值其实就是不断的读取 GPIO1_IO18 的值,如果按键按下的话相应的 IO 被拉低,那么
    GPIO1_IO18 值就为 0,如果按键未按下的话 GPIO1_IO18 的值就为 1。此函数中静态局部变量
    release 表示按键是否释放。

           第57 行是按键消抖延时函数,这个很多地方都有讲的,很简单,就不赘述了。

    1. 1 #include "bsp_clk.h"
    2. 2 #include "bsp_delay.h"
    3. 3 #include "bsp_led.h"
    4. 4 #include "bsp_beep.h"
    5. 5 #include "bsp_key.h"
    6. 6
    7. 7 /*
    8. 8 * @description : main 函数
    9. 9 * @param : 无
    10. 10 * @return : 无
    11. 11 */
    12. 12 int main(void)
    13. 13 {
    14. 14 int i = 0;
    15. 15 int keyvalue = 0;
    16. 16 unsigned char led_state = OFF;
    17. 17 unsigned char beep_state = OFF;
    18. 18
    19. 19 clk_enable(); /* 使能所有的时钟 */
    20. 20 led_init(); /* 初始化 led */
    21. 21 beep_init(); /* 初始化 beep */
    22. 22 key_init(); /* 初始化 key */
    23. 23
    24. 24 while(1)
    25. 25 {
    26. 26 keyvalue = key_getvalue();
    27. 27 if(keyvalue)
    28. 28 {
    29. 29 switch (keyvalue)
    30. 30 {
    31. 31 case KEY0_VALUE:
    32. 32 beep_state = !beep_state;
    33. 33 beep_switch(beep_state);
    34. 34 break;
    35. 35 }
    36. 36 }
    37. 37 i++;
    38. 38 if(i==50)
    39. 39 {
    40. 40 i = 0;
    41. 41 led_state = !led_state;
    42. 42 led_switch(LED0, led_state);
    43. 43 }
    44. 44 delay(10);
    45. 45 }
    46. 46 return 0;
    47. 47 }

            main.c 函数先初始化 led 灯、蜂鸣器和按键,然后在 while(1)循环中不断的调用函数
    key_getvalue 来读取按键值,如果 KEY0 按下的话就打开/关闭蜂鸣器。 LED0 作为系统提示指
    示灯闪烁,闪烁周期大约为 500ms。

    四、测试

            前面几节课中的通用 Makefile,修改变量 TARGET 为 key,在变量 INCDIRS和 SRCDIRS 中追加“ bsp/gpio” 和“ bsp/key”。

    1. 1 CROSS_COMPILE ?= arm-linux-gnueabihf-
    2. 2 TARGET ?= key
    3. 3
    4. 4 /* 省略掉其它代码...... */
    5. 5
    6. 6 INCDIRS := imx6ul \
    7. 7 bsp/clk \
    8. 8 bsp/led \
    9. 9 bsp/delay \
    10. 10 bsp/beep \
    11. 11 bsp/gpio \
    12. 12 bsp/key
    13. 13
    14. 14 SRCDIRS := project \
    15. 15 bsp/clk \
    16. 16 bsp/led \
    17. 17 bsp/delay \
    18. 18 bsp/beep \
    19. 19 bsp/gpio \
    20. 20 bsp/key
    21. 21
    22. 22 /* 省略掉其它代码...... */
    23. 23
    24. 24 clean:
    25. 25 rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)

    第 2 行修改变量 TARGET 为“ key”,也就是目标名称为“ key”。
    第 11、 12 行在变量 INCDIRS 中添加 GPIO 和按键驱动头文件(.h)路径。
    第 19、 20 行在变量 SRCDIRS 中添加 GPIO 和按键驱动文件(.c)路径。

            使用 Make 命令编译代码,编译成功以后使用软件 imxdownload 将编译完成的 key.bin 文件
    下载到 SD 卡中,命令如下:
    chmod 777 imxdownload //给予 imxdownload 可执行权限,一次即可
    ./imxdownload key.bin /dev/sdd //烧写到 SD 卡中
            烧写成功以后将 SD 卡插到开发板的 SD 卡槽中,然后复位开发板。如果代码运行正常的
    话 LED0 会以大约 500ms 周期闪烁, 按下开发板上的 KEY0 按键,蜂鸣器打开,再按下 KEY0
    按键,蜂鸣器关闭。

    五、总结

            这一节是非常重要的一个内容,作为驱动开发基石的输入和输出一定要学会记牢。主要内容包括按键驱动原理、硬件设计原理、驱动开发和测试。其中最重要的是驱动的开发和测试。


    本文为参考正点原子开发板配套教程整理而得,仅用于学习交流使用,不得用于商业用途。

  • 相关阅读:
    全网最全Python系列教程(非常详细)---集合讲解(学Python入门必收藏)
    Mysql_Note9
    C#中List、Dictionary、HashSet用法以及区别
    基于BP神经网络识别手写字体MINST字符集
    Synchronized锁
    Vue2 常用用法
    泰迪云课堂《Python中文自然语言处理基础与实战》
    16.2.2 创建存储函数
    可观测实践|如何利用 Prometheus 精细化观测云产品
    十年架构师:读懂这篇微服务实战笔记,金九银十薪资直接翻倍
  • 原文地址:https://blog.csdn.net/jiage987450/article/details/133324303