linux(第十六期)--按键输入实验-- Ubuntu20.04

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

前言

一、按键输入简介

二、硬件原理分析

三、实验程序编写

1、按键驱动编写

2、main.c编写

四、编译下载

1、编写makefile文件

2、编译下载

总结


前言

前面几次试验都是讲解如何使用 I.MX6U 的 GPIO 输出控制功能,I.MX6U 的 IO 不仅能作为输出,而且也可以作为输入。I.MX6U-ALPHA 开发板上有一个按键,按键连接了一个 IO,将这个 IO 配置为输入功能,读取这个 IO 的值即可获取按键的状态 (按下或松开)。本次实验通过这个按键来控制蜂鸣器的开关,通过本次实验的学习来掌握如何将 I.MX6UL 的IO作为输入来使用。


一、 按键输入简介

按键就两个状态:按下或弹起,将按键连接到一个 IO上,通过读取这个 IO 的值就知道按键是按下的还是弹起的。至于按键按下的时候是高电平还是低电平要根据实际电路来判断。本次实验我们的主要工作就是配置按键所连接的 IO 为输入功能,然后读取这个 IO 的值来判断按键是否按下。

I.MX6U开发板上有一个按键 KEY0,本章我们将会编写代码通过这个 KEY0 按键来控制开发板上的蜂鸣器,按一下 KEY0 蜂鸣器打开,再按一下蜂鸣器就关闭。

二、硬件原理分析

本试验我们用到的硬件有:

1) LED 灯 LED0。

2)蜂鸣器。

3)1 个按键 KEY0

按键 KEY0 的原理图如下:

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

三、实验程序编写

1、按键驱动编写

本次实验还是以上一次实验为基础,重新创建一个VScode工程,名字命名为7_key,在工程目录的 bsp 文件夹中创建名为"key"和"gpio"两个文件夹。按键相关的驱动文件都放 到"key"文件夹中,本章试验我们对 GPIO 的操作编写一个函数集合,也就是编写一个 GPIO 驱动文件,GPIO 的驱动文件放到"gpio"文件夹里面。
新建 bsp_gpio.c 和 bsp_gpio.h 这两个文件,将这两个文件都保存到刚刚创建的 bsp/gpio 文
件夹里面,然后在 bsp_gpio.h 文件夹里面输入如下内容:

复制代码
#ifndef __BSP_GPIO_H
#define __BSP_GPIO_H

#include "fsl_iomuxc.h"
#include "fsl_common.h"
#include "MCIMX6Y2.h"
#include "cc.h"


/* 枚举类型和结构体定义 */
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);
void gpio_pinwrite(GPIO_Type *base,int pin, int value);
int gpio_pinread(GPIO_Type *base, int pin);


#endif

此文件中定义了了一个枚举类型 gpio_pin_direction_t 和结构体 gpio_pin_config_t。
枚举类型 gpio_pin_direction_t 表示 GPIO 方向,输入或输出。
结构体 gpio_pin_config_t 是 GPIO 的配置结构体,里面有 GPIO 的方向和默认输出电平两个成员变量。
然后在bsp_gpio.c中输入以下代码:

复制代码
#include "bsp_gpio.h"

/*
 * @description   : GPIO初始化。
 * @param - base  : 要初始化的GPIO组。
 * @param - pin   : 要初始化GPIO在组内的编号。
 * @param - config: GPIO配置结构体。
 * @return        : 无
 */

void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config)
{
    if(config->direction ==kGPIO_DigitalInput)//输入
    {
        base->GDIR &=~(1<<pin);
    }	
    else
    {
         base->GDIR |=(1<<pin);
         /* 设置默认输出电平 */
    }	    
}

/*
 * @description   : 指定GPIO输出高或者低电平。
 * @param - base  : 要输出的GPIO组。
 * @param - pin   : 要输出的GPIO脚号。
 * @param - value : 要输出的电平,1 输出高电平,0 输出低电平
 * @return        : 无
 */
void gpio_pinwrite(GPIO_Type *base,int pin, int value)
{
        if(value == 0) /* 写入0 */ 
        {
            base->DR &=~(1<<pin);
        }
        else            /* 写入1 */
        {
            base->DR |=(1<<pin);
        }
}

 /*
  * @description	 : 读取指定GPIO的电平值 。
  * @param - base	 : 要读取的GPIO组。
  * @param - pin	 : 要读取的GPIO脚号。
  * @return 		 : 无
  */
 int gpio_pinread(GPIO_Type *base, int pin)
 {
	 return (((base->DR) >> pin) & 0x1);
 }

