2. 将全局变量封装为模块接口
任务说明
在嵌入式C代码中,全局变量被多个模块直接读写会导致耦合度高、难以测试和维护。本案例将一组散落的全局变量封装到一个独立模块内部,仅通过函数接口对外提供访问,从而隔离修改影响。
重构前问题代码
/* 散落在各处的全局变量 */
int system_status = 0;
uint16_t adc_value = 0;
uint8_t fault_flags = 0;
float target_temp = 25.0f;
/* 模块A中直接修改 */
void task_a(void) {
system_status = 2;
adc_value = read_adc();
if (adc_value > 1000) fault_flags |= 0x01;
}
/* 模块B中直接读取 */
void task_b(void) {
if (fault_flags & 0x01) {
target_temp = 30.0f;
}
printf("status=%d adc=%u\n", system_status, adc_value);
}
封装后的模块接口
/* system_state.h */
#ifndef SYSTEM_STATE_H
#define SYSTEM_STATE_H
#include <stdint.h>
/* 状态标志位定义 */
#define FAULT_ADC_OVER 0x01
#define FAULT_TEMP_HIGH 0x02
void sys_set_status(int status);
int sys_get_status(void);
void sys_set_adc(uint16_t value);
uint16_t sys_get_adc(void);
void sys_set_fault(uint8_t mask);
void sys_clear_fault(uint8_t mask);
uint8_t sys_get_fault(void);
void sys_set_target_temp(float temp);
float sys_get_target_temp(void);
#endif /* SYSTEM_STATE_H */
/* system_state.c */
#include "system_state.h"
/* 全局变量全部收敛到本模块内部,并声明为static */
static int system_status = 0;
static uint16_t adc_value = 0;
static uint8_t fault_flags = 0;
static float target_temp = 25.0f;
void sys_set_status(int status) {
system_status = status;
}
int sys_get_status(void) {
return system_status;
}
void sys_set_adc(uint16_t value) {
adc_value = value;
}
uint16_t sys_get_adc(void) {
return adc_value;
}
void sys_set_fault(uint8_t mask) {
fault_flags |= mask;
}
void sys_clear_fault(uint8_t mask) {
fault_flags &= ~mask;
}
uint8_t sys_get_fault(void) {
return fault_flags;
}
void sys_set_target_temp(float temp) {
target_temp = temp;
}
float sys_get_target_temp(void) {
return target_temp;
}
重构后调用方式
/* 模块A */
#include "system_state.h"
void task_a(void) {
uint16_t adc = read_adc();
sys_set_adc(adc);
if (adc > 1000) {
sys_set_fault(FAULT_ADC_OVER);
}
sys_set_status(2);
}
/* 模块B */
#include "system_state.h"
void task_b(void) {
if (sys_get_fault() & FAULT_ADC_OVER) {
sys_set_target_temp(30.0f);
}
printf("status=%d adc=%u\n",
sys_get_status(), sys_get_adc());
}
编译与验证
# 编译(假设所有文件在同一目录)
gcc -c system_state.c -o system_state.o
gcc -c task_a.c -o task_a.o
gcc -c task_b.c -o task_b.o
gcc system_state.o task_a.o task_b.o -o firmware_sim
# 运行
./firmware_sim
验证要点:
- 编译时无“未定义符号”错误,说明所有变量访问均通过接口完成。
- 在
system_state.c中将任一static变量删除,编译其他模块应报错,证明外部无法直接访问内部变量。 - 运行后输出格式与重构前一致,说明行为未改变。
常见问题
- 接口膨胀:若变量过多,可考虑用结构体聚合相关状态,再提供整体读写接口。
- 原子性:在中断与主循环共享状态时,需在接口内增加临界区保护(如关中断或使用互斥锁),本案例未涉及中断场景。
- 性能开销:函数调用相比直接变量访问有少量额外开销,对时间敏感路径可声明为
inline或仍保留直接访问(需权衡耦合度)。