第45章 嵌入式开发:C++的'螺丝刀与扳手'之旅

第45章 嵌入式开发:C++的"螺丝刀与扳手"之旅

🎯 章前语:你有没有想过,为什么你的微波炉不会"蓝屏死机"?为什么汽车的ECU能精确到毫秒级控制发动机?答案就藏在嵌入式系统里——而C++正是这场"微观世界冒险"中最靠谱的编程语言。

45.1 什么是嵌入式系统?——藏在设备里的"隐形管家"

45.1.1 嵌入式系统的定义

嵌入式系统是"专门为特定任务设计的计算机系统",它不像你的PC那样能干所有事情(虽然PC也经常什么都干不好),而是专注于做一件事,并把它做到极致。

典型特征:

  • 🕐 实时性:必须在确定的时间内响应(你的汽车刹车可等不了操作系统更新)
  • 💾 资源受限:没有无限的RAM和存储(你的路由器可能只有16MB内存,照样跑得欢)
  • 🔒 可靠性:要稳定运行数年甚至数十年(你的心脏起搏器可不能"随机重启")
  • 低功耗:很多设备靠电池或能量采集活着

45.1.2 嵌入式系统全家福

领域示例C++使用程度
消费电子智能手表、IoT传感器⭐⭐⭐⭐⭐
汽车电子ECU、ADAS、仪表盘⭐⭐⭐⭐⭐
工业控制PLC、机器人控制器⭐⭐⭐⭐
医疗设备监护仪、输液泵⭐⭐⭐⭐⭐
航空航天飞控系统、卫星⭐⭐⭐⭐⭐

45.1.3 C++在嵌入式领域的江湖地位

C++之所以能在嵌入式领域称霸,靠的是三把刷子:

  1. 性能接近C:没有Java的GC开销,没有Python的解析开销
  2. 抽象能力:面向对象、模板让你写出优雅而高效代码
  3. 泛型编程:模板元编程在编译期搞定计算,运行时零成本

💡 老兵不死:很多人说C才是嵌入式正统,但C++只是"带保镖的C"——你可以在嵌入式项目里只用C++写C风格的代码,然后在需要的时候优雅地引入面向对象。这才是真正的"双修"境界。

45.2 开发环境搭建——给嵌入式开发者的"武器库"

45.2.1 交叉编译:在一台机器上"炼制"另一台机器的代码

嵌入式系统通常没有编译器(谁会在微波炉里装个GCC?),所以我们在开发机上编译,在目标机上运行。这个过程叫交叉编译

1
2
3
4
5
6
# 安装ARM Cortex-M交叉编译器(STM32用的那种)
# Linux/macOS
sudo apt-get install gcc-arm-none-eabi  # Debian/Ubuntu
brew install --cask gcc-arm-embedded    # macOS

# Windows: 去 ARM 官网下载 "ARM GNU Toolchain"

45.2.2 CMake与嵌入式项目

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# CMakeLists.txt - STM32F4项目的交叉编译配置
cmake_minimum_required(VERSION 3.20)
project(embedded_blink LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 指定交叉编译工具链
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR ARM)

# 交叉编译器路径
set(TOOLCHAIN_PREFIX arm-none-eabi-)
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++)

# 关键编译选项
add_compile_options(
    -mcpu=cortex-m4          # 目标CPU
    -mthumb                  # 使用Thumb指令集(省内存)
    -mfpu=fpv4-sp-d16        # 浮点单元
    -mfloat-abi=hard
    -fno-exceptions          # 禁用异常(省ROM)
    -fno-rtti                # 禁用RTTI(省ROM)
    -ffunction-sections      # 每个函数独立section
    -fdata-sections
)

add_link_options(
    -mcpu=cortex-m4
    -mthumb
    --specs=nosys.specs      # 不链接系统库
    -Wl,--gc-sections        # 链接时垃圾回收未使用代码
)

# 你的源文件
add_executable(blink.elf
    main.cpp
    system_clock.cpp
    gpio.cpp
)

45.2.3 IDE选择:工欲善其事,必先利其器

IDE优点缺点
STM32CubeIDE专为STM32优化,免费只能用于ST芯片
VS Code + PlatformIO轻量,插件丰富偶尔抽风
IAR EWARM工业级,代码优化强贵(土豪随意)
Keil MDKARM官方支持,教学首选32KB代码限制

😂 真实故事:某嵌入式工程师说"我用记事本写代码",然后用了两天时间配置好所有环境,第三天开始写程序发现芯片型号选错了。所以,选对IDE很重要,但更重要的是别选错芯片型号

45.3 寄存器编程与硬件抽象层(HAL)——“直接怼硬件"的艺术

45.3.1 直接寄存器操作:最原始也最有效

嵌入式芯片的 peripheral(外设)本质上就是一堆寄存器。操作它们就像调教会议室的空调——你得知道面板上每个按钮是什么意思。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// STM32F4 GPIO寄存器定义(精简版)
namespace STM32F4 {
    namespace GPIO {
        // GPIO基地址
        constexpr uint32_t GPIOA_BASE = 0x40020000;
        
        // 寄存器结构体(位域精确控制)
        struct GPIO_TypeDef {
            volatile uint32_t MODER;    // 模式寄存器
            volatile uint32_t OTYPER;   // 输出类型
            volatile uint32_t OSPEEDR; // 输出速度
            volatile uint32_t PUPDR;    // 上下拉
            volatile uint32_t IDR;      // 输入数据
            volatile uint32_t ODR;      // 输出数据
            volatile uint32_t BSRR;     // 位设置/清除
            volatile uint32_t LCKR;    // 配置锁存
            volatile uint32_t AFR[2];  // 复用功能选择
        };
        
        // 使用指针访问寄存器
        inline GPIO_TypeDef* const GPIOA = 
            reinterpret_cast<GPIO_TypeDef*>(GPIOA_BASE);
    }
}

// 点亮PA5 LED(经典嵌入式Hello World)
void led_blink_registers() {
    // 1. 使能GPIOA时钟( RCC->AHB1ENR)
    constexpr uint32_t RCC_BASE = 0x40023800;
    struct RCC_TypeDef {
        volatile uint32_t AHB1ENR;
        volatile uint32_t APB1ENR;
        volatile uint32_t APB2ENR;
    };
    auto RCC = reinterpret_cast<RCC_TypeDef*>(RCC_BASE);
    RCC->AHB1ENR |= (1 << 0);  // GPIOA时钟使能
    
    // 2. 设置PA5为输出模式(MODER5 = 01)
    STM32F4::GPIO::GPIOA->MODER &= ~(0b11 << (5 * 2));  // 先清除
    STM32F4::GPIO::GPIOA->MODER |= (0b01 << (5 * 2));  // 再设置
    
    // 3. 闪烁LED
    while (true) {
        STM32F4::GPIO::GPIOA->BSRR = (1 << 5);     // 亮(置位)
        for (volatile uint32_t i = 0; i < 500000; ++i);  // 延时
        STM32F4::GPIO::GPIOA->BSRR = (1 << (5 + 16)); // 灭(清除)
        for (volatile uint32_t i = 0; i < 500000; ++i);  // 延时
    }
}

45.3.2 硬件抽象层(HAL):让代码"芯片无关”

直接操作寄存器爽是爽,但换芯片就等于重写代码。HAL层把你的硬件操作封装成统一接口,以后换芯片只需要换实现,不改业务逻辑。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
// ============================================================
// 硬件抽象层:嵌入式代码的"防水层"
// ============================================================

// 45.3.2.1 数字输入输出接口
class IDigitalIO {
public:
    virtual ~IDigitalIO() = default;
    virtual void write(bool level) = 0;
    virtual bool read() const = 0;
    virtual void toggle() = 0;
};

// 45.3.2.2 STM32实现
class STM32DigitalIO : public IDigitalIO {
public:
    enum class Port { A, B, C, D };
    