文件 bsp_gpio.c 中有三个函数:gpio_init、gpio_pinread 和 gpio_pinwrite,函数 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。
接下来编写按键驱动文件,新建 bsp_key.c 和 bsp_key.h 这两个文件,将这两个文件都保存
到刚刚创建的 bsp/key 文件夹里面,然后在 bsp_key.h 文件夹里面输入如下内容:

复制代码
#ifndef __BSP_KEY_H
#define __BSP_KEY_H
#include "fsl_iomuxc.h"
#include "fsl_common.h"
#include "MCIMX6Y2.h"

/*按键值*/
enum keyvalue{
    KEY_NONE =0,
    KEY0_VALUE,

};

/*函数声明*/
void key_init(void);
int read_key(void);
int key_getvalue(void);
#endif

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

ALPHA 开发板上只有一个按键,因此枚举类型里面只到 KEY0_VALUE。
bsp_key.c 中输入如下所示内容:

复制代码
#include "bsp_key.h"
#include "bsp_delay.h"
#include "bsp_gpio.h"
/* key初始化 */
void key_init(void)
{
    gpio_pin_config_t key_config;
    /* 1、初始化IO复用,复用为GPIO1_IO018 */
	IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0);	
	/* 2、配置GPIO5_IO03的IO属性(0xF080)
        *bit 16: 0    HYS关闭(施密特触发器禁用)
        *bit [15:14]: 11  默认上拉(非下拉!0xF080对应上拉)
        *bit [13]:    0    keeper功能禁用
        *bit [12]:    1    pull/keeper使能(启用上拉)
        *bit [11]:    0    关闭开路输出
        *bit [7:6]:   10   速度100MHz
        *bit [5:3]:   000  R0/1驱动能力(非R0/6!0xF080对应R0/1)
        *bit [0]:     0    低转换率
*/
	IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18,0XF080);
	/* 3、初始化GPIO,设置GPIO5_IO01设置为输入*/
    key_config.direction = kGPIO_DigitalInput;
	gpio_init(GPIO1,18, &key_config);
		
}

/* 读取按键值  返回值:0按下,1未按下*/
int read_key(void)
{
    int ret =0;
    ret = gpio_pinread(GPIO1,18);
    return ret;
}


int key_getvalue(void)
{
    int ret =0;
    static unsigned char release =1;/* 为1表示按键值被释放*/

    if((release==1) && (gpio_pinread(GPIO1,18)== 0))
    {
        delay(10);
        release=0;
        if( gpio_pinread(GPIO1,18)== 0)/*如果延时10ms以后KEY0还是0,表示按键有效 */
        {
            ret =KEY0_VALUE;


        }

    }
    else if(gpio_pinread(GPIO1,18) == 1)/* 未按下*/
    {
        ret =KEY_NONE;
         release=1;
    }
    return ret; //返回按键值

}

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 表示按键是否释放。

注意:delay(10)为按键消抖,延时时间大约为 10ms,用于消除按键抖动。

按键消抖原理
当按键在 t1 时刻按键被按下以后按键值 就变为 0 ,这是最理想的状态。但是实际的按键是机械结构,加上刚按下去的一瞬间人手可能也有抖动,实际的按键电压变化过程如图:

其中 t1 时刻按键被按下,但是由于抖动的原因,直到 t2 时刻才稳定下来,t1 到 t2 这段时间就是抖动。一般这段时间就是十几 ms 左右,从图 中可以看出在抖动期间会有多次触发,如果不消除这段抖动的话软件就会误判,本来按键就按下了一次,结果软件读取 IO 值发现电平多次跳变以为按下了多次。所以我们需要跳过这段抖动时间再去读取按键的 IO值,也就是至少要在 t2 时刻以后再去读 IO 值。

2、main.c编写

复制代码
/**************************************************************

文件名	: 	 main.c
描述	   : I.MX6U开发板裸机实验  按键实验 
其他	   : 无
**************************************************************/
#include "main.h"
#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_led.h"
#include "bsp_beep.h"
#include "bsp_key.h"
/*
 * @description	: main函数
 * @param 	    : 无
 * @return 		: 无
 */
int main(void)
{
	int i =0;
	int keyvalue=0; 
	unsigned char led_state=OFF;
	unsigned char beep_state=OFF;
	clk_enable();		/* 使能所有的时钟*/
	led_init();			/* 初始化led*/
	beep_init();		/* 初始化beep*/
	key_init();			/* 初始化key*/


	while(1)			/* 死循环*/
	{	
		/* 按键控制蜂鸣器*/
		keyvalue=key_getvalue();

		if(keyvalue)	/* 如果为正,表示有效的按键值*/
		{
			switch ((keyvalue))
			{
				case KEY0_VALUE:
					beep_state = !beep_state;
					beep_switch(beep_state);
					break;
			}
		}

		i++;
		if(i == 50) /* 500毫秒灯翻转一次*/
		{
			i =0;
			led_state =!led_state;
			led_switch(LED0,led_state);
		}
		delay(10);
	}
	return 0;
}

