STM8学习笔记---按键KEY

217 阅读5分钟

用按键来控制LED灯的亮灭。按键接在PC4口,LED灯接在PD4口,LED灯由IO口高低电平控制亮灭,所以对于LED来说,IO口为输出口。按键是由外部电路来决定高低电平,假设按键未按下时默认为高电平,按键按下时为低电平。对于KEY来说,IO口为输入口,IO口要读取外部电平状态,根据电平状态来判断按键是否按下。
首先要设置IO口的状态。
在这里插入图片描述
端口方向寄存器, LED口要设置为输出模式, 所以PD4设置为1。按键KEY口要设置为输入模式,所以PC4设置为0。
在这里插入图片描述
端口控制寄存器,LED口需要输出高低电平,所以PD4口设置为1,推挽输出。
按键KEY口要读取外部电平,按键未按下时为高电平,按键按下时为低电平。所以PC4口设置为1,带上拉电阻输入,将IO口电平默认状态用上拉电阻拉为高电平。

在这里插入图片描述
LED口为输出模式,设置PD4口为1,输出最大速度10MHz。
按键KEY口为输出模式,设置PC4口为0,禁止外部中断。判断按键用查询方式,不需要开中断。
LED口初始化代码为:

#include "led.h"

void LED_GPIO_Init( void )
{
    PD_DDR |= ( 1 << 4 );        //PD4 输出 led
    PD_CR1 |= ( 1 << 4 );        //PD4 推挽输出
    PD_CR2 |= ( 1 << 4 );
}

控制LED亮灭要通过位操作来实现,所以要在头文件中对LED进行位定义。

#ifndef __LED_H
#define __LED_H
#include "iostm8s103F3.h"
#define  LED  PD_ODR_ODR4 			 //位定义 PD4定义为输出 LED
void LED_GPIO_Init(void);
#endif

用 LED 来表示PD4口的输出状态寄存器,这样在操作LED的时候就相当于操作PD口的输出寄存器 ODR4位。
在这里插入图片描述
按键口KEY的初始化代码为:

#include "key.h"

void KEY_GPIO_Init( void )
{
    PC_DDR &= ~( 1 << 4 );        //PC4 输入
    PC_CR1 |= ( 1 << 4 );         //带上拉电阻输入
    PC_CR2 &= ~( 1 << 4 );        //禁止外部中断
}

按键判断也要通过位操作实现,要在头文件中位定义。

#ifndef __KEY_H
#define __KEY_H
#include "iostm8s103F3.h"
#define  KEY  PC_IDR_IDR4 			 //位定义 PC4 定义为按键输入  
void KEY_GPIO_Init( void );
#endif

用 KEY 来表示PC4口的输入状态寄存器,这样在操作KEY的时候就相当于操作PC口的输入寄存器 IDR4位。
在这里插入图片描述
LED和按键KEY的初始化已经完成,下来开始写主函数。主函数要实现的功能主要是按键按下一次LED灯亮,按键再按下一次LED灯灭。也就是说按键 按下一次 LED灯的状态取反一次。主程序代码如下:

 while( 1 )
    {
        if( KEY == 0 )
        {
                LED = !LED;
        }
    }

按键按下为低电平,所以在循环中判断KEY的状态,若KEY为0,说明按键按下,此时将LED状态取反。将程序烧入单片机中测试,在按键的过程中发现有时候按键按下后LED灯的亮灭没发生变化,感觉是按键失灵了。用示波器看看按键IO口的波形。
在这里插入图片描述
通过波形可以看到,按键按下后,确实为低电平,但是在按键真正按下前出现了一个向下的脉冲。为什么会出现这个向下的脉冲呢?因为一般的按键内部结构是两个金属片,按键按下时两个金属片挨在一起形成通路,按键弹起时两个金属片分开形成断路。人用手指按键时,在按下的一瞬间手指可能会抖动,导致两个金属片短暂的接触后,又瞬间断开。然后手指将按键真正按下后,两个金属片稳定的接触在一起,形成一个稳定的低电平。
程序中按键失灵的感觉就是这个短暂的向下脉冲造成的。当按键时手指发生抖动,在IO口出现了一个瞬间的低电平,这时候如果程序刚好捕捉到了这个低电平,就会被当做一个按键事件处理,这时将状态取反。当按键真正被按下时,又出现一个低电平,这时程序认为是新的按键事件发生,又对LED状态取反一次。相当于真正的按键只按下去一次,但是程序中捕捉到了两次按键。也就是说按键真正的按下了一次,但是程序中对灯的状态取反了两次。相当于LED状态没变。由于按键按下时间非常短,LED灯状态在中间发生变化的时间太短,人眼观察不到,所以感觉LED状态没变。那么如何避免在按键时出现抖动的情况呢,一种方法时在按键的时候一瞬间按下去,不要出现抖动情况。一种是硬件上换更好的按键,从硬件上避免抖动出现。但是这两种方法都不太现实,能不能用软件方法解决这个问题呢?
通过观察上面按键的波形可以发现,按键按下后低电平会持续很长时间,在程序判断的时候可以通过两次低电平来确认按键按下,这两次低电平判断中间要等待一段时间。比如说判断到一次低电平后,延时一段时间,在判断一次,若此时还是低电平说明是按键正真的按下了。于是程序修改为:

 while( 1 )
    {
        if( KEY == 0 )
        {
            delay_ms( 10 );
            if( KEY == 0 )
            {
                LED = !LED;
            }
        }
    }

按键按下后,延时10ms,然后再判断按键状态,若按键状态没变,说明按键是真的被按下。此时在对LED灯的状态取反。将将程序烧到单片机中测试,发现增加延时后,按键的识别率大大的增强了。
修改后的主程序为:

#include "iostm8s103F3.h"
#include "led.h"
#include "key.h"
void SysClkInit( void )
{
    CLK_SWR = 0xe1;       //HSI为主时钟源  16MHz CPU时钟频率
    CLK_CKDIVR = 0x00;    //CPU时钟0分频,系统时钟0分频
}
void delay_ms( unsigned int ms )
{
    unsigned int  i, j;
    while( ms != 0 )
    {
        for( i = 0; i < 61; i++ )
            for( j = 0; j < 50; j++ );
        ms--;
    }
}
void main( void )
{
    SysClkInit();                         //时钟初始化
    __asm( "sim" );                       //禁止中断
    LED_GPIO_Init();                      //LED 初始化
    KEY_GPIO_Init();                      //KEY初始化
    __asm( "rim" );                       //开启中断
    LED = 0;
    while( 1 )
    {
        if( KEY == 0 )
        {
            delay_ms( 10 );
            if( KEY == 0 )
            {
                LED = !LED;
            }
        }
    }
}

如果实际测试中按键还会失灵,根据实际情况可以增加或者减小两次按键判断中间的延时。
当然除了软件上增加延时外,也可以给按键口增加一个滤波电容,通过电容将按键时的小毛刺滤掉。