Skip to content
Published on

写给程序员的欧姆定律:用数字理解电压、电流和电阻

分享
Authors

引言 —— 忘了接一个电阻,烧掉一颗 LED 的那天

把 LED 插到面包板上,长脚接 Arduino 的 D9,短脚接 GND。代码只有一行:digitalWrite(9, HIGH)。LED 瞬间刺眼地亮了一下,变暗,然后熄灭。重新插上,也不会再亮了。

这种事在软件里几乎不会发生。写错的代码会抛异常或者返回错误的值,但一个函数不会physically消失。而在硬件里,元件会真的消失。消失的原因几乎总是同一件事:某个数字没搞清楚。

上面那个电路里,那个数字就是电流。没有人决定 LED 该流过多大的电流——因为决定这件事的元件,也就是电阻,根本不存在。这篇文章的目标,就是让你能够自己算出这个数字。

电压、电流、电阻 —— 这三个数字究竟指什么

先把术语钉死。这三个概念一旦模糊,后面所有的计算都会跟着模糊。

电流(current)是流动。单位是安培,符号是 I。1 安培是指 1 秒内有 1 库仑的电荷通过某个截面。在单片机的世界里主要用毫安做单位:一颗 LED 大约 15mA,一个 Arduino 引脚能安全输出的最大值大约是 20mA。

电压(voltage)不是流动,而是差。单位是伏特,符号是 V。这里最关键的一点是,电压不是某一个点的属性,而是两点之间的关系。「D9 引脚的电压是 5V」这句话,其实是「D9 引脚与 GND 之间的电位差是 5V」的简化说法。不给出参考点的电压是没有意义的。

这一点对程序员来说是最陌生的地方。变量只有一个值,但电压永远需要两个点。万用表有两根表笔的原因就在这里。没法做出只有一根表笔的电压表。

电阻(resistance)是约束。单位是欧姆,符号是 R。它描述的是同样的电压之下,能让多少电流通过。

把这三者联系在一起的,就是欧姆定律。

V = I × R
I = V / R
R = V / I

这三种写法是同一个方程式,只是根据缺哪个量而选用不同的形式。实际搭电路时用得最多的是第三种形式:要施加的电压和想要的电流通常都已经定好了,找出能实现它们的电阻值,是元件选型的大部分工作。

代入一个数字看看。把一个 220 欧姆的电阻单独接在 5V 电源和 GND 之间,

电流 = 5V / 220欧姆 = 0.0227A = 22.7mA

这个 22.7mA 与其说是算出来的,不如说是物理规律强制决定的。只要电源维持 5V、电阻维持 220 欧姆,就不可能流过别的数值。

这里顺便理清一个程序员最容易产生的误解。电源决定电压,元件决定电流。「5V 2A 适配器」不是说它会硬塞进 2A,而是说它最多能供应 2A。实际流过的电流由所接电路的电阻决定。什么都不接,流过的就是 0A,适配器对此毫无意见。

水流类比在哪里成立,又在哪里失效

学电学时几乎必然会遇到水流类比:电压是水压,电流是流量,电阻是细管子。这个类比相当好用——而且一旦你清楚它精确地在哪里失效,会用得更好。

先看它说对的部分。水压越高,水流得越多;管子越细,流得越少。这就是欧姆定律。把几根管子并联起来,整体流量会增加——这就是并联电阻会降低总电阻的原因。水不会在管子中途凭空消失——这就是基尔霍夫电流定律。

接下来是失效的部分。

第一,水只要一端打开就会流动。而电流只有在闭合回路里才会流动。把一根导线接到 5V,另一端悬空,电流就是 0。把水管从水桶里拔出来,水会哗哗地流出来;但把导线从电路里拔出来,什么都不会发生。初学者忘了接 GND、然后问「我明明接了电源,为什么不工作」,原因就在这里。

第二,水会积在管子里,但电荷不会在电路里待命。水流类比会诱使你把电流想象成「被输送的物质」,但实际携带能量的是导线周围的电磁场,电子本身移动得极其缓慢。这个差异在高速信号上会体现出来:导线的长度和布局会改变信号的形状,这一点用流量模型是没法解释的。

第三,也是最重要的一点:在水流类比里,管子的电阻是固定的。但实际电路里有相当一部分元件的电阻并不固定。LED 的电阻会随着两端电压剧烈变化。晶体管的电阻会随第三个信号变化。一旦想用水流类比去理解 LED,就会直接得出完全错误的结论。这个话题会在 LED 限流电阻计算那篇文章里详细展开。

总结一下:在只有电阻和电源的电路里,尽管放心使用水流类比。而一旦出现有源器件,就该放下这个类比,去看那个元件真实的特性曲线。

手算串联和并联电路

