程序结构
Arduino 程序(也称为 Sketch)具有特定的结构,理解这个结构是编写 Arduino 代码的基础。
基本结构
每个 Arduino 程序必须包含两个核心函数:
void setup() {
// 初始化代码,只执行一次
}
void loop() {
// 主代码,无限循环执行
}
setup() 函数
setup() 函数在程序启动时运行一次,用于初始化设置:
- 配置引脚模式(输入/输出)
- 初始化串口通信
- 设置初始变量值
- 初始化库
void setup() {
// 设置引脚 13 为输出模式
pinMode(13, OUTPUT);
// 初始化串口通信,波特率 9600
Serial.begin(9600);
// 打印启动信息
Serial.println("Arduino 已启动!");
}
loop() 函数
loop() 函数在 setup() 执行完后无限循环运行:
- 读取传感器数据
- 控制输出设备
- 处理逻辑判断
- 与其他设备通信
void loop() {
// 点亮 LED
digitalWrite(13, HIGH);
// 等待 1 秒
delay(1000);
// 熄灭 LED
digitalWrite(13, LOW);
// 等待 1 秒
delay(1000);
}
完整程序示例
让我们看一个完整的程序,它展示了程序结构的各个部分:
/*
* 程序名称:呼吸灯
* 功能:让 LED 像呼吸一样渐亮渐暗
* 作者:Arduino 学习者
* 日期:2024
*/
// ========== 全局变量和常量 ==========
const int LED_PIN = 9; // LED 连接的 PWM 引脚
const int DELAY_TIME = 10; // 延时时间(毫秒)
int brightness = 0; // 当前亮度值
int fadeAmount = 5; // 亮度变化步进
// ========== 初始化函数 ==========
void setup() {
// 设置 LED 引脚为输出模式
pinMode(LED_PIN, OUTPUT);
// 初始化串口用于调试
Serial.begin(9600);
Serial.println("呼吸灯程序已启动");
}
// ========== 主循环函数 ==========
void loop() {
// 设置 LED 亮度
analogWrite(LED_PIN, brightness);
// 打印当前亮度到串口监视器
Serial.print("亮度: ");
Serial.println(brightness);
// 改变亮度值
brightness = brightness + fadeAmount;
// 到达边界时反转变化方向
if (brightness <= 0 || brightness >= 255) {
fadeAmount = -fadeAmount;
}
// 短暂延时,产生平滑效果
delay(DELAY_TIME);
}
代码组织顺序
Arduino 程序的标准组织顺序:
// 1. 头文件包含(如果有)
#include <Servo.h>
// 2. 宏定义和常量
#define BUTTON_PIN 2
const int LED_PIN = 13;
// 3. 全局变量
int counter = 0;
bool ledState = false;
// 4. 对象实例化(如果有)
Servo myServo;
// 5. setup() 函数
void setup() {
// 初始化代码
}
// 6. loop() 函数
void loop() {
// 主循环代码
}
// 7. 自定义函数(放在最后)
void myFunction() {
// 自定义功能
}
注释
良好的注释习惯能让代码更易读、易维护。
单行注释
使用 // 进行单行注释:
int sensorValue = 0; // 存储传感器读数
// 下面这行代码读取模拟引脚
sensorValue = analogRead(A0);
多行注释
使用 /* */ 进行多行注释:
/*
* 这是一个多行注释
* 可以跨越多行
* 常用于函数说明或代码块注释
*/
void setup() {
/*
这里也可以
使用多行注释
*/
}
注释最佳实践
/*
* 函数:readTemperature
* 功能:从 DS18B20 温度传感器读取温度
* 参数:无
* 返回:float 类型的温度值(摄氏度)
* 注意:需要提前初始化 OneWire 总线
*/
float readTemperature() {
// 发送温度转换命令
sensors.requestTemperatures();
// 读取温度值并返回
return sensors.getTempCByIndex(0); // 获取第一个传感器的温度
}
变量作用域
全局变量
在函数外部定义的变量,整个程序都可以访问:
int globalVar = 0; // 全局变量
void setup() {
globalVar = 10; // 可以访问和修改
}
void loop() {
globalVar++; // 每次循环增加 1
}
局部变量
在函数内部定义的变量,只在函数内有效:
void setup() {
int localVar = 5; // 局部变量,只在 setup 中有效
}
void loop() {
// localVar 在这里无法访问
int anotherVar = 10; // 这是另一个局部变量
}
静态变量
使用 static 关键字,变量值在函数调用之间保持:
void loop() {
static int counter = 0; // 只初始化一次
counter++; // 每次调用 loop 都增加
Serial.println(counter);
delay(1000);
}
自定义函数
将代码组织成函数可以提高可读性和复用性。
函数定义
// 返回类型 函数名(参数类型 参数名, ...)
void blinkLED(int pin, int duration) {
digitalWrite(pin, HIGH);
delay(duration);
digitalWrite(pin, LOW);
delay(duration);
}
完整示例
const int RED_LED = 9;
const int GREEN_LED = 10;
const int BLUE_LED = 11;
void setup() {
// 初始化所有 LED 引脚
pinMode(RED_LED, OUTPUT);
pinMode(GREEN_LED, OUTPUT);
pinMode(BLUE_LED, OUTPUT);
}
void loop() {
// 使用自定义函数控制 LED
fadeInOut(RED_LED, 100);
fadeInOut(GREEN_LED, 100);
fadeInOut(BLUE_LED, 100);
}
// 自定义函数:让指定 LED 渐亮渐暗
void fadeInOut(int pin, int speed) {
// 渐亮
for (int i = 0; i <= 255; i++) {
analogWrite(pin, i);
delay(speed);
}
// 渐暗
for (int i = 255; i >= 0; i--) {
analogWrite(pin, i);
delay(speed);
}
}
条件编译
Arduino 支持 C/C++ 的条件编译指令:
// 调试模式开关
#define DEBUG 1
void setup() {
Serial.begin(9600);
#ifdef DEBUG
Serial.println("调试模式已开启");
#endif
}
void loop() {
int value = analogRead(A0);
#ifdef DEBUG
Serial.print("传感器值: ");
Serial.println(value);
#endif
delay(100);
}
程序执行流程
理解程序的执行顺序对调试很重要:
上电/复位
↓
执行全局变量初始化
↓
执行 setup() 【仅一次】
↓
执行 loop() 第 1 次
↓
执行 loop() 第 2 次
↓
执行 loop() 第 3 次
↓
...
常见错误
1. 在 setup 中放循环逻辑
// 错误示例
void setup() {
while (true) { // 不要这样做!
digitalWrite(13, HIGH);
delay(1000);
}
}
// 正确做法
void setup() {
pinMode(13, OUTPUT);
}
void loop() {
digitalWrite(13, HIGH);
delay(1000);
}
2. 变量未初始化
// 错误示例
int counter;
void setup() {
// counter 的值是不确定的!
Serial.println(counter);
}
// 正确做法
int counter = 0; // 定义时初始化
3. 函数定义位置错误
// 错误:函数定义在 loop 之后但没有声明
void loop() {
myFunction(); // 编译错误:找不到函数
}
void myFunction() {
// ...
}
// 正确做法 1:先定义后使用
void myFunction() {
// ...
}
void loop() {
myFunction();
}
// 正确做法 2:先声明后定义
void myFunction(); // 函数声明
void loop() {
myFunction();
}
void myFunction() {
// 函数定义
}
下一步
理解了程序结构后,我们将学习如何控制 Arduino 的数字引脚,实现 LED 控制和按钮读取。