跳到主要内容

程序结构

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 控制和按钮读取。