嵌入式软件--51单片机 DAY 3

一、独立按键

按键的作用相当于一个开关,按下时接通(或断开),松开后断开(或接通)。

(1)需求

通过SW1、SW2、SW3、SW4四个独立按键分别控制LED1、LED2、LED3、LED4的亮灭,具体要求是,按一下(按下并松开)SW,LED点亮,再按一下SW,LED熄灭。

(2)硬件设计

<1>思路

为实现上述需求,需要设法令单片机感知到按键被按下,也就是说在按键被按下时,需要向单片机发送一个信号,当单片机收到该信号后,再执行控制LED的逻辑即可。

由于51单片机的GPIO引脚的默认均为高电平,因此只需将按键的一侧接入单片机的某个GPIO引脚,另一侧接地。这样一来,当按键按下时,引脚直接接地,就相当于向单片机发送了一个低电平信号。

<2>原理图

SW1 P42

SW2 P43

SW3 P32

SW4 P33

<3>软件设计

按照原理图,SW1按下时,P4.2引脚会被拉低;SW2按下时,P4.3引脚会被拉低;SW3被按下时,P3.2引脚会被拉低;SW4按下时,P3.3引脚会被拉低。

因此只需检测上述引脚是否变为低电平即可,若检测到变为低电平,就执行控制LED的逻辑。需要注意的是,按键的检测需要持续进行,所以需要不停的检查上述引脚是否变为低电平。

P42=0,SW1被按下,LED1亮 P00=0,再按下SW1,P00=1,LED1灭。独立按键一直实现低电平才能控制亮灭。

cpp 复制代码
#include <STC89C5xRC.H>
#define SW1  P42
#define SW2  P43
#define SW3  P32
#define SW4  P33
#define LED1 P00
#define LED2 P01
#define LED3 P02
#define LED4 P03

void main()
{
    while (1) { // 持续检测按键
        if (SW1 == 0) {
            LED1 = ~LED1;
        }

        if (SW2 == 0) {
            LED2 = ~LED2;
        }

        if (SW3 == 0) {
            LED3 = ~LED3;
        }

        if (SW4 == 0) {
            LED4 = ~LED4;
        }
    }
}

这个有缺陷,出现上述现象的原因是,当前代码的逻辑存在问题。当前代码的逻辑是,当按键处在按下的状态时,控制LED的逻辑会被多次触发,也就是LED会快速的闪烁,由于速度过快,人眼不能分辨出来,所以看起来LED就常亮的,但是亮度要比正常情况下低。当按键抬起后,控制LED的逻辑便不会再触发,此时LED会随机的处在按键抬起前最后一刻的状态。

要解决上述问题,就需要确保按键被按下时,控制LED的逻辑只被触发一次。

按键单次触发:

当前代码的逻辑是,只要按键处在按下的状态,就触发控制LED的逻辑。为实现单次触发,可以在按键按下后,等待按键抬起,并在抬起的一刻,执行控制LED的逻辑。

cpp 复制代码
#include <STC89C5xRC.H>
#define SW1  P42
#define SW2  P43
#define SW3  P32
#define SW4  P33
#define LED1 P00
#define LED2 P01
#define LED3 P02
#define LED4 P03

void main()
{
    while (1) { // 持续检测按键
        if (SW1 == 0) {
            while (SW1 == 0); // 等待按键抬起
            LED1 = ~LED1;
        }

        if (SW2 == 0) {
            while (SW2 == 0);
            LED2 = ~LED2;
        }

        if (SW3 == 0) {
            while (SW3 == 0);
            LED3 = ~LED3;
        }

        if (SW4 == 0) {
            while (SW4 == 0);
            LED4 = ~LED4;
        }
    }
}

上述代码已经能够实现预期效果了,但是偶尔会"失灵"。这个现象是由按键的物理特性导致的,由于按键内部的弹簧和触点具有弹性,当按键被按下或释放时,这些弹性材料会震动,从而导致触点在短时间内反复接触和断开。所以表面上我们虽然只按了一次按键,但实际上是按了多次。

了解了按键抖动的问题,也就能够解释当前的现象了。如果实际按下的次数是奇数,那么LED就会按照预期点亮或熄灭;而如果是偶数,LED最终还是会回到按下按键之前的状态,看起来就像是按键"失灵"了。

按键消抖

