嵌入式 C 语言特点

与通用 C 的区别

特性嵌入式 C通用 C
资源受限(KB 级内存)充足(MB/GB 级)
直接硬件访问寄存器操作抽象 API
实时性关键不重要
可靠性极高可接受失败

数据类型

使用固定宽度类型

#include <stdint.h>

// ✅ 推荐:明确位宽
uint8_t status;
uint16_t adc_value;
int32_t temperature;

// ❌ 避免:位宽不确定
char flag;      // 可能是 8 位或 32 位
int count;      // 可能是 16 位或 32 位
long timeout;   // 可能是 32 位或 64 位

位域操作

// 状态寄存器定义
typedef struct {
    uint8_t ready    : 1;  // bit 0
    uint8_t error    : 1;  // bit 1
    uint8_t mode     : 2;  // bit 2-3
    uint8_t reserved : 4;  // bit 4-7
} StatusReg_t;

// 使用
StatusReg_t status;
status.ready = 1;
status.mode = 0x02;

内存管理

避免动态分配

// ❌ 避免:malloc/free
void* buffer = malloc(size);
free(buffer);

// ✅ 推荐:静态分配
static uint8_t buffer[256];

// ✅ 推荐:内存池
#define POOL_SIZE 10
static uint8_t memory_pool[POOL_SIZE][BLOCK_SIZE];
static uint8_t pool_bitmap = 0;

const 和 volatile

// const:只读数据(存储在 Flash)
const uint16_t lookup_table[256] = {0};

// volatile:可能被外部修改
volatile uint8_t gpio_input;
volatile uint32_t* const REGISTER_ADDR = (uint32_t*)0x40020000;

// 组合使用
const volatile uint32_t* const HW_REGISTER = 
    (uint32_t*)0x40020000;

中断编程

中断服务函数

// 中断服务函数
void EXTI0_IRQHandler(void) __attribute__((interrupt));

void EXTI0_IRQHandler(void) {
    // 1. 清除中断标志
    EXTI->PR = EXTI_PR_PR0;
    
    // 2. 快速处理(置标志)
    g_button_pressed = 1;
    
    // 3. 如果需要,通知任务
    xSemaphoreGiveFromISR(xBinarySemaphore, NULL);
}

临界区保护

// 方法 1:关中断
uint32_t primask = __get_PRIMASK();
__disable_irq();
// 临界区代码
__set_PRIMASK(primask);

// 方法 2:使用库
__disable_irq();
// 代码
__enable_irq();

// 方法 3:FreeRTOS
taskENTER_CRITICAL();
// 代码
taskEXIT_CRITICAL();

位操作技巧

常用宏

// 置位
#define SET_BIT(REG, BIT)     ((REG) |= (1 << (BIT)))

// 清零
#define CLEAR_BIT(REG, BIT)   ((REG) &= ~(1 << (BIT)))

// 翻转
#define TOGGLE_BIT(REG, BIT)  ((REG) ^= (1 << (BIT)))

// 读位
#define READ_BIT(REG, BIT)    (((REG) >> (BIT)) & 1)

// 设置位域
#define SET_BITS(REG, POS, LEN, VAL) \
    ((REG) = (((REG) & ~(((1 << (LEN)) - 1) << (POS))) | \
             (((VAL) & ((1 << (LEN)) - 1)) << (POS))))

使用示例

// 配置 GPIO
#define GPIO_MODE_OUTPUT  1
#define GPIO_PIN_5        5

// 设置 PA5 为输出
SET_BITS(GPIOA->MODER, 10, 2, GPIO_MODE_OUTPUT);

// 切换 LED
TOGGLE_BIT(GPIOA->ODR, GPIO_PIN_5);

// 检查按键
if (READ_BIT(GPIOB->IDR, 0) == 0) {
    // 按键按下
}

代码优化

空间优化

// 使用更小的数据类型
uint8_t counter;  // 而不是 int

// 共用体节省内存
typedef union {
    uint32_t raw;
    struct {
        uint8_t b0, b1, b2, b3;
    } bytes;
} DataUnion_t;

