关于 embedded-best-practices
Embedded Best Practices Skill 为嵌入式系统开发和代码评审提供实践指导,重点面向 ESP32 与 ESP-IDF。内容涉及固件结构、FreeRTOS 任务、外设、内存、错误处理和电源管理。
主要功能
- 01ESP-IDF 项目与组件组织
- 02FreeRTOS 任务和同步指导
- 03内存分配与安全字符串处理
- 04错误处理与降级运行
- 05外设和中断实践
- 06WiFi、日志与电源管理
适用场景
- 01组织 ESP-IDF 固件项目
- 02评审 FreeRTOS 任务与队列设计
- 03处理外设初始化和中断
- 04降低内存与功耗风险
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ laurigates/ mcu-tinkering-lab --skill embedded-best-practices安装后在 Claude Code 中输入/embedded-best-practices确认已加载。
详细介绍
嵌入式开发最佳实践技能
此技能提供嵌入式系统开发的全面指导,重点关注 ESP32 和 ESP-IDF。
适用场景
- 编写新的固件代码
- 审查实现方案
- 学习嵌入式开发模式
- 调试问题
- 优化代码
ESP-IDF 项目结构
推荐布局
project/
├── main/
│ ├── CMakeLists.txt
│ ├── main.c
│ ├── Kconfig.projbuild
│ └── include/
│ └── project.h
├── components/
│ └── custom_component/
│ ├── CMakeLists.txt
│ ├── component.c
│ └── include/
│ └── component.h
├── CMakeLists.txt
├── sdkconfig.defaults
├── partitions.csv
└── README.md
组件组织
- 每个组件只负责一项职责
- 在 include/ 中提供清晰的公共接口
- 在 src/ 中实现私有逻辑
- 记录依赖项
FreeRTOS 最佳实践
任务设计
// Good: Proper task function
void sensor_task(void *pvParameters) {
sensor_config_t *config = (sensor_config_t *)pvParameters;
while (1) {
// Do work
read_sensor(config);
// Must yield to prevent watchdog
vTaskDelay(pdMS_TO_TICKS(100));
}
// Tasks should never return, but if they do:
vTaskDelete(NULL);
}
// Create with appropriate stack
xTaskCreate(sensor_task, "sensor", 4096, &config, 5, &task_handle);
栈大小
- 典型任务从 4096 字节开始
- 使用
uxTaskGetStackHighWaterMark()测量实际使用量 - 增加 25% 的安全余量
- 摄像头/网络任务可能需要 8192+
同步
// Mutex for shared resource protection
SemaphoreHandle_t mutex = xSemaphoreCreateMutex();
// Use with timeout, never infinite wait in production
if (xSemaphoreTake(mutex, pdMS_TO_TICKS(1000)) == pdTRUE) {
// Access shared resource
xSemaphoreGive(mutex);
} else {
ESP_LOGE(TAG, "Failed to acquire mutex");
}
队列使用
// Prefer queues for inter-task communication
QueueHandle_t data_queue = xQueueCreate(10, sizeof(sensor_data_t));
// Send with timeout
sensor_data_t data = {.value = 42};
if (xQueueSend(data_queue, &data, pdMS_TO_TICKS(100)) != pdTRUE) {
ESP_LOGW(TAG, "Queue full, dropping data");
}
// Receive
sensor_data_t received;
if (xQueueReceive(data_queue, &received, portMAX_DELAY) == pdTRUE) {
process_data(&received);
}
内存管理
静态分配与动态分配
// Prefer static for fixed resources
static StaticTask_t task_buffer;
static StackType_t task_stack[4096];
TaskHandle_t task = xTaskCreateStatic(
task_func, "task", 4096, NULL, 5,
task_stack, &task_buffer
);
// Dynamic for variable-size resources
char *buffer = heap_caps_malloc(size, MALLOC_CAP_DEFAULT);
if (buffer == NULL) {
ESP_LOGE(TAG, "Allocation failed");
return ESP_ERR_NO_MEM;
}
// ... use buffer ...
free(buffer);
字符串处理
// Bad
char buf[64];
sprintf(buf, "Value: %d", value);
// Good - prevents buffer overflow
char buf[64];
snprintf(buf, sizeof(buf), "Value: %d", value);
// For const strings, keep in flash
static const char *TAG = "mymodule";
ESP_LOGI(TAG, "Starting");
错误处理
ESP-IDF 错误处理模式
esp_err_t initialize_peripheral(void) {
esp_err_t ret;
ret = gpio_config(&io_conf);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "GPIO config failed: %s", esp_err_to_name(ret));
return ret;
}
ret = spi_bus_initialize(SPI2_HOST, &bus_cfg, DMA_CHAN);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "SPI init failed: %s", esp_err_to_name(ret));
// Clean up GPIO if needed
return ret;
}
return ESP_OK;
}
// Use ESP_ERROR_CHECK for fatal errors only
ESP_ERROR_CHECK(nvs_flash_init());
优雅降级
// Don't crash on non-fatal errors
if (wifi_connect() != ESP_OK) {
ESP_LOGW(TAG, "WiFi failed, running in offline mode");
run_offline_mode();
}
外设初始化
GPIO 配置
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << GPIO_NUM_2),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
ESP_ERROR_CHECK(gpio_config(&io_conf));
I2C 设置
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = GPIO_NUM_21,
.scl_io_num = GPIO_NUM_22,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 400000,
};
ESP_ERROR_CHECK(i2c_param_config(I2C_NUM_0, &conf));
ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM_0, conf.mode, 0, 0, 0));
中断处理程序
保持 ISR 简洁
// ISR - keep it SHORT
static void IRAM_ATTR gpio_isr_handler(void *arg) {
uint32_t gpio_num = (uint32_t)arg;
// Just signal, don't process
xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}
// Process in task
void gpio_task(void *arg) {
uint32_t io_num;
while (1) {
if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
// Heavy processing here, not in ISR
process_gpio_event(io_num);
}
}
}
IRAM 注意事项
- 使用
IRAM_ATTR标记 ISR 处理程序 - ISR 调用的函数也需要
IRAM_ATTR - 尽量减少 IRAM 使用量(上限约为 128KB)
WiFi 最佳实践
连接处理
// Use event loop for WiFi events
static void wifi_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data) {
if (event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_id == WIFI_EVENT_STA_DISCONNECTED) {
ESP_LOGI(TAG, "Disconnected, retrying...");
esp_wifi_connect();
}
}
// Register handler
ESP_ERROR_CHECK(esp_event_handler_instance_register(
WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL));
PSRAM 与 WiFi
- WiFi 会占用大量内存
- 对内存密集型应用启用 PSRAM
- 使用
CONFIG_SPIRAM_USE_MALLOC扩展堆
日志记录
日志级别
ESP_LOGE(TAG, "Error: critical failure"); // Always shown
ESP_LOGW(TAG, "Warning: unusual condition"); // Important
ESP_LOGI(TAG, "Info: normal operation"); // Default
ESP_LOGD(TAG, "Debug: detailed info"); // Development
ESP_LOGV(TAG, "Verbose: very detailed"); // Tracing
生产环境日志
- 通过 menuconfig 设置日志级别
- 在生产环境中减少日志记录(最低为 ESP_LOGW)
- 日志字符串会占用闪存空间
电源管理
轻睡眠
// Enable automatic light sleep
esp_pm_config_esp32_t pm_config = {
.max_freq_mhz = 240,
.min_freq_mhz = 80,
.light_sleep_enable = true,
};
ESP_ERROR_CHECK(esp_pm_configure(&pm_config));
深度睡眠
// Configure wakeup source
esp_sleep_enable_timer_wakeup(60 * 1000000); // 60 seconds
// Enter deep sleep
esp_deep_sleep_start();
其他资源
如需了解特定主题的更多详细信息,请参阅:
- ESP-IDF 编程指南
- FreeRTOS 文档
- ESP32 技术参考手册
评论
支持 Markdown