所以,当我们检测到信号为0时,不能莽撞的认为按键已经被按下了,而是要稍微等一段时间(约10ms),等按键稳定之后,再次检测,如果信号仍然为0,才可以确定按键确实被按下了。具体代码如下。

cpp 复制代码
#include <STC89C5xRC.H>
#include "Com_Util.h"
#define SW1 P42
#define SW2 P43
#define SW3 P32
#define SW4 P33
#define LED1 P00
#define LED2 P01
#define LED3 P02
#define LED4 P03
void main()
{
    while (1)
    {
        if (SW1==0)
        {
            Com_Util_Delay1ms(10);
            if(SW1==0){
            while (SW1==0);
           LED1=~LED1;
           }
        }
         if (SW2==0)
        {
            Com_Util_Delay1ms(10);
            if(SW2==0){
            while (SW2==0);
           LED2=~LED2;
           }
        }
         if (SW3==0)
        {
            Com_Util_Delay1ms(10);
            if(SW3==0){
            while (SW3==0);
           LED3=~LED3;
           }
        }
         if (SW4==0)
        {
            Com_Util_Delay1ms(10);
            if(SW4==0){
            while (SW4==0);
           LED4=~LED4;
           }
        }
        
    }
    
    while (1) {
    }
}

<4>规范代码

我们将检测按键的代码单独抽取到Int层

Int_Key.c

cpp 复制代码
#include "Int_Key.h"
#define SW1 P42
#define SW2 P43
#define SW3 P32
#define SW4 P33

bit Int_Key_IsSW1Pressed()
{
 if (SW1 == 0) {
  Com_Util_Delay1ms(10); // 延时消抖
  if (SW1 == 0) {
   return 1;
  }
 }
 return 0;
}

bit Int_Key_IsSW2Pressed()
{
 if (SW2 == 0) {
  Com_Util_Delay1ms(10); // 延时消抖
  if (SW2 == 0) {
   while (SW2 == 0); // 等待按键抬起
   return 1;
  }
 }
 return 0;
}

bit Int_Key_IsSW3Pressed()
{
 if (SW3 == 0) {
  Com_Util_Delay1ms(10); // 延时消抖
  if (SW3 == 0) {
   return 1;
  }
 }
 return 0;
}

bit Int_Key_IsSW4Pressed()
{
 if (SW4 == 0) {
   Com_Util_Delay1ms(10); // 延时消抖
 if (SW4 == 0) {
  while (SW4 == 0); // 等待按键抬起
  return 1;
 }
  }
 return 0;
}

Int_Key.h

cpp 复制代码
#ifndef __INT_KEY_H__
#define __INT_KEY_H__

#include <STC89C5xRC.H>
#include "Com_Util.h"
//检查按键SW1是否放下
//bit是否
bit Int_Key_IsSW1Pressed();

bit Int_Key_IsSW2Pressed();

bit Int_Key_IsSW3Pressed();

bit Int_Key_IsSW4Pressed();
#endif /* __INT_KEY_H__ */

main.c

cpp 复制代码
#include "Int_Key.h"
#define LED1 P00
#define LED2 P01
#define LED3 P02
#define LED4 P03
void main()
{
    while (1) {
 if (Int_Key_IsSW1Pressed()) {
  LED1 = ~LED1;
 }
 if (Int_Key_IsSW2Pressed()) {
  LED2 = ~LED2;
 }
 if (Int_Key_IsSW3Pressed()) {
  LED3 = ~LED3;
 }
 if (Int_Key_IsSW4Pressed()) {
  LED4 = ~LED4;
 }
 }
}

二、矩阵按键

1.需求

按下按键矩阵中的SW5到SW20按键后,数码管显示对应的按键编号。

2.硬件设计

由于按键矩阵中共有4x4=16个按键,如果每个按键都接入一个GPIO引脚,势必会造成引脚的浪费,为了节省引脚,我们同样可以借用动态扫描的思想,具体逻辑如下。

扫描流程:

将第一行进行检测,置为0接地。只要p24、p25、p26、p27.谁变成低电平,就是那个被按了。

故,第二行也是如此。这四行只能有一行接地,才能知道哪个按了。之后三四行依次接地。

选中哪一行,哪一行的引脚都会成为低电平,按下改行的按钮,该列的引脚电平会拉低。

行:P20 P21 P22 P23

列:P24 P25 P26 P27 默认都是高电平,只有按下按钮导通才会拉低电平。

3.软件设计

需求:按下SW5--SW20,数码管显示对应的编号。

