一、什么是按键
1、按键的物理特性
- 平时没人按的时候,弹簧把按键按钮弹开。此时内部断开的。
- 有人按下的时候,手的力量克服弹簧的弹力,将按钮按下,此时内部保持接通(闭合)状态;如果手拿开,则弹簧作用下按钮又弹开,同时内部又断开。
- 一般的按键都有 4 个引脚,这 4 个引脚成 2 对:其中一对是常开触点(像上面描述的不按则断开,按下则闭合);一对是常闭触点(平时不按时是闭合的,按下后是断开的)。
2、按键的电学原理(结合原理图分析)
- 硬件接法:
SW5:GPH0_2
SW6:GPH0_3
SW7/8/9/10:GPH2_0/1/2/3
- 按键的电路连接分析:平时按钮没有按下时,按钮内部断开,GPIO 引脚处电压为高电平;当有人按下按钮时,按钮内部导通,外部 VDD 经过电阻和按钮连接到地,形成回路,此时 GPIO 引脚处电压就变成了低电平。此时 VDD 电压全部分压在了电阻上(这个电阻就叫分压电阻,这个电阻不能太小,因为电阻的功率是U*U/R)
- 总结:按键的工作方法:其实就是按键的按下与弹开,分别对应GPIO的两种电平状态(按下则 GPIO 为低电平,弹开则 GPIO 为高电平)。此时 SoC 内部可以通过检测这个 GPIO 的电平高低来判断按键有没有被按下,这个判断结果即可作为 SoC 的输入信号。
3、按键属于输入类设备
- 按键一般用来做输入设备(由人向 SoC 发送信息的设备,叫输入设备),由人向 SoC 发送按键信号(按键信号有 2 种:按下信号和弹开信号)。
- 有些设备就是单纯的输入设备,譬如按键、触摸屏等;有些设备就是单纯的输出设备,譬如LCD;还有一些设备是既能输入又能输出的,叫输入输出设备(IO),譬如串口。
4、按键的 2 种响应方法
- SoC 处理按键有 2 种思路:轮询方式和中断方式。
- 轮询方式,就是 SoC 主动的每隔一段时间去读取(按键所对应的)GPIO 的电平高低,以此获得按键信息;缺点在于 CPU 要一直注意按键事件,会影响 CPU 做其他事情。
- 中断方式,就是 SoC 事先设定好 GPIO 触发的中断所对应的中断处理程序 ISR,当外部按键按下或弹开时会自动触发 GPIO 对应的外部中断,导致 ISR 执行,从而自动处理按键信息。
二、轮询方式处理按键
1、X210开发板的按键接法
- 查原理图,找到按键对应的 GPIO:
SW5:GPH0_2
SW6:GPH0_3
SW7/8/9/10:GPH2_0/1/2/3
- 原理图上可以看出:按下时是低电平,弹起时是高电平。
2、按键对应的 GPIO 模式设置
- 按键接到 GPIO 上,按键按下还是弹起,决定外部电路的接通与否,从而决定这个 GPIO 引脚的电压是高还是低;这个电压可以作为这个 GPIO 引脚的输入信号,此时 GPIO 配置为输入模式,即可从 SoC 内部读取该引脚的电平为 1 还是 0(1 对应高电平,0 对应低电平)。
- GPH0CON(0xE0200C00)
GPH0DAT(0xE0200C04)
GPH2CON(0xE0200C40)
GPH2DAT(0xE0200C44)
- 应该在 CON 寄存器中将 GPIO 设置为 input 模式,然后去读取 DAT 寄存器(读取到的相应位的值为 1 表示外部是高电平(对应按键弹起),读取到的位的值为 0 表明外部是低电平(按键按下))。
三、 轮询方式处理按键的程序流程
(1) 第一步,先初始化 GPIO 模式为 input;
(2) 第二步,循环读取 GPIO 的电平值,然后判断有无按键按下。
四、代码编写和调试
$ cat key.c
// 定义操作寄存器的宏
#define GPH0CON 0xE0200C00
#define GPH0DAT 0xE0200C04
#define GPH2CON 0xE0200C40
#define GPH2DAT 0xE0200C44
#define rGPH0CON (*(volatile unsigned int *)GPH0CON)
#define rGPH0DAT (*(volatile unsigned int *)GPH0DAT)
#define rGPH2CON (*(volatile unsigned int *)GPH2CON)
#define rGPH2DAT (*(volatile unsigned int *)GPH2DAT)
/************************************************************************************/
#define BIT_WIDTH_GPH0_CON (4)
#define BIT_LOCATION_GPH0_2_FUNC (0xf << 2 * BIT_WIDTH_GPH0_CON)
#define GPH0_2_FUNC_INPUT (0x0 << 2 * BIT_WIDTH_GPH0_CON)
#define GPH0_2_FUNC_OUTPUT (0x1 << 2 * BIT_WIDTH_GPH0_CON)
#define GPH0_2_FUNC_EXT_INT2 (0Xf << 2 * BIT_WIDTH_GPH0_CON)
#define BIT_LOCATION_GPH0_3_FUNC (0xf << 3 * BIT_WIDTH_GPH0_CON)
#define GPH0_3_FUNC_INPUT (0x0 << 3 * BIT_WIDTH_GPH0_CON)
#define GPH0_3_FUNC_OUTPUT (0x1 << 3 * BIT_WIDTH_GPH0_CON)
#define GPH0_3_FUNC_EXT_INT3 (0Xf << 3 * BIT_WIDTH_GPH0_CON)
/************************************************************************************/
#define BIT_WIDTH_GPH2_CON (4)
#define BIT_LOCATION_GPH2_0_FUNC (0xf << 0 * BIT_WIDTH_GPH2_CON)
#define GPH2_0_FUNC_INPUT (0x0 << 0 * BIT_WIDTH_GPH2_CON)
#define GPH2_0_FUNC_OUTPUT (0x1 << 0 * BIT_WIDTH_GPH2_CON)
#define GPH2_0_FUNC_EXT_INT16 (0Xf << 0 * BIT_WIDTH_GPH2_CON)
#define BIT_LOCATION_GPH2_1_FUNC (0xf << 1 * BIT_WIDTH_GPH2_CON)
#define GPH2_1_FUNC_INPUT (0x0 << 1 * BIT_WIDTH_GPH2_CON)
#define GPH2_1_FUNC_OUTPUT (0x1 << 1 * BIT_WIDTH_GPH2_CON)
#define GPH2_1_FUNC_EXT_INT17 (0Xf << 1 * BIT_WIDTH_GPH2_CON)
#define BIT_LOCATION_GPH2_2_FUNC (0xf << 2 * BIT_WIDTH_GPH2_CON)
#define GPH2_2_FUNC_INPUT (0x0 << 2 * BIT_WIDTH_GPH2_CON)
#define GPH2_2_FUNC_OUTPUT (0x1 << 2 * BIT_WIDTH_GPH2_CON)
#define GPH2_2_FUNC_EXT_INT18 (0Xf << 2 * BIT_WIDTH_GPH2_CON)
#define BIT_LOCATION_GPH2_3_FUNC (0xf << 3 * BIT_WIDTH_GPH2_CON)
#define GPH2_3_FUNC_INPUT (0x0 << 3 * BIT_WIDTH_GPH2_CON)
#define GPH2_3_FUNC_OUTPUT (0x1 << 3 * BIT_WIDTH_GPH2_CON)
#define GPH2_3_FUNC_EXT_INT19 (0Xf << 3 * BIT_WIDTH_GPH2_CON)
// 初始化按键
void key_init(void)
{
// 设置GPHxCON寄存器,设置为输入模式
// GPH0CON的bit8~15全部设置为0,即可
rGPH0CON &= ~(BIT_LOCATION_GPH0_2_FUNC | BIT_LOCATION_GPH0_3_FUNC);
// GPH2CON的bit0~15全部设置为0,即可
rGPH2CON &= ~(BIT_LOCATION_GPH2_0_FUNC | BIT_LOCATION_GPH2_1_FUNC |
BIT_LOCATION_GPH2_2_FUNC | BIT_LOCATION_GPH2_3_FUNC);
}
void key_polling(void)
{
// 依次,挨个去读出每个GPIO的值,判断其值为1还是0.如果为1则按键按下,为0则弹起
// 轮询的意思就是反复循环判断有无按键,隔很短时间
while (1)
{
// 对应开发板上标着LEFT的那个按键
if (!(rGPH0DAT & (1<<2)))
{
printf("KEY LEFT press!\r\n");
}
// 对应开发板上标着DOWN的那个按键
if (!(rGPH0DAT & (1<<3)))
{
printf("KEY DOWN press!\r\n");
}
// 对应开发板上标着UP的那个按键
if (!(rGPH2DAT & (1<<0)))
{
printf("KEY UP press!\r\n");
}
// 对应开发板上标着RIGHT的那个按键
if (!(rGPH2DAT & (1<<1)))
{
printf("KEY RIGHT press!\r\n");
}
// 对应开发板上标着BACK的那个按键
if (!(rGPH2DAT & (1<<2)))
{
printf("KEY BACK press!\r\n");
}
// 对应开发板上标着MENU的那个按键
if (!(rGPH2DAT & (1<<3)))
{
printf("KEY MENU press!\r\n");
}
}
}
源自朱有鹏老师.