    STM32DigitalIO(Port port, uint8_t pin, bool initialLevel = false)
        : port_(port), pin_(pin) {
        initClock();
        configurePin();
        if (initialLevel) setBit(); else clearBit();
    }
    
    void write(bool level) override {
        if (level) setBit(); else clearBit();
    }
    
    bool read() const override {
        return (gpioBase()->IDR >> pin_) & 1;
    }
    
    void toggle() override {
        gpioBase()->ODR ^= (1 << pin_);
    }
    
private:
    Port port_;
    uint8_t pin_;
    
    static constexpr uint32_t GPIO_BASE[] = {
        0x40020000, 0x40020400, 0x40020800, 0x40020C00
    };
    
    uint32_t* getAHB1ENR() {
        return reinterpret_cast<uint32_t*>(0x40023800);
    }
    
    auto gpioBase() const {
        return reinterpret_cast<GPIO_TypeDef*>(GPIO_BASE[static_cast<int>(port_)]);
    }
    
    void initClock() {
        uint32_t* ahb1enr = getAHB1ENR();
        *ahb1enr |= (1 << static_cast<int>(port_));
    }
    
    void configurePin() {
        auto gpio = gpioBase();
        // 清除MODER位
        gpio->MODER &= ~(0b11 << (pin_ * 2));
        // 设置为输出模式
        gpio->MODER |= (0b01 << (pin_ * 2));
    }
    
    void setBit() { gpioBase()->BSRR = (1 << pin_); }
    void clearBit() { gpioBase()->BSRR = (1 << (pin_ + 16)); }
    
    struct GPIO_TypeDef {
        volatile uint32_t MODER, OTYPER, OSPEEDR, PUPDR;
        volatile uint32_t IDR, ODR, BSRR, LCKR;
        volatile uint32_t AFR[2];
    };
};

// 45.3.2.3 使用示例
void hal_demo() {
    // 创建一个LED对象(PA5)
    STM32DigitalIO led(STM32DigitalIO::Port::A, 5);
    
    // 创建一个按钮对象(PC13,STM32蓝色按钮)
    STM32DigitalIO button(STM32DigitalIO::Port::C, 13);
    
    // 主循环
    while (true) {
        if (button.read()) {
            led.toggle();  // 按下按钮,LED翻转
        }
        delay_ms(50);  // 防抖延时
    }
}

45.3.3 更高级的抽象:C++模板的威力

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// 45.3.3.1 模板化的GPIO,完全类型安全
template<uint32_t BaseAddr, uint8_t Pin>
class GPIO_Pin {
public:
    using Port = std::integral_constant<uint32_t, BaseAddr>;
    using PinNum = std::integral_constant<uint8_t, Pin>;
    
    static void init() {
        enableClock();
        setMode(Mode::Output);
    }
    
    enum class Mode { Input, Output, Alternate, Analog };
    static void setMode(Mode m) {
        auto gpio = reinterpret_cast<GPIO_TypeDef*>(BaseAddr);
        gpio->MODER = (gpio->MODER & ~(0b11 << (Pin * 2))) | 
                      (static_cast<uint32_t>(m) << (Pin * 2));
    }
    
    static void high() {
        reinterpret_cast<GPIO_TypeDef*>(BaseAddr)->BSRR = (1 << Pin);
    }
    
    static void low() {
        reinterpret_cast<GPIO_TypeDef*>(BaseAddr)->BSRR = (1 << (Pin + 16));
    }
    
    static bool read() {
        return (reinterpret_cast<GPIO_TypeDef*>(BaseAddr)->IDR >> Pin) & 1;
    }
    
    static void toggle() {
        auto gpio = reinterpret_cast<GPIO_TypeDef*>(BaseAddr);
        gpio->ODR ^= (1 << Pin);
    }
    
private:
    struct GPIO_TypeDef {
        volatile uint32_t MODER, OTYPER, OSPEEDR, PUPDR;
        volatile uint32_t IDR, ODR, BSRR, LCKR;
        volatile uint32_t AFR[2];
    };
    
    static void enableClock() {
        // 简化的时钟使能
        volatile uint32_t* RCC_AHB1ENR = reinterpret_cast<uint32_t*>(0x40023800);
        uint32_t portIndex = (BaseAddr - 0x40020000) / 0x400;
        *RCC_AHB1ENR |= (1 << portIndex);
    }
};

// 45.3.3.2 使用:编译时就能知道引脚编号,零运行时开销
using LED1 = GPIO_Pin<0x40020000, 5>;   // PA5
using LED2 = GPIO_Pin<0x40020400, 5>;   // PB5
using BTN = GPIO_Pin<0x40020800, 13>;  // PC13

void template_gpio_demo() {
    LED1::init();
    BTN::init();
    
    while (BTN::read()) {  // 等待按下
        LED1::toggle();
        busy_wait(500);
    }
}

💡 编译器优化:上面这些模板代码,LED1::toggle() 调用在编译后就变成了一条 BSRR 寄存器写指令,没有任何函数调用开销。这就是C++“零成本抽象"的真正含义。简单说:你获得了C++的写法,却有着汇编的性能——这才是模板元编程的正确打开方式,而不是拿来写那些奇奇怪怪的"类型 Olympics”。

45.4 中断与实时编程——嵌入式的"灵魂"

45.4.1 中断机制:让CPU"随叫随到"

中断简单说就是"硬件有事找CPU,CPU放下手头工作先处理,处理完再回来"。

中断的使用场景:

  • 定时器溢出(精确计时)
  • 外部按键(外部触发)
  • UART收到数据(通信事件)
  • ADC转换完成(传感器采样)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 45.4.1.1 C++风格的中断处理类
class InterruptController {
public:
    // 启用/禁用中断的RAII封装
    class CriticalSection {
    public:
        CriticalSection() {
            // 进入临界区:禁用所有中断
            __asm volatile ("cpsid i" : : : "memory");
        }
        ~CriticalSection() {
            // 退出临界区:启用所有中断
            __asm volatile ("cpsie i" : : : "memory");
        }
        CriticalSection(const CriticalSection&) = delete;
        CriticalSection& operator=(const CriticalSection&) = delete;
    };
    
    // 模板化的中断处理函数注册
    template<void (*Handler)()>
    static void registerInterrupt(uint8_t irqNumber) {
        // 实际实现依赖具体芯片
        // 这里展示概念
    }
};

// 45.4.1.2 使用RAII临界区保护共享数据
volatile uint32_t g_counter = 0;

void timer_interrupt_handler() {
    g_counter++;  // 中断里递增计数器
}

void safe_increment() {
    // 临界区保护:即使在临界区里被打断,也会等中断处理完再继续
    InterruptController::CriticalSection cs;
    g_counter++;
}

45.4.2 定时器:嵌入式的"心跳"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// 45.4.2.1 STM32定时器抽象
class Timer {
public:
    enum class Prescaler : uint16_t {
        DIV_1 = 1,
        DIV_2 = 2,
        DIV_4 = 4,
        DIV_8 = 8,
        // ... 等等
    };
    
    explicit Timer(uint32_t baseAddress) : base_(baseAddress) {}
    
    void configure(uint32_t periodUs, Prescaler prescaler = Prescaler::DIV_1) {
        auto timer = reinterpret_cast<TIM_TypeDef*>(base_);
        
        // 设置预分频(决定计数精度)
        timer->PSC = static_cast<uint16_t>(prescaler) - 1;
        
        // 设置自动重装载值(决定周期)
        // 假设APB1时钟为84MHz
        timer->ARR = (84'000'000 / static_cast<uint16_t>(prescaler)) * periodUs / 1'000'000;
        
        // 清除更新中断标志
        timer->SR = 0;
        
        // 启用定时器
        timer->CR1 |= CR1_CEN;
    }
    
    void enableUpdateInterrupt() {
        reinterpret_cast<TIM_TypeDef*>(base_)->DIER |= DIER_UIE;
    }
    