检查是否被按下,函数返回编号,编号被数码管函数调用刷新显示。

Int_KeyMatrix.h

cpp 复制代码
#ifndef __INT_KEYMATRIX_H__
#define __INT_KEYMATRIX_H__
#include "Com_Util.h"
//检测按键是否被按下,如果被按下就返回,否则返回0
u8 Int_KeyMatrix_CheckSW();


#endif /* __INT_KEYMATRIX_H__ */

Int_KeyMatrix.c

cpp 复制代码
#include "Int_KeyMatrix.h"
#include <STC89C5xRC.H>
u8 Int_KeyMatrix_CheckSW()
{
    //检测第一行
    P2=0XFE;//1111 1110
    if (P24==0)
    {
      Com_Util_Delay1ms(10);
      if(P24==0)
      {
        while (P24=0);
       return 5; 
      }
    }
    if (P25==0)
    {
      Com_Util_Delay1ms(10);
      if(P25==0)
      {
        while (P25=0);
       return 6; 
      }
    }
    if (P26==0)
    {
      Com_Util_Delay1ms(10);
      if(P26==0)
      {
        while (P26=0);
       return 7; 
      }
    }
    if (P27==0)
    {
      Com_Util_Delay1ms(10);
      if(P27==0)
      {
        while (P27=0);
       return 8; 
      }
    }
    //检测第二行
    P2=0xFD;   //1111 1101
     if (P24==0)
    {
      Com_Util_Delay1ms(10);
      if(P24==0)
      {
        while (P24=0);
       return 9; 
      }
    }
    if (P25==0)
    {
      Com_Util_Delay1ms(10);
      if(P25==0)
      {
        while (P25=0);
       return 10; 
      }
    }
    if (P26==0)
    {
      Com_Util_Delay1ms(10);
      if(P26==0)
      {
        while (P26=0);
       return 11; 
      }
    }
    if (P27==0)
    {
      Com_Util_Delay1ms(10);
      if(P27==0)
      {
        while (P27=0);
       return 12; 
      }
    }
    //检测第三行
    P2=0XFB;  //1111 1011
     if (P24==0)
    {
      Com_Util_Delay1ms(10);
      if(P24==0)
      {
        while (P24=0);
       return 13; 
      }
    }
    if (P25==0)
    {
      Com_Util_Delay1ms(10);
      if(P25==0)
      {
        while (P25=0);
       return 14; 
      }
    }
    if (P26==0)
    {
      Com_Util_Delay1ms(10);
      if(P26==0)
      {
        while (P26=0);
       return 15; 
      }
    }
    if (P27==0)
    {
      Com_Util_Delay1ms(10);
      if(P27==0)
      {
        while (P27=0);
       return 16; 
      }
    }
    //检测第四行
    P2=0XF7; //1111 0111
     if (P24==0)
    {
      Com_Util_Delay1ms(10);
      if(P24==0)
      {
        while (P24=0);
       return 17; 
      }
    }
    if (P25==0)
    {
      Com_Util_Delay1ms(10);
      if(P25==0)
      {
        while (P25=0);
       return 18; 
      }
    }
    if (P26==0)
    {
      Com_Util_Delay1ms(10);
      if(P26==0)
      {
        while (P26=0);
       return 19; 
      }
    }
    if (P27==0)
    {
      Com_Util_Delay1ms(10);
      if(P27==0)
      {
        while (P27=0);
       return 20; 
      }
}
}

Int_DigitalTube.h

cpp 复制代码
#ifndef __INT_DIGITALTUBE_H__
#define __INT_DIGITALTUBE_H__
#define SMG_EN P36
#define LED_EN P34

#include "Com_Util.h"

void Int_DigitalTube_Init();
void Int_DigitalTube_DisplayNum(u32 num);
void Int_DigitalTube_Refresh();

#endif /* __INT_DIGITALTUBE_H__ */

Int_DigitalTube.c

cpp 复制代码
#include "Int_DigitalTube.h"
#include <STC89C5xRC.H>

// 数字0-9的编码
static u8 s_codes[10] = {
    0x3F, // 0
    0x06, // 1
    0x5B, // 2
    0x4F, // 3
    0x66, // 4
    0x6D, // 5
    0x7D, // 6
    0x07, // 7
    0x7F, // 8
    0x6F  // 9
};

static u8 s_buffer[8];

void Int_DigitalTube_Init()
{
    SMG_EN = 0;
    LED_EN = 0;
}

