在看门狗定时器存在的情况下编程

时间:2022-08-03 03:47:09

I am new to embedded systems programming, although I have done courses during studies, practical programming is still a bit further away.

我是嵌入式系统编程的新手,虽然我在学习期间完成了课程,但实际编程还是有点远。

Here is the problem: I have to program a small system on NXP LPC2103 microcontroller (ARM 7 based), without an operating system. It has a watchdog timer which needs to be regularly updated. The system has a GPRS modem with TCP/IP stack embedded, and initializing this takes time longer than the watchdog needs to timeout. When I call the initialization function, the system resets.

问题在于:我必须在NXP LPC2103微控制器(基于ARM 7)上编写一个小型系统,而不需要操作系统。它有一个看门狗定时器,需要定期更新。该系统具有嵌入了TCP / IP堆栈的GPRS调制解调器,并且初始化这需要比看门狗需要超时的时间更长的时间。当我调用初始化函数时,系统会重置。

I spoke to a more experienced colleague and he suggested that I need to exit and reenter the same initialization function from the main function in which I bite the watchdog timer so long until the function finishes executing. The idea sounds good, but I would like to also hear some other experiences. Also, a reference (book or website) could be also useful, because I couldn't find anything specific to this.

我和一位经验丰富的同事交谈过,他建议我需要退出并重新进入同一个初始化函数,这个函数来自我将看门狗定时器咬了很长时间,直到函数完成执行。这个想法听起来不错,但我还想听听其他一些经历。此外,参考(书籍或网站)也可能有用,因为我找不到任何具体的内容。

I wouldn't like to call watchdog timer from the initialization function, I don't find this good.

我不想从初始化函数调用看门狗定时器,我没有发现这个好。

5 个解决方案

#1


6  

I wouldn't like to call watchdog timer from the initialization function, I don't find this good.

我不想从初始化函数调用看门狗定时器,我没有发现这个好。

It might be overkill for this one situation, but a general technique I've used for long running operations where you might want to perform other work is to have the long running function accept a callback function pointer that will be periodically called. The pattern that I usually use is to have a callback prototype that might look like:

对于这种情况可能有点过头了,但是我用于长时间运行的一般技术,你可能想要执行其他工作是让长时间运行的函数接受将定期调用的回调函数指针。我通常使用的模式是有一个回调原型,可能看起来像:

int (callback_t*)(void* progress, void* context);

The long running function will periodically call the callback, with some information that indicates it's progress (how that progress is represented to what it means is dependent on the details of the particular function) and with a context value that the original caller passed in along with the callback pointer (again - what that parameter means and how it's interpreted is entirely up to the callback). generically, the return value of the callback function might be used to indicate that the 'long running thing' should cancel or otherwise change behavior.

长时间运行的函数将定期调用回调,其中一些信息指示它的进度(如何表示该进度表示它取决于特定函数的详细信息)以及原始调用者传入的上下文值以及回调指针(再次 - 该参数的含义以及它的解释方式完全取决于回调)。一般来说,回调函数的返回值可能用于指示“长时间运行的东西”应该取消或以其他方式改变行为。

This way, your initialization function can take a callback pointer with a context value, and just periodically call it. Obviously, in your situation, those callbacks would have to occur often enough to keep the watchdog happy.

这样,初始化函数可以使用带有上下文值的回调指针,并且只是定期调用它。显然,在你的情况下,那些回调必须经常发生,以保持看门狗的快乐。

int watchdog_callback( void* progress, void* context)
{
    kick_the_watchdog();

    return 0;  // zero means 'keep going...'
}


void init_modem( callback_t pCallback, void* callback_context)
{
    // do some stuff

    pCallback( 0, callback_context);

    // do some other stuff

    pCallback( 1, callback_context);


    while (waiting_for_modem()) {
         // do work...

         pCallback( 2, callback_context);
    }    
}