    void start() {
        reinterpret_cast<TIM_TypeDef*>(base_)->CR1 |= CR1_CEN;
    }
    
    void stop() {
        reinterpret_cast<TIM_TypeDef*>(base_)->CR1 &= ~CR1_CEN;
    }
    
    bool hasElapsed() const {
        return (reinterpret_cast<TIM_TypeDef*>(base_)->SR & 1) != 0;
    }
    
    void clearFlag() {
        reinterpret_cast<TIM_TypeDef*>(base_)->SR = 0;
    }
    
private:
    uint32_t base_;
    
    struct TIM_TypeDef {
        volatile uint32_t CR1, CR2, SMCR, DIER;
        volatile uint32_t SR, EGR, CCMR1, CCMR2;
        volatile uint32_t CCER, CNT, PSC, ARR;
        // ... 更多寄存器
    };
    
    static constexpr uint32_t CR1_CEN = 1;
    static constexpr uint32_t DIER_UIE = 1;
};

// 45.4.2.2 精确延时的正确姿势
class Delay {
public:
    static void us(uint32_t usec) {
        Timer timer(0x40010000);  // TIM2基地址
        timer.configure(usec);
        timer.start();
        while (!timer.hasElapsed()) { }
        timer.stop();
    }
    
    static void ms(uint32_t msec) {
        for (uint32_t i = 0; i < msec; ++i) {
            us(1000);
        }
    }
};

45.4.3 实时操作系统的"轻量级选手":状态机

很多嵌入式系统其实不需要完整RTOS,一个好的状态机就能搞定大部分问题。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// 45.4.3.1 枚举类状态机(单线程,非抢占)
enum class State {
    Idle,
    Measuring,
    Transmitting,
    Error
};

class SensorNode {
public:
    SensorNode() : state_(State::Idle), errorCount_(0) {}
    
    void run() {
        switch (state_) {
            case State::Idle:
                handleIdle();
                break;
            case State::Measuring:
                handleMeasuring();
                break;
            case State::Transmitting:
                handleTransmitting();
                break;
            case State::Error:
                handleError();
                break;
        }
    }
    
private:
    State state_;
    uint8_t errorCount_;
    
    void handleIdle() {
        // 检查是否该开始测量
        if (shouldStartMeasurement()) {
            startMeasurement();
            state_ = State::Measuring;
        }
    }
    
    void handleMeasuring() {
        if (isMeasurementComplete()) {
            if (validateData()) {
                state_ = State::Transmitting;
            } else {
                state_ = State::Error;
            }
        }
    }
    
    void handleTransmitting() {
        if (sendData()) {
            state_ = State::Idle;  // 成功,回归空闲
        } else if (++errorCount_ > 3) {
            state_ = State::Error;  // 连续失败,进入错误处理
        }
    }
    
    void handleError() {
        if (resetPeripheral()) {
            errorCount_ = 0;
            state_ = State::Idle;
        }
    }
    
    // 虚函数,由子类实现具体硬件操作
    virtual bool shouldStartMeasurement() = 0;
    virtual void startMeasurement() = 0;
    virtual bool isMeasurementComplete() = 0;
    virtual bool validateData() = 0;
    virtual bool sendData() = 0;
    virtual bool resetPeripheral() = 0;
};

45.5 外设通信:UART、SPI、I2C——设备间的"社交网络"

45.5.1 UART:串口通信,老当益壮

UART是最简单的通信协议,两根线(TX/RX)就能聊天,调试嵌入式系统的必备良品。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// 45.5.1.1 UART环形缓冲区
class CircularBuffer {
public:
    static constexpr size_t SIZE = 64;
    
    CircularBuffer() : head_(0), tail_(0) {}
    
    bool write(uint8_t byte) {
        size_t next = (head_ + 1) % SIZE;
        if (next == tail_) return false;  // 缓冲区满
        buffer_[head_] = byte;
        head_ = next;
        return true;
    }
    
    std::optional<uint8_t> read() {
        if (tail_ == head_) return std::nullopt;  // 缓冲区空
        uint8_t byte = buffer_[tail_];
        tail_ = (tail_ + 1) % SIZE;
        return byte;
    }
    
    size_t available() const {
        if (head_ >= tail_) return head_ - tail_;
        return SIZE - tail_ + head_;
    }
    
private:
    uint8_t buffer_[SIZE];
    size_t head_;
    size_t tail_;
};

// 45.5.1.2 UART外设类
class UART {
public:
    UART(uint32_t baseAddr, uint32_t baudrate = 115200) 
        : base_(baseAddr), rxBuffer_() {
        configure(baudrate);
    }
    
    void configure(uint32_t baudrate) {
        // 假设APB1时钟84MHz,UART时钟42MHz
        auto uart = reinterpret_cast<UART_TypeDef*>(base_);
        
        // 禁用UART进行配置
        uart->CR1 = 0;
        
        // 设置波特率
        // BRR = UART时钟 / 波特率
        uart->BRR = 42'000'000 / baudrate;
        
        // 8位数据,1位停止,无校验
        uart->CR1 &= ~(1 << 12);  // M = 0
        uart->CR2 = 0;
        
        // 使能发送和接收
        uart->CR1 |= (1 << 3) | (1 << 2);  // TE | RE
        
        // 使能UART
        uart->CR1 |= (1 << 13);  // UE
    }
    
    void send(uint8_t byte) {
        auto uart = reinterpret_cast<UART_TypeDef*>(base_);
        while (!(uart->SR & SR_TXE)) { }  // 等待发送缓冲区空
        uart->DR = byte;
    }
    
    void sendString(const char* str) {
        while (*str) {
            send(*str++);
        }
    }
    
    std::optional<uint8_t> receive() {
        return rxBuffer_.read();
    }
    
    // 在中断处理函数中调用
    void handleRxInterrupt() {
        auto uart = reinterpret_cast<UART_TypeDef*>(base_);
        if (uart->SR & SR_RXNE) {
            rxBuffer_.write(uart->DR);
        }
    }
    
private:
    uint32_t base_;
    CircularBuffer rxBuffer_;
    
    struct UART_TypeDef {
        volatile uint32_t SR, DR, BRR, CR1, CR2, CR3, GTPR;
    };
    
    static constexpr uint32_t SR_TXE = (1 << 7);
    static constexpr uint32_t SR_RXNE = (1 << 5);
};

// 45.5.1.3 printf重定向到UART
extern "C" int _write(int fd, char* buf, int size) {
    UART* uart = getUART();  // 假设获取UART实例的函数
    for (int i = 0; i < size; ++i) {
        uart->send(buf[i]);
    }
    return size;
}

void uart_demo() {
    UART uart(0x40011000, 115200);
    uart.sendString("Hello from embedded C++!\r\n");
    
    // 发送格式化数据(需要重定向printf)
    // printf("Temperature: %.2f C\r\n", readTemperature());
}

45.5.2 SPI:高速同步通信

SPI比UART快得多,适合连接显示屏、存储芯片、传感器等。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// 45.5.2.1 SPI管理器(使用RAII管理片选)
class SPIManager {
public:
    SPIManager(uint32_t baseAddr) : base_(baseAddr) {}
    
    void configure(uint32_t maxFreqHz, bool cpol, bool cpha) {
        auto spi = reinterpret_cast<SPI_TypeDef*>(base_);
        
        // 禁用SPI配置
        spi->CR1 = 0;
        
        // 设置为主模式
        spi->CR1 |= (1 << 2);  // MSTR
        
        // 设置时钟分频
        uint32_t br = calculatePrescaler(maxFreqHz);
        spi->CR1 |= (br << 3);  // BR[2:0]
        
        // 设置CPOL和CPHA
        if (cpol) spi->CR1 |= (1 << 1);
        if (cpha) spi->CR1 |= (1 << 0);
        
        // 8位数据帧
        spi->CR1 &= ~(1 << 11);  // DFF = 0
        
        // 使能SPI
        spi->CR1 |= (1 << 6);  // SPE
    }
    
