# 三、使用按钮

时不时地，你会想要与用户互动。与用户交互最常见的元素是按钮。现在电子产品中最常见的按钮是按钮。该按钮不会持续关闭电路，但只会持续按下。使用按钮最基本的例子是按下按钮时打开发光二极管。我将在下一节对此进行描述。

## 按钮

这个例子是在使用按钮时的 Hello World。基本上它所做的是当一个按钮被按下时，它打开发光二极管，然后一旦按钮被释放，它就关闭。这是零件清单和接线:

本节零件清单:

*   Arduino 一号
*   按钮
*   USB 电缆
*   1 个 100 欧姆的发光二极管电阻器(引脚 13)
*   1 个 10k 欧姆的按钮电阻器
*   面包板
*   1 个 5 毫米发光二极管
*   5x 面包板跳线

![](img/image028.png)

图 27:按钮接线模式

这个例子的代码非常简单。我们不断从引脚 2 读取状态，如果它处于高电平状态，我们会打开发光二极管。请注意，我们将发光二极管连接到引脚 13，以便板载发光二极管也会发光。下面是代码:

```c
    // variable holding button state
    int buttonState = 0;

    // LED pin
    int ledPin = 13;

    // Button pin
    int buttonPin = 2;

    void setup() {
      // initialize pins
      pinMode(ledPin, OUTPUT);
      pinMode(buttonPin, INPUT);    
    }

    void loop(){
      // read the state of the button
      buttonState = digitalRead(buttonPin);

      // pressed button is in state high
      if (buttonState == HIGH) {    
        // LED on   
        digitalWrite(ledPin, HIGH); 
      }
      else {
        // LED off
        digitalWrite(ledPin, LOW);
      }
    }

```

在本例中，我们将首次读取引脚输入。我们用`digitalRead`功能来做。检查读数的循环比人眼能注意到的要快得多，所以我们没有注意到按钮的有趣效果。我们看到发光二极管立即打开，当按钮最初被按下时，我们无法感知任何振荡。此外，我们在释放按钮后没有看到指示灯闪烁一会儿。让我们看看下一个例子，深入了解这个有趣的效果。

## 古怪的按钮

本节的接线和零件与上一节相同(请查看这些示意图)。区别在于编程和使用。例如，在使用按钮时，打开和关闭发光二极管并不是最有用的情况。当涉及微控制器时，通常是某种状态的改变。所以我们按下按钮的时候会改变 LED 的状态。为此，我们将使用以下代码:

```c
    // variable holding button state
    int buttonState;

    // variable holding previous button state
    int previousButtonState = LOW;

    // variable holding the LED state
    int ledState = HIGH;

    // LED pin
    int ledPin = 13;
    // Button pin
    int buttonPin = 2;

    void setup() {
      // initialize pins
      pinMode(ledPin, OUTPUT);
      pinMode(buttonPin, INPUT);
    }

    void loop(){
      // read the state of the button
      int reading = digitalRead(buttonPin);

      // if the button is pressed, change the state
      if (reading != buttonState) {
        buttonState = reading;

          // toggle the LED state only when the button is pushed
          if (buttonState == HIGH) {
            ledState = !ledState;
          }
      }

      // turn the LED on or off depending on the state 
      digitalWrite(ledPin, ledState);

      // save current reading so that we can compare in the next loop
      previousButtonState = reading;
    }

```

现在，试着玩一下这个按钮。如果你这样做了，你会注意到它的行为不像你预期的那样；它有时会错过按键，表现得有点古怪。你会注意到灯一直亮着，即使它不应该亮，反之亦然。虽然我们有`digitalRead`功能，但这里有一个有趣的模拟效应在起作用。

开关由金属零件制成，按下按钮就可以将它们连接在一起。这个动作会产生一个运动，开关内部的金属表面会弹开，然后再结合几次(在很短的时间内)。这是一种已知的效果，例如，当打开和关闭灯泡时，它不代表问题。在我们的第一个例子中，只要按钮被按下，发光二极管就一直亮着，这并不代表问题。但是，当涉及到逻辑电路时，我们可以在短时间内快速读取变化，我们不能确定按钮是否被按下。

