【小常识】从按钮开关看上拉pull-up电阻、下拉电阻

啥是上拉(pull-up)电阻和下拉(pull-down)电阻 ?

在用 Arduino 做实验时, 按钮开关(Button/Switch)是很常见的应用,
然后你常常看到文件说要接个电阻, 又看到"上拉电阻"或"下拉电阻"!
电阻(resistor),不论"上拉电阻"或"下拉电阻", 它们一定是连接到你要读取的输入 pin,
电阻另一端如果是连接到 + 或 VCC 那就叫做上拉(pull-up)电阻; 反之, 电阻如果另一端是连接到 - 或 GND 那就叫做下拉(pull-down)电阻!
就这样?
当然就只是连接方式不同啊, 阿不然你认为还要怎样?

把 pin 连接电阻再连接到 + 或 VCC, 则 pin 脚空接时会是在 HIGH, 所以称之为上拉(pull-up); 反之, 把 pin 连到电阻再接到 - GND 则空接时将是 LOW, 因此就称之为下拉(pull-down).

为何要接个电阻?
这是因为当 MCU 的 GPIO(全称General-Purpose Input /Output Ports,中文意思是通用I/O端口。), 某 pin 被设定为 INPUT mode 时, 是在输入高阻抗(input impedance)状态, 意思是相当于另一端串接了一个超级大的电阻挡住, 那意味着 pin 在空接时就等于没有连接到任何电路, 此时用digitalRead(pin)去读取它则常常因受到环境噪声的影响, 有时读取到 HIGH 有时却读取到 LOW, 就是所谓的 floating 状态!

不过请注意, 以上只是比喻, pin 内部另一端并不是真的串接电阻,事实上是串接一个 MOSFET(MOS Field Effect Transistor)的电子闸(Gate),MOSFET 中文是金属氧化物半导体场效应管, 这已经超出Arduino课程范围),即使 这样按钮没按下也可能被误判为按下 !? 为了确保它在稳定的状态, 必须接个上拉电阻或下拉电阻! 还有还有, 如果不接个电阻, 当你按下按钮,就会把 VCC 直接连接到 GND, 那样就短路(Short) 啰 !

那到底要用上拉(pull-up)? 还是要用下拉(pull-down)?

答案是都可以! 不过要注意这时你写的程序码会有所不同 ! 通常建议是用上拉(pull-up)方式,
(其实那是对早期 IC 才比较建议用上拉方式, 现在真的没差别!)
也就是电阻一端连接 pin,电阻另一端连接 + 或 VCC, 这时按钮开关当然是一端接到 pin,另一端接到 GND;
要注意, 这样在按钮开关没按下时读取 pin 结果是 HIGH, 当按钮按下之时读取 pin 则会是 LOW 的状态 !

如果你是采用下拉(pull-down)方式连接, 就是说, 把电阻一端连接 pin, 电阻另一端连接 - 或 GND, (此时按钮开关当然是一端接到 pin, 另一端接到 + VCC);
这样在按钮开关没有按下时, 读取 pin 答案是 LOW, 当按下按钮时, 读取 pin 则会得到 HIGH 的状态 !

那到底要用几Ω(Ohm)的电阻来上拉或下拉 ?
在 Arduino 官网上是建议用 10KΩ的电阻: http://arduino.cc/en/Tutorial/DigitalPins

其实一般来说, 10K 到 100K之间都是很好的选择,
现在用上拉电阻(连接到 + VCC)来解说, 显然在该 pin 空接时应该是 HIGH, 可是, 如果上拉的电阻太大, 则空接时将因电阻太大使得 pin 的电压太小, 通常电压太小(小于一半又小一些以下)就会被误判为 LOW;
反之, 如果上拉电阻太小, 那按钮按下时就相当于 VCC 直接连到 GND, 也就是相当于短路(Short) !!

使用内部上拉电阻(Internal pull-up)最方便 !

Arduino 的设计者考虑 GPIO pin 可能拿来做数字输入(Digital Input), 所以每支 pin 内部都已经内建了一个 20KΩ的上拉(Pull-up)电阻, 你只要这样:
pinMode(pin, INPUT_PULLUP);
这样就可以启动内建的上拉电阻, 省去外接一个上拉电阻的麻烦 😃
此时, 如果要测试按钮, 则只要拿一条杜邦线或任意的导线从 GND 拉出然后碰触 pin, 这样就等于按下按钮, digialRead(pin) 就会读到 LOW 的值.

请注意, 网络上有时会看到说以下两句也可以:

   pinMode(pin, INPUT);           // set pin to input
   digitalWrite(pin, HIGH);       // turn on pullup resistors