main.c 函数先初始化 led 灯、蜂鸣器和按键,然后在 while(1)循环中不断的调用函数 key_getvalue 来读取按键值,如果 KEY0 按下的话就打开/关闭蜂鸣器。LED0 作为系统提示指示灯闪烁,闪烁周期大约为 500ms。本章例程的软件编写就到这里结束了,接下来就是编译下载验证了。

四、编译下载

1、编写makefile文件

使用之前编写的通用makefile,修改变量TARGET 为key,在变量 INCDIRS和SRCDIRS 中追加"bsp/key"和"bsp/gpio",修改完成以后如下所示:

复制代码
CROSS_COMPILE ?= arm-linux-gnueabihf-
TARGET		  ?= key

CC 		:= $(CROSS_COMPILE)gcc
LD		:= $(CROSS_COMPILE)ld
OBJCOPY := $(CROSS_COMPILE)objcopy
OBJDUMP := $(CROSS_COMPILE)objdump

INCUDIRS		:=  imx6ul \
								bsp/clk \
								bsp/led \
								bsp/delay\
								bsp/beep\
								bsp/key\
								bsp/gpio
			
SRCDIRS			:=  project \
								bsp/clk \
								bsp/led \
								bsp/delay\
								bsp/beep\
								bsp/key\
								bsp/gpio
								
INCLUDE			:= $(patsubst %, -I %, $(INCUDIRS))

SFILES			:= $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S))
CFILES			:= $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))
SFILENDIR		:= $(notdir  $(SFILES))
CFILENDIR		:= $(notdir  $(CFILES))

SOBJS			:= $(patsubst %, obj/%, $(SFILENDIR:.S=.o))
COBJS			:= $(patsubst %, obj/%, $(CFILENDIR:.c=.o))
OBJS			:= $(SOBJS) $(COBJS)
VPATH			:= $(SRCDIRS)

.PHONY: clean

$(TARGET).bin : $(OBJS)
	$(LD) -Timx6ul.lds -o $(TARGET).elf $^
	$(OBJCOPY) -O binary -S $(TARGET).elf $@
	$(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis

$(SOBJS) : obj/%.o : %.S
	$(CC) -Wall -nostdlib -c -O2  $(INCLUDE) -o $@ $<

$(COBJS) : obj/%.o : %.c
	$(CC) -Wall -nostdlib -c -O2  $(INCLUDE) -o $@ $<

clean:
	rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)

print:
	@echo INCLUDE = $(INCLUDE)
	@echo SFILES = $(SFILES)
	@echo CFILES = $(CFILES)
	@echo SFILENDIR = $(SFILENDIR)
	@echo CFILENDIR = $(CFILENDIR)
	@echo SFILENDIR = $(SFILENDIR)
	@echo SOBJS = $(SOBJS)
	@echo COBJS = $(COBJS)
	@echo OBJS = $(OBJS)

2、编译下载

复制代码
chmod 777 imxdownload  //给予imxdownload可执行权限,一次即可
./imxdownload key.bin /dev/sdb

烧写成功以后将 SD 卡插到开发板的 SD 卡槽中,然后复位开发板。如果代码运行正常的

话 LED0 会以大约 500ms 周期闪烁, 按下开发板上的 KEY0 按键,蜂鸣器打开,再按下 KEY0

按键,蜂鸣器关闭。


总结

通过本次实验的学习来掌握如何将 I.MX6UL 的IO作为输入来使用。

相关推荐
祁思妙想2 小时前
使用Docker部署Python前后端项目
运维·docker·容器
nbsaas-boot2 小时前
Linux 服务(systemd)最完整使用文档
linux·运维·服务器
宇钶宇夕2 小时前
CoDeSys入门实战一起学习(五):CoDeSys V3 车库门控制编程全解析系列(手册基础第五篇)
运维·自动化
xin_nai2 小时前
TCP/IP协议
服务器·网络·tcp/ip
不染尘.2 小时前
Linux的基本管理及命令(下)
linux·windows·ssh
IT 行者2 小时前
Spring Security OAuth2 ID Token 生成机制深度解析
服务器·spring
三花猫咪2 小时前
UBUNTU串口打印终端,X86主机变开发板
linux·运维·嵌入式硬件·ubuntu
HalvmånEver2 小时前
Linux:深入剖析 System V IPC上(进程间通信八)
linux·运维·数据库·c++·system v·管道pipe
EndingCoder2 小时前
泛型类和高级用法
linux·运维·前端·ubuntu·typescript