    // RAII片选管理
    class ScopedCS {
    public:
        explicit ScopedCS(GPIO_PinBase& csPin) : cs_(csPin) {
            cs_.low();  // 片选拉低,开始通信
        }
        ~ScopedCS() {
            cs_.high();  // 片选拉高,结束通信
        }
    private:
        GPIO_PinBase& cs_;
    };
    
    uint8_t transfer(uint8_t data) {
        auto spi = reinterpret_cast<SPI_TypeDef*>(base_);
        spi->DR = data;
        while (!(spi->SR & SR_RXNE)) { }
        return spi->DR;
    }
    
    void transferBuffer(const uint8_t* tx, uint8_t* rx, size_t len) {
        for (size_t i = 0; i < len; ++i) {
            rx[i] = transfer(tx[i]);
        }
    }
    
private:
    uint32_t base_;
    
    struct SPI_TypeDef {
        volatile uint32_t CR1, CR2, SR, DR, CRCPR, RXCRCR, TXCRCR;
    };
    
    static constexpr uint32_t SR_RXNE = (1 << 0);
    
    uint32_t calculatePrescaler(uint32_t maxFreqHz) {
        // 假设SPI时钟42MHz
        constexpr uint32_t spiClock = 42'000'000;
        uint32_t ratio = spiClock / maxFreqHz;
        
        if (ratio <= 2) return 0;   // Fosc/2
        if (ratio <= 4) return 1;   // Fosc/4
        if (ratio <= 8) return 2;   // Fosc/8
        if (ratio <= 16) return 3;  // Fosc/16
        if (ratio <= 32) return 4;  // Fosc/32
        if (ratio <= 64) return 5;  // Fosc/64
        return 6;                   // Fosc/128
    }
};

// 45.5.2.2 使用示例:读写外部Flash
// 注意:W25Q64是一款经典的8MB Flash芯片,某宝几块钱就能买到
// 学会了读写它,你就可以给项目加上"数据日志"功能——再也不用担心断电丢数据了!
class W25Q64Flash {
public:
    W25Q64Flash(SPIManager& spi, GPIO_PinBase& csPin) 
        : spi_(spi), csPin_(csPin) {}
    
    uint8_t readStatusReg() {
        SPIManager::ScopedCS cs(csPin_);
        spi_.transfer(0x05);  // RDSR命令
        return spi_.transfer(0xFF);
    }
    
    void writeEnable() {
        SPIManager::ScopedCS cs(csPin_);
        spi_.transfer(0x06);  // WREN命令
    }
    
    void writeData(uint16_t addr, const uint8_t* data, size_t len) {
        writeEnable();
        {
            SPIManager::ScopedCS cs(csPin_);
            spi_.transfer(0x02);  // Page Program命令
            spi_.transfer(addr >> 8);
            spi_.transfer(addr & 0xFF);
            // 注意:W25Q64地址是3字节,这里用简化版2字节地址
            // 实际产品代码请根据datasheet调整
            
            for (size_t i = 0; i < len; ++i) {
                spi_.transfer(data[i]);
            }
        }
        waitForWriteComplete();
    }
    
    void readData(uint16_t addr, uint8_t* buffer, size_t len) {
        SPIManager::ScopedCS cs(csPin_);
        spi_.transfer(0x03);  // Read Data命令
        spi_.transfer(addr >> 8);
        spi_.transfer(addr & 0xFF);
        // W25Q64支持连续读取,地址会自动递增
        
        for (size_t i = 0; i < len; ++i) {
            buffer[i] = spi_.transfer(0xFF);
        }
    }
    
private:
    SPIManager& spi_;
    GPIO_PinBase& csPin_;
    
    void waitForWriteComplete() {
        while (readStatusReg() & 0x01) { }  // 等待BUSY位清零
    }
};

45.5.3 I2C:两线制的优雅

I2C只需要两根线(SCL+SDA),适合连接多个低速设备,是传感器的最爱。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// 45.5.3.1 I2C管理器
// 注意:本实现是简化版,用于教学目的。
// 实际产品代码需要处理:Repeated Start、Clock Stretching、多字节传输等细节
class I2CManager {
public:
    I2CManager(uint32_t baseAddr) : base_(baseAddr) {}
    
    void configure(uint32_t clockFreqHz = 100'000) {
        auto i2c = reinterpret_cast<I2C_TypeDef*>(base_);
        
        // 复位I2C
        i2c->CR1 |= (1 << 15);
        i2c->CR1 &= ~(1 << 15);
        
        // 设置时钟频率(APB1 = 42MHz)
        constexpr uint32_t apb1Clock = 42'000'000;
        i2c->CR2 = (apb1Clock / 1'000'000) & 0x3F;  // FREQ[5:0]
        
        // 设置快速模式下的时钟
        uint32_t ccr = apb1Clock / (clockFreqHz * 3);  // 简化计算
        i2c->CCR = ccr & 0xFFF;
        
        // 设置Rise Time
        i2c->TRISE = ((apb1Clock / 1'000'000) + 1) & 0x3F;
        
        // 使能I2C
        i2c->CR1 |= (1 << 0);  // PE
    }
    
    bool write(uint8_t addr7bit, const uint8_t* data, size_t len, uint32_t timeout = 1000) {
        auto i2c = reinterpret_cast<I2C_TypeDef*>(base_);
        uint32_t startTime = getTick();
        
        // 发送起始位
        i2c->CR1 |= (1 << 8);  // START
        if (!waitForFlag(I2C_SR1_SB, startTime, timeout)) return false;
        
        // 发送地址(写)
        i2c->DR = (addr7bit << 1);
        if (!waitForFlag(I2C_SR1_ADDR, startTime, timeout)) return false;
        (void)i2c->SR1; (void)i2c->SR2;  // 清除ADDR标志
        
        // 发送数据
        for (size_t i = 0; i < len; ++i) {
            i2c->DR = data[i];
            if (!waitForFlag(I2C_SR1_TXE, startTime, timeout)) return false;
        }
        
        // 发送停止位
        i2c->CR1 |= (1 << 9);  // STOP
        
        return true;
    }
    
    bool read(uint8_t addr7bit, uint8_t* buffer, size_t len, uint32_t timeout = 1000) {
        auto i2c = reinterpret_cast<I2C_TypeDef*>(base_);
        uint32_t startTime = getTick();
        
        // 发送起始位
        i2c->CR1 |= (1 << 8);  // START
        if (!waitForFlag(I2C_SR1_SB, startTime, timeout)) return false;
        
        // 发送地址(读)
        i2c->DR = (addr7bit << 1) | 1;
        if (!waitForFlag(I2C_SR1_ADDR, startTime, timeout)) return false;
        (void)i2c->SR1; (void)i2c->SR2;
        
        // 接收数据
        for (size_t i = 0; i < len; ++i) {
            if (i == len - 1) {
                i2c->CR1 &= ~(1 << 10);  // 关闭ACK
            }
            if (!waitForFlag(I2C_SR1_RXNE, startTime, timeout)) return false;
            buffer[i] = i2c->DR;
        }
        
        // 发送停止位
        i2c->CR1 |= (1 << 9);  // STOP
        
        return true;
    }
    
private:
    uint32_t base_;
    
    struct I2C_TypeDef {
        volatile uint32_t CR1, CR2, OAR1, OAR2, DR;
        volatile uint32_t SR1, SR2, CCR, TRISE;
    };
    
    static constexpr uint32_t I2C_SR1_SB = (1 << 0);      // Start bit
    static constexpr uint32_t I2C_SR1_ADDR = (1 << 1);    // Address sent
    static constexpr uint32_t I2C_SR1_TXE = (1 << 7);      // TX empty
    static constexpr uint32_t I2C_SR1_RXNE = (1 << 6);   // RX not empty
    