One nice thing about this pattern is that it can be used in different situations - you might have a function that reads or writes a large amount of data. The callback pattern might be used to have something display the progress.

关于这种模式的一个好处是它可以在不同的情况下使用 - 你可能有一个读取或写入大量数据的函数。回调模式可能用于显示进度的某些内容。

Note that if you find that you have other long-running functions, the same watchdog_callback() function could be used to allow them to deal with preventing the watchdog from reseting. However, if you find yourself needing to rely on this type of thing often for the watchdog in particular, then you might need to consider how your tasks are interacting and either break them down more or use a more complex watchdog scheme that has the watchdog managed by its own task that other tasks interact with to keep the watchdog timer happy indirectly.

请注意,如果您发现有其他长时间运行的函数,则可以使用相同的watchdog_callback()函数来允许它们处理阻止看门狗重置的问题。但是,如果您发现自己需要经常为监视程序依赖此类事物,那么您可能需要考虑您的任务如何进行交互并将其分解得更多,或者使用具有监视程序管理的更复杂的监视程序方案通过自己的任务,其他任务与之交互以保持监视程序计时器间接满意。

#2


5  

Generally, there are two approaches that I have adopted for this situation.

一般来说,我采用了两种方法来应对这种情况。

State Machine Initialisation

The first is much as your colleague has suggested: implemented the initialisation routines in a state machine called as part of the main loop and then stop calling the initialisation routines and start calling the main routines.

第一个就像你的同事建议的那样:在作为主循环一部分的状态机中实现初始化例程,然后停止调用初始化例程并开始调用主例程。

This is a simple and clean function, but can be a little awkward when it comes to particular long processes such as starting up a low frequency oscillator.

这是一个简单而干净的功能,但在涉及特殊的长过程(例如启动低频振荡器)时可能会有点尴尬。

Time-Limited ISR Watchdog Handling

There is another alternative if you have a 'systick' or equivalent interrupt, for example an interrupt that is fired every 1 ms. In this situation, you can consider feeding the watchdog (e.g.) every 50 calls of the interrupt, but limiting the number of times the watchdog is fed to equate to the maximum allowable time for the initialisation routines to complete. It is then generally necessary (if you have, as you should in my opinion, a windowed watchdog) to have a short synchronisation loop at the end of the initialisation to ensure that the watchdog isn't fed before the minimum window time is reached, but this is trivial to implement.

如果你有一个'systick'或等效的中断,还有另一种选择,例如每1 ms发出一次中断。在这种情况下,您可以考虑每隔50次中断调用(例如)馈送看门狗,但限制看门狗被馈送的次数等于初始化例程完成的最大允许时间。然后通常需要(如果你有,我认为应该是一个窗口看门狗)在初始化结束时有一个短的同步循环,以确保在达到最小窗口时间之前没有馈送看门狗,但这很难实现。