我们来看下图。本质上，峰值的每一次变化都会导致发光二极管状态的变化。因此，使用这个按钮而不进行某种噪声过滤，就相当于玩彩票。当你松开或按下按钮时，你不会确定发光二极管是打开还是关闭。这里有一个图，显示了在短时间按下按钮时电流的变化。记住，尖峰的每一次变化都是 LED 状态的变化:

![](img/image029.png)

图 28:短按按钮后释放时的电量读数

人眼不会察觉到每一个变化，但我们会在按键结束时注意到错误的状态变化。在下一节中，我们将实现一种过滤技术来避免这种奇怪的行为。电路和零件将保持与本节相同。

## 带噪声过滤软件的按钮

零件列表和接线与上一示例中的相同。区别在于过滤按钮噪音的软件:

```c
    // variable holding the button state
    int buttonState;

    // variable holding previous button state
    int previousButtonState = LOW;

    // variable holding the LED state
    int ledState = HIGH;

    int buttonPin = 2;
    const int ledPin = 13;

    // millis always stored in long
    // becomes too big for int after 32,767 millis or around 32 seconds

    // timestamp of a previous bounce
    long previousDebounceTime = 0;

    // debounce time in millis, if the LED is still quirky increase this value
    long debounceDelay = 50;

    void setup() {
      pinMode(buttonPin, INPUT);
      pinMode(ledPin, OUTPUT);
    }

    void loop() {
      // for now it's the same as in previous example
      int reading = digitalRead(buttonPin);

      // if the previous reading is different than the previous button state 
      if (reading != previousButtonState) {
        // we'lll reset the debounce timer
        previousDebounceTime = millis();
      }

      // if the reading is the same over a period of debounceDelay
      if ((millis() - previousDebounceTime) > debounceDelay) {
        // check if the button state is changed
        if (reading != buttonState) {
          buttonState = reading;

          // toggle the LED state only when the button is pushed
          if (buttonState == HIGH) {
            ledState = !ledState;
          }
        }
      }

      // set the LED:
      digitalWrite(ledPin, ledState);

      // save current reading so that we can compare in the next loop
      previousButtonState = reading;
    }

```

现在，对一些人来说，代码清单可能看起来有点太复杂了，这是完全可以理解的。有一个巧妙的硬件技巧，我们可以用来过滤噪音。我们可以使用一种叫做电容器的电子元件。

## 带噪声过滤硬件的按钮

我们现在将使用几乎相同的组件和接线，就像我们使用古怪和基本的按钮示例一样。为了滤除噪声，我们将在电路中增加一个电容。

本节零件清单:

*   Arduino 一号
*   按钮
*   USB 电缆
*   1 个 100 欧姆的发光二极管电阻器
*   1 个 10k 欧姆的按钮电阻器
*   面包板
*   1 个 5 毫米发光二极管
*   5x 面包板跳线
*   10uF 电容器

![](img/image030.png)

图 29:带电容过滤噪声的按钮

这个程序和古怪的按钮是一样的。噪声滤波分配由电容决定。电容器有一个有趣的特点，只要不充满电，它就能让电流通过。当它被填满时，电流不再流过它。但是，如果一些波动开始发生，它会立即开始释放积累的电流(基本上平滑了信号)。实际上，过滤电噪声是电容器的主要用途之一。有各种类型和尺寸的电容器。以下是一些基本的电容形式:

![](img/image031.png)

图 30:基本电容形式

有些类型的电容，其接线方式并不重要。对某些人来说，连接正极和负极引脚时必须小心。在电容上，正极引脚标有加号，负极引脚标有“0”号。因此，没有像发光二极管那样找出引脚极性的技巧。也有一些类型的电容器具有较长的引脚，该引脚是正引脚；这与发光二极管的规则相同。但是电容器上的正极或负极引脚需要有清晰的标记。

在这一节中，我介绍了按钮组件以及它们如何与 Arduino 结合使用。我还展示了一些鲜为人知的古怪按钮，以及如何在软件和硬件层面处理它。在下一节中，我将向您展示如何使用 Arduino 来产生声音信号。