// 使用位域
struct {
    uint8_t flag1 : 1;
    uint8_t flag2 : 1;
    uint8_t mode  : 3;
} flags;  // 只占 1 字节

时间优化

// 使用查表代替计算
const uint16_t sin_table[256] = {...};
uint16_t sin_value = sin_table[i];

// 使用移位代替乘除
x = y * 8;   // → x = y << 3;
x = y / 4;   // → x = y >> 2;

// 循环展开
for (i = 0; i < 4; i++) {  // →
    array[i] = 0;
}
// 展开为:
array[0] = 0; array[1] = 0; 
array[2] = 0; array[3] = 0;

状态机

实现模式识别

typedef enum {
    STATE_IDLE,
    STATE_START,
    STATE_PROCESS,
    STATE_END
} State_t;

State_t current_state = STATE_IDLE;

void state_machine(void) {
    switch (current_state) {
        case STATE_IDLE:
            if (start_condition) {
                current_state = STATE_START;
            }
            break;
            
        case STATE_START:
            init_resources();
            current_state = STATE_PROCESS;
            break;
            
        case STATE_PROCESS:
            if (process_complete) {
                current_state = STATE_END;
            }
            break;
            
        case STATE_END:
            cleanup();
            current_state = STATE_IDLE;
            break;
    }
}

错误处理

返回值检查

typedef enum {
    ERR_OK = 0,
    ERR_INVALID_PARAM = -1,
    ERR_TIMEOUT = -2,
    ERR_NO_MEMORY = -3
} Error_t;

Error_t sensor_read(float* value) {
    if (value == NULL) {
        return ERR_INVALID_PARAM;
    }
    
    // 读取传感器
    if (timeout) {
        return ERR_TIMEOUT;
    }
    
    *value = read_value;
    return ERR_OK;
}

// 使用
float temp;
Error_t err = sensor_read(&temp);
if (err != ERR_OK) {
    // 错误处理
}

调试技巧

断言

#include <assert.h>

void process_data(uint8_t* data, uint16_t size) {
    assert(data != NULL);
    assert(size > 0);
    assert(size <= MAX_SIZE);
    
    // 处理代码
}

日志宏

#define LOG_LEVEL_ERROR   0
#define LOG_LEVEL_INFO    1
#define LOG_LEVEL_DEBUG   2

#define LOG_LEVEL         LOG_LEVEL_DEBUG

#if LOG_LEVEL >= LOG_LEVEL_ERROR
#define LOG_ERROR(fmt, ...) \
    printf("[E] " fmt "\n", ##__VA_ARGS__)
#endif

#if LOG_LEVEL >= LOG_LEVEL_INFO
#define LOG_INFO(fmt, ...) \
    printf("[I] " fmt "\n", ##__VA_ARGS__)
#endif

#if LOG_LEVEL >= LOG_LEVEL_DEBUG
#define LOG_DEBUG(fmt, ...) \
    printf("[D] %s:%d " fmt "\n", \
           __FILE__, __LINE__, ##__VA_ARGS__)
#endif

编码规范

命名规范

// 类型:大驼峰 + _t
typedef struct { ... } SensorData_t;

// 函数:小写 + 下划线
void sensor_init(void);
float sensor_read_temperature(void);

// 变量:小写
uint8_t counter;
float temperature;

// 常量:大写
#define MAX_RETRY  10
#define TIMEOUT_MS 1000

// 宏函数:大写
#define MIN(a, b)  ((a) < (b) ? (a) : (b))

注释规范

/**
 * @brief  读取温度传感器
 * @param  sensor_id: 传感器 ID
 * @param  value: 存储读取值的指针
 * @retval 成功返回 ERR_OK,失败返回错误码
 */
Error_t sensor_read_temperature(uint8_t sensor_id, float* value);

结语

嵌入式 C 语言编程需要兼顾效率、可靠性和可维护性。遵循最佳实践,你的代码会更优秀!


参考资料:MISRA C 规范、C 语言编程规范、嵌入式软件设计指南