确实是可以这样做没错! (现在版本仍可以) , 但是, 那是在 Arduino 1.0.1 版本以前才需要, 现在版本 pinMode( )INPUT_PULLUP 就可以!!

有时甚至会看到有人只写了digitalWrite(pin, HIGH);pinMode(pin, INPUT); 都没写, 那样也是可以的, 这是因为 Arduino 启动时已经把全部 GPIO 的pin设定为 INPUT, 也就是说, 预设 (default, 默认)就是 INPUT 的 mode.

既然 Arduino 开机之后, 预设(Default; 默认)所有的 GPIO pin 都是在 INPUT 的 pinMode, 所以, 如果你想要使用某 pin 做 OUTPUT 输出, 但是如果忘了设定 pinMode(pin, OUTPUT);然后就直接digitalWrite(pin, HIGH);则此时其实相当于做pinMode(pin, INPUT_PULLUP);
这时该 pin 如果连接 LED又串接个电阻(通常这样你想digitalWrite( )送出 HIGH 不是吗 ?), 则其电压将会大约是 1.7 Volt, 不是你想要的 5 Volt, 因此, 该 pin 所连接的 LED 将会有点暗暗的不够亮 ! (注意在 VCC 为 5 Volt 环境下, 3 Volt 以下将被 digitalRead( )判定为 LOW.)

最后要注意的是, pin 13 不适合使用内部上拉电阻设定pin 13做输入; 虽然 Arduino 所有的 pin, 包括 pin 0 到 pin 13, 以及 A0 到 A5, 都可以用digitalWrite(pin, HIGH_or_LOW)做数字输出; 做输入analogRead(pin)的 是 GPIO pin 0 到 pin13;
可是可是, 因为 pin 13 出厂就串接一个 LED 灯又串接一个电阻, 所以如果你使用 pinMode(13, INPUT_PULLUP);则连接按钮后, digitalRead(13)将不论如何读都是 LOW的值 ! 因为即使按钮按下, pin 13 大约也只有 1.7 Volt, 不是你想要的 5 Volt电压! 而通常在电源 5Volt 情况下, 低于 3Volt 是被认定为 0 的 !
参考: http://arduino.cc/en/Reference/Constants

如果你坚持一定要用 pin 13 当作输入, 请设定 pinMode(13, INPUT); 然后使用外接的上拉电阻或下拉电阻 !

参考:
http://arduino.cc/en/Tutorial/DigitalPins
http://www.atmel.com/images/doc0856.pdf
http://www.atmel.com/dyn/resources/prod_documents/DOC2559.PDF

按钮去抖

使用上拉(Pull-up)电阻或是下拉(Pull-down)电阻, 跟去抖动(debouncing)一点关系也没有喔 !

加个上拉电阻或下拉电阻的主要目的是消除引脚的输入噪音(Input noise), 就是前面说的因为输入 pin 在输入高阻抗(input impedance)状态, 相当于另一端有串接个超级大的电阻挡住, 以至于 pin 在空接时常因受到环境噪声的影响, 呈现所谓的 floating 状态!

抖动(Bouncing)的产生, 是因为人的手按下开关时, 因为开关机械接触点不会立即密合,
这使得在 MCU 看来, 瞬间等于按下又放掉, 放掉又按下, … 等于 0.1 秒内就这样按放按放…数次甚至十几次或数十次 !

这个问题在大多数情况下并没有问题, 例如, 你希望按钮按下就 LED 亮, 放掉就熄灭,
那不会有问题! 因为抖动很快, LED忽亮忽灭肉眼无法察觉, 就算察觉你也不会在乎 😃
可是, 如果你是希望按钮单击是 toggle, 就是说单击开, 单击关,
那这时就会有问题!
因为, 你以为按了一下, 如果程序感应到6下或是18下(偶数), 那岂非等于没按到 !?
要消除这个问题称为去抖动(DeBouncing)处理, 去抖动虽然可以用比较复杂的硬件电路解决这问题, 但通常硬件电路都偷懒没做;
所以, 必须在程序码内处理!
最简单的处理方法就是, 使用 delay( )隔一下下再读取做确认, 例如:

   int val = digitalRead(pin);
   delay(38);  // 0.038秒
   int val22 = digitalRead(pin); 
   if(val22 != val) {
      delay(15);  val22 = digitalRead(pin);  // 不会这么倒霉  :-)
   }
   delay(168);  // 故意不要连续读取
   if(val == val22) {
      if(val == HIGH) {
         // 应该真的是 HIGH
      }else{
         // 应该真的是 LOW
      }
   } // 确定不是抖动

用硬件去抖动处理可参考:

http://www.labbookpages.co.uk/electronics/debounce.html

其实有个简单的方法, 就是并联一个电容 !
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值