C语言 为慢速模块制作驱动程序库,看门狗友好

carvr3hs  于 2023-01-08  发布在  其他
关注(0)|答案(2)|浏览(116)
    • 背景**

我正在做一些库来管理互联网协议通过GPRS,一些部分的通信(通过UART)是相当缓慢的(有些可能需要超过30秒),因为模块必须通过GPRS连接。
首先我做了一个驱动程序库来控制模块和管理TCP/IP连接,这个库与阻塞函数一起工作,例如Init_GPRS_connection函数()可能需要几秒钟才能结束,我已经注意到这是不好的做法,因为现在我必须实现一个看门狗定时器,这种功能不友好whit短超时像看门狗有(我不能在计时器到期前踢它)
"我想什么"
我需要重写我的部分库,使其对看门狗友好,为此,我在此方案中非常严格,我需要内部具有状态机的函数,这些函数将通过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
}

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

  • 这是一个不太友好的实现,用户应该小心使用这个库驱动程序,因为额外的代码行总是必要的(有点违背使用函数的目的)。
  • 如果由于某种原因,模块在某个时间点没有应答,代码将卡在状态机中,因为看门狗将被踢出此函数,即使代码卡在循环中,这种情况也会破坏使用看门狗定时器的目的

"我的问题"
我应该使用什么样的实现来创建一个用户和看门狗友好的驱动程序库?其他驱动程序库如何管理它?

    • 额外信息**
  • 所有这些都是在嵌入式系统的背景下实现的
  • 我想在驱动程序函数之外实现看门狗踢出操作
pb3s4cty

pb3s4cty1#

鉴于你在哪里,并假设你没有什么太多的剧变,你的项目“做得很好”,你可能是添加变量看门狗超时扩展,使您设置一个计数器,是递减的计时器中断,如果计数器不是零,看门狗被重置。
这样,您就不会在主线程阻塞时允许定时器中断无限期地重置看门狗,但您可以在执行任何阻塞代码之前立即 * 扩展 * 看门狗,实质上就是为该操作设置超时。
所以你可能有(伪代码):

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的工作。其中描述的框架可以通过他的company获得。

xqk2d5yq

xqk2d5yq2#

你应该让你的库成为非阻塞的,但是除此之外,你根本不应该担心看门狗,看门狗的管理应该留给用户。
若要允许用户在库等待时执行其他工作,可以使用以下方法:
1.提供一个函数来将数据输入到库中(例如receive())。用户应该在数据可用时调用此函数,例如从中断调用。由于此函数可以从中断调用,因此请确保它不执行繁重的处理。通常,您只需缓冲数据并稍后处理它(步骤2)。
1.提供一个用户定期调用的函数,用于更新库的状态并执行任何其他内务处理任务(如超时检测)。通常,此函数称为run()process()tick()或类似名称。用户将在主循环中或从专用RTOS任务中调用此函数。
1.提供一种方法来告诉用户库的状态。你可以通过某种getState()函数或者使用回调函数或者两者都用。基于这些信息,用户可以实现他们自己的状态机来完成连接、断开连接等操作。

相关问题