    bool waitForFlag(uint32_t flag, uint32_t startTime, uint32_t timeout) {
        auto i2c = reinterpret_cast<I2C_TypeDef*>(base_);
        while (!(i2c->SR1 & flag)) {
            if (getTick() - startTime > timeout) return false;
        }
        return true;
    }
    
    uint32_t getTick() { return 0; }  // 简化实现
};

// 45.5.3.2 I2C传感器示例:MPU6050陀螺仪
// 这是一款经典的六轴IMU(惯性测量单元),内置三轴加速度计+三轴陀螺仪
// 无人机、平衡车、电子稳增...到处都有它的身影
class MPU6050 {
public:
    static constexpr uint8_t I2C_ADDR = 0x68;
    
    MPU6050(I2CManager& i2c) : i2c_(i2c) {}
    
    bool init() {
        // 唤醒MPU6050
        uint8_t pwrMgmt[] = {0x6B, 0x00};  // PWR_MGMT_1
        return i2c_.write(I2C_ADDR, pwrMgmt, 2);
    }
    
    bool readAccelerometer(int16_t& x, int16_t& y, int16_t& z) {
        uint8_t reg = 0x3B;  // ACCEL_XOUT_H
        if (!i2c_.write(I2C_ADDR, &reg, 1)) return false;
        
        uint8_t data[6];
        if (!i2c_.read(I2C_ADDR, data, 6)) return false;
        
        x = (data[0] << 8) | data[1];
        y = (data[2] << 8) | data[3];
        z = (data[4] << 8) | data[5];
        return true;
    }
    
private:
    I2CManager& i2c_;
};

45.6 嵌入式C++最佳实践——老司机的经验之谈

45.6.1 内存管理:拒绝"内存泄漏"这个隐形杀手

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// 45.6.1.1 静态内存分配是嵌入式首选
// ❌ 危险:动态分配可能在错误时失败
void dangerous() {
    auto* buffer = new uint8_t[1024];  // 分配失败怎么办?返回nullptr?还是抛异常?
    // 更糟糕的是:嵌入式系统new可能根本不会失败,直接返回nullptr
    // 然后你高兴地往里面写数据——恭喜你,喜提HardFault
    // ...
    delete[] buffer;
}

// ✅ 推荐:静态分配,编译时就知道大小
alignas(4) static uint8_t sensorBuffer[1024];  // 永不失败,编译时分配,不占用堆栈
size_t bufferIndex = 0;

// 45.6.1.2 内存池:实时系统的最佳选择
template<typename T, size_t N>
class MemoryPool {
public:
    static_assert(std::is_trivially_destructible<T>::value, 
                  "T must be trivially destructible");
    
    T* allocate() {
        if (freeCount_ == 0) return nullptr;
        T* ptr = &pool_[usedCount_];
        ++usedCount_;
        --freeCount_;
        return ptr;
    }
    
    void deallocate(T* ptr) {
        // 对于trivially destructible的类型,不需要做什么
        --usedCount_;
        ++freeCount_;
    }
    
    size_t available() const { return freeCount_; }
    
private:
    alignas(alignof(T)) uint8_t storage_[sizeof(T) * N];
    size_t usedCount_ = 0;
    size_t freeCount_ = N;
};

// 使用示例
MemoryPool<SensorReading, 16> sensorPool;

void readSensors() {
    auto* reading = sensorPool.allocate();
    if (reading) {
        reading->timestamp = getTick();
        reading->temperature = readTemperature();
        reading->humidity = readHumidity();
        processReading(reading);
        sensorPool.deallocate(reading);
    }
}

45.6.2 运行时断言与错误处理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// 45.6.2.1 嵌入式安全的断言宏
#ifdef NDEBUG
    #define ASSERT(expr) ((void)0)
    #define ASSERT_MSG(expr, msg) ((void)0)