在面包板上实际会遇到的排布方式只有两种:元件排成一条线的串联,以及元件并排接在同一对节点之间的并联。

串联 —— 电流相同,电压被分摊

假设把一个 220 欧姆和一个 330 欧姆的电阻依次接在 5V 电源上。

合成电阻 = 220欧姆 + 330欧姆 = 550欧姆
电流     = 5V / 550欧姆 = 0.00909A = 9.09mA

电流只有一条路可走,所以两个电阻都流过恰好 9.09mA。再套用一次欧姆定律,就能得到每个电阻两端的电压。

220欧姆两端的电压 = 0.00909A × 220欧姆 = 2.00V
330欧姆两端的电压 = 0.00909A × 330欧姆 = 3.00V
合计              = 5.00V

合计恰好等于电源电压。这不是巧合,而是一条定律,稍后会给它命名。而这种结构本身也有名字:分压器。意思是可以把 5V 分成 2V 和 3V,降低传感器输出、或者匹配模拟输入的量程时就直接用这一招。

并联 —— 电压相同,电流被分摊

把同样的 220 欧姆和 330 欧姆电阻这次并排接在 5V 和 GND 之间。因为两个电阻的两端都是同一对节点,所以两者两端都是 5V。

流过220欧姆的电流 = 5V / 220欧姆 = 22.7mA
流过330欧姆的电流 = 5V / 330欧姆 = 15.2mA
总电流            = 22.7 + 15.2 = 37.9mA

反推总电阻,得到:

合成电阻 = 5V / 0.0379A = 132欧姆

用公式也能验证:

合成电阻 = (220 × 330) / (220 + 330) = 72600 / 550 = 132欧姆

并联的合成电阻永远小于其中最小的那个电阻——这很自然,毕竟多开了一条路,总流量当然会增加。这个事实在实务中常被用来做安全检查:如果算出来的合成电阻比构成它的最小值还大,那说明中间某一步算错了。

相同的量被分摊的量合成电阻实际用途
串联电流电压各电阻之和分压器、LED 限流
并联电压电流小于其中最小值挂在电源轨上的所有负载

最后一栏很关键。在板子的 5V 电源轨上接三个传感器,这三个就互为并联,于是每一个所需的电流会直接相加。Arduino Uno 的 5V 轨大约能承受 450mA 上限,树莓派的 3.3V 轨大约是 50mA——判断是否会超过这个上限,靠的就是并联电流的加法。

基尔霍夫两大定律 —— 测量时用得上的工具

前面,串联电阻上电压之和恰好等于电源电压;并联支路的电流之和也恰好等于总电流。这两件事都有名字,而且不只是考试知识——它们是电路不工作时用来找原因的工具。

电压定律 —— 绕一圈回到原点,总和为零

沿着一个闭合回路绕一圈,把沿途电压的升与降全部加起来,结果精确地等于零。在电源处升高了 5V,那么经过各个元件后就必须总共降低 5V,才能回到出发点。如果回到出发点却发现电位变了,那就说明那根本不是同一个点——出现了矛盾。

这条定律在实战中是这样用的:一个 5V 电源接了一个电阻和一颗 LED,LED 不亮。用万用表测电阻两端,读数是 5V。答案在这一刻就出来了。既然绕一圈的总和必须为零,LED 两端的电压就必须是 0V。电压为零而电路又是闭合的,意味着 LED 那个位置实际上等同于一根导线——要么 LED 短路了,要么它的两只脚插在了面包板同一行上。

反过来的结果同样有用。如果电阻两端是 0V 而 LED 两端是 5V,那么电阻上没有压降就意味着根本没有电流流过。要么是 LED 装反了,要么是某只脚断了。

只测两次电压,就能区分这两种故障。这就是这条定律的实用价值。

电流定律 —— 流入节点多少,就流出多少

流入一个节点的电流总和,等于从这个节点流出的电流总和。因为电荷不会在节点里堆积。

前面的并联计算本身就是这条定律。从电源出来的 37.9mA 在分支点分成 22.7mA 和 15.2mA,又重新汇合成 37.9mA 流回 GND。

这条定律在实务中最重要的应用形式是电流预算。流回板子 GND 的电流,是所有负载消耗电流的总和。所以即便每个元件单独看都留有余量,总和也可能超过上限。树莓派每个 GPIO 引脚允许 16mA,但所有 GPIO 加在一起上限是 50mA。给四个引脚各通 15mA,单独看每个都合格,但总和 60mA 就不合格了。这个计算完全就是电流定律的加法。

元件一多,手算这种加法就变得繁琐。把板子和元件放进本站的接线验证工具,它会算出每个引脚的电流,以及 3.3V、5V 电源轨各自的总和,告诉你哪个上限会先被触发。可以用来和手算的结果互相印证。

