按键
什么是按键
本节介绍按键的原理、结构和工作原理,着重介绍了按键的电路接法和按下、弹起时对电路的影响;最后介绍了SoC处理按键时的2种思路。
按键的物理特性
(1)、平时没人按的时候,弹簧把按键按钮弹开。此时内部断开的。
(2)、有人按下的时候,手的力量克服弹簧的弹力,将按钮按下,此时内部保持接通(闭合)状态;如果手拿开,则弹簧作用下按钮又弹开,同时内部又断开。
(3)、一般的按键都有4个引脚,这4个引脚成2对:其中一对是常开触点(像上面描述的不按则断开,按下则闭合);一对是常闭触点(平时不按时是闭合的,按下后是断开的)
按键的电学原理(结合原理图分析)



(1)硬件接法: SW5:GPH0_2 SW6:GPH0_3 SW78910:GPH2_0123
(2)按键的电路连接分析:平时按钮没有按下时,按钮内部断开,GPIO引脚处电压为高电平;当有人按下按钮时,按钮内部导通,外部VDD经过电阻和按钮连接到地,形成回路,此时GPIO引脚处电压就变成了低电平。此时VDD电压全部分压在了电阻上(这个电阻就叫分压电阻,这个电阻不能太小,因为电阻的功率是U*U/R)
(3)总结:按键的工作方法:其实就是按键的按下与弹开,分别对应GPIO的两种电平状态(按下则GPIO为低电平,弹开则GPIO为高电平)。此时SoC内部可以通过检测这个GPIO的电平高低来判断按键有没有被按下,这个判断结果即可作为SoC的输入信号。
按键属于输入类设备
(1)按键一般用来做输入设备(由人向SoC发送信息的设备,叫输入设备),由人向SoC发送按键信号(按键信号有2种:按下信号和弹开信号)。
(2)有些设备就是单纯的输入设备,譬如按键、触摸屏等;有些设备就是单纯的输出设备,譬如LCD;还有一些设备是既能输入又能输出的,叫输入输出设备(IO),譬如串口。
按键的2种响应方法
(1)SoC处理按键有2种思路:轮询方式和中断方式。
(2)轮询方式,就是SoC主动的每隔一段时间去读取(按键所对应的)GPIO的电平高低,以此获得按键信息;缺点在于CPU要一直注意按键事件,会影响CPU做其他事情。
(3)中断方式,就是SoC事先设定好GPIO触发的中断所对应的中断处理程序ISR,当外部按键按下或弹开时会自动触发GPIO对应的外部中断,导致ISR执行,从而自动处理按键信息。
轮询方式处理按键
X210开发板的按键接法
(1)查原理图,找到按键对应的GPIO:SW5:GPH0_2 SW6:GPH0_3 SW78910:GPH2_0123
(2)原理图上可以看出:按下时是低电平,弹起时是高电平
按键对应的GPIO模式设置




