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

验证要点:

  1. 编译时无“未定义符号”错误,说明所有变量访问均通过接口完成。
  2. 在system_state.c中将任一static变量删除,编译其他模块应报错,证明外部无法直接访问内部变量。
  3. 运行后输出格式与重构前一致,说明行为未改变。

常见问题

  • 接口膨胀:若变量过多,可考虑用结构体聚合相关状态,再提供整体读写接口。
  • 原子性:在中断与主循环共享状态时,需在接口内增加临界区保护(如关中断或使用互斥锁),本案例未涉及中断场景。
  • 性能开销:函数调用相比直接变量访问有少量额外开销,对时间敏感路径可声明为inline或仍保留直接访问(需权衡耦合度)。