Arduino 从入门到进阶完整指南
版本:V1.2.0 | 更新日期:2026/11/08 12:00
本文档整合了最全的微课与进阶教材大纲,围绕 Arduino UNO 主板、传感器扩展板与面包板,提供从软件安装、基础语法到复杂电机控制的无缝完整教程。
1. 软件平台与基础架构
1.1 官方与第三方下载源
为了确保软件的安全与纯净,请务必从官方或国内权威镜像站下载开发环境:
- Arduino 官网: https://www.arduino.cc/en/software (支持 Windows, macOS, Linux)
- 清华大学 TUNA 镜像站(国内极速): https://mirrors.tuna.tsinghua.edu.cn/arduino/
- 上海交通大学镜像站: https://mirror.sjtu.edu.cn/arduino/
1.2 IDE 1.x 与 2.x 操作指南
Arduino IDE 是编写、编译并上传代码的专用软件。目前有两大主流版本:
- IDE 1.x(经典版): 界面朴素,对老旧电脑兼容性极好。工具栏包含验证、上传按钮。底部控制台显示编译信息,右下角显示开发板与端口。
- IDE 2.x(推荐版): 界面现代,支持代码自动补全。左侧新增了边栏,集成了开发板管理器、库管理器、调试器。串口监视器作为底部选项卡显示。
- 上传流程(通用): 用 USB 连接电脑 → 在“工具”选择开发板型号(UNO)及端口 → 点击“上传”箭头按钮,等待提示 Done uploading。
1.3 库管理器与生态拓展
对于复杂的外设(如屏幕、传感器),需要引入第三方库。在 IDE 中点击左侧的“库管理器”图标,搜索名称并安装。在代码头部使用 #include 调用。官方社区提供了大量开源的驱动支持。
- 官方社区与文档: Arduino 语言参考 与 官方论坛 是排错和获取第三方库的最佳去处。
1.4 代码基础结构与运行原理
在开始接线和编写逻辑之前,必须了解 Arduino 程序的“空壳”结构。每一个 Arduino 程序都由 setup() 和 loop() 两个必须存在的核心函数构成。
void setup() {
// 上电或复位后,只执行一次。用于初始化配置
}
void loop() {
// setup执行完毕后,无限循环执行。用于编写主逻辑
}
运行原理详解:
- setup() 函数(初始化): 当开发板通电或按下复位键时,程序从上到下执行,只执行一次。我们通常把引脚模式设置(pinMode)、启动串口通信(Serial.begin)等准备工作写在这里。
- loop() 函数(主循环): 当 setup() 执行完毕后,CPU 会立刻进入 loop() 函数。一旦 loop() 里的代码执行到底部,它会自动跳回顶部无限循环执行,直到断电或复位。我们所有的传感器读取、逻辑判断、控制输出,都写在这个无限循环里。
1.5 常用代码块的作用与搭配语法
为了在后续项目中灵活拼装代码,初学者必须先掌握以下几个最核心的“代码块组合”及其语法搭配:
1. 引脚模式配置块(准备阶段)
语法: pinMode(引脚号, 模式);
作用: 告诉单片机某个引脚是负责“输出电”还是“读取电”。
搭配: 必须放在 setup() 函数中。例如 pinMode(9, OUTPUT); 是把 9 号引脚设为输出。如果要读取按键,就写 INPUT_PULLUP。
2. 数字输出控制块(动作阶段)
语法: digitalWrite(引脚号, 状态);
作用: 控制引脚输出 5V 高电平(HIGH)或 0V 低电平(LOW)。
搭配: 通常放在 loop() 中,且该引脚必须事先在 setup 里设为 OUTPUT。常与 delay() 搭配使用,比如:输出 HIGH → 等 1 秒 → 输出 LOW。
3. 数字输入读取块(感知阶段)
语法: digitalRead(引脚号);
作用: 读取引脚当前的电平状态(HIGH 或 LOW),常用于判断按键是否被按下。
搭配: 必须先设为输入模式。读取后通常与 if 语句搭配,例如:if (digitalRead(2) == LOW) { ... }。
4. 模拟输入读取块(连续感知阶段)
语法: analogRead(模拟引脚);
作用: 读取 0-5V 之间的连续电压,并将其转换为 0-1023 之间的整数。
搭配: 只能用在 A0~A5 引脚上。读取后的数值通常配合 map() 函数,转换成 0-255 后交给 PWM 引脚使用。
5. 模拟输出(PWM)控制块
语法: analogWrite(引脚号, 数值);
作用: 在支持 PWM 的引脚上输出 0-255 之间的模拟值,用于调光或调速。
搭配: 引脚必须带 ~ 符号(如 9 号)。数值通常来自 analogRead 经过 map 处理后的结果。
6. 条件判断块(逻辑大脑)
语法: if (条件) { 动作A } else { 动作B }
作用: 根据条件是否成立,决定执行哪部分代码。
搭配: 可以嵌套,也可以配合逻辑运算符 &&(与)、||(或)使用。
1.6 编程符号与语法规则解析
在学习后续代码前,必须掌握以下基础符号的含义与搭配规则,这些是构成代码的“砖块”:
1. 大括号 { } —— 代码的“容器”
大括号用来定义一个函数或一段代码的边界。就像盒子一样,把相关的一组指令装在一起。例如 void setup() { ... },大括号里的所有内容都属于 setup 函数。大括号必须成对出现,少一个就会编译报错。
2. 分号 ; —— 语句的“句号”
在 C++ 中,每条执行指令(如 pinMode(9, OUTPUT);)结尾必须加英文分号。它代表“这一句话结束,请执行”。如果没有分号,编译器会把下一行也当成这一句的一部分,导致语法错误。
3. 双斜杠 // —— 代码的“隐身衣”(注释)
写在 // 后面的所有文字都不会被单片机执行,它们只是写给人看的说明。用来解释代码的作用,方便以后复习或让别人看懂。
4. 圆括号 ( ) —— 函数的“传参口”
圆括号专门跟在函数名后面,用来向函数传递参数。比如 pinMode(9, OUTPUT),意思就是告诉 pinMode 函数:“我要配置的是 9号引脚,模式是 输出”。参数之间用英文逗号隔开。
5. 双引号 "" —— 字符串的“衣服”
当你想打印一段文字(如 "Hello Arduino")时,必须用双引号把文字包裹起来,告诉单片机“这是一串字符,不是变量名”。
2. 硬件基础与电路连接
2.1 UNO 核心板与传感器扩展板
本指南实验均基于 Arduino UNO R3 与 Sensor Shield 传感器扩展板 叠层使用。扩展板将 UNO 所有引脚引出为标准的 S (信号)、V (电源)、G (地) 三针接口,用公对母杜邦线即可将任意引脚引出到面包板,极大简化了接线过程。
2.2 面包板的内部玄机
面包板两侧的红蓝电源轨竖向连通,中间孔位横向连通(5 孔一组),中间凹槽隔断上下半区。接线时,将扩展板或 UNO 的 5V 与 GND 分别接入面包板两侧的红蓝电源轨,后续所有需要供电的元件都从这两条轨取电。
2.3 电阻与电子元件原理
驱动 LED 时,必须串联 220Ω 限流电阻,以防电流过大烧毁 LED 或微控制器引脚。光敏电阻等阻值变化的传感器,需配合 10kΩ 固定电阻 组成分压电路,才能将电阻变化转化为可读取的电压。
3. 基础数字 I/O 与输出进阶
3.1 案例1:点亮第一颗 LED
作用: 验证数字输出,让 LED 以固定频率闪烁。
代码拆解与搭配逻辑:
1. pinMode(9, OUTPUT);:配置 9 号引脚为输出模式,相当于把 9 号门设为“只能出不能进”。
2. digitalWrite(9, HIGH);:让 9 号引脚输出高电平(5V),电流流出,点亮 LED。
3. delay(1000);:让 CPU 原地等待 1000 毫秒,保持当前状态。
4. 搭配逻辑:在 setup 中初始化引脚,在 loop 中交替执行“亮-等-灭-等”,从而形成闪烁效果。
void setup() {
pinMode(9, OUTPUT); // 将9号引脚设置为输出模式
}
void loop() {
digitalWrite(9, HIGH); // 9号引脚输出高电平(5V),点亮LED
delay(1000); // 程序暂停1000毫秒(1秒)
digitalWrite(9, LOW); // 9号引脚输出低电平(0V),熄灭LED
delay(1000); // 程序暂停1000毫秒(1秒)
}
接线步骤
所需设备:UNO + 扩展板(已插好)、面包板、LED 灯 1 个、220Ω 电阻 1 个、公对母杜邦线 2 根。
步骤:
1. 取一根杜邦线,母头插在扩展板 D9 的 S 针(信号),公头插在面包板任意一组(如第 1 行的 a 孔)。
2. 将 220Ω 电阻的一端插在第 1 行 b 孔,另一端插在第 1 行 d 孔(跨接在同组导通的孔内)。
3. 将 LED 长脚(正极)插在第 1 行 e 孔,短脚(负极)插在第 2 行 a 孔。
4. 再取一根杜邦线,母头插在扩展板 GND 的 G 针,公头插在面包板第 2 行 b 孔。
3.2 案例2:炫彩流水灯与 SOS 求救
作用: 通过数组控制多个 LED 依次点亮,并模拟摩斯密码 SOS 求救信号。
代码拆解与搭配逻辑:
模块 1:数组与引脚定义
int ledPins[] = {2, 3, 4, 5, 6}; 定义了一个存放 5 个引脚号的数组。它把多个引脚打包在一起,方便后续统一管理。
模块 2:初始化 for 循环
for (int i = 0; i < 5; i++) { pinMode(ledPins[i], OUTPUT); } 这段代码遍历数组中的每一个引脚,并将其全部初始化为输出模式。相比写 5 遍 pinMode 更简洁。
模块 3:流水灯正向与逆向逻辑
使用两个 for 循环。第一个从 0 到 4 递增,依次点亮;第二个从 4 到 0 递减,实现反向流水效果。每次点亮后配合 delay(100) 产生视觉暂留。
模块 4:SOS 频闪逻辑
依靠三个独立的 for 循环组合。第一个循环重复 3 次短闪(200ms),第二个循环重复 3 次长闪(600ms),第三个再次重复 3 次短闪。最终组合成“SOS”的求救信号节奏。
int ledPins[] = {2, 3, 4, 5, 6}; // 定义一个数组,存放5个LED的引脚号
void setup() {
for (int i = 0; i < 5; i++) { // 循环5次
pinMode(ledPins[i], OUTPUT); // 将数组中的5个引脚都设置为输出模式
}
}
void loop() {
for (int i = 0; i < 5; i++) { // 从左到右依次点亮
digitalWrite(ledPins[i], HIGH); // 点亮当前引脚
delay(100); // 延时100毫秒
digitalWrite(ledPins[i], LOW); // 熄灭当前引脚
}
for (int i = 4; i >= 0; i--) { // 从右到左依次点亮
digitalWrite(ledPins[i], HIGH); // 点亮当前引脚
delay(100); // 延时100毫秒
digitalWrite(ledPins[i], LOW); // 熄灭当前引脚
}
}
void setup() {
pinMode(9, OUTPUT); // 设置9号引脚为输出模式
}
void loop() {
// 字母 S:3次短闪
for (int i = 0; i < 3; i++) {
digitalWrite(9, HIGH); delay(200); digitalWrite(9, LOW); delay(200);
}
// 字母 O:3次长闪
for (int i = 0; i < 3; i++) {
digitalWrite(9, HIGH); delay(600); digitalWrite(9, LOW); delay(200);
}
// 字母 S:3次短闪
for (int i = 0; i < 3; i++) {
digitalWrite(9, HIGH); delay(200); digitalWrite(9, LOW); delay(200);
}
delay(2000); // 发完一轮SOS,等待2秒后重新开始
}
接线步骤
流水灯所需设备:LED 灯 5 个、220Ω 电阻 5 个、公对母杜邦线 6 根(5 根信号,1 根地线)。
流水灯接线:
1. 将 D2、D3、D4、D5、D6 的 S 针分别用杜邦线引到面包板第 1、3、5、7、9 行的 a 孔。
2. 每一行之间跨接 220Ω 电阻(如 1b 到 1d)。
3. 5 个 LED 的负极全部汇总到面包板的蓝色电源轨。
4. 从扩展板 GND 引一根线接到面包板蓝色电源轨,完成共地。
SOS 所需设备:LED 1 个、220Ω 电阻 1 个、杜邦线 2 根。
SOS 接线:同案例 1,接在 D9 即可。
3.3 案例3:按键控制与软件消抖
作用: 读取按键状态并控制 LED,解决机械按键在按下瞬间产生的抖动问题。
代码拆解与搭配逻辑:
模块 1:状态记录变量
lastReading 用于记录按键上一次的状态;lastDebounce 用于记录状态改变的时间点。这些变量必须定义在函数外部,才能跨循环保留数值。
模块 2:变化检测与计时
在 loop 中,如果当前读取的状态(reading)与上次不同,说明按键被动了,立刻用 millis() 记录下当前的时间戳。
模块 3:消抖时间过滤
通过 (millis() - lastDebounce) > 50 判断,如果距离上次变化已经超过了 50 毫秒,说明电平已经稳定,不是机械抖动,此时才真正读取并控制 LED。
int buttonPin = 2; // 按键连接在2号引脚
int ledPin = 13; // LED连接在13号引脚
int lastReading = HIGH; // 记录上一次的读取状态
unsigned long lastDebounce = 0; // 记录上一次状态改变的时间
void setup() {
pinMode(ledPin, OUTPUT); // 设置LED为输出模式
pinMode(buttonPin, INPUT_PULLUP); // 设置按键为输入模式,并启用内部上拉电阻
}
void loop() {
int reading = digitalRead(buttonPin); // 读取当前按键状态
if (reading != lastReading) { // 如果状态发生了变化(按下或松开)
lastDebounce = millis(); // 记录当前时间
}
if ((millis() - lastDebounce) > 50) { // 如果距离上次变化已经过了50毫秒(消抖)
if (reading == LOW) { // 如果当前状态是低电平(按下)
digitalWrite(ledPin, HIGH); // 点亮LED
} else {
digitalWrite(ledPin, LOW); // 熄灭LED
}
}
lastReading = reading; // 更新上一次的读取状态
}
接线步骤
所需设备:四脚轻触按键 1 个、LED 灯 1 个、220Ω 电阻 1 个、公对母杜邦线 3 根。
步骤:
1. 将按键跨接在面包板中间凹槽上方(如第 5 行的 e 孔和 f 孔),因为按键同侧两脚内部是不连通的。
2. 杜邦线母头接扩展板 D2 的 S 针,公头接按键左上角引脚(第 5 行 e 孔)。
3. 杜邦线母头接扩展板 GND,公头接按键左下角引脚(第 5 行 a 孔)。
4. LED 接在 D13 或面包板上均可(参考案例 1)。代码中使用了 INPUT_PULLUP,因此无需外部电阻即可读取按键状态。
3.4 案例4:蜂鸣器发声与电子琴
作用: 使无源蜂鸣器输出指定频率的声音,并模拟警报或制作电子琴。
代码拆解与搭配逻辑:
模块 1:tone() 发声原理
tone(引脚, 频率) 会驱动单片机在引脚上生成高频方波,频率越高,蜂鸣器振动越快,音调越尖。停止发声用 noTone(引脚)。
模块 2:宏定义音符
使用 #define NOTE_C4 262 给频率数值起个有意义的名字,不占内存且代码易读。
模块 3:多按键电子琴逻辑
通过连续的 if...else if 语句,依次检测 2、3、4 号引脚的状态。一旦检测到低电平(按键按下),立刻发出对应的音调;若都没按下,则停止发声。
void setup() {
pinMode(8, OUTPUT); // 设置8号引脚为输出模式
}
void loop() {
// 频率从200Hz逐渐升高到800Hz
for (int freq = 200; freq <= 800; freq += 10) {
tone(8, freq); // 在8号引脚输出对应频率的声音
delay(5); // 延时5毫秒
}
// 频率从800Hz逐渐降低到200Hz
for (int freq = 800; freq >= 200; freq -= 10) {
tone(8, freq); // 在8号引脚输出对应频率的声音
delay(5); // 延时5毫秒
}
}
// 定义音符频率的常量
#define NOTE_C4 262
#define NOTE_D4 294
#define NOTE_E4 330
void setup() {
pinMode(2, INPUT_PULLUP); // 设置按键引脚为输入,启用内部上拉
pinMode(3, INPUT_PULLUP);
pinMode(4, INPUT_PULLUP);
pinMode(8, OUTPUT); // 设置蜂鸣器引脚为输出
}
void loop() {
if (digitalRead(2) == LOW) tone(8, NOTE_C4); // 如果按键1按下,发出C4音
else if (digitalRead(3) == LOW) tone(8, NOTE_D4); // 如果按键2按下,发出D4音
else if (digitalRead(4) == LOW) tone(8, NOTE_E4); // 如果按键3按下,发出E4音
else noTone(8); // 如果都没按,停止发声
}
接线步骤
警报器所需设备:无源蜂鸣器 1 个、公对母杜邦线 2 根。
警报器接线:蜂鸣器正极(长脚)接扩展板 D8 的 S 针,负极接 GND。
电子琴所需设备:无源蜂鸣器 1 个、按键 3 个、杜邦线若干。
电子琴接线:
1. 蜂鸣器正极接 D8(S 针),负极接 GND。
2. 按键 1 两端分别接 D2 的 S 针和 GND。
3. 按键 2 两端分别接 D3 的 S 针和 GND。
4. 按键 3 两端分别接 D4 的 S 针和 GND。
4. 模拟信号与传感器生态
4.1 案例5:电位器 ADC 采集与调光
作用: 旋转电位器,手动控制 LED 亮度。
代码拆解与搭配逻辑:
模块 1:analogRead 读取模拟量
analogRead(A0) 读取电位器中心滑片上的电压,单片机将其转换为 0-1023 的数字。
模块 2:map 映射函数
因为 PWM 引脚只能接受 0-255 的数值,map(val, 0, 1023, 0, 255) 将 0-1023 等比例压缩为 0-255。
模块 3:analogWrite 输出
analogWrite(9, brightness) 输出 PWM 方波控制 LED 亮度。整个流程实现了“读取-转换-输出”的闭环。
void setup() {
pinMode(9, OUTPUT); // 设置9号引脚为输出模式
}
void loop() {
int val = analogRead(A0); // 读取A0引脚的模拟值(0-1023)
int brightness = map(val, 0, 1023, 0, 255); // 将0-1023映射到0-255的PWM范围
analogWrite(9, brightness); // 输出PWM信号控制LED亮度
}
接线步骤
所需设备:旋转电位器(3 脚)1 个、LED 1 个、220Ω 电阻 1 个、杜邦线若干。
步骤:
1. 将电位器插在面包板上,引脚分别占据 a、b、c 三行。
2. 电位器左侧引脚连 5V(扩展板 V 针),右侧引脚连 GND。
3. 中间引脚(滑片)用杜邦线连到扩展板 A0 的 S 针。
4. LED 正极经 220Ω 电阻接 D9 的 S 针,负极接 GND。
4.2 案例6:光敏电阻智能夜灯
作用: 根据环境亮度自动调节 LED 亮度。
代码拆解与搭配逻辑:
模块 1:光线与电压的反比逻辑
光线越暗,光敏电阻阻值越大,分压电路在 A0 处产生的电压就越高,analogRead 读取到的数值就越大。
模块 2:constrain 限幅保护
经过 map 映射后的数值有可能会超出 0-255 的范围。constrain() 函数强制将数值约束在合法范围内,防止程序崩溃。
模块 3:delay 控制刷新率
在循环末尾加一个极短的 delay(50),避免 ADC 读取过快导致灯光闪烁不稳定。
void setup() {
pinMode(9, OUTPUT); // 设置9号引脚为输出模式
}
void loop() {
int lightVal = analogRead(A0); // 读取A0引脚的模拟值(光线越暗,值越大)
int pwmVal = map(lightVal, 300, 1023, 0, 255); // 将环境光线映射到PWM输出范围
analogWrite(9, constrain(pwmVal, 0, 255)); // 限制PWM值在0-255之间,并输出控制LED
delay(50); // 短暂延时
}
接线步骤
所需设备:光敏电阻 1 个、10kΩ 固定电阻 1 个、LED 1 个、220Ω 电阻 1 个、杜邦线若干。
步骤:
1. 在面包板上搭建分压电路:将光敏电阻一端接 5V,另一端接 A0(S 针)。
2. 在 A0 所在的同一行,跨接 10kΩ 电阻到 GND。这样 A0 读取的电压会随光线变化。
3. LED 正极经 220Ω 电阻接 D9 的 S 针,负极接 GND。
4.3 案例7:热释电红外 (PIR) 报警
作用: 检测人体运动,触发声光报警。
代码拆解与搭配逻辑:
模块 1:传感器数字输出读取
与读取按键类似,使用 digitalRead(3) 读取传感器。当有人移动时,PIR 内部输出高电平(HIGH)。
模块 2:条件触发联动
通过 if (pirState == HIGH) 判断。如果高电平,点亮 LED 并同时调用 tone(8, 1000) 让蜂鸣器长鸣;否则关闭它们。
void setup() {
pinMode(3, INPUT); // 设置3号引脚为输入模式(连接PIR传感器)
pinMode(13, OUTPUT); // 设置13号引脚为输出模式(连接LED)
pinMode(8, OUTPUT); // 设置8号引脚为输出模式(连接蜂鸣器)
Serial.begin(9600); // 初始化串口通信
}
void loop() {
int pirState = digitalRead(3); // 读取PIR传感器的状态
if (pirState == HIGH) { // 如果检测到人体运动(高电平)
digitalWrite(13, HIGH); // 点亮LED
tone(8, 1000); // 蜂鸣器发声
} else { // 如果没有检测到运动
digitalWrite(13, LOW); // 熄灭LED
noTone(8); // 关闭蜂鸣器
}
delay(200); // 延时200毫秒
}
接线步骤
所需设备:HC-SR501 PIR 模块 1 个、LED 1 个、无源蜂鸣器 1 个、杜邦线若干。
步骤:
1. PIR 模块有 3 根针(VCC、OUT、GND)。直接用母对母杜邦线或公对母线将 VCC 接扩展板 5V,GND 接 GND,OUT 接 D3 的 S 针。
2. LED 正极经 220Ω 电阻接 D13 的 S 针,负极接 GND。
3. 蜂鸣器正极接 D8 的 S 针,负极接 GND。
4.4 案例8:热敏电阻温度计
作用: 读取热敏电阻的阻值变化,通过串口输出当前温度值。
代码拆解与搭配逻辑:
模块 1:模拟值转电压
raw * 5.0 / 1023.0 将读取到的 0-1023 数值还原成真实的电压值(0-5V)。
模块 2:分压公式算阻值
利用串联分压原理,已知上拉电阻(10kΩ)和总电压(5V),倒推出热敏电阻当前的阻值。
模块 3:物理公式转温度
通过 Steinhart-Hart 方程(包含对数 log 运算),把电阻值转换成开尔文温度,最后减去 273.15 转为摄氏度。
void setup() {
Serial.begin(9600); // 初始化串口通信,波特率9600
}
void loop() {
int raw = analogRead(A1); // 读取A1引脚的模拟值
float voltage = raw * 5.0 / 1023.0; // 将模拟值转换为电压值(0-5V)
// 根据分压公式计算热敏电阻阻值
float resistance = (10000.0 * voltage) / (5.0 - voltage);
// 使用Steinhart-Hart方程(B值法)计算温度
float temperature = 1.0 / (1.0 / 298.15 + log(resistance / 10000.0) / 3950.0) - 273.15;
Serial.println(temperature); // 将温度值打印到串口
delay(1000); // 延时1秒
}
接线步骤
所需设备:NTC 热敏电阻 1 个、10kΩ 固定电阻 1 个、杜邦线 3 根。
步骤:
1. 在面包板上搭建分压电路:热敏电阻一端接 5V,另一端连接 A1(S 针)。
2. 在 A1 的同组孔位,跨接 10kΩ 固定电阻到 GND。
5. 通信协议与显示系统
5.1 案例9:I2C OLED 屏幕显示
作用: 在 OLED 屏幕上打印文字和传感器数据。
代码拆解与搭配逻辑:
模块 1:引入库与对象创建
#include 引入屏幕驱动库,随后实例化 display 对象,并告诉它屏幕宽 128、高 64。
模块 2:初始化与坐标设置
begin() 负责握手,clearDisplay() 清屏,setCursor(x, y) 设置文字起笔的坐标。
模块 3:缓冲与刷新机制
println() 只把文字写进内存缓存里,必须调用 display.display() 才会将画面推送到屏幕上,这能防止屏幕闪烁。
#include // 引入I2C通信库
#include // 引入图形库
#include // 引入OLED驱动库
// 创建一个OLED对象,宽度128,高度64
Adafruit_SSD1306 display(128, 64, &Wire, -1);
void setup() {
// 初始化OLED屏幕,地址为0x3C
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay(); // 清空屏幕
display.setTextSize(1); // 设置文字大小
display.setTextColor(WHITE); // 设置文字颜色为白色
display.setCursor(0, 10); // 设置文字起始坐标
display.println("Hello Arduino"); // 打印文字
display.display(); // 将缓冲区内容刷新到屏幕
}
void loop() {
// 屏幕上显示静态内容,无需循环
}
接线步骤
所需设备:I2C 接口 0.96 寸 OLED 屏 1 个、母对母或公对母杜邦线 4 根。
步骤:
1. 扩展板 A4 是 SDA,A5 是 SCL。
2. 将 OLED 的 GND 接扩展板 GND,VCC 接 5V。
3. 将 OLED 的 SDA 接扩展板 A4 的 S 针。
4. 将 OLED 的 SCL 接扩展板 A5 的 S 针。
5.2 案例10:超声波测距仪
作用: 测量前方障碍物距离并打印到串口。
代码拆解与搭配逻辑:
模块 1:微秒级触发信号
先拉低 Trig 保证信号纯净,然后拉高并延时 10 微秒,再拉低。这 10 微秒的高电平就是启动超声波发射的“发令枪”。
模块 2:pulseIn 测量回响时间
Echo 引脚收到回波后变为高电平,持续的时间就是声波往返的时间。pulseIn(echoPin, HIGH) 就是用来抓取这个微秒级的时长。
模块 3:声速公式转换距离
声速约 340m/s(即 0.034cm/μs)。因为声波是一去一回,所以计算出的总距离必须除以 2。
const int trigPin = 9; // 触发引脚连接9号
const int echoPin = 10; // 回响引脚连接10号
void setup() {
Serial.begin(9600); // 初始化串口
pinMode(trigPin, OUTPUT); // 设置触发引脚为输出
pinMode(echoPin, INPUT); // 设置回响引脚为输入
}
void loop() {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10); // 发送10微秒的高电平脉冲触发超声波
digitalWrite(trigPin, LOW);
long duration = pulseIn(echoPin, HIGH); // 测量回响引脚高电平的持续时间
int distance = duration * 0.034 / 2; // 根据声速计算距离(单位:厘米)
Serial.print(distance); // 打印距离到串口
Serial.println(" cm");
delay(500); // 延时500毫秒
}
接线步骤
所需设备:HC-SR04 超声波模块 1 个、母对母或公对母杜邦线 4 根。
步骤:
1. VCC 接扩展板 5V,GND 接 GND。
2. Trig(触发)接扩展板 D9 的 S 针。
3. Echo(回响)接扩展板 D10 的 S 针。
5.3 案例11:DHT11 温湿度气象站
作用: 读取环境温度和湿度数据,需要安装 DHT 库。
代码拆解与搭配逻辑:
模块 1:DHT 对象定义
DHT dht(2, DHT11) 声明传感器型号为 DHT11,数据引脚为 2 号,程序会自动处理单总线的复杂时序。
模块 2:读取与间隔
调用 readTemperature() 和 readHumidity() 获取数据。DHT11 转换速度慢,必须配合 delay(2000) 保证每次读取都有效。
#include "DHT.h" // 引入DHT传感器库
DHT dht(2, DHT11); // 创建DHT对象,数据引脚接2号,型号为DHT11
void setup() {
Serial.begin(9600); // 初始化串口
dht.begin(); // 启动DHT传感器
}
void loop() {
float t = dht.readTemperature(); // 读取温度
float h = dht.readHumidity(); // 读取湿度
Serial.print(t); Serial.print("C "); // 打印温度
Serial.print(h); Serial.println("%"); // 打印湿度
delay(2000); // DHT11读取间隔至少2秒
}
接线步骤
所需设备:DHT11 模块(3 针或 4 针)1 个、杜邦线 3 根。
步骤:
1. 模块 VCC 接扩展板 5V,GND 接 GND。
2. 模块 DATA(数据)引脚接扩展板 D2 的 S 针。
5.4 案例12:红外遥控与串口通信
作用: 接收红外遥控器发出的键值,需要安装 IRremote 库。
代码拆解与搭配逻辑:
模块 1:中断解码
红外信号是一串极其短暂的脉冲。irrecv.decode(&results) 会在底层利用中断捕获脉冲,并存入 results 对象中。
模块 2:准备下一次接收
一旦读取成功,必须调用 irrecv.resume() 清空状态,否则无法接收下一个按键的信号。
#include // 引入红外遥控库
const int RECV_PIN = 3; // 红外接收头连接3号引脚
IRrecv irrecv(RECV_PIN); // 创建红外接收对象
decode_results results; // 用于存储解码结果
void setup() {
Serial.begin(9600); // 初始化串口
irrecv.enableIRIn(); // 启动红外接收
}
void loop() {
if (irrecv.decode(&results)) { // 如果接收到红外信号并解码成功
Serial.println(results.value, HEX); // 将按键值以十六进制打印到串口
irrecv.resume(); // 准备接收下一个信号
}
}
接线步骤
所需设备:HX1838 红外接收头(通常 3 针)1 个、杜邦线 3 根。
步骤:
1. 接收头 VCC 接 5V,GND 接 GND。
2. 接收头 OUT(数据)接扩展板 D3 的 S 针。
6. 执行器与电机控制系统
6.1 案例13:舵机扫频与角度控制
作用: 通过代码控制舵机旋转到指定角度(0-180度),并来回扫动。
代码拆解与搭配逻辑:
模块 1:对象绑定
myservo.attach(9) 是告诉单片机,我接下来要控制的舵机,信号线接在 9 号引脚上。
模块 2:角度写入与扫频
利用两个 for 循环,让角度 pos 从 0 递增到 180,再递减回 0,并不断用 write() 写进去,实现来回扫动。
#include // 引入舵机控制库
Servo myservo; // 创建舵机对象
void setup() {
myservo.attach(9); // 将舵机信号线连接到9号引脚
}
void loop() {
// 从0度旋转到180度
for (int pos = 0; pos <= 180; pos += 5) {
myservo.write(pos); // 设置舵机角度
delay(15); // 等待舵机转动到位
}
// 从180度旋转回0度
for (int pos = 180; pos >= 0; pos -= 5) {
myservo.write(pos);
delay(15);
}
}
接线步骤
所需设备:SG90 舵机 1 个(带 3 根线:棕、红、橙/黄)。
步骤:
1. 舵机的棕色线(GND)接扩展板 GND。
2. 红色线(VCC)接扩展板 5V。
3. 橙色或黄色线(信号)接扩展板 D9 的 S 针。
注意:如果舵机抖动严重,说明 UNO 供电不足,需使用外部 5V 电源供电,并将外部电源负极与 UNO 的 GND 相连(共地)。
6.2 案例14:步进电机精准定位
作用: 驱动步进电机按固定步数旋转,实现精确定位。
代码拆解与搭配逻辑:
模块 1:步进参数初始化
Stepper myStepper(一圈步数, 引脚1, 引脚2, 引脚3, 引脚4) 声明了一圈需要走多少步,以及控制各线圈的引脚编号。
模块 2:步数与速度控制
setSpeed(60) 规定了转速。step(步数) 控制转动的角度。传入正数正转,负数反转。必须注意,step 是阻塞执行的,转完之前程序会停在这一行。
#include // 引入步进电机库
const int stepsPerRev = 200; // 步进电机转一圈的步数
// 创建一个步进电机对象,控制引脚为8, 9, 10, 11
Stepper myStepper(stepsPerRev, 8, 9, 10, 11);
void setup() {
myStepper.setSpeed(60); // 设置电机转速为60 RPM
}
void loop() {
myStepper.step(stepsPerRev); // 正转一圈
delay(500); // 停顿500毫秒
myStepper.step(-stepsPerRev); // 反转一圈
delay(500);
}
接线步骤
所需设备:28BYJ-48 步进电机及 ULN2003 驱动板 1 套、母对母杜邦线 6 根。
步骤:
1. 步进电机的 5 针插头直接插在驱动板的白色插座上。
2. 驱动板上的 IN1、IN2、IN3、IN4 用杜邦线分别连接到扩展板的 D8、D9、D10、D11 的 S 针。
3. 驱动板的 VCC 接扩展板 5V(若电机不转,可尝试接外部电源),GND 接 GND。
6.3 案例15:直流电机调速与正反转
作用: 控制直流电机的正反转与转速。
代码拆解与搭配逻辑:
模块 1:方向控制逻辑
通过给 IN1 和 IN2 输入相反的高低电平(一个 HIGH 一个 LOW)来改变电流方向,从而控制电机正转或反转。
模块 2:速度控制逻辑
ENA 引脚必须连接带 PWM 功能的引脚(如 9 号)。使用 analogWrite() 输出 0-255 的占空比,即可实现从静止到全速的无级调速。
const int ENA = 9; // 使能引脚(控制速度),接9号PWM引脚
const int IN1 = 8; // 方向控制引脚1
const int IN2 = 7; // 方向控制引脚2
void setup() {
pinMode(ENA, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
}
void loop() {
// 正转,速度中等 (150/255)
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
analogWrite(ENA, 150);
delay(2000); // 转动2秒
// 反转,速度全开 (255/255)
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
analogWrite(ENA, 255);
delay(2000); // 转动2秒
}
接线步骤
所需设备:L298N 电机驱动模块 1 个、直流电机 1 个、外部电池盒(供电)、杜邦线若干。
步骤:
1. 将外部电源(如 9V 电池)正极接 L298N 的 12V 输入,负极接 GND。
2. 极其重要:将 L298N 的 GND 与扩展板的 GND 连在一起(共地)。
3. L298N 的 ENA 接扩展板 D9 的 S 针(必须是 PWM 引脚)。
4. IN1 接扩展板 D8,IN2 接扩展板 D7。
5. 将直流电机的两根线分别接入 L298N 的 OUT1 和 OUT2 端子。
6.4 案例16:综合智能小车避障逻辑
作用: 结合超声波传感器与直流电机,实现小车遇到障碍物自动后退转弯。
代码拆解与搭配逻辑:
模块 1:自定义函数封装
int getDistance() 把复杂的超声波时序代码全部藏起来,对外只提供一个“返回距离”的功能,让主程序看起来干净利落。
模块 2:条件决策与电机联动
在 loop 中,如果 dist < 20,立即执行刹车、停顿、单侧反转的指令,实现避障;否则,保持两侧前进。
const int trigPin = 12; // 超声波触发引脚
const int echoPin = 13; // 超声波回响引脚
const int motorLeft = 5; // 左电机速度控制(PWM)
const int motorRight = 6; // 右电机速度控制(PWM)
void setup() {
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(motorLeft, OUTPUT);
pinMode(motorRight, OUTPUT);
}
// 封装一个函数用于测量距离
int getDistance() {
digitalWrite(trigPin, LOW); delayMicroseconds(2);
digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW);
long duration = pulseIn(echoPin, HIGH);
return duration * 0.034 / 2;
}
void loop() {
int dist = getDistance(); // 获取距离
if (dist < 20) { // 如果距离小于20厘米
// 停止
digitalWrite(motorLeft, LOW);
digitalWrite(motorRight, LOW);
delay(500);
// 后退转向
digitalWrite(motorLeft, HIGH);
digitalWrite(motorRight, LOW);
delay(500);
} else {
// 安全,全速前进
digitalWrite(motorLeft, HIGH);
digitalWrite(motorRight, HIGH);
}
}
接线步骤
所需设备:HC-SR04 超声波 1 个、L298N 驱动板 1 个、两个直流电机、小车底盘、外部电源、杜邦线若干。
步骤:
1. 超声波 Trig 接扩展板 D12,Echo 接 D13。
2. L298N 的 ENA 接 D5(控制左轮速度),IN1 接 D8,IN2 接 D7(控制左轮方向)。
3. L298N 的 ENB 接 D6(控制右轮速度),IN3 接 D4,IN4 接 D3(控制右轮方向)。
4. 电机接入 OUT1/OUT2(左)、OUT3/OUT4(右)。
5. 必须确保 L298N 使用外部电池供电,并将 L298N 的 GND 与 UNO 扩展板的 GND 相连。
7. 底层进阶与入坟避坑
7.1 millis() 非阻塞调度
作用: 在多任务系统中替代 delay(),避免程序假死卡顿。
代码拆解与搭配逻辑:
模块 1:记录时间戳
previousMillis 就像一个秒表,记录上一次执行动作的时间点。
模块 2:时间差比较
每次循环都用当前时间 currentMillis 减去上次记录的时间。如果差值大于设定间隔,说明该干活了。
模块 3:更新时钟
干完活后,必须把 previousMillis = currentMillis,重置秒表,为下一次计时做准备。
unsigned long previousMillis = 0; // 记录上一次触发的时间
const long interval = 1000; // 设定时间间隔(1000毫秒)
void setup() {
pinMode(13, OUTPUT); // 设置13号引脚为输出
}
void loop() {
unsigned long currentMillis = millis(); // 获取当前系统运行时间
// 如果当前时间减去上次时间,大于设定的间隔
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis; // 更新上次触发时间
digitalWrite(13, !digitalRead(13)); // 翻转LED的状态
}
// 这里可以继续写其他任务,绝对不会被卡住
}
接线步骤
所需设备:无需外部元件,使用 UNO 板载 13 号引脚 LED 即可。若需外接,参考案例 1 接在 D13。
7.2 内存溢出与 F() 宏
作用: 防止 UNO 仅有的 2KB SRAM 被大量字符串耗尽导致不断重启。
代码拆解与搭配逻辑:
模块 1:内存存储机制
未加 F() 时,双引号里的字符串会被复制到 RAM 中,占空间。F() 宏强制它留在 Flash 闪存里,释放 RAM。
模块 2:全局应用
通常将程序中所有不需要修改的固定文本(如 Serial.println)都用 F() 包起来。
void setup() {
Serial.begin(9600); // 初始化串口
}
void loop() {
// 使用 F() 宏,将字符串强制存储在 Flash 闪存中,避免占用 SRAM
Serial.println(F("String stored in Flash"));
delay(1000);
}
接线步骤
所需设备:无需额外接线,只需将 UNO 用 USB 连接至电脑,打开 IDE 的串口监视器(波特率 9600)即可看到输出。