Arduino UNO

Arduino UNO 详细指南

1. Arduino UNO 概览

1.1 基本规格

// Arduino UNO R3 核心参数
- 微控制器:ATmega328P (8位 AVR)
- 工作电压:5V
- 输入电压:7-12V(推荐),6-20V(极限)
- 数字I/O引脚:14个(6个支持PWM)
- 模拟输入引脚:6个
- 闪存(Flash):32KB(其中0.5KB用于引导程序)
- SRAM:2KB
- EEPROM:1KB
- 时钟速度:16MHz
- USB接口:Type-B
- 尺寸:68.6mm × 53.4mm

1.2 引脚布局图

            ┌─────────────────┐
            │   USB Type-B    │
            └─────────────────┘
     ┌─┬───────────────────┬─┐
     │R│                   │R│
     │E│                   │E│
     │S│                   │S│
     │E│                   │E│
     │T│                   │T│
     └─┘                   └─┘
 ┌─────────────────────────────┐
│ D13    SCK        AREF     │
│ D12    MISO       GND      │
│ D11    MOSI  PWM  A0/14    │
│ D10    SS    PWM  A1/15    │
│ D9           PWM  A2/16    │
│ D8                 A3/17    │
│ D7                 A4/18(SDA)│
│ D6      PWM       A5/19(SCL)│
│ D5      PWM       A4        │
│ D4                 A3        │
│ D3      PWM  INT1 A2        │
│ D2      INT0      A1        │
│ TX(D1)           A0        │
│ RX(D0)            GND      │
└─────────────────────────────┘
   │  │                   │  │
   GND D13             VIN 5V

2. 引脚功能详解

2.1 数字引脚 (D0-D13)

// 数字引脚功能分布
const int pinFunctions[14] = {
    /* D0 */   RX,     // 串口接收
    /* D1 */   TX,     // 串口发送
    /* D2 */   INT0,   // 外部中断0
    /* D3 */   INT1,   // 外部中断1 + PWM
    /* D4 */   GPIO,   // 通用IO
    /* D5 */   PWM,    // 定时器0 PWM
    /* D6 */   PWM,    // 定时器0 PWM
    /* D7 */   GPIO,
    /* D8 */   GPIO,
    /* D9 */   PWM,    // 定时器1 PWM
    /* D10 */  PWM,    // 定时器1 PWM + SS(SPI从机选择)
    /* D11 */  PWM,    // 定时器2 PWM + MOSI(SPI主出从入)
    /* D12 */  MISO,   // SPI主入从出
    /* D13 */  SCK     // SPI时钟 + 板载LED
};

// PWM引脚:3, 5, 6, 9, 10, 11(标有~符号)
// 中断引脚:2, 3
// SPI引脚:10(SS), 11(MOSI), 12(MISO), 13(SCK)

2.2 模拟引脚 (A0-A5)

// 模拟引脚特性
// 分辨率:10位(0-1023)
// 参考电压:默认5V,可通过AREF引脚改变

const int analogPins = 6;  // A0-A5
// 特殊功能:
// A4 - SDA (I2C数据线)
// A5 - SCL (I2C时钟线)

2.3 电源引脚

// 电源相关引脚
- VIN:外部电源输入(7-12V)
- 5V:5V输出(最大电流500mA)
- 3.3V:3.3V输出(最大电流50mA)
- GND:接地(共3个)
- RESET:复位(低电平有效)

3. 基础程序示例

3.1 Hello World - 闪烁LED

// 最简单的Arduino程序
// 板载LED连接在D13引脚
const int LED_PIN = 13;

void setup() {
  // 初始化设置
  pinMode(LED_PIN, OUTPUT);
  Serial.begin(9600);  // 初始化串口
  Serial.println("Arduino UNO is ready!");
}

void loop() {
  // 主循环
  digitalWrite(LED_PIN, HIGH);  // LED亮
  delay(1000);                   // 等待1秒
  digitalWrite(LED_PIN, LOW);   // LED灭
  delay(1000);                   // 等待1秒
  
  // 串口输出状态
  static int count = 0;
  Serial.print("Blink count: ");
  Serial.println(count++);
}

3.2 按钮输入控制

// 按钮控制LED
const int BUTTON_PIN = 2;  // 按钮接D2
const int LED_PIN = 13;

void setup() {
  pinMode(BUTTON_PIN, INPUT_PULLUP);  // 使用内部上拉电阻
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  int buttonState = digitalRead(BUTTON_PIN);
  
  // 注意:使用上拉电阻时,按钮按下为LOW,释放为HIGH
  if (buttonState == LOW) {
    digitalWrite(LED_PIN, HIGH);  // 按下时LED亮
  } else {
    digitalWrite(LED_PIN, LOW);   // 释放时LED灭
  }
}

