七.从按键输入到GPIO通用驱动

Posted 银色的音色

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了七.从按键输入到GPIO通用驱动相关的知识,希望对你有一定的参考价值。

在前面的试验中,我们分别点亮了LED和蜂鸣器,这两个设备都是使用的GPIO的输出功能,下面我们来通过按键来做测试板子的输入功能。

硬件原理

还是查看底板原理图,

 开发板上有两个按钮,其中ON_OFF是将系统重启的,不用考虑,我们要用到就是KEY0,在按键按下时,IO通过KEY0接地被拉低,默认情况是通过10K的电阻拉高。再看下KEY0的去向

 KEY_0被连接到了UART1_CTS,查询手册,复用为GPIO1_IO18。

 

IO的初始化流程和前面输出应用的方法配置差不多,首先是复用设置,其次是电气属性,注意输入和输出底值不同,输入为0,就是把第17个bit置0(0为输出,1为输入)

void key_init(void)

    //GPIO复用初始化,复用为GPIO5_IO01
    IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0); 
    /* 
    配置UART1_CTS_B的电气属性    
    *bit 16:0 HYS关闭
    *bit [15:14]: 11 默认22K上拉
    *bit [13]: 1 pull功能
    *bit [12]: 1 pull/keeper使能
    *bit [11]: 0 关闭开路输出
    *bit [7:6]: 10 速度100Mhz
    *bit [5:3]: 000 关闭输出
    *bit [0]: 0 低转换率
     */
    IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18,0xF080);
    //第18位设置为输入
    GPIO1->GDIR &= ~(1<<18);

GPIO的DR寄存器在IO为输入时就是外部的高低电平。读取这个寄存器对应的bit就可以知道管脚高低电平状态。定义了数据读取函数以后还要添加个函数用来消除按键抖动效果。

// 读取按键值:0为按下,1为未按下
int read_key(void)

    int ret = 0;
    ret = GPIO1->DR >> 18 & 0x1;
    return ret;


//消除按键抖动:延时10ms
int key_getvalue(void)

    int ret = 0;
    static unsigned char release = 1;  //记录按键是否被释放:1时为释放状态
    if((release == 1) && (read_key() == 0)) //按下时
       release = 0;                    // 标记为按键按下
        delay(10);
        release = 0;
        if(read_key()==0)   //延时10ms以后还是按下,判定为有效
            
                ret = KEY0_VALUE;
            
    
    else if(read_key() == 1) //未按下
        
            ret = KEY_NONE;
            release = 1;   //标记按键释放
        
    return ret;

在读取IO状态时调用的是这个消除抖动的函数,在函数内延时了10ms,如果10ms后还是按下状态则响应为按键按下,防止按键抖动时发生抖动现象

 

 

整个bsp_key的文件夹如下

头文件

#ifndef __BSP_KEY_H
#define __BSP_KEY_H

#include "imx6ul.h"

/*枚举类型描述GPIO方向*/
typedef enum _gpio_pin_direction

    kGPIO_DigitalInput = 0U,
    kGPIO_DigitaoOutput = 1U
_gpio_pin_direction_t;

typedef struct gpio_pin_config

    _gpio_pin_direction_t direction;
    uint8_t outputLogic;
gpio_pin_config;

/*按键值*/
enum keyvalue
    KEY_NONE = 0, //没有按下是0
    KEY0_VALUE,
;


void gpio_init(GPIO_Type *base,int pin,gpio_pin_config *config);
int gpio_pinread(GPIO_Type *base,int pin);
void gpio_pinwrite(GPIO_Type *base,int pin,int value);

#endif
bsp_key.h

头文件里定义了个枚举类型对应每个按键的按键值

 

 

 KEY0_NONE就是没有按键按下,KEY0_VALUE意思是第1个按钮被按下。这个在整个演示过程没有提现出来,按键响应是直接赋值完成的。

#include "bsp_key.h"
#include "bsp_delay.h"
// 初始化按键
void key_init(void)

    //GPIO复用初始化,复用为GPIO5_IO01
    IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0); 
    IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18,0xF080);
    //第18位设置为输入
    GPIO1->GDIR &= ~(1<<18);


