模拟 IO 与 PWM
用 ADC 读取连续电压,用 PWM 模拟"半亮",把电位器旋钮变成调光器,做一个会呼吸的 LED。
用 ADC 读取连续电压,用 PWM 模拟"半亮",把电位器旋钮变成调光器,做一个会呼吸的 LED。
analogWrite 用 0~255 表示占空比,128 约等于 50%。map(value, fromLow, fromHigh, toLow, toHigh)。例如把 analogRead 的 0~1023 映射到 analogWrite 的 0~255:map(v, 0, 1023, 0, 255)。它做整数运算、会截断小数。是连接"读传感器"和"控输出"的桥梁。DEFAULT)。也可切成内部 1.1V(INTERNAL)以提高小信号分辨率,或外接 AREF 引脚电压。用 analogReference() 设置。测微弱信号时改小参考电压能提升精度。电位器(可变电阻)是最直观的模拟输入。三个脚:两端接 5V 和 GND,中间(滑片)接 A0。旋钮转动改变分压,A0 读到 0~5V。
// 读电位器,串口打印原始值和对应电压 const int potPin = A0; void setup() { Serial.begin(9600); // 模拟输入引脚不必调用 pinMode,默认即为输入 } void loop() { int raw = analogRead(potPin); // 0~1023 float volts = raw * (5.0 / 1023.0); // 换算成电压 Serial.print("raw="); Serial.print(raw); Serial.print(" volts="); Serial.println(volts, 2); // 保留 2 位小数 delay(200); }
把电位器接 A0,LED(串 330Ω)接 D9(带 ~,支持 PWM)。旋钮控制亮度:
// 旋电位器调节 LED 亮度 const int potPin = A0; const int ledPin = 9; // 必须是 ~ 引脚 void setup() { pinMode(ledPin, OUTPUT); } void loop() { int raw = analogRead(potPin); // 0~1023 // 把 ADC 的 0~1023 映射到 PWM 的 0~255 int bright = map(raw, 0, 1023, 0, 255); analogWrite(ledPin, bright); // 输出对应亮度 }
1023 / 255 ≈ 4.01,用 raw / 4 也大致可行,但 map() 更清晰、能自动处理边界(保证满旋对应 255),且换其他区间时不用重算除数。养成用 map 表达"区间对应关系"的习惯,代码可读性更好。
用 for 循环把亮度从 0 加到 255 再减回来,看起来就像呼吸。这是 PWM 的经典 demo:
// 呼吸灯 —— 亮度渐入渐出 const int ledPin = 9; void setup() { pinMode(ledPin, OUTPUT); } void loop() { // 渐亮:0 → 255 for (int b = 0; b <= 255; b++) { analogWrite(ledPin, b); delay(8); // 每级停 8ms,整个渐亮约 2 秒 } // 渐灭:255 → 0 for (int b = 255; b >= 0; b--) { analogWrite(ledPin, b); delay(8); } }
人眼对亮度的感受接近对数关系:从 0 到 50 的变化看起来很明显,从 200 到 255 却几乎看不出。所以线性的 analogWrite(0→255) 呼吸灯,会觉得"暗处变化快、亮处变化慢"。追求平滑可用 gamma 校正表(把线性值映射为指数曲线),属于进阶技巧。
| 特性 | digitalWrite | analogWrite (PWM) |
|---|---|---|
| 输出值 | HIGH / LOW(两态) | 0 ~ 255(占空比) |
| 本质 | 恒定高/低电平 | 快速方波,调占空比 |
| 适用引脚 | 任意数字引脚 | 仅带 ~ 的 PWM 引脚 |
| 典型用途 | 开关灯、继电器 | 调光、调速、蜂鸣器音调 |
| 是真模拟吗 | — | 否,是平均效果模拟 |
对没有 ~ 号的引脚调用 analogWrite,Arduino 会退化成:value < 128 输出 LOW,≥128 输出 HIGH,只有全灭/全亮两态,无法调光。若发现"呼吸灯只会突然亮灭",八成是把 LED 接到了非 PWM 引脚。Uno 支持 PWM 的是 D3、D5、D6、D9、D10、D11。
现实世界多是连续的模拟量,单片机靠 ADC 把电压量化成数字:Uno 的 ADC 是 10 位,analogRead(A0~A5) 把 0~5V 转成 0~1023(分辨率约 4.9mV),默认参考电压 5V,可用 analogReference() 改成内部 1.1V 提升小信号精度。电位器是最典型的模拟输入(分压原理)。反过来,数字引脚不能真输出中间电压,但可用 PWM——极快地在 0V/5V 间切换,靠调节高电平占比(占空比)模拟出平均电压。analogWrite(pin, 0~255) 在带 ~ 的 PWM 引脚(D3/D5/D6/D9/D10/D11)输出方波,0=全灭、255=全亮、128≈半亮;对非 PWM 引脚调用只会退化成开/关两态。map(v, 0,1023, 0,255) 把 ADC 读数线性映射到 PWM 区间,是"读传感器控输出"的桥梁。呼吸灯用循环让占空比 0↔255 往复即可;注意人眼亮度感知非线性,追求平滑需 gamma 校正。