跳到主要内容

模拟 I/O

模拟输入输出让 Arduino 能够处理连续变化的信号,如读取传感器数值或控制 LED 亮度。本章将详细介绍模拟 I/O 的原理和应用。

模拟 vs 数字

数字信号

  • 只有两种状态:高电平(5V)或低电平(0V)
  • 用于开关控制、数字通信
  • 抗干扰能力强

模拟信号

  • 可以在 0V 到 5V 之间连续变化
  • 用于传感器读取、精细控制
  • 更接近自然界的物理量
数字信号:    模拟信号:
│ ┌──┐ │ ╭─╮
5V ─┤ │ │ 5V ─┤ ╱ ╲
│ │ │ │ ╱ ╲
0V ─┴────┘ └── 0V ─┴─╱ ╲──
0 1 0 1 连续变化

模拟输入

Arduino Uno 有 6 个模拟输入引脚(A0-A5),使用 10 位 ADC(模数转换器),可以将 0-5V 电压转换为 0-1023 的数值。

analogRead() 函数

读取模拟引脚的电压值:

int value = analogRead(pin);
  • pin:模拟引脚编号(A0-A5,也可以直接用数字 0-5)
  • 返回值:0-1023(对应 0V-5V)

电压换算公式

电压 = (返回值 / 1023) × 5V

读取电位器

电位器(可变电阻)是最基础的模拟输入器件:

硬件连接

电位器引脚 1 ── 5V
电位器引脚 2 ── A0(中间引脚,信号输出)
电位器引脚 3 ── GND

代码

const int POT_PIN = A0;

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

void loop() {
int rawValue = analogRead(POT_PIN);
float voltage = rawValue * (5.0 / 1023.0);

Serial.print("原始值: ");
Serial.print(rawValue);
Serial.print(" | 电压: ");
Serial.print(voltage);
Serial.println(" V");

delay(100);
}

光敏电阻读取光线

光敏电阻(LDR)的阻值随光线强度变化:

硬件连接

5V ── LDR ── A0 ── 10kΩ 电阻 ── GND

代码

const int LDR_PIN = A0;

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

void loop() {
int lightValue = analogRead(LDR_PIN);

// 将读数映射为亮度等级
int brightnessLevel = map(lightValue, 0, 1023, 0, 100);

Serial.print("光线强度: ");
Serial.print(brightnessLevel);
Serial.println("%");

delay(500);
}

读取温度传感器(LM35)

LM35 温度传感器输出电压与温度成正比(10mV/°C):

硬件连接

LM35 VCC ── 5V
LM35 OUT ── A0
LM35 GND ── GND

代码

const int TEMP_PIN = A0;

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

void loop() {
int reading = analogRead(TEMP_PIN);

// 转换为电压
float voltage = reading * 5.0 / 1023.0;

// LM35: 10mV = 1°C
float temperature = voltage * 100;

Serial.print("温度: ");
Serial.print(temperature);
Serial.println(" °C");

delay(1000);
}

模拟输出(PWM)

Arduino 使用 PWM(脉冲宽度调制)模拟模拟输出。通过快速切换高低电平,改变平均电压。

PWM 原理

占空比 25%:     占空比 50%:     占空比 75%:
┌┐ ┌──┐ ┌───┐
││ │ │ │ │
│└──────── │ └──── │ └──
│ │ │
└─────────── └─────────── └────────
低亮度 中亮度 高亮度

analogWrite() 函数

输出 PWM 信号:

analogWrite(pin, value);
  • pin:PWM 引脚(D3、D5、D6、D9、D10、D11)
  • value:0-255(0 表示完全关闭,255 表示完全开启)

LED 亮度控制

硬件连接

Arduino D9 (PWM) ── LED 正极
└── 220Ω 电阻 ── GND

代码

const int LED_PIN = 9;

void setup() {
// PWM 引脚不需要 pinMode 设置
}

void loop() {
// 渐亮
for (int i = 0; i <= 255; i++) {
analogWrite(LED_PIN, i);
delay(10);
}

// 渐暗
for (int i = 255; i >= 0; i--) {
analogWrite(LED_PIN, i);
delay(10);
}
}

电位器控制 LED 亮度

结合模拟输入和 PWM 输出:

const int POT_PIN = A0;
const int LED_PIN = 9;

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

void loop() {
// 读取电位器值(0-1023)
int potValue = analogRead(POT_PIN);

// 映射到 PWM 范围(0-255)
int brightness = map(potValue, 0, 1023, 0, 255);

// 设置 LED 亮度
analogWrite(LED_PIN, brightness);

Serial.print("电位器: ");
Serial.print(potValue);
Serial.print(" | 亮度: ");
Serial.println(brightness);

delay(50);
}

控制舵机

舵机使用 PWM 信号控制角度(通常 0-180°):

#include <Servo.h>

Servo myServo;
const int SERVO_PIN = 9;
const int POT_PIN = A0;

