c-为慢速模块制作驱动程序库,对看门狗友好



上下文

我正在制作一些库来通过GPRS管理互联网协议,其中一些通信(通过UART进行)相当慢(有些可能需要30秒以上),因为模块必须通过GPRS连接。

首先,我制作了一个驱动程序库来控制模块和管理TCP/IP连接,这个库与阻塞函数一起工作,例如,像Init_GPRS_connection()这样的函数可能需要几秒钟才能结束,我注意到这是一种糟糕的做法,因为现在我必须实现一个看门狗定时器,这种功能不像看门狗那样的短超时(我不能在定时器到期前踢它)

我有什么想法

我需要重写我的部分库,使其对看门狗友好,为此,我在这个方案中遇到了困难,我需要内部有状态机的功能,这些功能将通过UART中断获取数据,以通过状态机前进,这样我就可以编写如下代码:

GPRS_typef Init_GPRS_connection(){
switch(state){ //state would be a global functions that take the current state of the state machine
....  //here would be all the states of the state machine
case end:
state = 0;
return Done;
}
}
while(Init_GPRS_connection() != Done){
Do_stuff(); //Like kick the Watchdog
}

但我看到了这个解决方案中的一些问题:

  • 这是一个不太用户友好的实现,用户应该小心使用这个库驱动程序,因为总是需要额外的代码行(有点违背了使用函数的目的)
  • 如果由于某种原因,模块在某个时刻不回答,代码就会卡在状态机中,因为即使代码卡在循环中,看门狗也会被踢出该功能,这种情况就无法达到使用看门狗Timer的目的

我的问题

我应该使用什么样的实现来制作用户和看门狗友好的驱动程序库?,其他驱动程序库是如何管理的?

额外信息

  • 所有这些都是在嵌入式系统的上下文中
  • 我想在驾驶员功能之外执行看门狗踢腿动作

给定您所处的位置,并假设您不会对您的项目造成太大的动荡"适当地做";,您可以添加可变看门狗超时扩展,这样您就可以设置一个计数器,该计数器在计时器中断中递减,如果计数器不为零,看门狗就会重置。

这样,当您的主线程被卡住时,您就不允许定时器中断无限期地重置看门狗,但您可以在执行任何阻塞代码之前立即扩展看门狗,本质上是为该操作设置超时。

所以你可能有(伪代码):

static volatile uint8_t wdg_repeat_count = 0 ;
void extendWatchdog( uint8_t repeat ) { wdg_repeat_count = repeat ; }
void timerISR( void )
{
if( wdg_repeat_count > 0 )
{
resetWatchdog() ;
wdg_repeat_count-- ;
}
}

然后你可以:

extendWatchdog( CONNECTION_INIT_WDG_TIMEOUT ) ;
while(Init_GPRS_connection() != Done){
Do_stuff(); //Like kick the Watchdog
}

或者继续使用您现有的基于非状态机的解决方案:

extendWatchdog( CONNECTION_INIT_WDG_TIMEOUT ) ;
bool connected = Init_GPRS_connection() ;
if( connected ) ...

这个想法与你现有的和你提出的都兼容,它只允许你将看门狗超时时间延长到硬件规定的时间之外。

我建议使用uint8_t,因为它可以防止懒惰的开发人员简单地设置一个大值并有效地禁用看门狗保护,而且它可能是原子的,因此可以在主上下文和中断上下文之间共享。

尽管如此,从一开始就在体系结构级别设计完整性基础设施显然会更好,而不是在事件发生后试图将其固定下来。例如,如果您使用RTOS,您可能会在低优先级任务中重置看门狗,如果该任务不足,将导致看门狗过期,并且;看门狗任务";可以用来监视其他任务,以确保它们按预期进行调度。

如果没有RTOS;"大循环";每个";任务";实现为状态机。在您的示例中,您似乎忽略了状态机的要点"初始化连接";应该是高级状态机的单个状态,该状态的内部可能本身就是一个状态机(层次状态机)。因此,整个系统将是主循环中的一个状态机,看门狗在每次循环迭代时重置一次。任何子状态中的任何东西都不应该阻塞,以确保循环时间低且具有确定性。例如,Arduino框架的loop()函数应该是这样工作的(如果操作得当——不幸的是,示例中很少出现这种情况)。为了理解如何实现实时确定性状态机架构,你可以做得比Miro Samek的工作更糟。其中所述的框架可通过他的公司获得。

您应该使您的库不阻塞,但除此之外,您根本不应该担心看门狗。看门狗的管理应该留给用户。

为了允许用户在您的库等待时进行其他工作,您可以使用以下方法:

  1. 提供一个函数,将数据馈送到库中(例如receive())。用户应该在数据可用时调用此函数,例如从中断中调用。由于此函数可以从中断中调用,请确保它不会进行繁重的处理。通常,您只需要缓冲数据,然后再进行处理(步骤2)
  2. 提供一个用户定期调用的函数,用于更新库的状态并执行任何其他内务管理任务(如超时检测)。通常,此函数被称为run()process()tick()或类似的函数。用户将在其主循环中或从专用RTOS任务中调用此函数
  3. 提供一种方法来告诉用户库的状态。您可以通过某种getState()函数或使用回调来完成,也可以同时使用两者。基于这些信息,用户可以实现自己的状态机来进行连接、断开连接等操作

最新更新