[PICO][Adv]面向对象编程

面向对象编程(Object-Oriented Programming,简称 OOP) 是一种编程范式,它将数据和操作数据的方法封装在对象中。通过使用 OOP,你可以更好地组织代码,使其更易于理解和维护。本文将介绍如何在 Raspberry Pi Pico 2 开发中使用面向对象编程技术,涵盖 Arduino 风格(基于 Arduino-Pico 核心,使用 C++)和 C/C++ 风格(基于 Pico SDK,使用 C 语言模拟 OOP 或直接使用 C++)两种方式。


1. 什么是面向对象编程?

面向对象编程的核心概念是对象。类是对象的蓝图或模板,而对象是类的实例。类可以包含属性(变量)和方法(函数),这些属性和方法定义了对象的行为和状态。

在 Pico 2 项目中,OOP 可以帮助你将复杂的代码分解为更小、更易管理的部分。例如,如果你正在控制多个 LED 灯,你可以为每个 LED 创建一个类,而不是为每个 LED 编写重复的代码。


2. 创建一个简单的类

2.1 Arduino 风格:使用 C++ 类

让我们从一个简单的例子开始。假设我们有一个 LED,我们可以通过 Pico 2 的 GPIO 引脚控制它的开关。创建一个 LED 类来封装这个功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class LED {
private:
uint8_t pin; // 存储 LED 连接的引脚号

public:
// 构造函数,初始化 LED 对象
LED(uint8_t p) : pin(p) {
pinMode(pin, OUTPUT);
}

// 方法:打开 LED
void on() {
digitalWrite(pin, HIGH);
}

// 方法:关闭 LED
void off() {
digitalWrite(pin, LOW);
}
};

使用类创建对象

1
2
3
4
5
6
7
8
9
10
11
12
LED led1(25);   // 板载 LED 使用 GPIO 25

void setup() {
// 无需额外初始化,构造函数已处理
}

void loop() {
led1.on();
delay(1000);
led1.off();
delay(1000);
}

2.2 C/C++ 风格:使用结构体与函数指针

在纯 C 语言中,没有类的概念,但可以通过结构体封装数据,并用函数指针模拟方法。这种方式在嵌入式 C 中很常见。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include "pico/stdlib.h"

// 定义 LED 结构体
typedef struct {
uint8_t pin;
void (*on)(struct LED* self);
void (*off)(struct LED* self);
} LED;

// 方法实现
void LED_on(LED* self) {
gpio_put(self->pin, 1);
}

void LED_off(LED* self) {
gpio_put(self->pin, 0);
}

// 初始化函数(类似构造函数)
void LED_init(LED* self, uint8_t pin) {
self->pin = pin;
self->on = LED_on;
self->off = LED_off;
gpio_init(pin);
gpio_set_dir(pin, GPIO_OUT);
}

使用对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main() {
stdio_init_all();

LED led1;
LED_init(&led1, 25);

while (true) {
led1.on(&led1);
sleep_ms(1000);
led1.off(&led1);
sleep_ms(1000);
}
return 0;
}

提示:C 语言的模拟虽然稍显繁琐,但能实现封装和多态(通过函数指针),是很多嵌入式项目的选择。


3. 类的继承

继承允许你创建一个新类,继承现有类的属性和方法,从而重用代码。例如,我们创建一个 RGBLED 类,它可以控制一个 RGB LED(共阳极或共阴极)。RGBLED 可以继承 LED 类(或包含多个 LED 对象),并添加新方法。

3.1 Arduino 风格:C++ 继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class RGBLED : public LED {
private:
uint8_t redPin, greenPin, bluePin;

public:
RGBLED(uint8_t r, uint8_t g, uint8_t b)
: LED(r), redPin(r), greenPin(g), bluePin(b) {
// 注意:LED 构造函数已初始化红色引脚为输出
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);
}

void setColor(uint8_t r, uint8_t g, uint8_t b) {
analogWrite(redPin, r);
analogWrite(greenPin, g);
analogWrite(bluePin, b);
}
};

使用 RGBLED

1
2
3
4
5
6
7
8
9
10
11
12
RGBLED rgb(9, 10, 11);  // 假设使用 PWM 引脚

void setup() { }

void loop() {
rgb.setColor(255, 0, 0); // 红色
delay(1000);
rgb.setColor(0, 255, 0); // 绿色
delay(1000);
rgb.setColor(0, 0, 255); // 蓝色
delay(1000);
}

3.2 C/C++ 风格:组合而非继承

在 C 语言中,我们通常使用组合(在一个结构体中包含另一个结构体)来模拟继承关系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 定义 LED 结构体(同上)
// ...

// 定义 RGBLED 结构体,包含三个 LED 对象
typedef struct {
LED red;
LED green;
LED blue;
void (*setColor)(struct RGBLED* self, uint8_t r, uint8_t g, uint8_t b);
} RGBLED;

void RGBLED_setColor(RGBLED* self, uint8_t r, uint8_t g, uint8_t b) {
// 对于共阴极 RGB LED,直接设置亮度(PWM)
// 实际需根据 LED 类型调整
analogWrite(self->red.pin, r);
analogWrite(self->green.pin, g);
analogWrite(self->blue.pin, b);
}

void RGBLED_init(RGBLED* self, uint8_t rPin, uint8_t gPin, uint8_t bPin) {
LED_init(&self->red, rPin);
LED_init(&self->green, gPin);
LED_init(&self->blue, bPin);
self->setColor = RGBLED_setColor;
}

说明:C 语言中组合比继承更灵活,也更易于理解。


4. 实际应用:机器人组件封装

假设你正在开发一个简单的机器人,它有两个轮子和一个超声波传感器。我们可以为每个组件创建类,使主逻辑更加清晰。

