有限状态机代码在 µC 中属于什么地方?
Where does finite-state machine code belong in µC?
我在 EE 论坛上问 this question。 Whosebug 上的你们比我们在 EE 上更了解编码,所以也许你们可以提供更多关于这方面的详细信息:)
当我学习微控制器时,老师教我总是以 while(1);
结束代码,循环中没有代码。
这是为了确保软件 "stuck" 保持中断工作。当我问他们是否可以在这个无限循环中放入一些代码时,他们告诉我这是个坏主意。知道了这一点,我现在尽力让这个循环保持空白。
我现在需要在微控制器中实现有限状态机。乍一看,该代码似乎属于此循环。这使编码更容易。
这是个好主意吗?有什么优缺点?
这就是我的计划:
void main(void)
{
// init phase
while(1)
{
switch(current_State)
{
case 1:
if(...)
{
current_State = 2;
}
else(...)
{
current_State = 3;
}
else
current_State = 4;
break;
case 2:
if(...)
{
current_State = 3;
}
else(...)
{
current_State = 1;
}
else
current_State = 5;
break;
}
}
而不是:
void main(void)
{
// init phase
while(1);
}
并使用中断管理 FSM
您应该实现的是您的选择和代码的调试难易程度。
有时使用 while(1) 是正确的;如果您的 uC 将完全处理中断 (ISR),请在代码末尾声明。而在其他一些应用程序中,uC 将与无限循环内的代码一起使用(称为轮询方法):
while(1)
{
//code here;
}
在其他一些应用程序中,您可能会混合使用 ISR 方法和轮询方法。
当说 'debugging easiness' 时,仅使用 ISR 方法(将 while(1); 语句放在末尾)将使您难以调试代码,因为在触发中断事件时,所选的调试器将不会给你一步一步的事件注册阅读和关注。另外,请注意,不建议编写完整的 ISR 代码,因为 ISR 事件应该进行最少的编码(例如增加计数器,raise/clear 标志,例如)并能够迅速退出。
进行嵌入式编程时,一种常用的习惯用法是使用 "super loop" - 初始化完成后开始的无限循环,根据需要分派程序的独立组件 运行 .在这种范例下,您可以按照您的建议 运行 超级循环中的有限状态机,并继续 运行 来自中断上下文的硬件管理功能,因为听起来您已经在做.这样做的缺点之一是您的处理器将始终处于高功率消耗状态 - 因为您总是 运行 宁那个循环,处理器永远不会进入睡眠状态。然而,这在您编写的任何代码中实际上也是一个问题 - 即使是空的无限 while 循环也会使处理器保持 运行ning。解决这个问题的方法通常是用一系列指令结束 while 循环,使处理器进入低功耗状态(完全依赖于体系结构),当有中断需要处理时将其唤醒。如果 FSM 中发生的事情不是由任何中断驱动的,通常使用的保持处理器定期唤醒的方法是初始化定时器以定期中断,以使您的主循环继续执行。
还有一件事要注意,如果您之前从中断上下文中执行所有代码 - 中断服务例程 (ISR) 确实应该尽可能短,因为它们确实 "interrupt" 主要执行程序的一部分,如果它们花费的时间太长,可能会导致意想不到的副作用。处理这个问题的一种正常方法是在你的超级循环中有处理程序,这些处理程序只是由 ISR 发出信号,这样大部分需要完成的处理都在有时间的时候在主上下文中完成,而不是中断一个主要上下文的潜在时间关键部分。
就像说return所有功能都在一个地方,或者其他习惯。在一种类型的设计中,您可能想要这样做,一种纯粹基于 interrupt/event 的设计。有些产品完全相反,有轮询,甚至没有驱动。以及介于两者之间的任何东西。
重要的是进行系统工程,仅此而已。中断增加了复杂性和风险,它们比不使用它们的代价更高。自动使任何设计中断驱动是一个错误的决定,仅仅意味着没有在设计、需求、风险等方面投入任何努力。
理想情况下,您希望您的大部分代码都在主循环中,您希望您的中断精益求精,以便降低其他时间关键任务的延迟。并非所有 MCU 都有复杂的中断优先级系统,这会让您浪费大量时间或让您的所有应用程序都在处理程序中。输入您的系统工程,可能有助于选择单片机,但在这里您又增加了风险。
您必须问自己,您的 mcu 必须执行哪些任务,如果每个任务从事件发生到它们必须开始响应并必须完成,每个任务是否存在任何延迟怎么办,根据 event/task 如果它的任何部分可以推迟怎么办。做任务的时候能不能任意打断,时间上能不能有空隙。您会为硬件设计、cpld 或 fpga 设计做的所有问题。除非你在那里有真正的并行性。
在现实世界的解决方案中,您可能最终得到的是中断处理程序中的某些部分和主(无限)循环中的某些部分。主循环轮询中断留下的面包屑 and/or 直接轮询状态寄存器以了解循环期间要做什么。 If/when 你到达了你需要实时的地方你仍然可以使用主超级循环,你的实时响应来自循环的可能路径和任何这些路径的最坏情况时间。
大多数时候您不需要做这么多工作。也许是一些中断,也许是一些轮询,一个主循环完成了一定比例的工作。
如果 teacher/other 说只有一种方法可以做某事,而其他一切根据定义都是错误的,那么您应该从电子工程领域知道...是时候找一位新老师或者假装了喝下苦艾酒,通过 class 并继续你的生活。另请注意,class房间体验并非真实世界。 MCU 开发可能会出错的地方太多了,您实际上是在一个受控的沙箱中,理想情况下您只能使用几个变量,这样您就不必花费数年时间来尝试度过几个月 class.他们在 class 中陈述的部分规则是为了让你通过 class and/or 让老师通过 class,如果你告诉人们函数不能大于 X 或没有 gotos 之类的。当 class 结束或添加到您的终生愿望清单时,您应该做的第一件事就是质疑所有这些规则。自己研究尝试,掉坑里挖出来。
它属于一个线程,该线程执行它以响应来自生产者-消费者队列的输入消息。所有中断等都会触发队列的输入,线程通过其 FSM 串行处理它们。
这是我发现避免不可调试的混乱同时保持低延迟和高效CPU使用中断驱动的唯一方法I/O。
'while(1);'呃!
我在 EE 论坛上问 this question。 Whosebug 上的你们比我们在 EE 上更了解编码,所以也许你们可以提供更多关于这方面的详细信息:)
当我学习微控制器时,老师教我总是以 while(1);
结束代码,循环中没有代码。
这是为了确保软件 "stuck" 保持中断工作。当我问他们是否可以在这个无限循环中放入一些代码时,他们告诉我这是个坏主意。知道了这一点,我现在尽力让这个循环保持空白。
我现在需要在微控制器中实现有限状态机。乍一看,该代码似乎属于此循环。这使编码更容易。
这是个好主意吗?有什么优缺点?
这就是我的计划:
void main(void)
{
// init phase
while(1)
{
switch(current_State)
{
case 1:
if(...)
{
current_State = 2;
}
else(...)
{
current_State = 3;
}
else
current_State = 4;
break;
case 2:
if(...)
{
current_State = 3;
}
else(...)
{
current_State = 1;
}
else
current_State = 5;
break;
}
}
而不是:
void main(void)
{
// init phase
while(1);
}
并使用中断管理 FSM
您应该实现的是您的选择和代码的调试难易程度。 有时使用 while(1) 是正确的;如果您的 uC 将完全处理中断 (ISR),请在代码末尾声明。而在其他一些应用程序中,uC 将与无限循环内的代码一起使用(称为轮询方法):
while(1)
{
//code here;
}
在其他一些应用程序中,您可能会混合使用 ISR 方法和轮询方法。
当说 'debugging easiness' 时,仅使用 ISR 方法(将 while(1); 语句放在末尾)将使您难以调试代码,因为在触发中断事件时,所选的调试器将不会给你一步一步的事件注册阅读和关注。另外,请注意,不建议编写完整的 ISR 代码,因为 ISR 事件应该进行最少的编码(例如增加计数器,raise/clear 标志,例如)并能够迅速退出。
进行嵌入式编程时,一种常用的习惯用法是使用 "super loop" - 初始化完成后开始的无限循环,根据需要分派程序的独立组件 运行 .在这种范例下,您可以按照您的建议 运行 超级循环中的有限状态机,并继续 运行 来自中断上下文的硬件管理功能,因为听起来您已经在做.这样做的缺点之一是您的处理器将始终处于高功率消耗状态 - 因为您总是 运行 宁那个循环,处理器永远不会进入睡眠状态。然而,这在您编写的任何代码中实际上也是一个问题 - 即使是空的无限 while 循环也会使处理器保持 运行ning。解决这个问题的方法通常是用一系列指令结束 while 循环,使处理器进入低功耗状态(完全依赖于体系结构),当有中断需要处理时将其唤醒。如果 FSM 中发生的事情不是由任何中断驱动的,通常使用的保持处理器定期唤醒的方法是初始化定时器以定期中断,以使您的主循环继续执行。
还有一件事要注意,如果您之前从中断上下文中执行所有代码 - 中断服务例程 (ISR) 确实应该尽可能短,因为它们确实 "interrupt" 主要执行程序的一部分,如果它们花费的时间太长,可能会导致意想不到的副作用。处理这个问题的一种正常方法是在你的超级循环中有处理程序,这些处理程序只是由 ISR 发出信号,这样大部分需要完成的处理都在有时间的时候在主上下文中完成,而不是中断一个主要上下文的潜在时间关键部分。
就像说return所有功能都在一个地方,或者其他习惯。在一种类型的设计中,您可能想要这样做,一种纯粹基于 interrupt/event 的设计。有些产品完全相反,有轮询,甚至没有驱动。以及介于两者之间的任何东西。
重要的是进行系统工程,仅此而已。中断增加了复杂性和风险,它们比不使用它们的代价更高。自动使任何设计中断驱动是一个错误的决定,仅仅意味着没有在设计、需求、风险等方面投入任何努力。
理想情况下,您希望您的大部分代码都在主循环中,您希望您的中断精益求精,以便降低其他时间关键任务的延迟。并非所有 MCU 都有复杂的中断优先级系统,这会让您浪费大量时间或让您的所有应用程序都在处理程序中。输入您的系统工程,可能有助于选择单片机,但在这里您又增加了风险。
您必须问自己,您的 mcu 必须执行哪些任务,如果每个任务从事件发生到它们必须开始响应并必须完成,每个任务是否存在任何延迟怎么办,根据 event/task 如果它的任何部分可以推迟怎么办。做任务的时候能不能任意打断,时间上能不能有空隙。您会为硬件设计、cpld 或 fpga 设计做的所有问题。除非你在那里有真正的并行性。
在现实世界的解决方案中,您可能最终得到的是中断处理程序中的某些部分和主(无限)循环中的某些部分。主循环轮询中断留下的面包屑 and/or 直接轮询状态寄存器以了解循环期间要做什么。 If/when 你到达了你需要实时的地方你仍然可以使用主超级循环,你的实时响应来自循环的可能路径和任何这些路径的最坏情况时间。
大多数时候您不需要做这么多工作。也许是一些中断,也许是一些轮询,一个主循环完成了一定比例的工作。
如果 teacher/other 说只有一种方法可以做某事,而其他一切根据定义都是错误的,那么您应该从电子工程领域知道...是时候找一位新老师或者假装了喝下苦艾酒,通过 class 并继续你的生活。另请注意,class房间体验并非真实世界。 MCU 开发可能会出错的地方太多了,您实际上是在一个受控的沙箱中,理想情况下您只能使用几个变量,这样您就不必花费数年时间来尝试度过几个月 class.他们在 class 中陈述的部分规则是为了让你通过 class and/or 让老师通过 class,如果你告诉人们函数不能大于 X 或没有 gotos 之类的。当 class 结束或添加到您的终生愿望清单时,您应该做的第一件事就是质疑所有这些规则。自己研究尝试,掉坑里挖出来。
它属于一个线程,该线程执行它以响应来自生产者-消费者队列的输入消息。所有中断等都会触发队列的输入,线程通过其 FSM 串行处理它们。
这是我发现避免不可调试的混乱同时保持低延迟和高效CPU使用中断驱动的唯一方法I/O。
'while(1);'呃!