功率 —— 电阻为什么会发热,以及如何选额定功率

给电阻通上电流,电阻就会发热。这不是副作用,而是电阻本职工作的一部分。电阻材料内部,电子与原子碰撞,损失动能,那部分能量变成了晶格振动——也就是热。电阻是把电能转换成热能的元件,限制电流就意味着把相应的那部分能量当作热量扔掉。

被扔掉的量就是功率,单位是瓦特。

P = V × I

代入欧姆定律,还能得到两个更方便的变形。

P = I² × R
P = V² / R

用前面串联的例子算一下。330 欧姆上流过 9.09mA,两端电压 3.00V。

P = 3.00V × 0.00909A = 0.0273W = 27.3mW

一般碳膜电阻的额定功率是 1/4W,也就是 250mW。27.3mW 只是它的九分之一,完全没问题。

现在换一种情况。把一个 100 欧姆的电阻单独接在 12V 电源上,

电流 = 12V / 100欧姆 = 0.12A = 120mA
功率 = 12V × 0.12A = 1.44W

1.44W。如果这里插一个 1/4W 的电阻会怎样?这几乎是额定值的六倍,电阻体会升到几百摄氏度。碳膜氧化,阻值逐渐上升,色环烧成黑色,最后膜层断裂、变成开路。运气不好的话,面包板的塑料会在那之前就已经熔化。这种情况需要用 3W 以上的水泥电阻。

额定值要留足余量,这是基本原则。实务中常用的标准是:选择额定值至少是计算功耗两倍以上的电阻。原因有两个。电阻的额定值通常是在环境温度 25 摄氏度、空气能自由流通的条件下标定的,而一旦被塞进外壳内部或夹在其他元件之间,就不再是这个条件了。而且长时间在接近额定值的状态下使用,阻值本身也会缓慢漂移。

额定值实际元件形态安全可用的实际功率典型用途
1/8W(125mW)极小的碳膜电阻60mW 以下板面空间紧张之处
1/4W(250mW)最常见的棕色电阻体125mW 以下LED 限流、上拉
1/2W(500mW)稍粗一些的电阻体250mW 以下12V 级别的电路
2W 以上白色方形水泥电阻1W 以下电源部分、假负载

LED 限流电阻的功耗几乎总在 50mW 以下,所以 1/4W 已经绰绰有余——这也是新手套件里的电阻几乎清一色都是 1/4W 的原因。但要记住这不代表「所有电阻都用 1/4W 就够」;一旦开始接触 12V 以上的电路,就必须动手算一算。

用万用表实际测量(以及烧保险丝的那个错误)

当计算和现实对不上时,判断的依据是测量值。万用表就是硬件世界的调试器,用好它必须牢记一个不对称的地方。

电压要并联测,在元件两端量

电压是两点之差,所以只需要把表笔分别搭在想知道的两个点上——完全不需要动电路。想测某个电阻两端,把两根表笔分别搭在它的左脚和右脚上就够了。

之所以能这样测,是因为电压表的内阻非常大,通常是 10 兆欧姆。测一个 5V 的点时,漏进电压表的电流是:

5V / 10000000欧姆 = 0.0000005A = 0.5微安

跟电路里流过的几十毫安相比,这几乎等于没有。所以测量这个动作不会扰动电路。

测量时不要弄丢参考点这一点很重要。把黑表笔固定夹在 GND 上,用红表笔到处点,是失误最少的做法。这样每一个读数都是相对 GND 的电位,彼此之间可以直接比较。

电流要串联测,断开电路把表接进去

电流是通过某一点的量,所以要测它就必须让它真的流过那个点——也就是要把电路在某处断开,把断口两端接到表笔上,让电流真正流经万用表。

电流表的内阻非常小。mA 挡大约是 10 欧姆左右,10A 挡大约是 0.01 欧姆。之所以要小,是为了尽量少扰动电路。但即便如此也不是绝对的零,所以测量这个动作本身会稍微改变电路。在 mA 挡的 10 欧姆分流电阻上通过 20mA 时,

0.02A × 10欧姆 = 0.2V

0.2V 消耗在万用表内部。这就是负担电压。在 5V 电路里,0.2V 是 4%,通常可以忽略;但在裕量本就吃紧的 3.3V 电路里,单是这个值就可能让电路突然停止工作。如果某个症状只在测量的那一刻消失,这个负担电压就值得怀疑。

几乎每个人都会犯一次的错误 —— 用电流挡测电压

习惯了测电压之后再去测电流,手往往会按惯性动作:把拨盘转到 A 挡,但表笔还留在原来测 5V 和 GND 的位置。这就等于把电流表并联接到了电源上。

来算一下那一瞬间会发生什么。电流表基本上就是一根几乎没有电阻的导线。用 mA 挡的 10 欧姆来算,

