本实验对应的例程在光盘资料的:i.MX6UL终结者光盘资料\04_裸机例程源码\6_key 目录下,我们在Ubuntu系统下使用命令“mkdir 6_key”建立“6_key”文件夹,如图 1所示:
使用命令“cd 6_key”进入6_key文件。如图 2所示:
然后使用命令“cp -r …/5_beep/* ./”将上一章试验中的所有内容拷贝到刚刚新建的“6_key”里面,如图 3所示:
拷贝完成以后的工程如图 4所示:
使用命令“mkdir drivers/gpio”在drivers驱动目录下新建“gpio”文件夹。GPIO操作驱动都放在这个目录下。如图 5所示:
使用命令“vi drivers/gpio/gpio.h”新建gpio.h文件。如图 14.3.6所示:
添加如下内容:
1 #ifndef _BSP_GPIO_H
2 #define _BSP_GPIO_H
3 #define _BSP_KEY_H
4 #include "imx6ul.h"
5
6 /* 枚举类型和结构体定义 */
7 typedef enum _gpio_pin_direction
8 {
9 kGPIO_DigitalInput = 0U, /* 输入 */
10 kGPIO_DigitalOutput = 1U, /* 输出 */
11 } gpio_pin_direction_t;
12
13
14 typedef struct _gpio_pin_config
15 {
16 gpio_pin_direction_t direction; /* GPIO方向:输入还是输出 */
17 uint8_t outputLogic; /* 如果是输出的话,默认输出电平 */
18 } gpio_pin_config_t;
19
20
21 /* 函数声明 */
22 void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config);
23 int gpio_pinread(GPIO_Type *base, int pin);
24 void gpio_pinwrite(GPIO_Type *base, int pin, int value);
25
26 #endif
gpio.h 中定义了一个枚举类型 gpio_pin_direction_t 和结构体 gpio_pin_config_t。
gpio_pin_direction_t:GPIO方向,输入或输出。
gpio_pin_config_t:GPIO配置结构体,里面有 GPIO 的方向和默认输出电平两个成员变量。
部分截图如图 7所示:
添加完成之后,保存退出文件。
使用命令“vi drivers/gpio/gpio.c”新建gpio.c文件。如图 8所示:
添加内容如下:
1 #include "gpio.h"
2
3 /*
4 * @description : GPIO初始化。
5 * @param - base : 要初始化的GPIO组。
6 * @param - pin : 要初始化GPIO在组内的编号。
7 * @param - config : GPIO配置结构体。
8 * @return : 无
9 */
10 void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config)
11 {
12 if(config->direction == kGPIO_DigitalInput) /* 输入 */
13 {
14 base->GDIR &= ~( 1 << pin);
15 }
16 else /* 输出 */
17 {
18 base->GDIR |= 1 << pin;
19 gpio_pinwrite(base,pin, config->outputLogic);/* 设置默认输出
电平 */
20 }
21 }
22
23 /*
24 * @description : 读取指定GPIO的电平值 。
25 * @param - base : 要读取的GPIO组。
26 * @param - pin : 要读取的GPIO脚号。
27 * @return : 无
28 */
29 int gpio_pinread(GPIO_Type *base, int pin)
30 {
31 return (((base->DR) >> pin) & 0x1);
32 }
33
34 /*
35 * @description : 指定GPIO输出高或者低电平 。
36 * @param - base : 要输出的的GPIO组。
37 * @param - pin : 要输出的GPIO脚号。
38 * @param - value : 要输出的电平,1 输出高电平, 0 输出低低电平
39 * @return : 无
40 */
41 void gpio_pinwrite(GPIO_Type *base, int pin, int value)
42 {
43 if (value == 0U)
44 {
45 base->DR &= ~(1U << pin); /* 输出低电平 */
46 }
47 else
48 {
49 base->DR |= (1U << pin); /* 输出高电平 */
50 }
51 }
52
53
文件 gpio.c 中有三个函数: gpio_init、 gpio_pinread 和 gpio_pinwrite。
gpio_init:用于初始化指定的GPIO引脚,实际上就是配置的是GDIR寄存器,此函数的三个参数含义如下:
base: 要初始化的GPIO所属于的GPIO 组,我们本实验中使用的“UART1_CTS”引脚即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功能,都会用到此驱动来读或写GPIO。
部分截图图 9。
添加之后保存并退出。
然后使用命令“mkdir drivers/key”在drivers驱动目录下新建“key”文件夹。按键驱动都放在这个目录下。如图 10所示:
然后使用命令“vi drivers/key/key.h”新建“key.h”文件,如图 11所示:
然后再在key.h里添加如下内容:
1 #ifndef _BSP_KEY_H
2 #define _BSP_KEY_H
3 #include "imx6ul.h"
4
5 /* 定义按键值 */
6 enum keyvalue{
7 KEY_NONE = 0,
8 KEY0_VALUE,
9 KEY1_VALUE,
10 KEY2_VALUE,
11 };
12
13 /* 函数声明 */
14 void key_init(void);
15 int key_getvalue(void);
16
17
18 #endif
这里定义了一个枚举类型“keyvalue”,代表不同的按键,由于开发板上只有一个gpio功能按键,所以我们只用到KEY0_VALUE,剩下的是一些函数声明,添加后如图 12所示:
然后保存退出,之后使用命令“vi drivers/key/key.c”新建key.c文件。如图 13所示:
添加内容如下:
1 #include "key.h"
2 #include "gpio.h"
3 #include "delay.h"
4
5 /*
6 * @description : 初始化按键
7 * @param : 无
8 * @return : 无
9 */
10 void key_init(void)
11 {
12 gpio_pin_config_t key_config;
13
14 /* 1、初始化IO复用, 复用为GPIO1_IO18 */
15 IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0);
16
17 /* 2、、配置UART1_CTS_B的IO属性
18 *bit 16:0 HYS关闭
19 *bit [15:14]: 11 默认22K上拉
20 *bit [13]: 1 pull功能
21 *bit [12]: 1 pull/keeper使能
22 *bit [11]: 0 关闭开路输出
23 *bit [7:6]: 10 速度100Mhz
24 *bit [5:3]: 000 关闭输出
25 *bit [0]: 0 低转换率
26 */
27 IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18,0xF080);
28
29 /* 3、初始化GPIO */
30 //GPIO1->GDIR &= ~(1 << 18); /* GPIO1_IO18设置为输入 */
31 key_config.direction = kGPIO_DigitalInput;
32 gpio_init(GPIO1,18, &key_config);
33
34 }
35
36 /*
37 * @description : 获取按键值
38 * @param : 无
39 * @return : 0 没有按键按下,其他值:对应的按键值
40 */
41 int key_getvalue(void)
42 {
43 int ret = 0;
44 static unsigned char release = 1; /* 按键松开 */
45
46 if((release==1)&&(gpio_pinread(GPIO1, 18) == 0)) /* KEY0 */
47 {
48 delay(10); /* 延时消抖 */
49 release = 0; /* 标记按键按下 */
50 if(gpio_pinread(GPIO1, 18) == 0)
51 ret = KEY0_VALUE;
52 }
53 else if(gpio_pinread(GPIO1, 18) == 1)
54 {
55 ret = 0;
56 release = 1; /* 标记按键释放 */
57 }
58
59 return ret;
60 }
此文件一共有两个函数key_init 和 key_getvalue:
key_init:用来初始化KEY所使用的 GPIO。我们查阅手册可以知道,引脚UART1_CTS需要复用成GPIO1_IO18来使用,IO速度配置成100MHz,默认22K上拉。
key_getvalue:用来获取UART1_CTS引脚的电平状态,只有一个返回值,返回0代表没有被按下,反之不是0代表被按下。函数中静态局部变量release 表示按键是否释放,即按键是否弹起。
在这里我们要注意,我们印象中按键按下弹起的过程,电压是瞬间由高到低,由低变高。如图 14所示:
但是时间上,当类似按键这种机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,图 15是按键按下弹起瞬间电压效果图:
抖动时间的长短由按键的机械特性决定,一般为5ms~10ms。这个时间对于CPU来说就非常长了,如果程序一直在循环读IO电平值的话,会引起程序多次误读。
为了不产生这种现象,我们需要在软件上添加按键消抖。原理很简单,我们可以了解到,按键按下后有一段时间会产生抖动,所以我们需要在读取到引脚电平发生变化后,跳过这段时间,再去读取一次引脚的电平值。
程序检测到按键由高变低后,第48行做了延时10ms的工作。第50行重新读取电平状态,确定按下后再返回按键实际状态。
部分截图如图 16所示:
修改之后保存退出。
然后使用命令“vi main.c”修改主函数。如图 17所示:
修改内容如下。
1 #include "clk.h"
2 #include "delay.h"
3 #include "led.h"
4 #include "beep.h"
5 #include "key.h"
6
7 /*
8 * @description : main函数
9 * @param : 无
10 * @return : 无
11 */
12 int main(void)
13 {
14 int i = 0;
15 int keyvalue = 0;
16 unsigned char led_state = OFF;
17 unsigned char beep_state = OFF;
18
19 clk_enable(); /* 使能所有的时钟 */
20 led_init(); /* 初始化led */
21 beep_init(); /* 初始化beep */
22 key_init(); /* 初始化key */
23
24 while(1)
25 {
26 keyvalue = key_getvalue();
27 if(keyvalue)
28 {
29 switch ((keyvalue))
30 {
31 case KEY0_VALUE:
32 beep_state = !beep_state;
33 beep_switch(beep_state);
34 break;
35 }
36 }
37
38 i++;
39 if(i==50)
40 {
41 i = 0;
42 led_state = !led_state;
43 led_switch(LED0, led_state);
44 }
45 delay(10);
46 }
47
48 return 0;
49 }
我们在main.c里添加key.h头文件,然后主函数中添加初始化key函数,然后在while(1)循环中扫描按键,当按键按下时就打开或关闭蜂鸣器。部分截图如图 18所示:
然后保存并退出。