void setup() {
myServo.attach(SERVO_PIN);
Serial.begin(9600);
}

void loop() {
int potValue = analogRead(POT_PIN);

// 将 0-1023 映射到 0-180 度
int angle = map(potValue, 0, 1023, 0, 180);

myServo.write(angle);

Serial.print("角度: ");
Serial.println(angle);

delay(15); // 给舵机足够时间转动
}

综合示例:自动夜灯

根据环境光线自动调节 LED 亮度:

const int LDR_PIN = A0;
const int LED_PIN = 9;

// 校准值(根据实际环境调整)
const int DARK_THRESHOLD = 300; // 低于此值认为是黑暗
const int BRIGHT_THRESHOLD = 800; // 高于此值认为是明亮

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

void loop() {
int lightLevel = analogRead(LDR_PIN);

// 根据光线强度计算 LED 亮度
// 环境越暗,LED 越亮
int ledBrightness;

if (lightLevel <= DARK_THRESHOLD) {
ledBrightness = 255; // 全亮
} else if (lightLevel >= BRIGHT_THRESHOLD) {
ledBrightness = 0; // 关闭
} else {
// 线性映射
ledBrightness = map(lightLevel, DARK_THRESHOLD, BRIGHT_THRESHOLD, 255, 0);
}

analogWrite(LED_PIN, ledBrightness);

Serial.print("光线: ");
Serial.print(lightLevel);
Serial.print(" | LED亮度: ");
Serial.println(ledBrightness);

delay(100);
}

综合示例:简易温度计

使用 LM35 和多个 LED 显示温度范围:

const int TEMP_PIN = A0;
const int LED_COLD = 8; // 蓝色 LED - 低温
const int LED_NORM = 9; // 绿色 LED - 正常
const int LED_HOT = 10; // 红色 LED - 高温

void setup() {
Serial.begin(9600);
pinMode(LED_COLD, OUTPUT);
pinMode(LED_NORM, OUTPUT);
pinMode(LED_HOT, OUTPUT);
}

void loop() {
float temperature = readTemperature();

// 根据温度点亮不同 LED
if (temperature < 20) {
digitalWrite(LED_COLD, HIGH);
digitalWrite(LED_NORM, LOW);
digitalWrite(LED_HOT, LOW);
} else if (temperature < 30) {
digitalWrite(LED_COLD, LOW);
digitalWrite(LED_NORM, HIGH);
digitalWrite(LED_HOT, LOW);
} else {
digitalWrite(LED_COLD, LOW);
digitalWrite(LED_NORM, LOW);
digitalWrite(LED_HOT, HIGH);
}

Serial.print("温度: ");
Serial.print(temperature);
Serial.println(" °C");

delay(1000);
}

float readTemperature() {
int reading = analogRead(TEMP_PIN);
float voltage = reading * 5.0 / 1023.0;
return voltage * 100;
}

高级技巧

平滑传感器读数

使用移动平均减少传感器噪声:

const int SENSOR_PIN = A0;
const int NUM_SAMPLES = 10;

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

void loop() {
int average = getSmoothedReading();

Serial.print("平滑值: ");
Serial.println(average);

delay(100);
}

int getSmoothedReading() {
long sum = 0;
for (int i = 0; i < NUM_SAMPLES; i++) {
sum += analogRead(SENSOR_PIN);
delayMicroseconds(100); // 短暂延时让 ADC 稳定
}
return sum / NUM_SAMPLES;
}

改变 PWM 频率

Arduino 默认 PWM 频率约为 490Hz(D5、D6 为 980Hz),某些应用需要调整:

// 注意:这会改变定时器设置,可能影响其他功能

// 设置 D9、D10 的 PWM 频率为 62.5kHz
void setup() {
// 将 Timer1 设置为模式 5(快速 PWM,8位)
TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM10);
TCCR1B = _BV(CS10) | _BV(WGM12); // 无预分频
}

void loop() {
analogWrite(9, 128); // D9 输出 50% 占空比
analogWrite(10, 64); // D10 输出 25% 占空比
}

常见问题

1. 模拟读数不稳定

原因

  • 电源噪声
  • 传感器信号弱
  • ADC 参考电压不稳定

解决方法

  • 添加去耦电容(0.1μF)
  • 使用移动平均算法
  • 使用外部参考电压

2. PWM 输出有噪音

原因

  • PWM 频率过低,能听到蜂鸣声
  • 负载电流过大

解决方法

  • 提高 PWM 频率
  • 添加滤波电路
  • 使用驱动电路

3. 模拟输入精度不够

Arduino 的 10 位 ADC 提供约 4.9mV 的分辨率。如需更高精度:

  • 使用外部 ADC 芯片(如 ADS1115,16 位)
  • 使用运算放大器放大信号
  • 降低参考电压提高分辨率

下一步

掌握了模拟 I/O 后,我们将学习串口通信,这是 Arduino 与电脑、其他设备交互的重要方式。