3.3 模拟输入读取

// 读取电位器值
const int POT_PIN = A0;  // 电位器中间引脚接A0

void setup() {
  Serial.begin(9600);
  // 注意:模拟输入引脚不需要pinMode设置
}

void loop() {
  int sensorValue = analogRead(POT_PIN);
  
  // 转换为电压值
  float voltage = sensorValue * (5.0 / 1023.0);
  
  Serial.print("ADC Value: ");
  Serial.print(sensorValue);
  Serial.print(" | Voltage: ");
  Serial.print(voltage);
  Serial.println("V");
  
  delay(500);
}

4. 中断应用

4.1 外部中断

// 使用外部中断检测按钮按下
const int INTERRUPT_PIN = 2;  // D2支持外部中断0
const int LED_PIN = 13;

volatile bool buttonPressed = false;  // volatile确保中断中正确读取

void setup() {
  pinMode(LED_PIN, OUTPUT);
  pinMode(INTERRUPT_PIN, INPUT_PULLUP);
  
  // 设置中断
  // 参数:中断号,中断处理函数,触发模式
  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), 
                  buttonISR, FALLING);
}

void loop() {
  if (buttonPressed) {
    digitalWrite(LED_PIN, HIGH);
    delay(2000);  // LED亮2秒
    digitalWrite(LED_PIN, LOW);
    buttonPressed = false;  // 重置标志
  }
}

// 中断服务程序(必须简短!)
void buttonISR() {
  buttonPressed = true;
}

4.2 定时器中断

// 使用定时器1产生精确中断
#include <TimerOne.h>

const int LED_PIN = 13;
bool ledState = false;

void setup() {
  pinMode(LED_PIN, OUTPUT);
  Serial.begin(9600);
  
  // 初始化定时器1,设置中断间隔1000000微秒(1秒)
  Timer1.initialize(1000000);
  // 绑定中断处理函数
  Timer1.attachInterrupt(timerISR);
}

void loop() {
  // 主程序可以执行其他任务
  Serial.println("Main loop running...");
  delay(100);
}

// 定时器中断处理函数
void timerISR() {
  ledState = !ledState;
  digitalWrite(LED_PIN, ledState);
}

5. PWM控制

5.1 LED调光

// 使用PWM控制LED亮度
const int LED_PIN = 9;  // 必须是PWM引脚(3,5,6,9,10,11)

void setup() {
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  // 呼吸灯效果
  for (int brightness = 0; brightness <= 255; brightness++) {
    analogWrite(LED_PIN, brightness);
    delay(10);
  }
  
  for (int brightness = 255; brightness >= 0; brightness--) {
    analogWrite(LED_PIN, brightness);
    delay(10);
  }
}

// PWM频率:
// 引脚5,6:约980Hz
// 引脚3,9,10,11:约490Hz

5.2 控制舵机

#include <Servo.h>

Servo myServo;
const int SERVO_PIN = 9;

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

void loop() {
  // 读取电位器控制舵机
  int potValue = analogRead(A0);
  // 将0-1023映射到0-180度
  int angle = map(potValue, 0, 1023, 0, 180);
  
  myServo.write(angle);
  
  Serial.print("Potentiometer: ");
  Serial.print(potValue);
  Serial.print(" -> Angle: ");
  Serial.print(angle);
  Serial.println("°");
  
  delay(50);
}

6. 通信协议

6.1 串口通信

// 完整的串口通信示例
void setup() {
  Serial.begin(9600);
  while (!Serial) {
    ; // 等待串口连接
  }
  Serial.println("=== Arduino UNO Serial Monitor ===");
  Serial.println("Commands: LED_ON, LED_OFF, STATUS");
}

void loop() {
  // 检查是否有数据可读
  if (Serial.available() > 0) {
    String command = Serial.readStringUntil('\n');
    command.trim();  // 去除首尾空格
    
    Serial.print("Received: ");
    Serial.println(command);
    
    // 处理命令
    if (command == "LED_ON") {
      digitalWrite(LED_BUILTIN, HIGH);
      Serial.println("Built-in LED turned ON");
    } 
    else if (command == "LED_OFF") {
      digitalWrite(LED_BUILTIN, LOW);
      Serial.println("Built-in LED turned OFF");
    }
    else if (command == "STATUS") {
      int sensorValue = analogRead(A0);
      float voltage = sensorValue * (5.0 / 1023.0);
      
      Serial.println("=== System Status ===");
      Serial.print("Analog A0: ");
      Serial.print(sensorValue);
      Serial.print(" (");
      Serial.print(voltage);
      Serial.println("V)");
      Serial.print("Free RAM: ");
      Serial.print(freeMemory());
      Serial.println(" bytes");
    }
    else {
      Serial.println("Unknown command");
    }
  }
  
  // 定期发送数据
  static unsigned long lastSend = 0;
  if (millis() - lastSend > 5000) {
    Serial.print("Uptime: ");
    Serial.print(millis() / 1000);
    Serial.println(" seconds");
    lastSend = millis();
  }
}

