嵌入式 C 语言编程技巧与最佳实践
嵌入式 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 语言编程需要兼顾效率、可靠性和可维护性。遵循最佳实践,你的代码会更优秀! ...