4.1 Arduino 风格:完整的类设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// 轮子类
class Wheel {
private:
uint8_t pinFwd, pinRev;

public:
Wheel(uint8_t fwd, uint8_t rev) : pinFwd(fwd), pinRev(rev) {
pinMode(pinFwd, OUTPUT);
pinMode(pinRev, OUTPUT);
}

void forward() {
digitalWrite(pinFwd, HIGH);
digitalWrite(pinRev, LOW);
}

void backward() {
digitalWrite(pinFwd, LOW);
digitalWrite(pinRev, HIGH);
}

void stop() {
digitalWrite(pinFwd, LOW);
digitalWrite(pinRev, LOW);
}
};

// 超声波传感器类
class UltrasonicSensor {
private:
uint8_t trigPin, echoPin;

public:
UltrasonicSensor(uint8_t trig, uint8_t echo) : trigPin(trig), echoPin(echo) {
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
}

long getDistance() {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
long duration = pulseIn(echoPin, HIGH);
return duration * 0.034 / 2; // 距离(cm)
}
};

// 使用
Wheel leftWheel(2, 3);
Wheel rightWheel(4, 5);
UltrasonicSensor sensor(6, 7);

void setup() { }

void loop() {
long distance = sensor.getDistance();
if (distance > 20) {
leftWheel.forward();
rightWheel.forward();
} else {
leftWheel.stop();
rightWheel.stop();
}
delay(100);
}

4.2 C/C++ 风格:结构体模拟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/pwm.h"

// 轮子结构体
typedef struct {
uint8_t pin_fwd;
uint8_t pin_rev;
void (*forward)(struct Wheel* self);
void (*backward)(struct Wheel* self);
void (*stop)(struct Wheel* self);
} Wheel;

void Wheel_forward(Wheel* self) {
gpio_put(self->pin_fwd, 1);
gpio_put(self->pin_rev, 0);
}
void Wheel_backward(Wheel* self) {
gpio_put(self->pin_fwd, 0);
gpio_put(self->pin_rev, 1);
}
void Wheel_stop(Wheel* self) {
gpio_put(self->pin_fwd, 0);
gpio_put(self->pin_rev, 0);
}
void Wheel_init(Wheel* self, uint8_t fwd, uint8_t rev) {
self->pin_fwd = fwd;
self->pin_rev = rev;
self->forward = Wheel_forward;
self->backward = Wheel_backward;
self->stop = Wheel_stop;
gpio_init(fwd); gpio_set_dir(fwd, GPIO_OUT);
gpio_init(rev); gpio_set_dir(rev, GPIO_OUT);
}

// 超声波传感器结构体
typedef struct {
uint8_t trig_pin;
uint8_t echo_pin;
long (*get_distance)(struct UltrasonicSensor* self);
} UltrasonicSensor;

long UltrasonicSensor_get_distance(UltrasonicSensor* self) {
gpio_put(self->trig_pin, 0);
sleep_us(2);
gpio_put(self->trig_pin, 1);
sleep_us(10);
gpio_put(self->trig_pin, 0);
uint64_t duration = 0;
while (gpio_get(self->echo_pin) == 0);
absolute_time_t start = get_absolute_time();
while (gpio_get(self->echo_pin) == 1);
duration = absolute_time_diff_us(start, get_absolute_time());
return duration * 0.034 / 2;
}
void UltrasonicSensor_init(UltrasonicSensor* self, uint8_t trig, uint8_t echo) {
self->trig_pin = trig;
self->echo_pin = echo;
self->get_distance = UltrasonicSensor_get_distance;
gpio_init(trig); gpio_set_dir(trig, GPIO_OUT);
gpio_init(echo); gpio_set_dir(echo, GPIO_IN);
}

int main() {
stdio_init_all();

Wheel leftWheel, rightWheel;
Wheel_init(&leftWheel, 2, 3);
Wheel_init(&rightWheel, 4, 5);

UltrasonicSensor sensor;
UltrasonicSensor_init(&sensor, 6, 7);

while (true) {
long distance = sensor.get_distance(&sensor);
if (distance > 20) {
leftWheel.forward(&leftWheel);
rightWheel.forward(&rightWheel);
} else {
leftWheel.stop(&leftWheel);
rightWheel.stop(&rightWheel);
}
sleep_ms(100);
}
return 0;
}

注意:在 C 语言中,我们通过函数指针模拟方法,并显式传递 self 指针,实现了类似面向对象的效果。


5. 总结

面向对象编程是一种强大的编程范式,它可以帮助你更好地组织和管理 Pico 2 代码。通过使用类和对象(C++)或结构体与函数指针(C),你可以将复杂的代码分解为更小、更易管理的部分。

特性 Arduino 风格(C++) C/C++ 风格(C 语言模拟)
封装 使用 classpublic/private 使用结构体 + 函数指针
继承 使用 : 直接继承 通常使用组合(包含子对象)
多态 虚函数表 显式函数指针
适用性 简洁、自然 资源消耗极小,适合纯 C 项目

6. 附加资源与练习

  • 练习 1:创建一个 Button 类(C++)或结构体(C),用于读取按钮状态,并在按下时点亮 LED。
  • 练习 2:扩展 RGBLED 类,添加一个 fade() 方法,使 LED 的颜色逐渐变化。
  • 练习 3:使用面向对象思想,将 Pico 2 的 I2C 或 SPI 外设封装成类,简化传感器驱动开发。
  • 资源

通过不断实践和探索,你将能够更好地掌握 Pico 2 中的面向对象编程技术,并应用于更复杂的嵌入式项目。


[PICO][Adv]面向对象编程
https://ka5fxt.cn/2026/03/30/PICO-Adv-面向对象编程/
发布于
2026年3月30日
许可协议