// 计算可用内存
extern int __heap_start, *__brkval; 
int freeMemory() {
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

6.2 I2C通信

// I2C LCD1602显示
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// I2C地址通常是0x27或0x3F,根据实际情况调整
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup() {
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  
  lcd.setCursor(0, 0);
  lcd.print("Arduino UNO");
  lcd.setCursor(0, 1);
  lcd.print("I2C LCD Test");
  
  delay(2000);
  lcd.clear();
}

void loop() {
  // 显示温湿度(假设有I2C温湿度传感器)
  float temperature = readTemperature();
  float humidity = readHumidity();
  
  lcd.setCursor(0, 0);
  lcd.print("Temp: ");
  lcd.print(temperature);
  lcd.print("C");
  
  lcd.setCursor(0, 1);
  lcd.print("Humidity: ");
  lcd.print(humidity);
  lcd.print("%");
  
  delay(2000);
}

// I2C设备扫描
void scanI2C() {
  Serial.println("Scanning I2C devices...");
  byte error, address;
  int nDevices = 0;
  
  for (address = 1; address < 127; address++) {
    Wire.beginTransmission(address);
    error = Wire.endTransmission();
    
    if (error == 0) {
      Serial.print("I2C device found at 0x");
      if (address < 16) Serial.print("0");
      Serial.print(address, HEX);
      Serial.println();
      nDevices++;
    }
  }
  
  if (nDevices == 0) {
    Serial.println("No I2C devices found");
  }
}

6.3 SPI通信

// SPI OLED显示
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_MOSI  11  // D11
#define OLED_CLK   13  // D13
#define OLED_DC    9   // D9
#define OLED_CS    10  // D10
#define OLED_RESET 8   // D8

Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);

void setup() {
  Serial.begin(9600);
  
  // 初始化OLED
  if (!display.begin(SSD1306_SWITCHCAPVCC)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;); // 死循环
  }
  
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
}

void loop() {
  // 显示系统信息
  display.clearDisplay();
  
  display.setCursor(0, 0);
  display.println(F("Arduino UNO"));
  display.println(F("=========="));
  
  display.print(F("Uptime: "));
  display.print(millis() / 1000);
  display.println(F("s"));
  
  display.print(F("Free RAM: "));
  display.print(freeMemory());
  display.println(F(" bytes"));
  
  // 读取模拟值
  int sensorValue = analogRead(A0);
  display.print(F("A0: "));
  display.print(sensorValue);
  
  display.display();
  delay(1000);
}

7. 传感器集成

7.1 DHT11温湿度传感器

#include <DHT.h>

#define DHTPIN 2      // D2引脚
#define DHTTYPE DHT11 // DHT11类型

DHT dht(DHTPIN, DHTTYPE);

void setup() {
  Serial.begin(9600);
  dht.begin();
  Serial.println("DHT11 Test");
}

void loop() {
  delay(2000);  // DHT11需要至少2秒的读取间隔
  
  float humidity = dht.readHumidity();
  float temperature = dht.readTemperature();
  
  if (isnan(humidity) || isnan(temperature)) {
    Serial.println("Failed to read from DHT sensor!");
    return;
  }
  
  Serial.print("Humidity: ");
  Serial.print(humidity);
  Serial.print("%\t");
  Serial.print("Temperature: ");
  Serial.print(temperature);
  Serial.println("°C");
}

7.2 HC-SR04超声波测距

const int TRIG_PIN = 9;  // D9
const int ECHO_PIN = 10; // D10

void setup() {
  Serial.begin(9600);
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
}

void loop() {
  // 发送10μs的高电平触发信号
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);
  
  // 读取回波时间
  long duration = pulseIn(ECHO_PIN, HIGH);
  
  // 计算距离(声速340m/s)
  float distance = duration * 0.034 / 2;
  
  Serial.print("Distance: ");
  Serial.print(distance);
  Serial.println(" cm");
  
  delay(100);
}

