PWN 按钮调光

本文最后更新于:2021年10月13日 下午

任务内容:
两个按键A、B,A 按下小灯变亮一点,B 按下小灯变暗一点。

知识准备

· 按键防抖

​ 机械式开关在切换过程中,电子信号并非立即从 0 变成 1( 或从 1 变成 0 ),而会经过短暂的,像下图一样忽高忽低变化的弹跳现象。虽然弹跳动作的时间非常短暂,但微电脑仍将读取到连续变化的开关信号,导致程序误操作。

信号弹跳问题

​ 为了避免上述状况,读取机械式开关信号时,程序(或者硬件)需要加入所谓的**消除弹跳 ( de-bouncing ) **处理机制。最简单的方式,就是在发现输入信号变化时,先暂停 10~30毫秒,然后再读取一次,以便确定输入值。

代码示例:

​ 如下图所示,在 “单击” 操作中,信号改变了两次。

one click

​ 那么我们可以声明一个 click 的变量,记录信号改变的次数,每当此变量值为 2 ,代表按了一下按钮。具备 “ 过滤 ” 弹跳信号的开关代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const byte LED = 13;		// LED 的脚位
const byte SW = 2; //开关脚位
boolean lastState = LOW; //记录上次开关状态,预设为 LOW
boolean toggle = LOW; //输出给 LED 的信号,预设为 LOW
byte click = 0; //开关信号的改变次数,预设为 0

void setup() {
pinMode(LED, OUTPUT);
pinMode(SW, INPUT);
lastState = digitalRead(SW); //读取开关的初始值
}

void loop() {
boolean b1 = digitalRead(SW); //读取目前开关的值

if (b1 != lastState) { //如果和之前的开关值不同
delay(20); //等待 20 毫秒
boolean b2 = digitalRead(SW); //再读取一次开关值

if (b1 == b2) { //确认两次开关值是否一致
lastState = b1; //储存开关状态
click++; //增加信号变化次数
}
}

if (click == 2) { //如果开关状态改变两次
click = 0; //状态次数归零
toggle = !toggle; //取反
digitalWrite(LED, toggle); //输出
}
}

​ loop() 区块不停地读取开关的值,并且对比开关的信号是否和上一次不同。假如监测到开关的信号改变了,要等待 20 毫秒之后,再确认一次开关值。如果等待20毫秒后读取到的开关信号值和上一次读取到的一致,就确认开关的状态真的改变了。

· 开关的接法(上拉电阻 & 下拉电阻)

​ Arduino 的所有数字模拟引脚都能读取 / 输出 0 与 1 信号。只要输入值超过电源电压的一半,就代表高电压高电位;若输入值低于 0.25V,则代表低电位

这样的开关接法并不正确:

image-20210205152239449

​ 若没有按下开关,Arduino 的引脚既没接地接地,也为接到高电位。输入信号可能在 0 与 1 之间的模糊地带漂移,造成所谓的浮动信号,Arduino 将无法正确判断输入值。

​ 正确接法如下:

下拉电阻正确接法

​ 若开关没有被按下,数字第 2 脚将通过 10kΩ 接地,因而读取到低电位值;按下开关时,5V 电源将流入第 2 脚,产生高电位。如果没有 10kΩ 电阻,按下开关时,正电源将和接地直接相连,造成短路。

​ 像上图一样,在芯片的脚位连接一个电阻再接地,则此电阻称为下拉电阻

​ 当然也有上拉电阻,即将电阻接到电源,像下图这样:

上拉电阻接法

设计方案

image-20210205214735487

材料清单

材料 数量
Arduino Uno 1
面包板 1
1kΩ电阻 1
10kΩ电阻 2
LED 1
开关 2
导线 若干

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#define LED 9
#define BTN1 2
#define BTN2 3
boolean lastState1 = LOW;
boolean lastState2 = LOW;
byte click1 = 0;
byte click2 = 0;
int ledV = 0;

void setup()
{
pinMode(LED, OUTPUT);
pinMode(BTN1, INPUT);
pinMode(BTN2, INPUT);
lastState1 = digitalRead(BTN1);
lastState2 = digitalRead(BTN2);
analogWrite(LED, 255);
delay(50);
analogWrite(LED, 0);
delay(50);
analogWrite(LED, 255);
delay(50);
analogWrite(LED, 0);
}

void loop()
{
boolean a1 = digitalRead(BTN1);
boolean b1 = digitalRead(BTN2);

if (a1 != lastState1)
{
delay(50);
boolean a2 = digitalRead(BTN1);

if (a1 == a2)
{
lastState1 = a1;
click1++;
}
}

if (b1 != lastState2)
{
delay(50);
boolean b2 = digitalRead(BTN2);

if (b1 == b2)
{
lastState2 = b1;
click2++;
}
}

if (click1 == 2)
{
click1 = 0;
click2 = 0;
if (ledV == 255)
{
analogWrite(LED, 0);
delay(50);
analogWrite(LED, 255);
delay(50);
analogWrite(LED, 0);
delay(50);
analogWrite(LED, 255);
}
else
{
ledV += 30;
if (ledV >= 255)
{
ledV = 255;
}
analogWrite(LED, ledV);
if (ledV == 255)
{
analogWrite(LED, 0);
delay(50);
analogWrite(LED, 255);
delay(50);
analogWrite(LED, 0);
delay(50);
analogWrite(LED, 255);
}
}
}

if (click2 == 2)
{
click1 = 0;
click2 = 0;
if (ledV == 0)
{
analogWrite(LED, 255);
delay(50);
analogWrite(LED, 0);
delay(50);
analogWrite(LED, 255);
delay(50);
analogWrite(LED, 0);
}
else
{
ledV -= 30;
if (ledV <= 0)
{
ledV = 0;
}
analogWrite(LED, ledV);
if (ledV == 0)
{
analogWrite(LED, 255);
delay(50);
analogWrite(LED, 0);
delay(50);
analogWrite(LED, 255);
delay(50);
analogWrite(LED, 0);
}
}
}
}

成果展示


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!