|
| 1 | +// ISR 基础示例 - 演示中断与主线程的数据竞争问题 |
| 2 | +// 本示例模拟 ISR 执行环境,展示常见的陷阱 |
| 3 | + |
| 4 | +#include <cstdint> |
| 5 | +#include <cstdio> |
| 6 | +#include <thread> |
| 7 | +#include <atomic> |
| 8 | +#include <vector> |
| 9 | + |
| 10 | +// 模拟硬件寄存器 |
| 11 | +namespace hal { |
| 12 | + // 模拟 UART 数据寄存器(volatile 确保每次都访问硬件) |
| 13 | + volatile uint32_t UART_DR = 0; |
| 14 | + volatile uint32_t UART_SR = 0; |
| 15 | + |
| 16 | + constexpr uint32_t UART_SR_RXNE = 0x0020; // 接收数据非空 |
| 17 | + |
| 18 | + // 模拟中断使能 |
| 19 | + inline void enable_uart_irq() { /* 实际硬件操作 */ } |
| 20 | +} |
| 21 | + |
| 22 | +// ============================================================================ |
| 23 | +// 反面教材:有数据竞争的代码 |
| 24 | +// ============================================================================ |
| 25 | + |
| 26 | +namespace bad_example { |
| 27 | + // ❌ 问题:非原子的共享变量 |
| 28 | + int shared_counter = 0; |
| 29 | + uint8_t received_byte = 0; |
| 30 | + bool data_ready = false; |
| 31 | + |
| 32 | + // 模拟 UART 接收中断(ISR) |
| 33 | + void uart_rx_isr() { |
| 34 | + // 检查接收标志 |
| 35 | + if (hal::UART_SR & hal::UART_SR_RXNE) { |
| 36 | + received_byte = static_cast<uint8_t>(hal::UART_DR); |
| 37 | + shared_counter++; // ❌ 数据竞争! |
| 38 | + data_ready = true; // ❌ 数据竞争! |
| 39 | + } |
| 40 | + } |
| 41 | + |
| 42 | + // 主循环 |
| 43 | + void main_loop() { |
| 44 | + if (data_ready) { // ❌ 数据竞争! |
| 45 | + printf("Received: 0x%02X, count: %d\n", received_byte, shared_counter); |
| 46 | + data_ready = false; |
| 47 | + } |
| 48 | + } |
| 49 | +} |
| 50 | + |
| 51 | +// ============================================================================ |
| 52 | +// 正确做法:使用原子操作 |
| 53 | +// ============================================================================ |
| 54 | + |
| 55 | +class ISRUARTDriver { |
| 56 | +public: |
| 57 | + // 编译期检查:确保原子操作是无锁的 |
| 58 | + static_assert(std::atomic<int>::is_always_lock_free, |
| 59 | + "atomic<int> must be lock-free for ISR use!"); |
| 60 | + |
| 61 | + ISRUARTDriver() noexcept |
| 62 | + : shared_counter(0) |
| 63 | + , received_byte(0) |
| 64 | + , data_ready(false) |
| 65 | + {} |
| 66 | + |
| 67 | + // 模拟 UART 接收中断(ISR) |
| 68 | + void uart_rx_isr() noexcept { |
| 69 | + if (hal::UART_SR & hal::UART_SR_RXNE) { |
| 70 | + received_byte.store(static_cast<uint8_t>(hal::UART_DR), |
| 71 | + std::memory_order_relaxed); |
| 72 | + shared_counter.fetch_add(1, std::memory_order_relaxed); |
| 73 | + // release 确保之前的写入都完成 |
| 74 | + data_ready.store(true, std::memory_order_release); |
| 75 | + } |
| 76 | + } |
| 77 | + |
| 78 | + // 主循环 |
| 79 | + bool main_loop() { |
| 80 | + // acquire 确保能看到 ISR 中的所有写入 |
| 81 | + if (data_ready.load(std::memory_order_acquire)) { |
| 82 | + uint8_t data = received_byte.load(std::memory_order_relaxed); |
| 83 | + int count = shared_counter.load(std::memory_order_relaxed); |
| 84 | + printf("Received: 0x%02X, count: %d\n", data, count); |
| 85 | + data_ready.store(false, std::memory_order_release); |
| 86 | + return true; |
| 87 | + } |
| 88 | + return false; |
| 89 | + } |
| 90 | + |
| 91 | +private: |
| 92 | + std::atomic<int> shared_counter; |
| 93 | + std::atomic<uint8_t> received_byte; |
| 94 | + std::atomic<bool> data_ready; |
| 95 | +}; |
| 96 | + |
| 97 | +namespace good_example { |
| 98 | + // ✅ 使用原子变量的示例(参考上面的类实现) |
| 99 | +} |
| 100 | + |
| 101 | +// ============================================================================ |
| 102 | +// ISR 禁区演示 |
| 103 | +// ============================================================================ |
| 104 | + |
| 105 | +namespace isr_restriction_demo { |
| 106 | + std::atomic<bool> error_flag{false}; |
| 107 | + |
| 108 | + // ❌ 错误示例:在 ISR 中使用互斥锁 |
| 109 | + // class BadISR { |
| 110 | + // std::mutex mtx; |
| 111 | + // void isr() { |
| 112 | + // std::lock_guard<std::mutex> lock(mtx); // ❌ 可能死锁! |
| 113 | + // } |
| 114 | + // }; |
| 115 | + |
| 116 | + // ❌ 错误示例:在 ISR 中分配内存 |
| 117 | + // void bad_isr() { |
| 118 | + // int* p = new int; // ❌ 可能阻塞,可能抛异常 |
| 119 | + // delete p; |
| 120 | + // } |
| 121 | + |
| 122 | + // ❌ 错误示例:在 ISR 中进行长时间操作 |
| 123 | + // void bad_isr() { |
| 124 | + // for (volatile int i = 0; i < 1000000; ++i) { // ❌ 阻塞其他中断 |
| 125 | + // // 浪费 CPU 时间 |
| 126 | + // } |
| 127 | + // } |
| 128 | + |
| 129 | + // ✅ 正确示例:ISR 只做最少的工作 |
| 130 | + class GoodISR { |
| 131 | + std::atomic<bool> data_ready{false}; |
| 132 | + uint8_t buffer; |
| 133 | + |
| 134 | + public: |
| 135 | + void isr() noexcept { |
| 136 | + // 只读取数据并设置标志 |
| 137 | + buffer = static_cast<uint8_t>(hal::UART_DR); |
| 138 | + data_ready.store(true, std::memory_order_release); |
| 139 | + } |
| 140 | + |
| 141 | + bool get_data(uint8_t& data) noexcept { |
| 142 | + if (data_ready.load(std::memory_order_acquire)) { |
| 143 | + data = buffer; |
| 144 | + data_ready.store(false, std::memory_order_release); |
| 145 | + return true; |
| 146 | + } |
| 147 | + return false; |
| 148 | + } |
| 149 | + }; |
| 150 | +} |
| 151 | + |
| 152 | +// ============================================================================ |
| 153 | +// 主函数:模拟中断环境 |
| 154 | +// ============================================================================ |
| 155 | + |
| 156 | +int main() { |
| 157 | + printf("=== ISR 基础示例 ===\n\n"); |
| 158 | + |
| 159 | + // 演示正确做法 |
| 160 | + printf("正确示例:使用原子操作\n"); |
| 161 | + printf("------------------------------\n"); |
| 162 | + |
| 163 | + ISRUARTDriver isr_system; |
| 164 | + |
| 165 | + // 模拟 10 次中断 |
| 166 | + for (int i = 0; i < 10; ++i) { |
| 167 | + // 模拟硬件接收到数据 |
| 168 | + hal::UART_SR = hal::UART_SR_RXNE; |
| 169 | + hal::UART_DR = 0x40 + i; |
| 170 | + |
| 171 | + // 模拟中断触发 |
| 172 | + isr_system.uart_rx_isr(); |
| 173 | + |
| 174 | + // 清除硬件状态 |
| 175 | + hal::UART_SR = 0; |
| 176 | + } |
| 177 | + |
| 178 | + // 主循环处理数据 |
| 179 | + int processed = 0; |
| 180 | + while (processed < 10) { |
| 181 | + if (isr_system.main_loop()) { |
| 182 | + processed++; |
| 183 | + } |
| 184 | + } |
| 185 | + |
| 186 | + printf("\n=== ISR 设计原则 ===\n"); |
| 187 | + printf("1. ISR 中不能使用互斥锁(可能死锁)\n"); |
| 188 | + printf("2. ISR 中不能分配内存(可能阻塞/抛异常)\n"); |
| 189 | + printf("3. ISR 执行时间要短(影响响应性)\n"); |
| 190 | + printf("4. 访问共享数据必须使用原子操作\n"); |
| 191 | + printf("5. 用 release/acquire 内存序确保可见性\n"); |
| 192 | + |
| 193 | + return 0; |
| 194 | +} |
| 195 | + |
| 196 | +// 编译命令: |
| 197 | +// g++ -std=c++17 -Wall -Wextra -pthread 01_isr_basics.cpp -o 01_isr_basics |
0 commit comments