等级考试四级学习小组 加入小组

288个成员 29个话题 创建时间:2019-02-21

按键控制灯,按一次灯的状态切换一次的程序案例

发表于04-03 160次查看

在使用按键控制LED亮灭时,按键按下灯亮,不按下,灯熄灭,这样的任务比较直接和简单,也很容易实现。实现代码如下:

int keyD1 = 6;//按键开关接D1端口
int ledD2 = 10;//LED接D2端口
void setup() {
  pinMode(keyD1,INPUT);
  pinMode(ledD2,OUTPUT);
  Serial.begin(9600);
}
void loop() {
  int keyState = digitalRead(keyD1);//用按键状态变量接收按键输入值
  //Serial.println(keyState);
  if(keyState == 1){
    digitalWrite(ledD2,HIGH);
  } else {
    digitalWrite(ledD2,HIGH);
  }
}

但是很多老师和学员都有按键按一次,灯就切换一次状态的想法。也就是按一次按键松开后,灯由灭变亮,然后再按一次按键后,灯由亮变灭,往复循环。这个任务如何实现呢?

我们可以用一个变量来表示灯的状态,比如设一个light变量表示灯的状态,light受按键的控制,按一次light的状态切换一次。就可以了。light怎样受按键来切换呢?还是可以用if语句。如果按下按键,light自加。但是light的值要保障0和1两个状态,可以再嵌套如果为2的时候,light的值就变为0,就可以。示例程序如下:

int keyD1 = 6;
int ledD2 = 10;
int light = 0;//灯的状态
void setup() {
  pinMode(keyD1,INPUT);
  pinMode(ledD2,OUTPUT);
}
void loop() {
  if(digitalRead(keyD1)){
    light++;
    if(light == 2){
      light =0;
    }
  }
  if(light == 1){
    digitalWrite(ledD2,HIGH);
  }else{
    digitalWrite(ledD2,LOW);
  }
}

这个程序在上传到主控上执行时,发现按键并不灵敏,有时候按的时候会切换,有时候按的时候不切换,是什么原因呢?

这就要分析一下按一次按键这个动作,这个动作实际上包含了按键按下和松开按键两个动作。 刚才的程序,我们只是确认了按键按下,变量light自加,并没有注意到按键松开的这个动作,要考虑到按键按下去后,再松开,变量light才开始自加。怎样判断按键由按下变为松开呢?有两种思路,一种是只要你按着按键不松开,我的程序就一直等待。另外一种思路是用if去判断按键是否松开了。

先来看一直等待的思路,一直等待在编程中,我们很容易想到while循环,只要条件是真的时候,循环就一直执行。但是这个条件应该是只要按键没有松开就等待。按键没有松开就是按键输入值是1,所以等待的这段代码应该是:

while(digitalRead(keyD1)){
      continue;
    }

这段代码应该放到按键按下之后,light自加之前。所以整个代码就变为:

int keyD1 = 6;
int ledD2 = 10;
int light = 0;
void setup() {
  pinMode(keyD1,INPUT);
  pinMode(ledD2,OUTPUT);
}

void loop() {
  if(digitalRead(keyD1)){
    while(digitalRead(keyD1)){
      continue;
    }
    light++;
    if(light == 2){
      light =0;
    }
  }
  if(light == 1){
    digitalWrite(ledD2,HIGH);
  }else{
    digitalWrite(ledD2,LOW);
  }

这样再试一下,发现比原来要好一些,但是还是偶尔切换不过来,原因是什么?原因是:通常的按键所用开关为机械弹性开关。由于机械触电的弹性作用,按键在闭合及断开的瞬间均伴随有一连串的抖动。键抖动会引起一次按键被误读多次。这个抖动虽然很短,但是依然会影响主控对信号输入的判断。一般情况下这个抖动的时间为5ms到10ms之间。为了避免这个问题。我们可以在while循环前加一个10到20ms的延时,来消除这个抖动。

最终程序就变为:

int keyD1 = 6;
int ledD2 = 10;
int light = 0;
void setup() {
  pinMode(keyD1,INPUT);
  pinMode(ledD2,OUTPUT);
}
void loop() {
  if(digitalRead(keyD1)){
    delay(20);//消抖延时
    while(digitalRead(keyD1)){
      continue;
    }
    light++;
    if(light == 2){
      light =0;
    }
  }
  if(light == 1){
    digitalWrite(ledD2,HIGH);
  }else{
    digitalWrite(ledD2,LOW);
  }

现在再试试,是不是更好一些了。

刚才还提到了一个思路是,不用循环等待来判断按键是否松开了,用if语句来判断。如果按键松开了,那么light变量自加。那就是if(!digitalRead(keyD1)){light变量自加},综合之前的消抖延时,程序也可以有如下写法:

int keyD1 = 6;
int ledD2 = 10;
int light = 0;
void setup() {
  pinMode(keyD1,INPUT);
  pinMode(ledD2,OUTPUT);
  Serial.begin(9600);
}
void loop() {
  if(digitalRead(keyD1)){
    delay(20);
    if(!digitalRead(keyD1)){
      light++;
      if(light == 2){
        light = 0;
      }
    }
  }
  if(light == 1){
    digitalWrite(ledD2,HIGH);
  }else{
    digitalWrite(ledD2,LOW);
  }
}

上传到主控上去,试试,是不是感觉很完美,程序在判断按键按一次的时候用了if的三层嵌套。如果能自己写出来,说明你真的学会了if语句了。

看上去貌似很简单的一个任务,还是蕴含着很多的编程知识和数学知识在里面。是不是很有挑战性?

不过在电子学会的机器人技术等级考试里,按键消抖应该是五级的内容。对四级的小朋友来说还是有点儿难了。

如果看文字理解起来有些费劲,可以到等级考试四级课程里去看视频内容http://teach.zmdou.cn/my/course/137,可能会更容易理解一些。

发表回复
你还没有登录,请先 登录或 注册!