Home » 朱老师嵌入式 » 按键

按键

编 辑:Y ┊ 时 间:2022年04月20日 ┊ 访问: 16 次

什么是按键

本节介绍按键的原理、结构和工作原理,着重介绍了按键的电路接法和按下、弹起时对电路的影响;最后介绍了SoC处理按键时的2种思路。

按键的物理特性

(1)、平时没人按的时候,弹簧把按键按钮弹开。此时内部断开的。
(2)、有人按下的时候,手的力量克服弹簧的弹力,将按钮按下,此时内部保持接通(闭合)状态;如果手拿开,则弹簧作用下按钮又弹开,同时内部又断开。
(3)、一般的按键都有4个引脚,这4个引脚成2对:其中一对是常开触点(像上面描述的不按则断开,按下则闭合);一对是常闭触点(平时不按时是闭合的,按下后是断开的)

按键的电学原理(结合原理图分析)

97499-4zxyjyta9v.png

58507-h97oeqx17pq.png

63195-zl421bams79.png

(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模式设置

29068-mnym0fce1ub.png

24314-tgdow23yau.png

32830-q7gvuz8356i.png

20460-t2q0j5szpk.png

(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();
        }
    }
}



Copyright © 2026 Y 版权所有.网站运行:13年238天21小时27分