嵌入式固件架构

Otu*_*tus 2 c firmware

我一直在编写越来越复杂的固件,并开始注意到我对设计模式和架构的了解有点缺乏.我正在努力发展这些技能,并希望得到一些投入.注意:这适用于微控制器的嵌入式c.

我现在正在使用一个新项目的概念作为练习,如下所示:

  1. 我们有一个带用户I/O的电池管理模块
  2. 主控制器负责I/O(按钮,LCD,UART调试),检测插入/拔出充电器等事件,以及管理高级操作.
  3. 副控制器是电池管理控制器(几乎是定制PMIC),能够监控电池电量,运行充电/放电固件等.
  4. PMIC与电量计IC接口,用于从中读取电池信息
  5. 两个控制器,电量计和LCD之间的接口都是I2C

这是一个粗略的系统图:

在此输入图像描述

现在我要做的是提出一个良好的固件架构,允许扩展(添加多个电池,添加更多传感器,将LCD(或其他)接口从I2C更改为SPI等),以及用于测试(通过UART模拟按钮按下,用模拟值替换电池读数以测试PMIC充电固件等).

我通常做的是为每个外设编写自定义驱动程序,为每个块编写固件模块.我将实现一个标记模块以及将在整个系统中使用的全局可用的get/set.例如,我的定时器将设置100Hz,5Hz,1Hz标志,主循环将处理这些标志并以所需速率调用各个模块.然后模块本身可以设置主循环的标志来处理I2C事务完成,事务超时,温度超过等事件.

我希望从中获得的是一些关于更好地构建系统以实现我的可伸缩性,封装和抽象目标的建议.看起来我正在做的是一种伪事件驱动的系统,但是一个被黑客攻击的系统.无论如何,这是我对架构图的尝试:

在此输入图像描述

在此输入图像描述

sup*_*cat 5

"事件总线"的概念过于复杂.在许多情况下,最简单的方法是最小化需要异步发生的事物的数量,而是具有"主要轮询"例程,该例程在"通常方便"的基础上运行并且为每个子系统调用轮询例程.在编译中单独使用这样的例程可能会有所帮助,因此该文件的本质只是其他子系统使用的所有轮询函数的列表,而不是具有其自身语义的任何东西.如果有一个"获取按钮按下"例程,则可以在该例程中有一个循环,该循环调用主轮询例程,直到按下按钮,键盘超时,或者调用者需要处理的其他事情.然后,这将允许使用以下代码实现主UI:

void maybe_do_something_fun(void)
{
  while(1)
  {
    show_message("Do something fun?");
    wait_for_button();
    if (button_hit(YES_BUTTON))
    {
      ... do something fun
      return;
    }
    else if (button_hit(NO_BUTTON))
    {
      ... do something boring
      return;
    }
  } while(1);
}
Run Code Online (Sandbox Code Playgroud)

这通常比试图有一个巨大的状态机和说,如果代码是方便多了STATE_MAYBE_DO_SOMETHING_FUN状态,并且yesno按钮被按下,就需要提前到STATE_START_DOING_SOMETHING_FUNSTATE_START_DOING_SOMETHING_BORING状态.

请注意,如果使用这种方法,则需要确保调用main_poll之间的最坏情况时间总是满足通过处理的轮询操作的及时性要求main_poll,但是在可以满足该要求的情况下,这种方法可以是比执行预先安排的多线程代码所需的一切以及使其可靠地工作所需的锁和其他防护装置更加方便和有效.