嵌入式 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 语言编程规范、嵌入式软件设计指南