什么是看門狗
看門狗定時(shí)器是一種電子定時(shí)器润匙,用于檢測嵌入式系統(tǒng)中的錯(cuò)誤并從中恢復(fù)就珠。 看門狗定時(shí)器的基本原理很簡單但很有效蛉威。 在特定時(shí)間段內(nèi)仿荆,系統(tǒng)必須通知(喂狗)看門狗它仍在運(yùn)行。 如果看門狗未收到此通知鲤桥,則它假定存在故障并將系統(tǒng)置于已知狀態(tài)揍拆。 通常,看門狗將重置(復(fù)位)處理器茶凳。 但是嫂拴,對于更復(fù)雜的系統(tǒng),看門狗可能必須觸發(fā)一系列操作才能將系統(tǒng)置于已知的安全狀態(tài)慧妄,例如電梯顷牌,檢測到系統(tǒng)故障就不能簡單的復(fù)位,不然后果很嚴(yán)重塞淹,而是在看門狗中斷觸發(fā)后讓系統(tǒng)處于一個(gè)安全的狀態(tài),比如停止運(yùn)動罪裹,并打開安全保護(hù)裝置饱普。
基本的看門狗保護(hù)
平時(shí)常用的一種方式是創(chuàng)建一個(gè)任務(wù)运挫,在任務(wù)里周期性的喂狗。這種方式在這個(gè)喂狗任務(wù)故障或者系統(tǒng)完全崩潰(如hardfault)導(dǎo)致喂狗任務(wù)無法正常執(zhí)行時(shí)套耕,可以發(fā)揮作用谁帕。
現(xiàn)在我們處于RTOS下,情況就有點(diǎn)復(fù)雜了冯袍,某個(gè)或多個(gè)任務(wù)失效了匈挖,并不會導(dǎo)致整個(gè)系統(tǒng)崩潰。這些情況可能是:
- 某個(gè)任務(wù)里進(jìn)入了死循環(huán)康愤,但是仍能正常調(diào)度其它任務(wù)儡循;
- 兩個(gè)或多個(gè)任務(wù)因資源問題進(jìn)入了死鎖的狀態(tài);
- 某個(gè)低優(yōu)先級任務(wù)因?yàn)楦邇?yōu)先級任務(wù)一直搶占了CPU而得不到運(yùn)行征冷;
可見择膝,這種方式在RTOS環(huán)境下能發(fā)揮的效果有限!<旒ぁ肴捉!
提高魯棒性
前面提到的失效原因,都是因?yàn)榭撮T狗沒能檢測到每一個(gè)任務(wù)的運(yùn)行狀態(tài)叔收。所以齿穗,優(yōu)化的辦法的就是讓它能檢測到每個(gè)任務(wù)的運(yùn)行。一個(gè)方法就是其它任務(wù)周期性的給喂狗任務(wù)發(fā)送通知饺律,喂狗任務(wù)如果收到所有任務(wù)的通知就進(jìn)行一次喂狗窃页。這樣,如果某個(gè)任務(wù)故障了蓝晒,那將無法正常的發(fā)送喂狗通知腮出,喂狗條件無法得到滿足,因此一段時(shí)間后看門狗定時(shí)器將會超時(shí)芝薇。
下面是FreeRTOS下的簡單實(shí)現(xiàn)方式:
#define TASK_1_BIT (1UL << 0UL)
#define TASK_2_BIT (1UL << 1UL)
/* 看門狗任務(wù)設(shè)為最高優(yōu)先級胚嘲,這樣在收到低優(yōu)先級的任務(wù)發(fā)來喂狗通知后能及時(shí)喂狗 */
void task_watchdog(void * pvParameters)
{
uint32_t ulNotifiedValue;
while (1)
{
/* 等待喂狗通知 */
xTaskNotifyWait( 0x00, 0x00, &ulNotifiedValue, portMAX_DELAY );
/* 判斷是否收到了所有其他任務(wù)的通知 */
if( (ulNotifiedValue & (TASK_1_BIT | TASK_2_BIT)) == (TASK_1_BIT | TASK_2_BIT) )
{
/* 收到則喂狗 */
feed_dog();
/* 清除通知,等待一下次喂狗 */
ulTaskNotifyValueClear(NULL, UINT_MAX);
}
}
}
void task_1(void * pvParameters)
{
while (1)
{
/* 通知喂狗 */
xTaskNotify( xTaskWatchdogHandle, TASK_1_BIT, eSetBits );
/* 努力干活 */
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void task_2(void * pvParameters)
{
uint32_t ulNotifiedValue;
BaseType_t result;
while (1)
{
/* 類似這種等待不能永久等待洛二,而是有個(gè)超時(shí)時(shí)間馋劈,后面根據(jù)判斷是否收到消息再做處理 */
/* 官方文檔有句話是這么說的:NOTE! Real applications should not block indefinitely,
but instead time out occasionally in order to handle error conditions
that may prevent the interrupt from sending any more notifications. */
result = xTaskNotifyWait( 0x00, ULONG_MAX, &ulNotifiedValue, 2000 / portTICK_PERIOD_MS );
if (pdTRUE == result)
{
/* 努力干活 */
}
/* 通知喂狗 */
xTaskNotify( xTaskWatchdogHandle, TASK_2_BIT, eSetBits );
}
}