电流 = 5V / 10欧姆 = 0.5A = 500mA

mA 插孔上的保险丝通常是 200mA 或 500mA 规格,会立刻烧断。如果当时插在 10A 插孔上,电阻是 0.01 欧姆,理论上会是几百安培,实际上则是电源能供应多少就流多少。USB 电源的保护电路会把它切断,但锂电池会把表笔烧得通红。

而这场事故会悄无声息地过去。保险丝烧断的万用表,测电压完全正常,只是电流永远显示 0。于是第二天你会拆开电路,纳闷「电流怎么是 0」。如果电流始终读 0,先怀疑万用表的保险丝、再怀疑电路,才是正确的顺序。

防止这种情况的习惯很简单:一测完电流,立刻把表笔插回电压插孔。让表笔的位置——而不是拨盘——保持在默认状态,手按惯性动作也不会闯祸。

测电阻有一个前提条件

测电阻的原理是万用表自己施加一个小电压,再测量产生的电流。所以需要满足两个前提。

第一,电路的电源必须关闭。如果电源还通着,万用表施加的电压会和电路里的电压混在一起,读数会乱套,严重时还会损坏万用表。

第二,要测的电阻必须从电路中脱离出来。如果still插在板子上测,就会把和它并联的其他路径也一起测进去。前面已经看到,并联合成值永远小于最小的那个,所以读出来的值会比真实值小。如果一个标着 220 欧姆的电阻测出来是 132 欧姆,不是电阻坏了,而是旁边并联着一个 330 欧姆的电阻。

用 Arduino 直接读电压

不用万用表,也可以用板子测电压——模拟输入做的正是这件事。Arduino Uno 的 ADC 会把 0V 到参考电压之间的电压,转换成 0 到 1023 之间的整数。

// 读取 A0 上的电压并换算成伏特。
// 注意:Uno 的 A0 只在 5V 以内安全。更高的电压必须先经过分压器。

const int SENSE_PIN = A0;
const float VREF = 5.0;      // 参考电压。用 USB 供电时实际会在 4.7~5.1V 之间波动。
const float ADC_STEPS = 1024.0;

// 使用分压器时的数值。不用的话两者都留 0,并省略下面的修正。
const float R_TOP = 10000.0;    // 靠近被测对象的一侧
const float R_BOTTOM = 20000.0; // 靠近 GND 的一侧

void setup() {
  Serial.begin(115200);
}

void loop() {
  int raw = analogRead(SENSE_PIN);

  // 一步是 5.0 / 1024 = 0.00488V,大约 4.9mV。
  float pinVolts = (raw * VREF) / ADC_STEPS;

  // 如果经过了分压器,换算回原始电压。
  // 分压比 = R_BOTTOM / (R_TOP + R_BOTTOM) = 20000 / 30000 = 0.667
  float sourceVolts = pinVolts * ((R_TOP + R_BOTTOM) / R_BOTTOM);

  Serial.print("raw=");
  Serial.print(raw);
  Serial.print("  pin=");
  Serial.print(pinVolts, 3);
  Serial.print("V  source=");
  Serial.print(sourceVolts, 3);
  Serial.println("V");

  delay(500);
}

这段代码里藏着两个教训。

一个是分辨率。把 5V 分成 1024 档,一档大约是 4.9mV。比这更小的变化,无论代码写得多精细,原理上都无法分辨。这个极限是跨不过去的。

另一个是参考电压。上面的代码把 VREF 写死成 5.0,但用 USB 供电的 Arduino,它的 5V 轨实际上会在 4.7V 到 5.1V 之间波动。也就是说,每一个读数里其实都已经带着百分之几的误差。如果需要精确的数值,要么用万用表实测 5V 引脚的真实电压,把那个值填进 VREF,要么使用内置的参考电压。这里同样适用那条原则:依据是测量,而不是计算。

结语 —— 选元件说到底就是决定电流

到这里,所有内容可以浓缩成一句话:在电路里选元件,绝大多数时候就是在决定用多少毫安。

电压通常是给定的。板子要么是 5V,要么是 3.3V,轮不到你选。元件需要的电压也写在数据手册上。剩下的自由度是电流,而决定电流的元件就是电阻。这就是为什么选电阻值的计算会占据电路设计中如此大的比重。

回到这篇文章开头那颗 LED,那个电路为什么会失败,现在已经很清楚了。因为没有电阻,就没有任何元件在决定电流。没有元件来决定,电流就会一路冲到物理规律允许的最大值——而这个最大值,几乎总是比元件能承受的还要大。

下一篇会把这个 LED 电路老老实实地算一遍。用数字确认 LED 为什么不能像电阻那样对待,以及为什么每种颜色最终都需要不同的电阻值。