7.3 光敏电阻

const int LDR_PIN = A0;  // 光敏电阻接A0

void setup() {
  Serial.begin(9600);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  int ldrValue = analogRead(LDR_PIN);
  
  Serial.print("Light Level: ");
  Serial.println(ldrValue);
  
  // 根据光线控制LED
  if (ldrValue < 500) {  // 光线暗
    digitalWrite(LED_BUILTIN, HIGH);
  } else {
    digitalWrite(LED_BUILTIN, LOW);
  }
  
  delay(500);
}

8. 高级应用

8.1 多任务调度

// 使用millis()实现非阻塞多任务
unsigned long previousMillisLED = 0;
unsigned long previousMillisSensor = 0;
unsigned long previousMillisDisplay = 0;

const long intervalLED = 500;
const long intervalSensor = 1000;
const long intervalDisplay = 2000;

void setup() {
  Serial.begin(9600);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  unsigned long currentMillis = millis();
  
  // 任务1: LED闪烁
  if (currentMillis - previousMillisLED >= intervalLED) {
    previousMillisLED = currentMillis;
    static bool ledState = false;
    digitalWrite(LED_BUILTIN, ledState);
    ledState = !ledState;
  }
  
  // 任务2: 读取传感器
  if (currentMillis - previousMillisSensor >= intervalSensor) {
    previousMillisSensor = currentMillis;
    int sensorValue = analogRead(A0);
    // 处理传感器数据...
  }
  
  // 任务3: 更新显示
  if (currentMillis - previousMillisDisplay >= intervalDisplay) {
    previousMillisDisplay = currentMillis;
    Serial.print("Uptime: ");
    Serial.print(currentMillis / 1000);
    Serial.println("s");
  }
}

8.2 数据记录器

// 简易数据记录器
#include <SD.h>

const int chipSelect = 10;  // SD卡模块CS引脚接D10

void setup() {
  Serial.begin(9600);
  
  // 初始化SD卡
  Serial.print("Initializing SD card...");
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    return;
  }
  Serial.println("card initialized.");
  
  // 创建数据文件
  File dataFile = SD.open("datalog.txt", FILE_WRITE);
  if (dataFile) {
    dataFile.println("Time(ms),A0,A1,A2");
    dataFile.close();
  }
}

void loop() {
  // 读取传感器
  int a0 = analogRead(A0);
  int a1 = analogRead(A1);
  int a2 = analogRead(A2);
  
  // 保存到SD卡
  File dataFile = SD.open("datalog.txt", FILE_WRITE);
  if (dataFile) {
    dataFile.print(millis());
    dataFile.print(",");
    dataFile.print(a0);
    dataFile.print(",");
    dataFile.print(a1);
    dataFile.print(",");
    dataFile.println(a2);
    dataFile.close();
    
    Serial.print("Data saved: ");
    Serial.print(millis());
    Serial.print(", ");
    Serial.print(a0);
    Serial.print(", ");
    Serial.print(a1);
    Serial.print(", ");
    Serial.println(a2);
  }
  
  delay(1000);  // 每秒记录一次
}

9. 优化技巧

9.1 内存优化

// Arduino UNO只有2KB SRAM,需要优化
void setup() {
  Serial.begin(9600);
  
  // 1. 使用PROGMEM存储常量字符串
  const char menuText[] PROGMEM = "Welcome to Arduino UNO";
  
  // 2. 使用F()宏将字符串存储在Flash中
  Serial.println(F("This string is in Flash memory"));
  
  // 3. 避免使用String类,使用字符数组
  char buffer[20];  // 而不是 String str;
  
  // 4. 减小全局变量,使用局部变量
}

// 检查内存使用
void checkMemory() {
  Serial.print("Free RAM: ");
  Serial.print(freeMemory());
  Serial.println(" bytes");
}

9.2 性能优化

// 优化数字读写
void optimizedDigitalWrite() {
  // 直接操作寄存器(速度更快)
  
  // 替代 digitalWrite(13, HIGH);
  PORTB |= (1 << PB5);  // D13 = PB5
  
  // 替代 digitalWrite(13, LOW);
  PORTB &= ~(1 << PB5);
  
  // 替代 digitalRead(8);
  // bool state = (PINB & (1 << PB0)) != 0;
}

// 减少delay()使用
void noDelayBlink() {
  static unsigned long lastToggle = 0;
  static bool ledState = false;
  
  if (millis() - lastToggle > 1000) {
    ledState = !ledState;
    digitalWrite(LED_BUILTIN, ledState);
    lastToggle = millis();
  }
}