This is quite a clean solution (as it doesn't make the initialisation routines into an unnecessary state machine) and deals with the issues of an initialisation routine hanging. It is, however, very important that the limit on watchdog calls in the ISR is enforced.

这是一个非常干净的解决方案(因为它不会使初始化例程进入不必要的状态机)并处理初始化例程挂起的问题。但是,强制执行ISR中看门狗呼叫的限制非常重要。

Discussion

Both have their advantages and disadvantages, but it's useful to have different approaches for different requirements. I tend to prefer the latter solution where I have things like a low frequency oscillator (which can take a while to start) as it avoids over-complicating the initialisation routines, which can be complicated enough on their own!

两者都有其优点和缺点,但是针对不同的要求采用不同的方法是有用的。我倾向于选择后一种解决方案,其中我有一些低频振荡器(可能需要一段时间才能启动),因为它避免了过度复杂的初始化程序,这可能足够复杂!

I'm sure others will offer other alternatives ideas as well...

我相信其他人也会提供其他的替代想法......

#3


3  

The Watchdog in LPC2103 is highly customizable. You have many options to control it:

LPC2103中的看门狗可高度定制。你有很多选择来控制它:

You can to not enable it until your initialization sequence is over.

在初始化序列结束之前,您无法启用它。

You can extend the period between feeds to very long time.

您可以将Feed之间的时间段延长很长时间。

The question is for what are you using the watchdog?

问题是你使用看门狗是什么?

If it used to check if your software running well and not freezing, I don't see how the ISR option from AI will help you (ISR can continue work even your program is stuck).

如果它用于检查您的软件是否运行良好而不是冻结,我不会看到AI的ISR选项如何帮助您(ISR可以继续工作,即使您的程序被卡住)。

For details about Watchdog options see WatchDog Timer (WDT) chapter (17) in the User manual for your MCU. http://www.nxp.com/documents/user_manual/UM10161.pdf

有关看门狗选项的详细信息,请参阅MCU用户手册中的看门狗定时器(WDT)章节(17)。 http://www.nxp.com/documents/user_manual/UM10161.pdf

#4


2  

Watchdogs are great, but also a pain in the rear when your program or system does not fit it easily. The work best when you have code that looks (generally) like:

看门狗很棒,但当你的程序或系统不适合它时,后面也会很痛苦。如果您的代码看起来(通常)如下,则效果最佳:

Watchdog_init();

hardware_init();
subsystem1_init();
subsystem2_init();
subsystem3_init();
...
subsystemN_init();

forever {
   Watchdog_tickle();

   subsystem1_work();
   subsystem2_work();
   subsystem3_work();
   ...
   subsystemN_work();
}

Very often you can design your program in such a way that this works, and generally it is very fool proof (but not totally).

通常,您可以通过这种方式设计程序,通常它非常简单(但不完全)。

But in cases like yours this does not work so well. You end up having to design and create (or possibly use a library) a framework that has various conditions that must be met that control if/when the watchdog get petted. This can be very tricky, though. The complexity of this code could itself introduce its own errors. You could very well write a perfect application except for the watchdog framework and your project may reset a lot, or all of your code might be bad and just continually pet the watchdog, causing it to never reset.

但在像你这样的情况下,这种方法效果不佳。您最终必须设计和创建(或可能使用库)一个框架,该框架具有必须满足的各种条件,以控制监视器是否/何时被宠爱。不过,这可能非常棘手。此代码的复杂性本身可能会引入自己的错误。你可以很好地编写一个完美的应用程序,除了看门狗框架,你的项目可能会重置很多,或者你的所有代码可能都很糟糕,只是不断地关注看门狗,导致它永远不会重置。

One good way to change the above code to handle more complicated situations would be to change the subsystemX_work functions to keep up with state. This can be done with static variables in the functions or by using function pointers rather than functions and change the actual function that is executed to reflect the current state of that subsystem. Each subsystem becomes a state machine.

更改上述代码以处理更复杂情况的一种好方法是更改​​subsystemX_work函数以跟上状态。这可以通过函数中的静态变量或使用函数指针而不是函数来完成,并更改执行的实际函数以反映该子系统的当前状态。每个子系统都成为一个状态机。

Another way to go about working around long intentional waits with a quick biting watchdog is to break up the long running function into shorter pieces. Rather than:

使用快速咬合的看门狗来解决长期故意等待的另一种方法是将长时间运行功能分解为更短的部分。而不是:

slow_device_init();
Watchdog_tickle();

You could do:

你可以这样做:

slow_device_init_begin();
Watchdog_tickle();
slow_device_init_finish();
Watchdog_tickle();

And then extend this to stretch the watchdog timer by doing:

然后扩展它以通过执行以下操作来拉伸看门狗定时器:

slow_device_init_begin();
for ( i = SLOW_DEV_TRIES; i ; i--) {
   Watchdog_tickle();
   if (slow_device_init_done()) {
       break;
   }
}
Watchdog_tickle();

Even still it can get more and more complicated. Often you end up having to create a watchdog delegate which just checks for conditions to be met and does or does not pet the watchdog based on these conditions. This begins to get very complicated. It can be implemented by making an object for each of your subsystems that has some method/function to call to test the subsystem's health. The health methods could be very complex and could even change as the state of that subsystem changes, though it should be as simple as possible so that it is as easy as possible to verify that the code is correct, and also because changes to how the subsystem works will require changes to how you measure health.

即使它仍然会变得越来越复杂。通常,您最终必须创建一个监视程序委托,它只检查要满足的条件,并根据这些条件对监视程序进行操作或不接受监视程序。这开始变得非常复杂。它可以通过为每个子系统创建一个具有某些方法/函数的对象来调用子系统的运行状况来实现。健康方法可能非常复杂,甚至可能随着子系统状态的变化而改变,尽管它应该尽可能简单,以便尽可能简单地验证代码是否正确,还因为更改了子系统工作将需要改变您测量健康的方式。

If you can ensure that some code runs at regular intervals then you could just have an integer for each subsystem that acts as the subsystem's local watchdog. Some code (maybe in a timer interrupt handler, but not necessarily) will decrement and test each subsystem's variable. If it reaches 0 for any subsystem then the watchdog is not tickled.

如果您可以确保某些代码定期运行,那么您可以为每个充当子系统本地监视程序的子系统提供一个整数。一些代码(可能在定时器中断处理程序中,但不一定)会减少并测试每个子系统的变量。如果任何子系统达到0,则看门狗不会被发痒。

Watchdog_periodic() {
   for_each subsustem in subsystem_list { // not C, but you get the idea
      if ( 0 > --(subsystem->count_down) ) {
           // Do something that causes a reset. This could be returning and not petting
           // the hardware watchdog, doing a while(1);, or something else
      }
   }
   Watchdog_tickle();
}

Then each subsystem can tickle its own count_down for varying amounts of time by setting it's count_down to a positive value.

然后,通过将count_down设置为正值,每个子系统可以在不同的时间内触发自己的count_down。

You should also notice that this is really just a software watchdog, even though it may make use of the hardware watchdog to do the actual reset.

您还应该注意到,这实际上只是一个软件看门狗,即使它可以利用硬件看门狗来进行实际复位。

You should also note that the more complicated the watchdog framework the more oppurtunity there is for errors in it as well as oppurtunity for errors in other code to cause it to work improperly. For instance a pointer error such as:

您还应该注意到,看门狗框架越复杂,其中的错误就越容易受到影响,以及其他代码中的错误导致其无法正常工作。例如指针错误,例如:

int x;
fscanf(input, "%i", x); // Passed uninitialized x rather than address of x

could result in setting some subsystem's count_down value, which could end up keeping the watchdog from biting when it should.

可能导致设置一些子系统的count_down值,这可能最终导致看门狗不应该咬人。

#5


1  

You might reconsider where in code the WD timer is serviced.

您可能会重新考虑WD计时器的代码服务位置。

Typically the WD timer needs to be service during idle time (idle loop or idle task) and in the lowest level drivers (e.g. when you are reading/writing from/to the GPRS modem or the MAC for your TCP/IP connection, etc.).

通常,WD定时器需要在空闲时间(空闲循环或空闲任务)和最低级别驱动程序中进行服务(例如,当您从/向GPRS调制解调器读取/写入时,或者用于TCP / IP连接的MAC等。 )。

If this is not sufficient, your firmware may also be doing nothing but burning up CPU cycles in a delay routine. Its fine to add a WD timer service here but you may have to adjust your delay timer to account for the WD service time.

如果这还不够,那么您的固件可能只会在延迟例程中烧掉CPU周期。可以在这里添加WD计时器服务,但您可能需要调整延迟计时器以考虑WD服务时间。

If your application simply has some long, CPU intensive tasks that take more time to execute that the WD timer period allows, you might consider making the WD timer interval a bit longer. This may not always be possible but I like to keep WD timer references out of the upper layers of the firmware to keep the application layer as portable as possible. WD timers are typically hardware dependent, therefore any WD timer references in your code are rarely portable. The low-level drivers are rarely portable anyway so this typically is a better place to service a WD timer.

如果您的应用程序只是执行了一些长时间的CPU密集型任务,这些任务需要花费更多时间来执行WD定时器周期允许的时间,您可以考虑使WD定时器间隔更长一些。这可能并不总是可行,但我喜欢将WD定时器参考保留在固件的上层之外,以使应用层尽可能便携。 WD定时器通常依赖于硬件,因此代码中的任何WD定时器引用都很少可移植。低级驱动程序很少是便携式的,因此这通常是维护WD计时器的更好地方。

#1


6  

I wouldn't like to call watchdog timer from the initialization function, I don't find this good.

我不想从初始化函数调用看门狗定时器,我没有发现这个好。

It might be overkill for this one situation, but a general technique I've used for long running operations where you might want to perform other work is to have the long running function accept a callback function pointer that will be periodically called. The pattern that I usually use is to have a callback prototype that might look like:

对于这种情况可能有点过头了,但是我用于长时间运行的一般技术,你可能想要执行其他工作是让长时间运行的函数接受将定期调用的回调函数指针。我通常使用的模式是有一个回调原型,可能看起来像:

int (callback_t*)(void* progress, void* context);

The long running function will periodically call the callback, with some information that indicates it's progress (how that progress is represented to what it means is dependent on the details of the particular function) and with a context value that the original caller passed in along with the callback pointer (again - what that parameter means and how it's interpreted is entirely up to the callback). generically, the return value of the callback function might be used to indicate that the 'long running thing' should cancel or otherwise change behavior.

