嵌入式C++:如何为微控制器编写代码而不抓狂

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么在嵌入式中使用C++不是自虐,而是超能力

\n\n

当听到“嵌入式开发”时,眼前通常会浮现一位拿着烙铁、用纯C语言编写代码并对着数据手册祈祷的严肃工程师。或者相反——一位热衷于在Arduino上使用digitalWrite()并因点亮LED而欣喜的爱好者。但在这两个世界之间,存在着一个黄金中间地带——基于C++的嵌入式开发。

\n\n

没错,就是那个因模板和虚函数而令人望而生畏的C++。事实上,现代微控制器(ARM Cortex-M、RISC-V)完全能够处理C++代码。更重要的是,合理使用面向对象编程、封装甚至模板,可以使您的嵌入式代码更清晰、更安全、更快速。

\n\n

在本文中,我们将探讨如何编写高效的微控制器C++代码,避开虚函数表的陷阱,并学习以C++风格在寄存器层面操作硬件。准备好了吗?

\n\n

1. 基础:嵌入式C++与普通C++的区别

\n\n

嵌入式开发的首要规则:资源有限。您没有千兆字节的RAM和千兆赫兹的处理器。您只有32 KB的闪存和8 KB的RAM。每个字节都至关重要。因此,嵌入式中的C++不是关于“编写一个具有10层继承的类”,而是关于对抽象的控制。

\n\n

可以放心使用的特性:

\n
    \n
  • 类和结构体——用于分组数据和函数。如果没有虚函数,这(在内存上)是零成本的。
  • \n
  • 命名空间——避免产生像hal_gpio_init()这样的前缀。
  • \n
  • constexpr和模板——编译时计算,节省时钟周期。
  • \n
  • 引用和常量引用传递——比指针更安全。
  • \n
\n\n

非必要不使用的特性:

\n
    \n
  • 动态内存(new/delete)——堆会碎片化,分配器可能失败。在裸机项目中是禁忌。
  • \n
  • 异常(try/catch)——需要运行时支持,占用大量空间。
  • \n
  • 虚函数——每个虚函数都会在RAM/Flash中创建一个表(vtable),调用通过双重间接寻址。但有时它们是有道理的(驱动程序)。
  • \n
  • RTTI(typeid)——完全不需要。
  • \n
\n\n

核心很简单:如果您不使用“重量级”特性,C++编译器生成的代码与C一样高效。检查反汇编代码——您会感到满意。

\n\n

2. 实践:以C++风格操作寄存器

\n\n

在经典的嵌入式C中,我们编写类似这样的代码:

\n
#define GPIOA_BASE 0x40020000\n#define GPIOA_MODER (*(volatile uint32_t*)(GPIOA_BASE + 0x00))\nGPIOA_MODER |= (1 << 10); // 设置第10位(输出)
\n\n

这可行,但看起来过时了。在C++中,我们可以创建一个没有额外开销的便捷封装。

\n\n

示例:用于操作引脚的类

\n
#include <cstdint>\n\nclass GpioPin {\npublic:\n    // 禁止复制\n    GpioPin(const GpioPin&) = delete;\n    GpioPin& operator=(const GpioPin&) = delete;\n\n    // 模式枚举\n    enum class Mode : uint32_t {\n        Input = 0b00,\n        Output = 0b01,\n        AltFunc = 0b10,\n        Analog = 0b11\n    };\n\n    // 构造函数:接受端口地址和引脚编号(0-15)\n    GpioPin(volatile uint32_t* port_base, uint8_t pin)\n        : port_(port_base), pin_(pin) {\n        // 可以立即配置引脚\n    }\n\n    // 设置工作模式\n    void setMode(Mode mode) {\n        volatile uint32_t* moder = port_ + 0x00; // MODER寄存器偏移\n        *moder &= ~(0b11 << (pin_ * 2));\n        *moder |= (static_cast<uint32_t>(mode) << (pin_ * 2));\n    }\n\n    // 设置高电平\n    void high() {\n        volatile uint32_t* bsrr                

博客

书籍推荐