Loading...
Loading...
Compare original and translation side by side
Quick navigation
- Memory patterns, ESP32 heap fragmentation, ETL containers →
references/memory-patterns.md- C99/C11 patterns, UART read-to-clear, _Static_assert →
references/c-patterns.md- Debugging workflows (stack overflow, heap corruption, HardFault) →
references/debugging.md- Coding style and conventions →
references/coding-style.md- Firmware architecture, RTOS IPC, low-power, ESP32 deep sleep, CI/CD →
references/architecture.md- STM32 pitfalls, CubeMX, hard-to-debug issues, code review →
references/stm32-pitfalls.md- Design patterns, SOLID for embedded, HAL design, C/C++ callbacks →
references/design-patterns.md- MPU protection, watchdog hierarchy, safety-critical, protocols, linker scripts →
references/safety-hardware.md
快速导航
- 内存模式、ESP32堆碎片化、ETL容器 →
references/memory-patterns.md- C99/C11模式、UART读清除、_Static_assert →
references/c-patterns.md- 调试工作流(栈溢出、堆损坏、HardFault) →
references/debugging.md- 编码风格与规范 →
references/coding-style.md- 固件架构、RTOS进程间通信、低功耗、ESP32深度休眠、CI/CD →
references/architecture.md- STM32常见陷阱、CubeMX、难调试问题、代码评审 →
references/stm32-pitfalls.md- 设计模式、嵌入式SOLID原则、HAL设计、C/C++回调 →
references/design-patterns.md- MPU保护、看门狗层级、安全关键设计、协议、链接脚本 →
references/safety-hardware.md
| Need | Solution | Notes |
|---|---|---|
| Short-lived local data | Stack | Keep < 256 bytes per frame; profile with |
| Fixed singleton objects | | Zero-initialized before |
| Fixed array of objects | Object pool ( | O(1) alloc/free, no fragmentation |
| Temporary scratch space | Arena / bump allocator | Reset whole arena at end of operation |
| Variable-size messages | Ring buffer of fixed-size slots | Simplest ISR-safe comms pattern |
| Custom lifetime control | Placement new + static storage | Full control, no heap involvement |
| Never in ISR | Any of the above except stack | Allocator calls are not ISR-safe |
| Avoid entirely | | Non-deterministic; fragmentation risk |
| 需求 | 解决方案 | 说明 |
|---|---|---|
| 短生命周期本地数据 | 栈 | 每帧栈占用保持在256字节以内;使用 |
| 固定单例对象 | 文件或函数作用域的 | |
| 固定对象数组 | 对象池( | O(1)时间复杂度分配/释放,无碎片化问题 |
| 临时暂存空间 | 竞技场/碰撞分配器 | 操作完成后整体重置竞技场 |
| 可变大小消息 | 固定大小槽位的环形缓冲区 | 最简单的ISR安全通信模式 |
| 自定义生命周期控制 | placement new + 静态存储 | 完全可控,不涉及堆 |
| ISR中禁止使用 | 除栈以外的所有分配方式 | 分配器调用不具备ISR安全性 |
| 完全避免使用 | | 非确定性;存在碎片化风险 |
class SpiGuard {
public:
explicit SpiGuard(SPI_HandleTypeDef* spi) : spi_(spi) {
HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET);
}
~SpiGuard() {
HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET);
}
// Non-copyable, non-movable — guard is tied to this scope
SpiGuard(const SpiGuard&) = delete;
SpiGuard& operator=(const SpiGuard&) = delete;
private:
SPI_HandleTypeDef* spi_;
};
// Usage: CS deasserts automatically at end of scope
void read_sensor() {
SpiGuard guard(&hspi1);
// ... transfer bytes ...
} // CS deasserts hereclass SpiGuard {
public:
explicit SpiGuard(SPI_HandleTypeDef* spi) : spi_(spi) {
HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET);
}
~SpiGuard() {
HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET);
}
// 不可复制、不可移动——守卫与当前作用域绑定
SpiGuard(const SpiGuard&) = delete;
SpiGuard& operator=(const SpiGuard&) = delete;
private:
SPI_HandleTypeDef* spi_;
};
// 使用示例:作用域结束时CS引脚自动取消断言
void read_sensor() {
SpiGuard guard(&hspi1);
// ... 传输字节 ...
} // 此处CS引脚自动取消断言references/memory-patterns.md_Static_assertstatic_assertreferences/memory-patterns.md_Static_assertstatic_assertreferences/memory-patterns.mdtemplate<typename T, size_t N>
class ObjectPool {
public:
template<typename... Args>
T* allocate(Args&&... args) {
for (auto& slot : slots_) {
if (!slot.used) {
slot.used = true;
return new (&slot.storage) T(std::forward<Args>(args)...);
}
}
return nullptr; // Pool exhausted — handle at call site
}
void free(T* obj) {
obj->~T();
for (auto& slot : slots_) {
if (reinterpret_cast<T*>(&slot.storage) == obj) {
slot.used = false;
return;
}
}
}
private:
struct Slot {
alignas(T) std::byte storage[sizeof(T)];
bool used = false;
};
Slot slots_[N]{};
};references/memory-patterns.mdtemplate<typename T, size_t N>
class ObjectPool {
public:
template<typename... Args>
T* allocate(Args&&... args) {
for (auto& slot : slots_) {
if (!slot.used) {
slot.used = true;
return new (&slot.storage) T(std::forward<Args>(args)...);
}
}
return nullptr; // 池耗尽——调用方自行处理
}
void free(T* obj) {
obj->~T();
for (auto& slot : slots_) {
if (reinterpret_cast<T*>(&slot.storage) == obj) {
slot.used = false;
return;
}
}
}
private:
struct Slot {
alignas(T) std::byte storage[sizeof(T)];
bool used = false;
};
Slot slots_[N]{};
};volatilevolatile// Define register layout matching the hardware manual
struct UartRegisters {
volatile uint32_t SR; // Status register
volatile uint32_t DR; // Data register
volatile uint32_t BRR; // Baud rate register
volatile uint32_t CR1; // Control register 1
};
// Map to the hardware base address
auto* uart = reinterpret_cast<UartRegisters*>(0x40011000U);
// Read status — volatile ensures each read hits the hardware
if (uart->SR & (1U << 5)) { // RXNE bit
uint8_t byte = static_cast<uint8_t>(uart->DR);
}volatilevolatile// 定义与硬件手册匹配的寄存器布局
struct UartRegisters {
volatile uint32_t SR; // 状态寄存器
volatile uint32_t DR; // 数据寄存器
volatile uint32_t BRR; // 波特率寄存器
volatile uint32_t CR1; // 控制寄存器1
};
// 映射到硬件基地址
auto* uart = reinterpret_cast<UartRegisters*>(0x40011000U);
// 读取状态——volatile确保每次读取都直接访问硬件
if (uart->SR & (1U << 5)) { // RXNE位
uint8_t byte = static_cast<uint8_t>(uart->DR);
}std::atomic#include <atomic>
// Atomic: ISR and task can access without disabling interrupts
std::atomic<uint32_t> adc_value{0};
// In ISR:
void ADC_IRQHandler() {
adc_value.store(ADC1->DR, std::memory_order_relaxed);
}
// In task:
uint32_t val = adc_value.load(std::memory_order_relaxed);
// Critical section for larger structures (ARM Cortex-M):
struct SensorFrame { uint32_t timestamp; int16_t x, y, z; };
volatile SensorFrame latest_frame{};
void update_frame_from_isr(const SensorFrame& f) {
__disable_irq();
latest_frame = f;
__enable_irq();
}std::atomic#include <atomic>
// 原子变量:ISR和任务无需禁用中断即可访问
std::atomic<uint32_t> adc_value{0};
// ISR中代码:
void ADC_IRQHandler() {
adc_value.store(ADC1->DR, std::memory_order_relaxed);
}
// 任务中代码:
uint32_t val = adc_value.load(std::memory_order_relaxed);
// 更大结构的临界区实现(ARM Cortex-M平台):
struct SensorFrame { uint32_t timestamp; int16_t x, y, z; };
volatile SensorFrame latest_frame{};
void update_frame_from_isr(const SensorFrame& f) {
__disable_irq();
latest_frame = f;
__enable_irq();
}| Pointer type | Use in embedded? | Guidance |
|---|---|---|
| Raw pointer (observing) | Yes | For non-owning references; make ownership explicit in naming |
| Raw pointer (owning) | Carefully | Only into static/pool storage where lifetime is obvious |
| Yes, with care | Zero overhead; use with custom deleters for pool objects |
| Yes | Returns pool objects to their pool on destruction |
| Avoid | Reference counting uses heap and is non-deterministic |
| Avoid | Tied to |
// unique_ptr with pool deleter — zero heap, automatic return to pool
ObjectPool<SensorData, 8> sensor_pool;
auto deleter = [](SensorData* p) { sensor_pool.free(p); };
using PooledSensor = std::unique_ptr<SensorData, decltype(deleter)>;
PooledSensor acquire_sensor() {
return PooledSensor(sensor_pool.allocate(), deleter);
}| 指针类型 | 嵌入式中是否可用 | 指导说明 |
|---|---|---|
| 原始指针(观测用) | 是 | 用于非持有引用;通过命名明确所有权 |
| 原始指针(持有用) | 谨慎使用 | 仅指向生命周期明确的静态/池存储 |
| 是,需谨慎 | 零开销;搭配自定义删除器用于池对象 |
| 是 | 析构时自动将池对象归还到对应对象池 |
| 避免使用 | 引用计数需要堆,且非确定性 |
| 避免使用 | 与 |
// 带池删除器的unique_ptr——零堆占用,自动归还到对象池
ObjectPool<SensorData, 8> sensor_pool;
auto deleter = [](SensorData* p) { sensor_pool.free(p); };
using PooledSensor = std::unique_ptr<SensorData, decltype(deleter)>;
PooledSensor acquire_sensor() {
return PooledSensor(sensor_pool.allocate(), deleter);
}// constexpr: computed at compile time, no runtime cost
constexpr uint32_t BAUD_DIVISOR = PCLK_FREQ / (16U * TARGET_BAUD);
static_assert(BAUD_DIVISOR > 0 && BAUD_DIVISOR < 65536, "Baud divisor out of range");
// std::array: bounds info preserved, unlike raw arrays
std::array<uint8_t, 64> tx_buffer{};
// std::span: non-owning view, no allocation, C++20 but often available via ETL
// std::string_view: for string literals and buffers, no heap
// CRTP replaces virtual dispatch — zero runtime overhead
// See references/memory-patterns.md §2 for full example| Avoid | Reason | Alternative |
|---|---|---|
| Code size (10-30% increase), non-deterministic stack unwind | |
| Runtime type tables increase ROM | CRTP, explicit type tags |
| Heap allocation | |
| Requires OS primitives | RTOS tasks |
| Heap allocation for captures | Function pointers, templates |
| Virtual destructors in deep hierarchies | vtable size, indirect dispatch, blocks inlining | CRTP or flat hierarchies (≤2 levels); virtual OK for non-critical paths |
-fno-exceptions -fno-rttietl::vectoretl::mapetl::string// constexpr:编译时计算,无运行时开销
constexpr uint32_t BAUD_DIVISOR = PCLK_FREQ / (16U * TARGET_BAUD);
static_assert(BAUD_DIVISOR > 0 && BAUD_DIVISOR < 65536, "波特率除数超出范围");
// std::array:保留边界信息,不同于原始数组
std::array<uint8_t, 64> tx_buffer{};
// std::span:非持有视图,无分配,C++20特性,通常也可通过ETL获取
// std::string_view:用于字符串字面量和缓冲区,无堆占用
// CRTP替代虚函数分发——零运行时开销
// 完整示例参考references/memory-patterns.md第2节| 避免使用 | 原因 | 替代方案 |
|---|---|---|
| 代码体积增加10-30%,栈展开非确定性 | |
| 运行时类型表增加ROM占用 | CRTP、显式类型标签 |
| 堆分配 | |
| 需要操作系统原语 | RTOS任务 |
| 捕获内容需要堆分配 | 函数指针、模板 |
| 深层继承体系中的虚析构函数 | 虚表占用ROM、间接分发、阻碍内联 | CRTP或扁平继承体系(≤2层);非关键路径可以使用虚函数 |
-fno-exceptions -fno-rttietl::vectoretl::mapetl::string| Anti-Pattern | Problem | Fix |
|---|---|---|
| Heap-allocates when captures exceed small-buffer threshold (~16-32 bytes, implementation-dependent) | Function pointer + |
Arduino | Every concatenation/conversion heap-allocates; 10Hz × 4 sensors = 3.4M alloc/day → fragmentation | Fixed |
| Same fragmentation; violates MISRA C++ Rule 21.6.1 | Object pool, static array, or ETL fixed-capacity container |
| Atomic ref-counting overhead, control block on heap | |
| Deep virtual hierarchies | vtable per class (ROM), indirect dispatch, blocks inlining | CRTP or flat hierarchy (≤2 levels) |
Hidden | Dynamic allocation on every operation — often hidden in library APIs | ETL containers ( |
| | |
| ISR handler name mismatch | Misspelled ISR name silently falls through to | Verify names against startup |
| Heap allocation, locking, non-reentrant — crashes or deadlocks | |
| Unbounded recursion | Stack overflow on MCU with 1–8KB stack; MISRA C Rule 17.2 bans recursion | Convert to iterative with explicit stack |
std::string::operator+=std::vector::push_backstd::functionstd::anystd::regex| 反模式 | 问题 | 修复方案 |
|---|---|---|
带捕获的 | 当捕获内容超出小缓冲区阈值(约16-32字节,依实现而定)时会进行堆分配 | 函数指针 + |
循环中使用Arduino | 每次拼接/转换都会进行堆分配;10Hz × 4个传感器 = 每天340万次分配 → 碎片化 | 固定 |
周期任务中使用 | 同样存在碎片化问题;违反MISRA C++规则21.6.1 | 对象池、静态数组或ETL固定容量容器 |
任何场景下使用 | 原子引用计数开销、控制块存储在堆上 | 带池删除器的 |
| 深层虚继承体系 | 每个类对应虚表(占用ROM)、间接分发、阻碍内联 | CRTP或扁平继承体系(≤2层) |
隐式使用 | 每次操作都会动态分配——通常隐藏在库API中 | ETL容器( |
使用 | | 使用带显式内存序的 |
| ISR处理函数名称不匹配 | 拼写错误的ISR名称会静默落入 | 对照启动 |
ISR中使用 | 堆分配、锁、不可重入——会引发崩溃或死锁 | 在ISR外使用 |
| 无界递归 | 栈大小仅1–8KB的MCU会发生栈溢出;MISRA C规则17.2禁止使用递归 | 转换为带显式栈的迭代实现 |
std::string::operator+=std::vector::push_backstd::functionstd::anystd::regex| Symptom | Likely cause | First action |
|---|---|---|
| Crash after N hours of uptime | Heap fragmentation | Switch to pools/ETL containers; cite MISRA C Rule 21.3; see |
| HardFault with BFAR/MMFAR valid | Null/wild pointer dereference or bus fault | Read CFSR sub-registers; check MMARVALID/BFARVALID before using address registers; see |
| Stack pointer in wrong region | Stack overflow | Check |
| ISR data looks stale | Missing | Add |
| Random corruption near ISR | Data race | Apply atomics or critical section; see |
| Use-after-free | Object returned to pool while still referenced | Verify no aliasing; use unique_ptr with pool deleter |
| MPU fault in task | Task overflowed its stack into neighboring region | Increase stack size or reduce frame depth |
| Uninitialized read | Local variable used before assignment | Enable |
HardFault on first | FPU not enabled in CPACR | `SCB->CPACR |
| ISR does nothing / default handler runs | ISR function name misspelled | Verify name against startup |
| 症状 | 可能原因 | 首要处理措施 |
|---|---|---|
| 运行N小时后崩溃 | 堆碎片化 | 切换到对象池/ETL容器;引用MISRA C规则21.3;ESP32专属指导参考 |
| 带有效BFAR/MMFAR的HardFault | 空/野指针解引用或总线错误 | 读取CFSR子寄存器;使用地址寄存器前检查MMARVALID/BFARVALID;参考 |
| 栈指针处于错误区域 | 栈溢出 | 检查 |
| ISR数据看起来过时 | 缺失 | 为共享变量添加 |
| ISR附近出现随机损坏 | 数据竞争 | 应用原子变量或临界区;参考 |
| 释放后使用 | 对象被归还到池后仍被引用 | 检查无别名;使用带池删除器的unique_ptr |
| 任务中触发MPU故障 | 任务栈溢出侵入相邻区域 | 增加栈大小或减少帧深度 |
| 未初始化读取 | 局部变量赋值前被使用 | 开启 |
首次 | CPACR中未启用FPU | 执行任何浮点代码前执行`SCB->CPACR |
| ISR无响应/运行默认处理函数 | ISR函数名称拼写错误 | 对照启动 |
Is the bug reproducible on a host (PC)?
├── YES → Use AddressSanitizer (ASan) + Valgrind
│ Compile embedded logic for PC with -fsanitize=address
│ See references/debugging.md §5
└── NO → Is it a memory layout/access issue?
├── YES → Enable MPU; add stack canaries; read CFSR on fault
│ See references/debugging.md §1, §4
└── NO → Is it a data-race between ISR and task?
├── YES → Audit shared state; apply atomics/critical section
│ See references/debugging.md §3
└── NO → Use GDB watchpoint on the corrupted address
See references/debugging.md §6clang-tidyclang-analyzer-*cppcoreguidelines-*cppcheck --enable=allbug是否可以在主机(PC)上复现?
├── 是 → 使用AddressSanitizer (ASan) + Valgrind
│ 为PC编译嵌入式逻辑时添加-fsanitize=address参数
│ 参考references/debugging.md第5节
└── 否 → 是否是内存布局/访问问题?
├── 是 → 启用MPU;添加栈金丝雀;故障时读取CFSR
│ 参考references/debugging.md第1、4节
└── 否 → 是否是ISR和任务之间的数据竞争?
├── 是 → 审计共享状态;应用原子变量/临界区
│ 参考references/debugging.md第3节
└── 否 → 对损坏地址使用GDB观察点
参考references/debugging.md第6节clang-tidyclang-analyzer-*cppcoreguidelines-*cppcheck --enable=allstd::expectedtl::expectedetl::expectedenum class SensorError : uint8_t { not_ready, crc_fail, timeout };
[[nodiscard]] std::expected<SensorReading, SensorError> read_sensor() {
if (!sensor_ready()) return std::unexpected(SensorError::not_ready);
auto raw = read_raw();
if (!verify_crc(raw)) return std::unexpected(SensorError::crc_fail);
return SensorReading{.temp = convert(raw)};
}std::optional[[nodiscard]] std::optional<SensorReading> read_sensor() {
if (!sensor_ready()) return std::nullopt;
return SensorReading{.temp = read_temp(), .humidity = read_humidity()};
}[[nodiscard]][[nodiscard]][[nodiscard]]__attribute__((warn_unused_result))assertvoid write_to_pool(uint8_t* buf, size_t len) {
assert(buf != nullptr);
assert(len <= MAX_PACKET_SIZE); // Trips in debug, removed in release with NDEBUG
// ...
}NVIC_SystemReset()std::expectedtl::expectedetl::expectedenum class SensorError : uint8_t { not_ready, crc_fail, timeout };
[[nodiscard]] std::expected<SensorReading, SensorError> read_sensor() {
if (!sensor_ready()) return std::unexpected(SensorError::not_ready);
auto raw = read_raw();
if (!verify_crc(raw)) return std::unexpected(SensorError::crc_fail);
return SensorReading{.temp = convert(raw)};
}std::optional[[nodiscard]] std::optional<SensorReading> read_sensor() {
if (!sensor_ready()) return std::nullopt;
return SensorReading{.temp = read_temp(), .humidity = read_humidity()};
}[[nodiscard]][[nodiscard]][[nodiscard]]__attribute__((warn_unused_result))assertvoid write_to_pool(uint8_t* buf, size_t len) {
assert(buf != nullptr);
assert(len <= MAX_PACKET_SIZE); // 调试模式下触发,发布模式下通过NDEBUG移除
// ...
}NVIC_SystemReset()// HAL interface as a concept (or just template parameter)
template<typename Hal>
class SensorController {
public:
explicit SensorController(Hal& hal) : hal_(hal) {}
std::optional<float> read_temperature() {
auto raw = hal_.i2c_read(SENSOR_ADDR, TEMP_REG, 2);
if (!raw) return std::nullopt;
return convert_raw_to_celsius(*raw);
}
private:
Hal& hal_;
};
// Production: uses real hardware
SensorController<StmHal> controller(real_hal);
// Test: uses mock — same code, no vtable, no overhead in production
SensorController<MockHal> test_controller(mock_hal);// HAL接口作为concept(或直接作为模板参数)
template<typename Hal>
class SensorController {
public:
explicit SensorController(Hal& hal) : hal_(hal) {}
std::optional<float> read_temperature() {
auto raw = hal_.i2c_read(SENSOR_ADDR, TEMP_REG, 2);
if (!raw) return std::nullopt;
return convert_raw_to_celsius(*raw);
}
private:
Hal& hal_;
};
// 生产环境:使用真实硬件
SensorController<StmHal> controller(real_hal);
// 测试环境:使用mock——相同代码,无虚表,生产环境无开销
SensorController<MockHal> test_controller(mock_hal);| Architecture | Complexity | Best for |
|---|---|---|
| Superloop (bare-metal polling) | Lowest | < 5 tasks, loose timing, fully deterministic |
| Cooperative scheduler (time-triggered) | Low | Hard real-time, safety-critical (IEC 61508 SIL 1–2), analyzable |
| RTOS preemptive (FreeRTOS/Zephyr) | Medium | Complex multi-task, priority-based scheduling |
| Active Object (QP framework) | Highest | Event-heavy, hierarchical state machines, protocol handling |
references/architecture.mdreferences/stm32-pitfalls.md| 架构 | 复杂度 | 适用场景 |
|---|---|---|
| 超级循环(裸机轮询) | 最低 | <5个任务、时序要求宽松、完全确定性的场景 |
| 协作式调度器(时间触发) | 低 | 硬实时、安全关键(IEC 61508 SIL 1–2)、可分析的场景 |
| RTOS抢占式(FreeRTOS/Zephyr) | 中等 | 复杂多任务、基于优先级调度的场景 |
| 活动对象(QP框架) | 最高 | 事件密集、分层状态机、协议处理的场景 |
references/architecture.mdreferences/stm32-pitfalls.mdheap_caps_malloc(size, MALLOC_CAP_SPIRAM)ps_malloc()std::vectorstd::stringstd::mapetl::vector<T, N>etl::string<N>etl::map<K, V, N>etl::queue_spsc_atomic<T, N>lib_deps = ETLCPP/Embedded Template Libraryreferences/architecture.mduxTaskGetStackHighWaterMark()references/memory-patterns.mdreferences/memory-patterns.mdheap_caps_malloc(size, MALLOC_CAP_SPIRAM)ps_malloc()std::vectorstd::stringstd::mapetl::vector<T, N>etl::string<N>etl::map<K, V, N>etl::queue_spsc_atomic<T, N>lib_deps = ETLCPP/Embedded Template Libraryreferences/architecture.mduxTaskGetStackHighWaterMark()references/memory-patterns.mdreferences/memory-patterns.mdreferences/coding-style.mdsnake_casePascalCasekConstantNameALL_CAPStrailing_underscore_#pragma once#ifndef HEADER_H_[[nodiscard]]references/coding-style.mdsnake_casePascalCasekConstantNameALL_CAPStrailing_underscore_#pragma once#ifndef HEADER_H_[[nodiscard]]| File | Read when |
|---|---|
| Implementing arena, ring buffer, DMA buffers, lock-free SPSC, singletons, linker sections, ESP32 heap fragmentation, ETL containers |
| Writing C99/C11 firmware, C memory pools, C error handling, C/C++ interop, MISRA C rules, UART read-to-clear mechanism, |
| Diagnosing stack overflow, heap corruption, HardFault, data races, NVIC priority issues, or running ASan/GDB |
| Naming conventions, feature usage table, struct packing, attributes, include guards |
| Choosing firmware architecture, FreeRTOS IPC patterns, low-power modes, ESP32 deep sleep + fast wake, CI/CD pipeline setup, binary size budgets |
| CubeMX code generation issues, HAL vs LL selection, hard-to-debug issues (cache, priority inversion, flash stall), code review checklist |
| HAL design patterns (CRTP/template/virtual/opaque), dependency injection strategies, SOLID for embedded, callback + trampoline patterns |
| MPU protection patterns, watchdog hierarchy, IEC 61508 fault recovery, peripheral protocol selection (UART/I2C/SPI/CAN), linker script memory placement |
| 文件 | 适用场景 |
|---|---|
| 实现竞技场、环形缓冲区、DMA缓冲区、无锁SPSC、单例、链接节、ESP32堆碎片化、ETL容器 |
| 编写C99/C11固件、C内存池、C错误处理、C/C++互操作、MISRA C规则、UART读清除机制、 |
| 诊断栈溢出、堆损坏、HardFault、数据竞争、NVIC优先级问题,或运行ASan/GDB |
| 命名规范、特性使用表、结构体打包、属性、包含守卫 |
| 选择固件架构、FreeRTOS IPC模式、低功耗模式、ESP32深度休眠+快速唤醒、CI/CD流水线设置、二进制大小预算 |
| CubeMX代码生成问题、HAL与LL选择、难调试问题(缓存、优先级反转、flash停顿)、代码评审检查清单 |
| HAL设计模式(CRTP/模板/虚函数/不透明)、依赖注入策略、嵌入式SOLID原则、回调+跳板模式 |
| MPU保护模式、看门狗层级、IEC 61508故障恢复、外设协议选择(UART/I2C/SPI/CAN)、链接脚本内存布局 |