在生产中的微控制器项目中使用C ++的五年

在本文中,我将告诉您如何将我工作了五年的公司从管理微控制器的项目转移到C到C ++,以及它的来龙去脉(破坏者:一切都不好)。

关于我自己的一点


我开始在C微控制器下编写程序,只有在Pascal上学,然后学习汇编程序,并花了大约3年的时间研究各种微控制器架构及其外围设备。 然后,他们花了几年的时间进行并行研究,从而获得了在C#和C ++中进行实际工作的经验。 在这段时间之后,我又很长时间回到了对微控制器进行编程的工作,他们已经具有从事实际项目的必要理论基础。

第一年


我对C的过程风格一无所知,但是在我的实际项目中开始实际实践的企业使用“面向对象风格的C编程”。 它看起来像这样。

typedef const struct _uart_init { USART_TypeDef *USARTx; uint32_t baudrate; ... } uart_cfg_t; int uart_init (uart_cfg_t *cfg); int uart_start_tx (int fd, void *d, uint16_t l); int uart_tx (int fd, void *d, uint16_t l, uint32_t timeout); 

这种方法具有以下优点:

  1. 该代码仍然是C代码,因此具有以下优点:
    • 控制“对象”更容易,因为很容易跟踪谁和何处导致了什么原因和发生原因的顺序(中断除外,但本文没有);
    • 存储“指向对象的指针”就足以记住返回的fd;
    • 如果删除了“对象”,那么当您尝试使用它时,您将在函数的返回值中收到相应的错误;
  2. 通过在此处使用的HAL对此类对象进行抽象,可以从其自己的初始化结构编写可自定义任务的对象(并且在缺少HAL功能的情况下,可以将对寄存器的访问隐藏在“对象”内部)。

缺点:

  1. 如果有人删除了“对象”,然后创建了另一种不同类型的对象,则可能会发生新对象获得旧对象的fd且无法确定进一步行为的情况。 可以很容易地更改此行为,而不是以链表的内存消耗为代价,而不是使用具有键值的数组(每个索引fd的数组存储指向对象结构的指针)。
  2. 无法在“全局对象”下静态标记内存。 由于在大多数应用程序中,“对象”仅创建一次,并且不再被删除,因此它看起来像“拐杖”。 在这里,当创建一个对象时,可以将一个指针传递给它的内部结构,该内部结构是在布局过程中静态分配的,但这将进一步混淆初始化代码并破坏封装。

当被问及为何在构建整个基础架构时未选择C ++时,他们回答如下:“好吧,C ++导致大量额外成本,不受控制的内存成本以及庞大的可执行固件文件。” 也许他们是对的。 确实,在开始设计之初,只有GCC 3.0.5,它对C ++的友好程度并不高(我们仍然必须使用它来编写QNX6下的程序)。 没有constexpr和C ++ 11/14,它允许您创建全局对象,这些对象本质上是.data区域中的数据,它们是在编译阶段计算的以及它们的方法。

对于这个问题,为什么不写在寄存器上呢?我得到一个明确的答案,即使用“对象”可以让您“在一天内”配置相同类型的应用程序。

意识到所有这些并意识到现在的C ++与GCC 3.0.5并不相同,我开始重写C ++功能的主要部分。 首先,请先使用微控制器的硬件外围设备,然后再使用外部设备的外围设备。 实际上,这只是当时可用的一个更方便的外壳。

第二年和第三年


我重写了用C ++开发项目所需的一切,并继续用C ++立即编写新模块。 但是,这些只是C之上的外壳。意识到我使用C ++不够用之后,我开始使用它的优点:模板,仅标头类,constexpr等。 一切进展顺利。

第四和第五年


  • 所有对象都是全局的,并且在编译阶段包含彼此的链接(根据项目的体系结构);
  • 在布局阶段为所有对象分配内存;
  • 每个引脚按类对象;
  • 一个封装了所有引脚的对象,以一种方法对其进行初始化;
  • 一个RCC控制对象,封装了硬件总线上的所有对象;
  • 根据客户协议的CAN <-> RS485转换器项目包含60个对象;
  • 如果某个对象在某个对象的HAL或类级别处于该级别,则您不仅要“解决问题”,还必须考虑如何解决此问题,以便此修复程序可以在此模块的所有可能配置上起作用;
  • 在查看最终固件的地图,asm和bin文件(或在微控制器中启动调试)之前,无法计算使用的模板和constexpr;
  • 如果模板出错,则输出一条消息,其长度为GCC项目配置的三分之一。 阅读和理解其中的内容是一项单独的成就。

总结


现在我了解以下内容:
  1. 使用“通用模块构造函数”只会使程序不必要地复杂。 调整一个新项目的配置寄存器比深入研究对象之间的关系,然后深入HAL库要容易得多。
  2. 不要害怕使用C ++,因为它会“浪费大量内存”或“不如C优化”。 不,不是。 您需要担心,使用对象和许多抽象层会使代码难以阅读,而调试它将是一项壮举。
  3. 如果您不使用任何“复杂”的东西,例如C ++的模板,继承和其他吸引人的魅力,那为什么还要使用C ++呢? 只是为了物品? 值得吗? 并且为了静态全局对象而未在某些项目上禁止使用new / delete?

综上所述,我们可以说使用C ++的明显简单性只是反复增加项目复杂性而不增加速度或内存的借口。

Source: https://habr.com/ru/post/zh-CN461113/


All Articles