(1)按键接到GPIO上,按键按下还是弹起,决定外部电路的接通与否,从而决定这个GPIO引脚的电压是高还是低;这个电压可以作为这个GPIO引脚的输入信号,此时GPIO配置为输入模式,即可从SoC内部读取该引脚的电平为1还是0(1对应高电平,0对应低电平)。
(2)GPH0CON(0xE0200C00) GPH2DAT(0xE0200C04) GPH2CON(0xE0200C40) GPH2DAT(0xE0200C44)
(3)应该在CON寄存器中将GPIO设置为input模式,然后去读取DAT寄存器(读取到的相应位的值为1表示外部是高电平(对应按键弹起),读取到的位的值为0表明外部是低电平(按键按下))
轮询方式处理按键的程序流程
(1)第一步,先初始化GPIO模式为input;
(2)第二步,循环读取GPIO的电平值,然后判断有无按键按下
代码编写和调试
makefile:
key.bin: start.o led.o key.o
arm-linux-ld -Ttext 0x0 -o key.elf $^
arm-linux-objcopy -O binary key.elf key.bin
arm-linux-objdump -D key.elf > key.dis
gcc mkv210_image.c -o mkx210
./mkx210 key.bin 210.bin
%.o : %.S
arm-linux-gcc -o $@ $< -c -nostdlib
%.o : %.c
arm-linux-gcc -o $@ $< -c -nostdlib
clean:
rm *.o *.elf *.bin *.dis mkx210 -f
定义操作寄存器的宏
#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)
key_init
初始化按键
// 初始化按键
void key_init(void)
{
// 设置GPHxCON寄存器,设置为输入模式
// GPH0CON的bit8~15全部设置为0,即可
rGPH0CON &= ~(0xFF<<8);
// GPH2CON的bit0~15全部设置为0,即可
rGPH2CON &= ~(0xFFFF<<0);
}led.c:
// 亮1个led
void led1(void)
{
rGPJ0CON = 0x11111111;
rGPJ0DAT = ((0<<3) | (1<<4) | (1<<5));
}
// 亮2个led
void led2(void)
{
rGPJ0CON = 0x11111111;
rGPJ0DAT = ((0<<3) | (0<<4) | (1<<5));
}
// 亮3个led
void led3(void)
{
rGPJ0CON = 0x11111111;
rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));
}
void led_off(void)
{
rGPJ0CON = 0x11111111;
rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));
}
轮询:
void key_polling(void)
{
// 依次,挨个去读出每个GPIO的值,判断其值为1还是0.如果为1则按键按下,为0则弹起
// 轮询的意思就是反复循环判断有无按键,隔很短时间
while (1) {
// 对应开发板上标着LEFT的那个按键
if (rGPH0DAT & (1<<2)) {
// 为1,说明没有按键
led_off();
} else {
// 为0,说明有按键
led1();
}
}start.S
调用函数
...
// 从这里之后就可以开始调用C程序了
bl key_init
bl key_polling
...完整代码:
start.S
/*
* 文件名: start.S
* 作者: 朱老师
* 描述: 演示轮询方式处理按键
*/
#define WTCON 0xE2700000
#define SVC_STACK 0xd0037d80
.global _start // 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
// 第1步:关看门狗(向WTCON的bit5写入0即可)
ldr r0, =WTCON
ldr r1, =0x0
str r1, [r0]
// 第2步:设置SVC栈
ldr sp, =SVC_STACK
// 第3步:开/关icache
mrc p15,0,r0,c1,c0,0; // 读出cp15的c1到r0中
//bic r0, r0, #(1<<12) // bit12 置0 关icache
orr r0, r0, #(1<<12) // bit12 置1 开icache
mcr p15,0,r0,c1,c0,0;
// 从这里之后就可以开始调用C程序了
bl key_init
bl key_polling
// 汇编最后的这个死循环不能丢
b .
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)
// 初始化按键
void key_init(void)
{
// 设置GPHxCON寄存器,设置为输入模式
// GPH0CON的bit8~15全部设置为0,即可
rGPH0CON &= ~(0xFF<<8);
// GPH2CON的bit0~15全部设置为0,即可
rGPH2CON &= ~(0xFFFF<<0);
}
void key_polling(void)
{
// 依次,挨个去读出每个GPIO的值,判断其值为1还是0.如果为1则按键按下,为0则弹起
// 轮询的意思就是反复循环判断有无按键,隔很短时间
while (1)
{
// 对应开发板上标着LEFT的那个按键
if (rGPH0DAT & (1<<2))
{
// 为1,说明没有按键
led_off();
}
else
{
// 为0,说明有按键
led1();
}
}
}
led.c
#define GPJ0CON 0xE0200240
#define GPJ0DAT 0xE0200244
#define rGPJ0CON *((volatile unsigned int *)GPJ0CON)
#define rGPJ0DAT *((volatile unsigned int *)GPJ0DAT)
void delay(void);
// 该函数要实现led闪烁效果
void led_blink(void)
{
// led初始化,也就是把GPJ0CON中设置为输出模式
//volatile unsigned int *p = (unsigned int *)GPJ0CON;
//volatile unsigned int *p1 = (unsigned int *)GPJ0DAT;
rGPJ0CON = 0x11111111;
while (1)
{
// led亮
rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));
// 延时
delay();
// led灭
rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));
// 延时
delay();
}
}
// 亮1个led
void led1(void)
{
rGPJ0CON = 0x11111111;
rGPJ0DAT = ((0<<3) | (1<<4) | (1<<5));
}
// 亮2个led
void led2(void)
{
rGPJ0CON = 0x11111111;
rGPJ0DAT = ((0<<3) | (0<<4) | (1<<5));
}
// 亮3个led
void led3(void)
{
rGPJ0CON = 0x11111111;
rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));
}
void led_off(void)
{
rGPJ0CON = 0x11111111;
rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));
}
void delay(void)
{
volatile unsigned int i = 900000; // volatile 让编译器不要优化,这样才能真正的减
while (i--); // 才能消耗时间,实现delay
}
设置关联多个按钮
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)
// 初始化按键
void key_init(void)
{
// 设置GPHxCON寄存器,设置为输入模式
// GPH0CON的bit8~15全部设置为0,即可
rGPH0CON &= ~(0xFF<<8);
// GPH2CON的bit0~15全部设置为0,即可
rGPH2CON &= ~(0xFFFF<<0);
}
void key_polling(void)
{
// 依次,挨个去读出每个GPIO的值,判断其值为1还是0.如果为1则按键按下,为0则弹起
// 轮询的意思就是反复循环判断有无按键,隔很短时间
while (1)
{
// 对应开发板上标着LEFT的那个按键
if (rGPH0DAT & (1<<2))
{
// 为1,说明没有按键
led_off();
}
else
{
// 为0,说明有按键
led1();
}
// 对应开发板上标着DOWN的那个按键
if (rGPH0DAT & (1<<3))
{
// 为1,说明没有按键
led_off();
}
else
{
// 为0,说明有按键
led2();
}
// 对应开发板上标着UP的那个按键
if (rGPH2DAT & (1<<0))
{
// // 为0,说明有按键
led3();
}
}
}