我一直在编写越来越复杂的固件,并且开始注意到我对设计模式和体系结构的知识有点缺乏。我正在努力发展这些技能,并希望能提供一些意见。注意:这是用于嵌入式c的微控制器。
我现在正在将一个新项目的概念作为练习来进行,
这是一个粗略的系统图:
现在我要做的是提供一个良好的固件体系结构,该体系结构将具有可扩展性(添加多个电池,添加更多传感器,将LCD(或其他)接口从I2C更改为SPI等),并进行测试(通过UART模拟按钮按下,将电池读数替换为模拟值以测试PMIC充电固件等)。
我通常要做的是为每个外设编写一个定制驱动程序,并为每个模块编写一个固件模块。我还将使用在整个系统中使用的全局可用的get / set来实现标记模块。例如,我的计时器将设置100Hz,5Hz,1Hz,标志,主循环将以所需的速率处理和调用各个模块。然后,模块本身可以为主循环设置标志,以处理诸如I2C事务完成,事务超时,温度过高等事件。
我希望从中得到一些建议,以更好的方式构建系统来实现我的可伸缩性,封装和抽象目标。看来我正在做的是一种伪事件驱动的系统,但是被一起黑客入侵了。 无论如何,这是我尝试的体系结构图:
答案 0 :(得分:5)
“事件总线”的概念过于复杂。在许多情况下,最简单的方法是最大程度地减少需要异步发生的事物的数量,但是要有一个“主轮询”例程,该例程以“尽可能方便”的方式运行,并为每个子系统调用轮询例程。本身在编译中包含这样的例程可能会有所帮助,这样该文件的本质将仅仅是其他子系统使用的所有轮询功能的列表,而不是具有其自身语义的任何东西。如果有一个“获取按钮按下”例程,则该例程中可能有一个循环,该循环调用主轮询例程,直到按下按钮,键盘超时或调用者需要处理的其他事情为止。这样就可以使用以下代码来实现主界面:
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);
}
这通常比尝试拥有巨型状态机要方便得多,它说如果代码处于STATE_MAYBE_DO_SOMETHING_FUN
状态并且按下了yes
或no
按钮,它将需要进入STATE_START_DOING_SOMETHING_FUN
或STATE_START_DOING_SOMETHING_BORING
状态。
请注意,如果使用这种方法,则需要确保两次调用main_poll之间的最坏情况始终满足通过main_poll
处理的轮询操作的及时性要求,但是在这种情况下可以满足,这种方法比进行抢先调度的多线程代码以及使其可靠运行所需的锁和其他防护措施所进行的所有操作都更加方便和高效。