单片机开发之理解面向对象思想
假设项目组现在有个需求,需要为一个智能家居系统开发LED灯控制模块,共有三种LED灯,单色LED、RGB灯、智能灯带(SPI控制)。
小张大学刚毕业,觉得这个需求就是控制个LED灯,没有什么难度,于是自告奋勇,承担了开发工作。由于功能简单,小张很快就开发完了,于是往仓库推送了代码。然后主管审查了小张的代码,意料之中小张用的是面向过程的编程思想,写的代码是又臭又长,不好维护。好在项目不是很着急,于是给小张培训了一下面向对象的编程思想,让他重构了代码。
本文第一部分面向过程的设计思路是小张初版的代码,第二部分面向过程的设计思路是最终版的代码。两者对比起来看就能发现面向对象编程思想的魅力所在。
目录
1. 面向过程的设计思路
面向过程就是按照功能流程来组织代码,重点是"做什么事情,按什么顺序做"。
1.1. 按功能模块划分
// ============ 硬件抽象层 ============
// GPIO操作函数
void gpio_init(uint8_t pin, uint8_t mode);
void gpio_write(uint8_t pin, uint8_t value);
uint8_t gpio_read(uint8_t pin);
// PWM操作函数
void pwm_init(uint8_t pin, uint16_t frequency);
void pwm_set_duty(uint8_t pin, uint16_t duty);
void pwm_stop(uint8_t pin);
// SPI操作函数
void spi_init(uint8_t cs_pin);
void spi_transmit(uint8_t* data, uint16_t len);
// ============ LED硬件控制层 ============
// 单色LED控制
void simple_led_init(uint8_t pin);
void simple_led_on(uint8_t pin, uint8_t brightness);
void simple_led_off(uint8_t pin);
// RGB LED控制
void rgb_led_init(uint8_t r_pin, uint8_t g_pin, uint8_t b_pin);
void rgb_led_on(uint8_t r_pin, uint8_t g_pin, uint8_t b_pin,
uint8_t r_val, uint8_t g_val, uint8_t b_val, uint8_t brightness);
void rgb_led_off(uint8_t r_pin, uint8_t g_pin, uint8_t b_pin);
// LED灯带控制
void led_strip_init(uint8_t cs_pin, uint16_t led_count);
void led_strip_on(uint8_t cs_pin, uint16_t led_count, uint8_t brightness);
void led_strip_off(uint8_t cs_pin, uint16_t led_count);
1.2. 数据结构定义
// ============ 配置数据结构 ============
// 系统中所有LED的配置信息
typedef enum {
LED_TYPE_SIMPLE,
LED_TYPE_RGB,
LED_TYPE_STRIP
} led_type_t;
typedef struct {
uint8_t led_id;
led_type_t type;
uint8_t brightness;
uint8_t is_on;
// 根据类型使用不同的引脚配置
union {
struct {
uint8_t gpio_pin;
} simple;
struct {
uint8_t red_pin;
uint8_t green_pin;
uint8_t blue_pin;
uint8_t red_value;
uint8_t green_value;
uint8_t blue_value;
} rgb;
struct {
uint8_t spi_cs_pin;
uint16_t led_count;
} strip;
} config;
} led_info_t;
// 全局LED配置数组
#define MAX_LEDS 10
led_info_t g_led_list[MAX_LEDS];
uint8_t g_led_count = 0;
1.3. 系统初始化流程
// ============ 系统初始化 ============
void led_system_init(void) {
// 第1步:硬件初始化
gpio_init_all();
pwm_init_all();
spi_init_all();
// 第2步:加载LED配置
load_led_config();
// 第3步:初始化各个LED
for (int i = 0; i < g_led_count; i++) {
switch (g_led_list[i].type) {
case LED_TYPE_SIMPLE:
simple_led_init(g_led_list[i].config.simple.gpio_pin);
break;
case LED_TYPE_RGB:
rgb_led_init(g_led_list[i].config.rgb.red_pin,
g_led_list[i].config.rgb.green_pin,
g_led_list[i].config.rgb.blue_pin);
break;
case LED_TYPE_STRIP:
led_strip_init(g_led_list[i].config.strip.spi_cs_pin,
g_led_list[i].config.strip.led_count);
break;
}
}
printf("LED系统初始化完成,共%d个LED\n", g_led_count);
}
void load_led_config(void) {
// 硬编码配置(实际项目中可能从EEPROM或配置文件读取)
g_led_list[0] = (led_info_t){1, LED_TYPE_SIMPLE, 0, 0, {.simple = {12}}};
g_led_list[1] = (led_info_t){2, LED_TYPE_RGB, 0, 0, {.rgb = {5, 6, 7, 255, 255, 255}}};
g_led_list[2] = (led_info_t){3, LED_TYPE_STRIP, 0, 0, {.strip = {8, 30}}};
g_led_count = 3;
}
1.4. 核心控制逻辑
// ============ LED控制接口 ============
void led_turn_on(uint8_t led_id, uint8_t brightness) {
// 第1步:查找LED
int index = find_led_by_id(led_id);
if (index < 0) {
printf("LED %d 未找到\n", led_id);
return;
}
// 第2步:更新状态
g_led_list[index].brightness = brightness;
g_led_list[index].is_on = 1;
// 第3步:根据类型执行相应操作
switch (g_led_list[index].type) {
case LED_TYPE_SIMPLE:
simple_led_on(g_led_list[index].config.simple.gpio_pin, brightness);
break;
case LED_TYPE_RGB:
rgb_led_on(g_led_list[index].config.rgb.red_pin,
g_led_list[index].config.rgb.green_pin,
g_led_list[index].config.rgb.blue_pin,
g_led_list[index].config.rgb.red_value,
g_led_list[index].config.rgb.green_value,
g_led_list[index].config.rgb.blue_value,
brightness);
break;
case LED_TYPE_STRIP:
led_strip_on(g_led_list[index].config.strip.spi_cs_pin,
g_led_list[index].config.strip.led_count,
brightness);
break;
}
printf("LED %d 已开启,亮度 %d%%\n", led_id, brightness);
}
void led_turn_off(uint8_t led_id) {
// 第1步:查找LED
int index = find_led_by_id(led_id);
if (index < 0) return;
// 第2步:更新状态
g_led_list[index].is_on = 0;
// 第3步:根据类型执行相应操作
switch (g_led_list[index].type) {
case LED_TYPE_SIMPLE:
simple_led_off(g_led_list[index].config.simple.gpio_pin);
break;
case LED_TYPE_RGB:
rgb_led_off(g_led_list[index].config.rgb.red_pin,
g_led_list[index].config.rgb.green_pin,
g_led_list[index].config.rgb.blue_pin);
break;
case LED_TYPE_STRIP:
led_strip_off(g_led_list[index].config.strip.spi_cs_pin,
g_led_list[index].config.strip.led_count);
break;
}
printf("LED %d 已关闭\n", led_id);
}
// 批量控制
void led_control_all(uint8_t brightness) {
printf("开始控制所有LED,亮度:%d%%\n", brightness);
for (int i = 0; i < g_led_count; i++) {
led_turn_on(g_led_list[i].led_id, brightness);
}
printf("所有LED控制完成\n");
}
// 辅助函数
int find_led_by_id(uint8_t led_id) {
for (int i = 0; i < g_led_count; i++) {
if (g_led_list[i].led_id == led_id) {
return i;
}
}
return -1;
}
1.5. 主程序流程
// ============ 主程序 ============
int main(void) {
// 第1步:系统初始化
led_system_init();
// 第2步:执行控制逻辑
while (1) {
// 场景1:单独控制
led_turn_on(1, 80); // 厨房灯
delay_ms(1000);
led_turn_on(2, 60); // 客厅RGB灯
delay_ms(1000);
led_turn_on(3, 40); // 卧室灯带
delay_ms(2000);
// 场景2:全部关闭
led_turn_off(1);
led_turn_off(2);
led_turn_off(3);
delay_ms(2000);
// 场景3:批量控制
led_control_all(50);
delay_ms(3000);
}
}
2. 面向对象的设计思路
2.1. 基类:灯(Light)
// 所有灯的通用"图纸"
typedef struct {
uint8_t id; // 灯的编号
uint8_t brightness; // 亮度 0-100
uint8_t is_on; // 开关状态
// 所有灯都应该有的基本功能(接口)
void (*turn_on)(void* self);
void (*turn_off)(void* self);
void (*set_brightness)(void* self, uint8_t level);
void (*get_status)(void* self);
} Light;
2.2. 子类:不同类型的LED灯
// 单色LED灯
typedef struct {
Light base; // 继承基本灯的功能
uint8_t gpio_pin; // GPIO引脚号
} SimpleLED;
// RGB LED灯
typedef struct {
Light base; // 继承基本灯的功能
uint8_t red_pin;
uint8_t green_pin;
uint8_t blue_pin;
uint8_t red_value; // 红色分量
uint8_t green_value; // 绿色分量
uint8_t blue_value; // 蓝色分量
} RgbLED;
// 智能LED灯带
typedef struct {
Light base; // 继承基本灯的功能
uint8_t spi_cs_pin;
uint16_t led_count; // LED数量
uint8_t* led_data; // LED数据缓冲区
} LedStrip;
2.3. 面向过程的具体实现
虽然接口统一,但每种LED的具体实现是面向过程的:
// 单色LED的具体实现(面向过程的步骤)
void simple_led_turn_on(void* self) {
SimpleLED* led = (SimpleLED*)self;
// 第1步:设置GPIO为输出模式
gpio_set_mode(led->gpio_pin, GPIO_OUTPUT);
// 第2步:根据亮度计算PWM占空比
uint16_t pwm_value = (led->base.brightness * 255) / 100;
// 第3步:启动PWM输出
pwm_set_duty(led->gpio_pin, pwm_value);
// 第4步:更新状态
led->base.is_on = 1;
printf("单色LED %d 已开启,亮度: %d%%\n", led->base.id, led->base.brightness);
}
// RGB LED的具体实现(面向过程的步骤)
void rgb_led_turn_on(void* self) {
RgbLED* led = (RgbLED*)self;
// 第1步:计算各颜色通道的PWM值
uint16_t red_pwm = (led->red_value * led->base.brightness) / 100;
uint16_t green_pwm = (led->green_value * led->base.brightness) / 100;
uint16_t blue_pwm = (led->blue_value * led->base.brightness) / 100;
// 第2步:设置各个颜色通道
pwm_set_duty(led->red_pin, red_pwm);
pwm_set_duty(led->green_pin, green_pwm);
pwm_set_duty(led->blue_pin, blue_pwm);
// 第3步:更新状态
led->base.is_on = 1;
printf("RGB LED %d 已开启,颜色: R%d G%d B%d,亮度: %d%%\n",
led->base.id, led->red_value, led->green_value, led->blue_value, led->base.brightness);
}
// LED灯带的具体实现(面向过程的步骤)
void led_strip_turn_on(void* self) {
LedStrip* strip = (LedStrip*)self;
// 第1步:准备SPI数据
for (int i = 0; i < strip->led_count * 3; i++) {
strip->led_data[i] = (strip->base.brightness * 255) / 100;
}
// 第2步:选中SPI设备
gpio_write(strip->spi_cs_pin, 0);
// 第3步:发送数据到LED灯带
spi_transmit(strip->led_data, strip->led_count * 3);
// 第4步:取消选中
gpio_write(strip->spi_cs_pin, 1);
// 第5步:更新状态
strip->base.is_on = 1;
printf("LED灯带 %d 已开启,%d个LED,亮度: %d%%\n",
strip->base.id, strip->led_count, strip->base.brightness);
}
2.4. 多态的威力(统一接口)
// 统一的控制函数(多态)
void control_all_lights(Light** lights, int count, uint8_t brightness) {
for (int i = 0; i < count; i++) {
// 不管是什么类型的LED,调用方式都一样
lights[i]->set_brightness(lights[i], brightness);
lights[i]->turn_on(lights[i]);
}
}
// 使用示例
int main() {
// 创建不同类型的LED对象
SimpleLED kitchen_light = {{1, 80, 0, simple_led_turn_on, simple_led_turn_off, simple_led_set_brightness, simple_led_get_status}, 12};
RgbLED living_room_light = {{2, 60, 0, rgb_led_turn_on, rgb_led_turn_off, rgb_led_set_brightness, rgb_led_get_status}, 5, 6, 7, 255, 200, 100};
LedStrip bedroom_strip = {{3, 40, 0, led_strip_turn_on, led_strip_turn_off, led_strip_set_brightness, led_strip_get_status}, 8, 30, NULL};
// 统一管理(多态的好处)
Light* all_lights[] = {(Light*)&kitchen_light, (Light*)&living_room_light, (Light*)&bedroom_strip};
// 一键控制所有LED - 每种LED用自己的方式实现
control_all_lights(all_lights, 3, 75);
return 0;
}
3. 总结
3.1. 面向对象设计的好处
- 统一接口:不管什么LED,都用
turn_on()开灯- 便于扩展:新增LED类型不影响现有代码
- 便于管理:可以用同一套代码控制所有LED
3.2. 面向过程实现的必要性
- 单色LED需要控制一个GPIO
- RGB LED需要控制三个PWM通道
- LED灯带需要SPI通信
- 每种都有自己具体的硬件操作步骤
3.3. 两者结合
- 面向对象让你的代码结构清晰,易于维护
- 面向过程让你的代码能真正控制硬件
3.4. 实际开发中的价值
// 如果只用面向过程,代码可能是这样:
void turn_on_kitchen_light() { /* 控制GPIO12 */ }
void turn_on_living_room_light() { /* 控制RGB PWM */ }
void turn_on_bedroom_strip() { /* 控制SPI */ }
void set_kitchen_brightness(uint8_t level) { /* ... */ }
void set_living_room_brightness(uint8_t level) { /* ... */ }
// ... 每种LED都要写一套函数
// 用面向对象后:
Light* light = get_light_by_id(room_id);
light->turn_on(light); // 简洁统一
这就是面向对象的价值:用统一的方式处理不同的事物,让复杂系统变得简单易懂。但底层实现依然是面向过程的硬件操作步骤。
浙公网安备 33010602011771号