长时间运行的函数将定期调用回调,其中一些信息指示它的进度(如何表示该进度表示它取决于特定函数的详细信息)以及原始调用者传入的上下文值以及回调指针(再次 - 该参数的含义以及它的解释方式完全取决于回调)。一般来说,回调函数的返回值可能用于指示“长时间运行的东西”应该取消或以其他方式改变行为。

This way, your initialization function can take a callback pointer with a context value, and just periodically call it. Obviously, in your situation, those callbacks would have to occur often enough to keep the watchdog happy.

这样,初始化函数可以使用带有上下文值的回调指针,并且只是定期调用它。显然,在你的情况下,那些回调必须经常发生,以保持看门狗的快乐。

int watchdog_callback( void* progress, void* context)
{
    kick_the_watchdog();

    return 0;  // zero means 'keep going...'
}


void init_modem( callback_t pCallback, void* callback_context)
{
    // do some stuff

    pCallback( 0, callback_context);

    // do some other stuff

    pCallback( 1, callback_context);


    while (waiting_for_modem()) {
         // do work...

         pCallback( 2, callback_context);
    }    
}

One nice thing about this pattern is that it can be used in different situations - you might have a function that reads or writes a large amount of data. The callback pattern might be used to have something display the progress.

关于这种模式的一个好处是它可以在不同的情况下使用 - 你可能有一个读取或写入大量数据的函数。回调模式可能用于显示进度的某些内容。