#else
    #define ASSERT(expr) \
        do { if (!(expr)) { fatalError("Assertion failed: " #expr); } } while(0)
    #define ASSERT_MSG(expr, msg) \
        do { if (!(expr)) { fatalError(msg); } } while(0)
#endif

// 45.6.2.2 错误码比异常更受嵌入式欢迎
// 为什么不用异常?想象一下:你的心脏起搏器在运行时 throw std::bad_alloc()...
// 捕获?谁知道。程序直接上天。
// 所以嵌入式界普遍的观点是:异常是奢侈品,错误码才是日常
enum class ErrorCode : uint8_t {
    None = 0,
    InvalidParameter,
    Timeout,
    HardwareFault,
    BufferFull,
    NotInitialized
};

class Result {
public:
    Result(ErrorCode ec) : code_(ec) {}
    operator bool() const { return code_ == ErrorCode::None; }
    ErrorCode code() const { return code_; }
    
    static Result success() { return Result(ErrorCode::None); }
    template<typename T>
    static Result fail(ErrorCode ec) { return Result(ec); }
    
private:
    ErrorCode code_;
};

// 使用示例
Result initializeUART(uint32_t baudrate) {
    if (baudrate == 0) {
        return Result::fail(ErrorCode::InvalidParameter);
    }
    // ...
    return Result::success();
}

void config() {
    if (auto result = initializeUART(115200); !result) {
        // 处理错误,但不用异常
        errorHandler(result.code());
    }
}

45.6.3 编译时计算:模板元编程的力量

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// 45.6.3.1 编译时CRC计算
template<uint32_t Polynomial, size_t TableSize = 256>
struct CRC_Table {
    static constexpr uint32_t table[TableSize] = []{
        uint32_t t[TableSize];
        for (size_t i = 0; i < TableSize; ++i) {
            uint32_t crc = i;
            for (size_t j = 0; j < 8; ++j) {
                if (crc & 1) {
                    crc = (crc >> 1) ^ Polynomial;
                } else {
                    crc >>= 1;
                }
            }
            t[i] = crc;
        }
        return t;
    }();
};

template<typename Table>
uint32_t crc32(const uint8_t* data, size_t len, uint32_t initial = 0xFFFFFFFF) {
    uint32_t crc = initial;
    for (size_t i = 0; i < len; ++i) {
        crc = Table::table[(crc ^ data[i]) & 0xFF] ^ (crc >> 8);
    }
    return ~crc;
}

// 使用
using CRC32 = CRC_Table<0xEDB88320>;
uint32_t checksum = crc32<CRC32>(reinterpret_cast<uint8_t*>(0x08000000), 1024);

// 45.6.3.2 编译时链表(类型列表)
template<typename... Ts>
struct TypeList {};

template<typename List, typename T>
struct Append;

template<typename... Ts, typename T>
struct Append<TypeList<Ts...>, T> {
    using type = TypeList<Ts..., T>;
};

template<typename List, size_t Index>
struct TypeAt;

template<typename T, typename... Ts>
struct TypeAt<TypeList<T, Ts...>, 0> {
    using type = T;
};

template<typename T, typename... Ts, size_t Index>
struct TypeAt<TypeList<T, Ts...>, Index> {
    using type = typename TypeAt<TypeList<Ts...>, Index - 1>::type;
};

// 在嵌入式配置中非常有用
using SensorTypes = TypeList<MPU6050, BMP280, SI7021>;
using ThirdSensor = typename TypeAt<SensorTypes, 2>::type;  // SI7021

45.6.4 代码组织:模块化设计

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// 45.6.4.1 接口与实现分离
// -------------------------------------------
// sensor.hpp - 传感器接口定义
// -------------------------------------------
#pragma once
#include <cstdint>

// 传感器类型枚举
enum class SensorType { Temperature, Humidity, Pressure, Accelerometer };

// 传感器读数结构
struct SensorReading {
    SensorType type;
    uint32_t timestamp;
    float value;
    bool valid;
};

// 传感器接口(抽象基类)
class ISensor {
public:
    virtual ~ISensor() = default;
    
    virtual bool initialize() = 0;
    virtual bool read(SensorReading& reading) = 0;
    virtual SensorType type() const = 0;
};

// -------------------------------------------
// sensor.cpp - 传感器实现
// -------------------------------------------
class BMP280 : public ISensor {
public:
    BMP280(I2CManager& i2c) : i2c_(i2c), calibrated_(false) {}
    
    bool initialize() override {
        // 初始化BMP280气压传感器
        uint8_t idReg = 0xD0;
        uint8_t id;
        if (!i2c_.write(I2C_ADDR, &idReg, 1)) return false;
        if (!i2c_.read(I2C_ADDR, &id, 1)) return false;
        if (id != 0x58) return false;  // 检查ID
        
        // 配置传感器
        uint8_t config[] = {0xF2, 0x01};  // 湿度 oversampling x1
        uint8_t ctrl[] = {0xF4, 0x27};    // 温度+压力 oversampling x1, normal mode
        return i2c_.write(I2C_ADDR, config, 2) && i2c_.write(I2C_ADDR, ctrl, 2);
    }
    
    bool read(SensorReading& reading) override {
        reading.type = SensorType::Pressure;
        reading.timestamp = getTick();
        
        uint8_t reg = 0xF7;  // PRESS_MSB
        if (!i2c_.write(I2C_ADDR, &reg, 1)) {
            reading.valid = false;
            return false;
        }
        
        uint8_t data[3];
        if (!i2c_.read(I2C_ADDR, data, 3)) {
            reading.valid = false;
            return false;
        }
        
        int32_t adc_P = ((uint32_t)data[0] << 12) | ((uint32_t)data[1] << 4) | (data[2] >> 4);
        reading.value = compensatePressure(adc_P) / 256.0f;
        reading.valid = true;
        return true;
    }
    
    SensorType type() const override { return SensorType::Pressure; }
    
private:
    I2CManager& i2c_;
    bool calibrated_;
    
    static constexpr uint8_t I2C_ADDR = 0x76;
    
    float compensatePressure(int32_t adc_P) {
        // 简化的补偿计算,实际需要校准参数
        return adc_P * 0.001f;  // 转换为 kPa
    }
};

// 45.6.4.2 传感器管理器
class SensorManager {
public:
    void addSensor(ISensor* sensor) {
        if (sensorCount_ < MAX_SENSORS) {
            sensors_[sensorCount_++] = sensor;
        }
    }
    
    bool initializeAll() {
        bool allSuccess = true;
        for (size_t i = 0; i < sensorCount_; ++i) {
            if (!sensors_[i]->initialize()) {
                allSuccess = false;
            }
        }
        return allSuccess;
    }
    
    size_t readAll(SensorReading* readings, size_t maxCount) {
        size_t count = 0;
        for (size_t i = 0; i < sensorCount_ && count < maxCount; ++i) {
            if (sensors_[i]->read(readings[count])) {
                ++count;
            }
        }
        return count;
    }
    
private:
    static constexpr size_t MAX_SENSORS = 8;
    ISensor* sensors_[MAX_SENSORS];
    size_t sensorCount_ = 0;
};

45.7 调试与测试——嵌入式开发的"debug艺术"

45.7.1 调试方法论

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// 45.7.1.1 软件断点(当硬件断点不够用时)
#define SOFT_BREAKPOINT() \
    do { \
        volatile uint32_t* DBGMCU_CR = reinterpret_cast<uint32_t*>(0xE0042004); \
        *DBGMCU_CR |= (1 << 0);  /* 触发调试 */ \
    } while(0)

// 45.7.1.2 ITM追踪输出(比UART更快的调试方式)
class ITM {
public:
    static void init() {
        // 启用ITM时钟和跟踪
        volatile uint32_t* DEMCR = reinterpret_cast<uint32_t*>(0xE000EDFC);
        *DEMCR |= (1 << 24);  // TRCENA
        
        volatile uint32_t* ITM_LAR = reinterpret_cast<uint32_t*>(0xE0000FB0);
        *ITM_LAR = 0xC5ACCE55;  // 解锁ITM
        
        volatile uint32_t* SCB_DEMCR = reinterpret_cast<uint32_t*>(0xE000EDFC);
        *SCB_DEMCR = *SCB_DEMCR | (1 << 24) | (1 << 0);  // TRCENA | VC_HARDERR
    }
    
    static void write(uint32_t port, const char* msg) {
        if (port >= 32) return;
        
        volatile uint32_t* ITM_TER = reinterpret_cast<uint32_t*>(0xE0000E00);
        if (!(*ITM_TER & (1 << port))) return;  // 端口未启用
        
        while (*reinterpret_cast<volatile uint32_t*>(0xE0000E04) == 0) { }
        
        const char* p = msg;
        while (*p) {
            *reinterpret_cast<volatile uint32_t*>(0xE0000E80) = *p++;
        }
    }
};

// 45.7.1.3 运行时统计
class RuntimeStats {
public:
    struct FunctionProfile {
        const char* name;
        uint32_t callCount;
        uint32_t totalCycles;
        uint32_t minCycles;
        uint32_t maxCycles;
    };
    
    static void record(const char* name, uint32_t startCycle) {
        uint32_t elapsed = DWT->CYCCNT - startCycle;
        // 更新统计(需要加锁或使用原子操作)
    }
    
    static void printReport() {
        // 通过ITM或UART输出统计信息
    }
    
private:
    static constexpr size_t MAX_FUNCTIONS = 32;
    static FunctionProfile profiles_[MAX_FUNCTIONS];
    static size_t profileCount_;
    
    struct DWT_TypeDef {
        volatile uint32_t CTRL, CYCCNT, CPICNT, EXCCNT, SLEEPCNT, LSUCNT;
    };
};

// 使用
#define PROFILE_FUNCTION() \
    static uint32_t __cycles_start = DWT->CYCCNT; \
    auto __profile_guard = finally([]{ RuntimeStats::record(__FUNCTION__, __cycles_start); })

45.7.2 单元测试框架(嵌入式友好)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
// 45.7.2.1 极简嵌入式测试框架
#define TEST(name) void test_##name()
#define ASSERT_EQ(a, b) do { \
    if ((a) != (b)) { \
        testFailed(#a " != " #b, __LINE__); \
        return; \
    } \
} while(0)
#define ASSERT_TRUE(x) do { \
    if (!(x)) { \
        testFailed(#x " is false", __LINE__); \
        return; \
    } \
} while(0)
#define ASSERT_FALSE(x) do { \
    if (x) { \
        testFailed(#x " is true", __LINE__); \
        return; \
    } \
} while(0)
#define RUN_TEST(name) do { \
    currentTest = #name; \
    test_##name(); \
    if (!testPassed) return false; \
} while(0)

class TestRunner {
public:
    static bool testPassed;
    static const char* currentTest;
    
    static void testFailed(const char* condition, int line) {
        char msg[128];
        snprintf(msg, sizeof(msg), "FAILED: %s at line %d", condition, line);
        print(msg);
        testPassed = false;
    }
    
    static void print(const char* msg) {
        // 输出到UART或ITM
    }
    
    template<typename... Args>
    static void printFormatted(const char* fmt, Args... args) {
        char buf[256];
        snprintf(buf, sizeof(buf), fmt, args...);
        print(buf);
    }
};

bool TestRunner::testPassed = false;
const char* TestRunner::currentTest = nullptr;

// 45.7.2.2 测试示例
TEST(circular_buffer_basic) {
    CircularBuffer buf;
    
    // 测试写入和读取
    ASSERT_TRUE(buf.write(0x42));
    auto val = buf.read();
    ASSERT_TRUE(val.has_value());
    ASSERT_EQ(*val, 0x42);
    
    // 测试缓冲区空
    ASSERT_FALSE(buf.read().has_value());
    
    // 测试缓冲区满
    for (size_t i = 0; i < CircularBuffer::SIZE; ++i) {
        ASSERT_TRUE(buf.write(i));
    }
    ASSERT_FALSE(buf.write(0xFF));  // 应该失败
    
    testPassed = true;  // 所有断言通过
}

// 45.7.2.3 运行所有测试
bool runAllTests() {
    TestRunner::print("Running embedded unit tests...\r\n");
    int passed = 0, failed = 0;
    
    #define TEST_CASE(name) \
        do { \
            TestRunner::print("  Testing " #name "... "); \
            if (RUN_TEST(name)) { \
                TestRunner::print("PASSED\r\n"); \
                passed++; \
            } else { \
                TestRunner::print("FAILED\r\n"); \
                failed++; \
            } \
        } while(0)
    
    TEST_CASE(circular_buffer_basic);
    TEST_CASE(crc32_calculation);
    TEST_CASE(sensor_reading);
    
    // ... 更多测试
    
    TestRunner::printFormatted("Results: %d passed, %d failed\r\n", passed, failed);
    return failed == 0;
}

45.7.3 硬件在环测试(HIL)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// 45.7.3.1 Mock外设用于测试
class MockGPIO : public IDigitalIO {
public:
    MockGPIO() : level_(false), toggleCount_(0) {}
    
    void write(bool level) override {
        level_ = level;
        if (level) highCount_++; else lowCount_++;
    }
    
    bool read() const override { return level_; }
    void toggle() override { level_ = !level_; toggleCount_++; }
    
    bool getLevel() const { return level_; }
    uint32_t getToggleCount() const { return toggleCount_; }
    void reset() { level_ = false; toggleCount_ = 0; highCount_ = 0; lowCount_ = 0; }
    
private:
    bool level_;
    uint32_t toggleCount_;
    uint32_t highCount_;
    uint32_t lowCount_;
};

class MockI2C {
public:
    struct Transfer {
        uint8_t addr;
        std::vector<uint8_t> tx;
        std::vector<uint8_t> rx;
        bool success;
    };
    
    bool write(uint8_t addr, const uint8_t* data, size_t len) override {
        transfers_.push_back({addr, {data, data+len}, {}, true});
        return true;
    }
    
    void addResponse(uint8_t addr, std::vector<uint8_t> response) {
        for (auto& t : transfers_) {
            if (t.addr == addr) {
                t.rx = response;
            }
        }
    }
    
    const Transfer& lastTransfer() const { return transfers_.back(); }
    void reset() { transfers_.clear(); }
    
private:
    std::vector<Transfer> transfers_;
};

45.8 FreeRTOS简介——当状态机不够用的时候

⚠️ 注意:很多嵌入式项目根本不需要RTOS!先问自己:真的需要任务切换吗?如果只是几个外设交替工作,状态机 + 中断就够了。只有当你的系统需要"同时做很多事"且它们之间有优先级关系时,才考虑RTOS。

😂 血的教训:某新手听说RTOS很"高级",于是在一个简单的LED闪烁项目里硬上了FreeRTOS。结果代码量和复杂度翻了三倍,RAM不够用,最后老老实实改回裸机——一个while(1) { toggle(); delay(500); } 搞定。技术选型的第一原则:够用就好。

45.8.1 FreeRTOS核心概念

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
// 45.8.1.1 任务创建
#include "FreeRTOS.h"
#include "task.h"

// 任务函数签名必须是这样的
void vLEDTask(void* pvParameters) {
    // pvParameters可以传递初始化参数
    int ledPin = *static_cast<int*>(pvParameters);
    
    // 任务函数必须是一个无限循环
    for (;;) {
        // 做点事
        toggleGPIO(ledPin);
        
        // 延时(不是busy wait!)
        vTaskDelay(pdMS_TO_TICKS(500));  // 500ms
    }
    
    // 如果要删除任务
    vTaskDelete(NULL);
}

// 创建任务
void createLEDTask() {
    TaskHandle_t handle;
    int ledPin = 5;
    
    // 优先级1,堆栈大小256字
    xTaskCreate(
        vLEDTask,              // 任务函数
        "LED Task",            // 任务名称(用于调试)
        256,                   // 堆栈深度(字,不是字节)
        &ledPin,               // 传递给任务的参数
        1,                     // 优先级(0最低)
        &handle                // 任务句柄
    );
}

// 45.8.1.2 队列通信
QueueHandle_t sensorQueue;

void vSensorTask(void* pvParameters) {
    sensorQueue = xQueueCreate(10, sizeof(SensorReading));
    
    for (;;) {
        SensorReading reading;
        if (readSensor(&reading)) {
            // 发送数据到队列,超时100ms
            xQueueSend(sensorQueue, &reading, pdMS_TO_TICKS(100));
        }
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

void vDisplayTask(void* pvParameters) {
    SensorReading reading;
    
    for (;;) {
        if (xQueueReceive(sensorQueue, &reading, portMAX_DELAY) == pdTRUE) {
            displayUpdate(reading);
        }
    }
}

// 45.8.1.3 互斥量保护共享资源
SemaphoreHandle_t xMutex;

void initI2C() {
    xMutex = xSemaphoreCreateMutex();
}

bool safeI2CRead(uint8_t addr, uint8_t* data, size_t len) {
    if (xSemaphoreTake(xMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
        // 临界区
        bool success = i2c.read(addr, data, len);
        xSemaphoreGive(xMutex);
        return success;
    }
    return false;  // 获取信号量失败
}

45.8.2 何时使用RTOS vs 状态机

场景推荐方案
简单LED闪烁裸机 + delay
多个传感器轮询状态机
同时处理UART + 传感器 + 显示屏RTOS
硬实时控制(电机PWM)裸机 + 高优先级中断
软实时(UI更新 + 通信)RTOS
资源极度受限(<4KB RAM)裸机

45.9 实战项目:温湿度监控系统

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
// ============================================================
// 完整示例:基于STM32的温湿度监控系统
// ============================================================

#include <cstdint>
#include <array>

// -------------------------------------------
// 硬件抽象层
// -------------------------------------------
class Hardware {
public:
    static void init() {
        // 禁用所有中断
        __asm volatile ("cpsid i" : : : "memory");
        
        // 配置系统时钟
        SystemClock_Config();
        
        // 配置GPIO
        GPIO_Config();
        
        // 配置UART(用于调试输出)
        UART1_Config();
        
        // 配置I2C
        I2C1_Config();
        
        // 配置定时器(用于系统tick)
        Timer2_Config();
        
        // 启用中断
        __asm volatile ("cpsie i" : : : "memory");
    }
    
    // 简化实现
    static void SystemClock_Config() { /* 168MHz PLL */ }
    static void GPIO_Config() { /* LED, Button, I2C pins */ }
    static void UART1_Config() { /* 115200-8-N-1 */ }
    static void I2C1_Config() { /* 100kHz */ }
    static void Timer2_Config() { /* 1ms tick */ }
};

// -------------------------------------------
// 传感器驱动
// -------------------------------------------
class SI7021 {
public:
    static constexpr uint8_t I2C_ADDR = 0x40;
    
    static bool measure(float& humidity, float& temperature) {
        uint8_t cmd = 0xF5;  // Measure Humidity, Hold Master Mode
        uint8_t data[2];
        
        if (!I2C_WriteRead(I2C_ADDR, &cmd, 1, data, 2)) {
            return false;
        }
        
        uint16_t humRaw = (data[0] << 8) | data[1];
        humidity = (125.0f * humRaw / 65536.0f) - 6.0f;
        
        // 读取温度
        cmd = 0xE0;  // Read Previous Temperature
        if (!I2C_WriteRead(I2C_ADDR, &cmd, 1, data, 2)) {
            temperature = 0;
            return false;
        }
        
        uint16_t tempRaw = (data[0] << 8) | data[1];
        temperature = (175.72f * tempRaw / 65536.0f) - 46.85f;
        
        return true;
    }
    
private:
    static bool I2C_WriteRead(uint8_t addr, uint8_t* tx, size_t txLen, 
                              uint8_t* rx, size_t rxLen);
};

// -------------------------------------------
// 数据存储(环形缓冲区)
// -------------------------------------------
template<typename T, size_t N>
class CircularDataBuffer {
public:
    bool add(const T& item) {
        if (isFull()) return false;
        data_[tail_] = item;
        tail_ = (tail_ + 1) % N;
        return true;
    }
    
    T* get(size_t index) {
        if (index >= size()) return nullptr;
        return &data_[(head_ + index) % N];
    }
    
    bool isFull() const { return size() == N; }
    bool isEmpty() const { return head_ == tail_; }
    size_t size() const {
        if (tail_ >= head_) return tail_ - head_;
        return N - head_ + tail_;
    }
    
    void clear() { head_ = tail_ = 0; }
    
private:
    std::array<T, N> data_;
    size_t head_ = 0;
    size_t tail_ = 0;
};

// -------------------------------------------
// 显示器(OLED SSD1306)
// -------------------------------------------
class OLED1306 {
public:
    static void init() {
        // 初始化序列
        sendCommand(0xAE);  // Display off
        sendCommand(0xD5); sendCommand(0x80);  // Clock
        sendCommand(0xA8); sendCommand(0x3F);  // Mux ratio
        sendCommand(0xD3); sendCommand(0x00);  // Display offset
        sendCommand(0x40);  // Start line
        sendCommand(0x8D); sendCommand(0x14);  // Charge pump
        sendCommand(0xA1);  // Segment remap
        sendCommand(0xC8);  // COM scan direction
        sendCommand(0xDA); sendCommand(0x12);  // COM pins
        sendCommand(0x81); sendCommand(0xCF);  // Contrast
        sendCommand(0xD9); sendCommand(0xF1);  // Pre-charge
        sendCommand(0xDB); sendCommand(0x40);  // VCOMH
        sendCommand(0xA4);  // Display ON
        clear();
    }
    
    static void clear() {
        for (int page = 0; page < 8; ++page) {
            setCursor(0, page);
            for (int col = 0; col < 128; ++col) {
                sendData(0x00);
            }
        }
    }
    
    static void print(const char* str, uint8_t x, uint8_t y) {
        setCursor(x, y);
        while (*str) {
            sendData(font5x7[*str - 32]);
            ++str;
        }
    }
    
    static void printFloat(float val, uint8_t x, uint8_t y, int decimal = 2) {
        char buf[16];
        int len = snprintf(buf, sizeof(buf), "%.*f", decimal, val);
        print(buf, x, y);
    }
    
private:
    static void sendCommand(uint8_t cmd);
    static void sendData(uint8_t data);
    static void setCursor(uint8_t x, uint8_t y);
    
    static const uint8_t font5x7[95 * 5];  // 字体数据
};

// -------------------------------------------
// 主应用
// -------------------------------------------
class WeatherStation {
public:
    WeatherStation() 
        : displayUpdateCounter_(0),
          sensorErrorCount_(0),
          lastHumidity_(0),
          lastTemperature_(0) {}
    
    void init() {
        Hardware::init();
        OLED1306::init();
        OLED1306::print("Weather Station", 0, 0);
        OLED1306::print("Initializing...", 0, 2);
        
        // 传感器自检
        if (!testSensors()) {
            OLED1306::print("Sensor Error!", 0, 4);
            while (true);  // 停机
        }
        
        OLED1306::print("Ready!", 0, 4);
        delay_ms(1000);
        OLED1306::clear();
    }
    
    void run() {
        // 读取传感器
        if (readSensors()) {
            // 数据有效,存储并显示
            storeData();
            updateDisplay();
        } else {
            // 传感器错误处理
            handleSensorError();
        }
    }
    
private:
    bool testSensors() {
        float h, t;
        return SI7021::measure(h, t);
    }
    
    bool readSensors() {
        float h, t;
        if (SI7021::measure(h, t)) {
            lastHumidity_ = h;
            lastTemperature_ = t;
            sensorErrorCount_ = 0;
            return true;
        }
        return false;
    }
    
    void storeData() {
        Reading reading = {
            getTick(),
            lastTemperature_,
            lastHumidity_
        };
        buffer_.add(reading);
    }
    
    void updateDisplay() {
        if (++displayUpdateCounter_ >= 10) {  // 每10次循环更新一次(约1秒)
            displayUpdateCounter_ = 0;
            
            OLED1306::clear();
            OLED1306::print("Temperature:", 0, 0);
            OLED1306::printFloat(lastTemperature_, 70, 0);
            OLED1306::print("C", 110, 0);
            
            OLED1306::print("Humidity:", 0, 2);
            OLED1306::printFloat(lastHumidity_, 70, 2);
            OLED1306::print("%", 110, 2);
            
            OLED1306::print("Samples:", 0, 4);
            char buf[16];
            snprintf(buf, sizeof(buf), "%d", static_cast<int>(buffer_.size()));
            OLED1306::print(buf, 70, 4);
            
            // 如果有错误,显示警告
            if (sensorErrorCount_ > 0) {
                OLED1306::print("Err:", 0, 6);
                snprintf(buf, sizeof(buf), "%d", sensorErrorCount_);
                OLED1306::print(buf, 40, 6);
            }
        }
    }
    
    void handleSensorError() {
        sensorErrorCount_++;
        if (sensorErrorCount_ > 10) {
            // 连续错误,尝试重启传感器
            sensorErrorCount_ = 0;
            // 复位I2C总线等
        }
    }
    
    struct Reading {
        uint32_t timestamp;
        float temperature;
        float humidity;
    };
    
    CircularDataBuffer<Reading, 64> buffer_;
    uint32_t displayUpdateCounter_;
    uint8_t sensorErrorCount_;
    float lastHumidity_;
    float lastTemperature_;
};

// -------------------------------------------
// 主函数
// -------------------------------------------
int main() {
    WeatherStation station;
    
    station.init();
    
    while (true) {
        station.run();
        delay_ms(100);
    }
    
    return 0;
}

本章小结

🎯 核心知识点

  1. 嵌入式系统本质

    • 资源受限、实时性要求高、可靠性优先
    • C++的优势:性能接近C + 抽象能力 + 模板元编程
  2. 交叉编译

    • 开发环境与目标环境分离
    • CMake工具链配置是必备技能
  3. 硬件抽象层(HAL)

    • 接口抽象:IDigitalIO等接口类
    • 模板GPIO:编译期确定引脚,零运行时开销
    • RAII管理资源(临界区、片选)
  4. 外设编程

    • UART:调试输出、简单通信
    • SPI:高速设备(Flash、显示屏)
    • I2C:传感器、多设备总线
  5. 中断与实时

    • 临界区保护:RAII的InterruptController::CriticalSection
    • 定时器:精确计时和延时
    • 状态机:非抢占式实时系统的利器
  6. 内存管理

    • 静态分配 > 动态分配
    • 内存池:嵌入式友好的动态内存方案
    • 模板元编程:编译时计算CRC等
  7. 调试与测试

    • ITM追踪比UART更快
    • Mock外设用于单元测试
    • 嵌入式友好的轻量级测试框架
  8. RTOS慎用原则

    • 简单场景用状态机
    • 复杂并发场景才用FreeRTOS
    • 资源受限平台优先裸机

💡 老司机忠告

“在嵌入式开发中,不要过早优化,但要一开始就考虑资源限制。一个好的HAL设计比200行寄存器操作更值钱。记住:你写的代码可能会在某个心脏起搏器或汽车安全气囊控制器里运行——质量和可靠性永远是第一位。”


本章完

最后修改 March 29, 2026: 新增 C++ 教程 (41f3014)