// 读取按键值 0为按下,1为未按下
int read_key(void)

    int ret = 0;
    ret = GPIO1->DR >> 18 & 0x1;
    return ret;


//消除按键抖动:延时10ms
int key_getvalue(void)

    int ret = 0;
    static unsigned char release = 1;  //记录按键是否被释放:1时为释放状态
    if((release == 1) && (read_key() == 0)) //按下时
       release = 0;                    // 标记为按键按下
        delay(10);
        release = 0;
        if(read_key()==0)   //延时10ms以后还是按下,判定为有效
            
                ret = KEY0_VALUE;
            
    
    else if(read_key() == 1) //未按下
        
            ret = KEY_NONE;
            release = 1;   //标记按键释放
        
    return ret;
bsp_key.c

 

按键使用

这里用了三个外设:

  • KEY0
  • LED0
  • 蜂鸣器

LED保持闪烁表明程序在一直运行,KEY0按下可以切换蜂鸣器工作状态。直接放代码的main函数

int main(void)
   
    unsigned char led_state = OFF;
    unsigned char beep_state = OFF;
    int i = 0;
    int key_value;
    clk_enable();
    led_init();
    beep_init();
    key_init();
 
    while(1)
       key_value = key_getvalue();
        if(key_value == KEY0_VALUE) //按键按下
        
            switch (key_value)
            
                case KEY0_VALUE:
                    beep_state = !beep_state;
                    beep_switch(beep_state);
                    break;
            
        
        i++;
        if(i == 50)
        
            i = 0;
            led_state = !led_state;
            led_switch(LED0,led_state);
        
        delay(10);
    
    return 0;

整个过程就是初始化时钟、初始化外设,主循环里一直闪灯并且扫描KEY0,当按键按下改变蜂鸣器工作状态。还是要注意make的时候添加路径!

通用GPIO驱动

在完成了GPIO的输入输出试验后,可以归纳一下写个通用的gpio驱动!

 在头文件里定义了要用到数据类型,还声明了接口函数

#ifndef __BSP_GPIO_H
#define __BSP_GPIO_H

#include "imx6ul.h"

/*枚举类型描述GPIO方向*/
typedef enum _gpio_pin_direction

    kGPIO_DigitalInput = 0U,
    kGPIO_DigitalOutput = 1U,
 gpio_pin_direction_t;

typedef struct _gpio_pin_config

    gpio_pin_direction_t direction;
    uint8_t outputLogic;
 gpio_pin_config_t;


void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config);
int gpio_pinread(GPIO_Type *base, int pin);
void gpio_pinwrite(GPIO_Type *base, int pin, int value);

#endif

要注意的就是定义的结构体gpio_pin_config_t,里面有两个参数,一个是GPIO的方向,对应上面的枚举类型gpio_pin_direction_t,枚举值就一个进一个出。还有个默认值,在做输入时没实际用处,在做输出时可以设置初始值。

C代码里和前面的所有驱动一样,首先是一个初始化,再有就是定义数据的读写函数。

#include "bsp_gpio.h"

void gpio_init(GPIO_Type *base,
                int pin,
                gpio_pin_config_t *config)

    if(config->direction == kGPIO_DigitalInput) //gpio为输入
    
        base->GDIR &= ~(1 << pin);  
    
    //gpio为输出
    else
    
        base->GDIR |= 1<<pin;
        //设置默认电平
        gpio_pinwrite(base,pin,config->outputLogic);  
    


/*
读GPIO指定管脚
*/
int gpio_pinread(GPIO_Type *base,int pin)

    return (((base->DR >> pin)) &0x1);


/*
写GPIO指定管脚
*/
void gpio_pinwrite(GPIO_Type *base,int pin,int value)

    if (value == 0U)
    
        base->DR &= ~(1U << pin);
    
    else
    
        base->DR |= (1U<<pin);
    

在初始化函数中,我们用了两个指针(base和config),base对应GPIO_Type

 

 在对那个寄存器进行操作时直接引用就可以了,