Note that if you find that you have other long-running functions, the same watchdog_callback() function could be used to allow them to deal with preventing the watchdog from reseting. However, if you find yourself needing to rely on this type of thing often for the watchdog in particular, then you might need to consider how your tasks are interacting and either break them down more or use a more complex watchdog scheme that has the watchdog managed by its own task that other tasks interact with to keep the watchdog timer happy indirectly.

请注意,如果您发现有其他长时间运行的函数,则可以使用相同的watchdog_callback()函数来允许它们处理阻止看门狗重置的问题。但是,如果您发现自己需要经常为监视程序依赖此类事物,那么您可能需要考虑您的任务如何进行交互并将其分解得更多,或者使用具有监视程序管理的更复杂的监视程序方案通过自己的任务,其他任务与之交互以保持监视程序计时器间接满意。

#2


5  

Generally, there are two approaches that I have adopted for this situation.

一般来说,我采用了两种方法来应对这种情况。

State Machine Initialisation

The first is much as your colleague has suggested: implemented the initialisation routines in a state machine called as part of the main loop and then stop calling the initialisation routines and start calling the main routines.

第一个就像你的同事建议的那样:在作为主循环一部分的状态机中实现初始化例程,然后停止调用初始化例程并开始调用主例程。

This is a simple and clean function, but can be a little awkward when it comes to particular long processes such as starting up a low frequency oscillator.