10. 常见问题与调试

10.1 问题排查清单

// 常见问题及解决方案
void troubleshoot() {
  // 1. 上传失败
  //    - 检查USB线连接
  //    - 选择正确的板和端口
  //    - 重启Arduino IDE
  
  // 2. 程序无响应
  //    - 检查电源是否充足
  //    - 检查是否进入死循环
  //    - 使用看门狗复位
  
  // 3. 传感器读数异常
  //    - 检查接线是否正确
  //    - 检查参考电压
  //    - 添加滤波算法
  
  // 4. 内存不足
  //    - 优化字符串使用
  //    - 减少全局变量
  //    - 使用PROGMEM
}

10.2 串口调试工具

// 增强的调试函数
class Debug {
public:
  static void begin(unsigned long baud = 9600) {
    Serial.begin(baud);
    while (!Serial);
    Serial.println(F("=== Debug Started ==="));
  }
  
  static void log(const char* message, int value = -9999) {
    Serial.print(F("[LOG] "));
    Serial.print(millis());
    Serial.print(F("ms: "));
    Serial.print(message);
    
    if (value != -9999) {
      Serial.print(F(" = "));
      Serial.print(value);
    }
    Serial.println();
  }
  
  static void warn(const char* message) {
    Serial.print(F("[WARN] "));
    Serial.println(message);
  }
  
  static void error(const char* message) {
    Serial.print(F("[ERROR] "));
    Serial.println(message);
  }
  
  static void memory() {
    Serial.print(F("[MEMORY] Free RAM: "));
    Serial.print(freeMemory());
    Serial.println(F(" bytes"));
  }
};

// 使用示例
void setup() {
  Debug::begin(115200);
  Debug::log("Setup complete");
  Debug::memory();
}

void loop() {
  static int counter = 0;
  Debug::log("Loop count", counter++);
  
  if (counter > 100) {
    Debug::warn("Counter is getting large");
  }
  
  delay(1000);
  Debug::memory();
}

11. 项目示例:智能气象站

// 完整的气象站项目
#include <DHT.h>
#include <LiquidCrystal_I2C.h>

#define DHTPIN 2
#define DHTTYPE DHT11
#define LDR_PIN A0
#define RAIN_PIN A1

DHT dht(DHTPIN, DHTTYPE);
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup() {
  Serial.begin(9600);
  dht.begin();
  lcd.init();
  lcd.backlight();
  
  Serial.println(F("Weather Station V1.0"));
  lcd.setCursor(0, 0);
  lcd.print(F("Weather Station"));
  delay(2000);
  lcd.clear();
}

void loop() {
  // 读取所有传感器
  float humidity = dht.readHumidity();
  float temperature = dht.readTemperature();
  int lightLevel = analogRead(LDR_PIN);
  int rainValue = analogRead(RAIN_PIN);
  bool isRaining = rainValue < 500;  // 假设小雨时值较小
  
  // 串口输出
  Serial.println(F("=== Weather Data ==="));
  Serial.print(F("Temp: ")); Serial.print(temperature); Serial.println(F("°C"));
  Serial.print(F("Humidity: ")); Serial.print(humidity); Serial.println(F("%"));
  Serial.print(F("Light: ")); Serial.println(lightLevel);
  Serial.print(F("Rain: ")); Serial.println(isRaining ? "Yes" : "No");
  
  // LCD显示
  lcd.setCursor(0, 0);
  lcd.print(F("T:"));
  lcd.print(temperature, 1);
  lcd.print(F("C H:"));
  lcd.print(humidity, 0);
  lcd.print(F("%"));
  
  lcd.setCursor(0, 1);
  lcd.print(F("L:"));
  lcd.print(lightLevel);
  lcd.print(F(" R:"));
  lcd.print(isRaining ? "Yes" : "No ");
  
  // 延迟5秒
  delay(5000);
}

总结

Arduino UNO 是:

  • 入门最佳选择:引脚标记清晰,资源丰富
  • 功能全面:支持数字、模拟、PWM、通信
  • 社区强大:海量的库和教程
  • 扩展性好:各种扩展板(Shield)可用

学习建议

  1. 从基本的LED和按钮开始
  2. 逐步学习各种传感器
  3. 掌握串口调试技巧
  4. 学习通信协议(I2C、SPI)
  5. 尝试综合项目实践
  6. 关注内存和性能优化

Arduino UNO 虽然内存有限,但通过学习优化技巧,可以实现很多有趣的项目!

posted @ 2026-01-02 16:46  ukyo--碳水化合物  阅读(5)  评论(0)    收藏  举报