/**
 * @brief 内部方法,让数码管某一位显示特定数字
 *
 * @param position 片选, 从左到右[0-7]
 * @param num_code 显示想要的数字编码
 */
static void Int_DigitalTube_DisplaySingle(u8 position, u8 num_code)
{
    P0 = 0x00;
    // 位选:P15 P14 P13
    position <<= 3;
    P1 &= 0xC7;
    P1 |= position;

    // 段选:P0
    P0 = num_code;
}

void Int_DigitalTube_DisplayNum(u32 num)
{
    u8 i;
    for (i = 0; i < 8; i++) {
        s_buffer[i] = 0x00;
    }

    if (num == 0) {
        s_buffer[7] = s_codes[0];
        return;
    }

    i = 7;
    while (num > 0) {
        s_buffer[i] = s_codes[num % 10];
        num /= 10;
        i--;
    }
}

void Int_DigitalTube_Refresh()
{
    u8 i;
    for (i = 0; i < 8; i++) {
        Int_DigitalTube_DisplaySingle(i, s_buffer[i]);
        Com_Util_Delay1ms(1);
    }
}

main.c

cpp 复制代码
#include "Int_KeyMatrix.h"
#include "Int_DigitalTube.h"
void main()
{
    u8 key;
    Int_DigitalTube_Init();
    while (1)
    {       
        key=Int_KeyMatrix_CheckSW();
        if (key)
        {
          Int_DigitalTube_DisplayNum(key);
        }
        Int_DigitalTube_Refresh();
        
    }
    
}

4.按键阻塞问题

按下按键不松手的时候,扫描停了,数码管数字只显示最后一位,而且非常亮。

阻塞到了检测步骤,所以最后一个亮,但不扫描了,所以这样了。

5.改造矩阵按键代码

只需改造Int_KeyMatrix.c

cpp 复制代码
#include "Int_KeyMatrix.h"
#include <STC89C5xRC.H>
u8 Int_KeyMatrix_CheckSW()
{
  u8 i;
  u8 lines[4]={0xFE,0XFD,0XFB,0XF7};
  for ( i = 0; i < 4; i++)
  {
    P2=lines[i];
    if (P24==0)
    {
      Com_Util_Delay1ms(10);
      if(P24==0)
      {
        while (P24=0);
       return 5+4*i; 
      }
    }
    if (P25==0)
    {
      Com_Util_Delay1ms(10);
      if(P25==0)
      {
        while (P25=0);
       return 6+4*i; 
      }
    }
    if (P26==0)
    {
      Com_Util_Delay1ms(10);
      if(P26==0)
      {
        while (P26=0);
       return 7+4*i; 
      }
    }
    if (P27==0)
    {
      Com_Util_Delay1ms(10);
      if(P27==0)
      {
        while (P27=0);
       return 8+4*i; 
      }
    }
  }
}
  

把代表行的值写入数组,for循环遍历数组,循环每一行,找到i与列数值上的变化规律。

6.作业1:数码管分四组,独立按键分别与其计数

相关推荐
蓝斯49711 小时前
从.到实现一个Windows上的虚拟hid键盘设备
windows·单片机·计算机外设
熙芯XiChip11 小时前
硬实时与软实时RTOS核心区别
嵌入式硬件
伟大的前程11 小时前
unsigned int 定义的变量 在 电脑的 64 位系统中 和 32 位 cortex-m0 和 8 位的 51 单片机
单片机·嵌入式硬件
小渔村的拉线工12 小时前
1.HPM6E80 解析芯片整体工作原理和存储架构
单片机·嵌入式硬件·mcu·架构·hpm6e80
熙芯XiChip13 小时前
CAN与CANopen的本质区别
单片机
lilihewo13 小时前
Altium Designer 封装库的创建(分享pcb 3D封装库)
嵌入式硬件·51单片机
神罚天下ET14 小时前
典型arm位单片机启动流程(从上电到main.c)
c语言·arm开发·单片机
碧海银沙音频科技研究院15 小时前
蓝牙耳机使用WSDF5361锂保芯片进入船运模式实现方法
嵌入式硬件·算法
Zevalin爱灰灰16 小时前
模拟信号链电路——基于TL431的电压基准电路
单片机·嵌入式硬件
csdn杰哥16 小时前
瑞萨单片机开发教程[二十七] DTC 外部中断触发传输实验
单片机·嵌入式硬件·mongodb·瑞萨·ra6m5