config是我们前面说的在头文件里定义的数据类型,一个方向一个初始值。先定义输入输出(GDIR),判定如果是输出时按照定义的初始值输出。

读和写就是根据定义的pin的值直接操作DR对应的bit就可以了。读的时候是把DR里的数取出来右移,把pin对应的值和0x1做与运算(清除其他的,只保留1个bit的值);写函数是把1左移到pin对应的bit上,要是写0就是取反后和DR与,把bit置0,反之置1。

gpio通用驱动的使用(输入)

写好通用驱动时,就可以使用了,我们把前面的bsp_key进行修改

#include "bsp_key.h"
#include "bsp_gpio.h"
#include "bsp_delay.h"


// 初始化按键
void key_init(void)

    //GPIO复用初始化,复用为GPIO5_IO01
    //第18位设置为输入

    IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0); 
    IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18,0xF080);

    gpio_pin_config_t key_config;

    key_config.direction = kGPIO_DigitalInput;
    gpio_init(GPIO1,18,&key_config);




//消除按键抖动:延时10ms
int key_getvalue(void)

    int ret = 0;
    int _get_data = gpio_pinread(GPIO1,18);
    static unsigned char release = 1;  //记录按键是否被释放:1时为释放状态
    if((release == 1) && (_get_data == 0)) //按下时
       release = 0;                    // 标记为按键按下
        delay(10); 
        release = 0;
        if(_get_data == 0)   //延时10ms以后还是按下,判定为有效
            
                ret = KEY0_VALUE;
            
    
    else if(_get_data == 1) //未按下
        
            ret = KEY_NONE;
            release = 1;   //标记按键释放
        
    return ret;

因为可以直接调用读IO的函数,这里省略了一个read_key(),使用的时候一定要注意调用gpio_init()时候用引用方式传参。

还有就是初始化的时候因为定义的key_config是一个结构体,要用.指向其内部成员,而对应结构指针来说采用->方法,即

  • A *p则使用:p->play(); 左边是结构指针。
  • A p 则使用:p.paly(); 左边是结构变量。

后面读的函数很简单,没什么可说的!

gpio通用驱动的使用(输出)

照着前面的代码可以把LED点亮的代码修改一下,看看怎么使用这个通用驱动!

#include "bsp_led.h"
#include "bsp_gpio.h"

/*初始化LED*/
void led_init(void)
   
    // 复用、电气属性寄存器初始化

    IOMUXC_SetPinMux(IOMUXC_GPIO1_IO03_GPIO1_IO03,0);
    IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO03_GPIO1_IO03,0x10B0);
    // GPIO1方向寄存器,
    // GPIO1->GDIR = 0x8;
    
    gpio_pin_config_t gpio_config;

    gpio_config.direction = kGPIO_DigitalOutput;
    gpio_config.outputLogic = 0U;

    gpio_init(GPIO1,3,&gpio_config);



// 点亮LED
void led_on(void)

    // GPIO1->DR &= ~(1<<3); //bit3清零
    gpio_pinwrite(GPIO1,3,0U);



// 关闭LED
void led_off(void)

    // GPIO1->DR |=(1<<3);  //bit3置一
    gpio_pinwrite(GPIO1,3,1U);



void led_switch(int led, int status)
    
    switch(led)
    
        case LED0:
            if(status == ON)
                gpio_pinwrite(GPIO1,3,0U);
            else if(status == OFF)
                gpio_pinwrite(GPIO1,3,1U);
            break;
    

初始化的过程差不多,跟前面的对比一下就知道思路是什么样了!

以上是关于七.从按键输入到GPIO通用驱动的主要内容,如果未能解决你的问题,请参考以下文章

stm32—GPIO

linux通用GPIO驱动

Linux 输入设备 自定义键盘 input输入子系统 gpio-keys按键驱动

Linux 按键输入实验

Linux——Linux驱动之使用输入子系统设计按键驱动实战(输入子系统基本概念代码获取上报信息相关函数解析)

嵌入式Linux开发24——Linux 按键输入实验