七.从按键输入到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
头文件里定义了个枚举类型对应每个按键的按键值
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;
按键使用
这里用了三个外设:
- 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通用驱动的主要内容,如果未能解决你的问题,请参考以下文章
Linux 输入设备 自定义键盘 input输入子系统 gpio-keys按键驱动