• 七.从按键输入到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;
        }
    }

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

  • 相关阅读:
    二分图最大匹配
    Problems about trees
    Hackerrank Going to the Office
    多校题解
    HDU #2966 In case of failure
    K-D Tree
    UOJ #10 pyx的难题
    bzoj 1090 字符串折叠
    uva 1347 旅行
    bzoj 1059 矩阵游戏
  • 原文地址:https://www.cnblogs.com/yinsedeyinse/p/15756432.html
Copyright © 2020-2023  润新知