这是一个简单而干净的功能,但在涉及特殊的长过程(例如启动低频振荡器)时可能会有点尴尬。

Time-Limited ISR Watchdog Handling

There is another alternative if you have a 'systick' or equivalent interrupt, for example an interrupt that is fired every 1 ms. In this situation, you can consider feeding the watchdog (e.g.) every 50 calls of the interrupt, but limiting the number of times the watchdog is fed to equate to the maximum allowable time for the initialisation routines to complete. It is then generally necessary (if you have, as you should in my opinion, a windowed watchdog) to have a short synchronisation loop at the end of the initialisation to ensure that the watchdog isn't fed before the minimum window time is reached, but this is trivial to implement.

如果你有一个'systick'或等效的中断,还有另一种选择,例如每1 ms发出一次中断。在这种情况下,您可以考虑每隔50次中断调用(例如)馈送看门狗,但限制看门狗被馈送的次数等于初始化例程完成的最大允许时间。然后通常需要(如果你有,我认为应该是一个窗口看门狗)在初始化结束时有一个短的同步循环,以确保在达到最小窗口时间之前没有馈送看门狗,但这很难实现。

This is quite a clean solution (as it doesn't make the initialisation routines into an unnecessary state machine) and deals with the issues of an initialisation routine hanging. It is, however, very important that the limit on watchdog calls in the ISR is enforced.

这是一个非常干净的解决方案(因为它不会使初始化例程进入不必要的状态机)并处理初始化例程挂起的问题。但是,强制执行ISR中看门狗呼叫的限制非常重要。

Discussion

Both have their advantages and disadvantages, but it's useful to have different approaches for different requirements. I tend to prefer the latter solution where I have things like a low frequency oscillator (which can take a while to start) as it avoids over-complicating the initialisation routines, which can be complicated enough on their own!

两者都有其优点和缺点,但是针对不同的要求采用不同的方法是有用的。我倾向于选择后一种解决方案,其中我有一些低频振荡器(可能需要一段时间才能启动),因为它避免了过度复杂的初始化程序,这可能足够复杂!

I'm sure others will offer other alternatives ideas as well...

我相信其他人也会提供其他的替代想法......

#3


3  

The Watchdog in LPC2103 is highly customizable. You have many options to control it:

LPC2103中的看门狗可高度定制。你有很多选择来控制它:

You can to not enable it until your initialization sequence is over.

在初始化序列结束之前,您无法启用它。

You can extend the period between feeds to very long time.

您可以将Feed之间的时间段延长很长时间。

The question is for what are you using the watchdog?

问题是你使用看门狗是什么?

If it used to check if your software running well and not freezing, I don't see how the ISR option from AI will help you (ISR can continue work even your program is stuck).

如果它用于检查您的软件是否运行良好而不是冻结,我不会看到AI的ISR选项如何帮助您(ISR可以继续工作,即使您的程序被卡住)。

For details about Watchdog options see WatchDog Timer (WDT) chapter (17) in the User manual for your MCU. http://www.nxp.com/documents/user_manual/UM10161.pdf

有关看门狗选项的详细信息,请参阅MCU用户手册中的看门狗定时器(WDT)章节(17)。 http://www.nxp.com/documents/user_manual/UM10161.pdf

#4


2  

Watchdogs are great, but also a pain in the rear when your program or system does not fit it easily. The work best when you have code that looks (generally) like:

看门狗很棒,但当你的程序或系统不适合它时,后面也会很痛苦。如果您的代码看起来(通常)如下,则效果最佳:

Watchdog_init();

hardware_init();
subsystem1_init();
subsystem2_init();
subsystem3_init();
...
subsystemN_init();

forever {
   Watchdog_tickle();

   subsystem1_work();
   subsystem2_work();
   subsystem3_work();
   ...
   subsystemN_work();
}

Very often you can design your program in such a way that this works, and generally it is very fool proof (but not totally).

通常,您可以通过这种方式设计程序,通常它非常简单(但不完全)。

But in cases like yours this does not work so well. You end up having to design and create (or possibly use a library) a framework that has various conditions that must be met that control if/when the watchdog get petted. This can be very tricky, though. The complexity of this code could itself introduce its own errors. You could very well write a perfect application except for the watchdog framework and your project may reset a lot, or all of your code might be bad and just continually pet the watchdog, causing it to never reset.

但在像你这样的情况下,这种方法效果不佳。您最终必须设计和创建(或可能使用库)一个框架,该框架具有必须满足的各种条件,以控制监视器是否/何时被宠爱。不过,这可能非常棘手。此代码的复杂性本身可能会引入自己的错误。你可以很好地编写一个完美的应用程序,除了看门狗框架,你的项目可能会重置很多,或者你的所有代码可能都很糟糕,只是不断地关注看门狗,导致它永远不会重置。

One good way to change the above code to handle more complicated situations would be to change the subsystemX_work functions to keep up with state. This can be done with static variables in the functions or by using function pointers rather than functions and change the actual function that is executed to reflect the current state of that subsystem. Each subsystem becomes a state machine.

更改上述代码以处理更复杂情况的一种好方法是更改​​subsystemX_work函数以跟上状态。这可以通过函数中的静态变量或使用函数指针而不是函数来完成,并更改执行的实际函数以反映该子系统的当前状态。每个子系统都成为一个状态机。

Another way to go about working around long intentional waits with a quick biting watchdog is to break up the long running function into shorter pieces. Rather than:

使用快速咬合的看门狗来解决长期故意等待的另一种方法是将长时间运行功能分解为更短的部分。而不是:

slow_device_init();
Watchdog_tickle();

You could do:

你可以这样做:

slow_device_init_begin();
Watchdog_tickle();
slow_device_init_finish();
Watchdog_tickle();

And then extend this to stretch the watchdog timer by doing:

然后扩展它以通过执行以下操作来拉伸看门狗定时器:

slow_device_init_begin();
for ( i = SLOW_DEV_TRIES; i ; i--) {
   Watchdog_tickle();
   if (slow_device_init_done()) {
       break;
   }
}
Watchdog_tickle();

Even still it can get more and more complicated. Often you end up having to create a watchdog delegate which just checks for conditions to be met and does or does not pet the watchdog based on these conditions. This begins to get very complicated. It can be implemented by making an object for each of your subsystems that has some method/function to call to test the subsystem's health. The health methods could be very complex and could even change as the state of that subsystem changes, though it should be as simple as possible so that it is as easy as possible to verify that the code is correct, and also because changes to how the subsystem works will require changes to how you measure health.

即使它仍然会变得越来越复杂。通常,您最终必须创建一个监视程序委托,它只检查要满足的条件,并根据这些条件对监视程序进行操作或不接受监视程序。这开始变得非常复杂。它可以通过为每个子系统创建一个具有某些方法/函数的对象来调用子系统的运行状况来实现。健康方法可能非常复杂,甚至可能随着子系统状态的变化而改变,尽管它应该尽可能简单,以便尽可能简单地验证代码是否正确,还因为更改了子系统工作将需要改变您测量健康的方式。

If you can ensure that some code runs at regular intervals then you could just have an integer for each subsystem that acts as the subsystem's local watchdog. Some code (maybe in a timer interrupt handler, but not necessarily) will decrement and test each subsystem's variable. If it reaches 0 for any subsystem then the watchdog is not tickled.

如果您可以确保某些代码定期运行,那么您可以为每个充当子系统本地监视程序的子系统提供一个整数。一些代码(可能在定时器中断处理程序中,但不一定)会减少并测试每个子系统的变量。如果任何子系统达到0,则看门狗不会被发痒。

Watchdog_periodic() {
   for_each subsustem in subsystem_list { // not C, but you get the idea
      if ( 0 > --(subsystem->count_down) ) {
           // Do something that causes a reset. This could be returning and not petting
           // the hardware watchdog, doing a while(1);, or something else
      }
   }
   Watchdog_tickle();
}

Then each subsystem can tickle its own count_down for varying amounts of time by setting it's count_down to a positive value.

然后,通过将count_down设置为正值,每个子系统可以在不同的时间内触发自己的count_down。

You should also notice that this is really just a software watchdog, even though it may make use of the hardware watchdog to do the actual reset.

您还应该注意到,这实际上只是一个软件看门狗,即使它可以利用硬件看门狗来进行实际复位。

You should also note that the more complicated the watchdog framework the more oppurtunity there is for errors in it as well as oppurtunity for errors in other code to cause it to work improperly. For instance a pointer error such as:

您还应该注意到,看门狗框架越复杂,其中的错误就越容易受到影响,以及其他代码中的错误导致其无法正常工作。例如指针错误,例如:

int x;
fscanf(input, "%i", x); // Passed uninitialized x rather than address of x

could result in setting some subsystem's count_down value, which could end up keeping the watchdog from biting when it should.

可能导致设置一些子系统的count_down值,这可能最终导致看门狗不应该咬人。

#5


1  

You might reconsider where in code the WD timer is serviced.

您可能会重新考虑WD计时器的代码服务位置。

Typically the WD timer needs to be service during idle time (idle loop or idle task) and in the lowest level drivers (e.g. when you are reading/writing from/to the GPRS modem or the MAC for your TCP/IP connection, etc.).

通常,WD定时器需要在空闲时间(空闲循环或空闲任务)和最低级别驱动程序中进行服务(例如,当您从/向GPRS调制解调器读取/写入时,或者用于TCP / IP连接的MAC等。 )。

If this is not sufficient, your firmware may also be doing nothing but burning up CPU cycles in a delay routine. Its fine to add a WD timer service here but you may have to adjust your delay timer to account for the WD service time.

如果这还不够,那么您的固件可能只会在延迟例程中烧掉CPU周期。可以在这里添加WD计时器服务,但您可能需要调整延迟计时器以考虑WD服务时间。

If your application simply has some long, CPU intensive tasks that take more time to execute that the WD timer period allows, you might consider making the WD timer interval a bit longer. This may not always be possible but I like to keep WD timer references out of the upper layers of the firmware to keep the application layer as portable as possible. WD timers are typically hardware dependent, therefore any WD timer references in your code are rarely portable. The low-level drivers are rarely portable anyway so this typically is a better place to service a WD timer.

如果您的应用程序只是执行了一些长时间的CPU密集型任务,这些任务需要花费更多时间来执行WD定时器周期允许的时间,您可以考虑使WD定时器间隔更长一些。这可能并不总是可行,但我喜欢将WD定时器参考保留在固件的上层之外,以使应用层尽可能便携。 WD定时器通常依赖于硬件,因此代码中的任何WD定时器引用都很少可移植。低级驱动程序很少是便携式的,因此这通常是维护WD计时器的更好地方。