- When given the choice between being right or being kind, choose kind.
當你要在正確和善良之間做選擇的時候,選擇善良。
一、 在RTOS使用看門狗的策略
在由單片機構成的微型計算機系統中,由於單片機的工作常常會受到來自外界電磁場的干擾,造成程序的跑飛,而陷入死循環,程序的正常運行被打斷,由單片機控制的系統無法繼續工作,會造成整個系統的陷入停滯狀態,發生不可預料的後果,所以出於對單片機運行狀態進行實時監測的考慮,便產生了一種專門用於監測單片機程序運行狀態的芯片,俗稱“看門狗”(watchdog)。”
看門狗通常是一個定時器,定時器定時到預定時間就會產生復位信號讓單片機復位。要想避免看門狗復位單片機,要及時“餵狗”(恢復定時器初始值),也就是說只要程序能正常運行,就能及時餵狗,避免復位。反之,若程序跑飛,則無法及時餵狗,導致單片機能夠復位,解決了程序跑飛的問題。
1.1、回顧裸機中看門狗的使用
通常在裸機中編程的時候,一般都是一個大循環,採用時間片的方式去執行各種形式的任務。通常選擇一個固定的時間片(比如每5ms)給看門狗餵狗,當然遇到一些可能會操作比較久的任務時,會追加一些額外的餵狗操作。
1.2、RTOS中使用看門狗
查看了網上很多關於如何在RTOS中使用看門狗的策略,其實每種策略都有其使用場景,下面主要提到的幾種方式。
- 1、建立一個看門狗的定時任務,並將該任務定義爲最低優先級。
這種方式需要保證看門狗定時器的溢出時間一般要足夠長,且單片機不是經常滿負荷運行。(保證CPU的使用權不是一直被高優先級的線程搶佔) - 2、建立一個看門狗的定時任務,並將該任務定義爲最高優先級。
這種方式保證了餵狗的及時性,但是如果只有低優先級的任務死了(經歷少,倒是沒遇到過),這種方式下的看門狗就起不到作用。 - 3、多任務監測實現思路
將看門狗“餵狗”置於最高優先級,每個任務(或者某幾個重要的任務)定時向看門狗任務法消息,如果看門狗任務在一定時間內收全其他任務發來的消息才餵狗。這保證了能夠監測儘量多的任務。
二、RT-thread&STM32 中使用看門狗
使用STM32CubeMX來配置STM32的內部看門狗,超時時間爲1000ms。
我們定義一個線程每200ms喂一次狗,優先級爲RT_THREAD_PRIORITY_MAX-2。(這裏只是舉例,任務多了得仔細考慮如何餵狗)
extern IWDG_HandleTypeDef hiwdg;
/* 200ms 喂一次看門狗 */
static void iwdg_control(void *parameter)
{
int i = 0;
while(1)
{
rt_thread_mdelay(200);
i++;
/* Refresh IWDG: reload counter */
HAL_IWDG_Refresh(&hiwdg);
rt_kprintf("第%d次餵狗\n",i);
}
}
static rt_thread_t iwdg_tid = RT_NULL;
int iwdg_init(void)
{
/* 創建電機線程*/
iwdg_tid = rt_thread_create("iwdg_thread", // 線程名字
iwdg_control, // 線程入口函數
RT_NULL, // 線程入口參數
512, // 堆棧大小,
RT_THREAD_PRIORITY_MAX-2, // 線程優先級
5); // 時間片長度
/* 如果獲得線程控制塊,啓動這個線程 */
if (iwdg_tid != RT_NULL)
rt_thread_startup(iwdg_tid);
return 0;
}
INIT_DEVICE_EXPORT(iwdg_init); // 自動初始化
效果如下:
接下來屏蔽餵狗操作,可以看到看門狗起作用了,單片機不斷復位。
extern IWDG_HandleTypeDef hiwdg;
/* 200ms 喂一次看門狗 */
static void iwdg_control(void *parameter)
{
int i = 0;
while(1)
{
rt_thread_mdelay(200);
i++;
rt_kprintf("第%d次200ms\n",i);
}
}
static rt_thread_t iwdg_tid = RT_NULL;
int iwdg_init(void)
{
/* 創建電機線程*/
iwdg_tid = rt_thread_create("iwdg_thread", // 線程名字
iwdg_control, // 線程入口函數
RT_NULL, // 線程入口參數
512, // 堆棧大小,
RT_THREAD_PRIORITY_MAX-2, // 線程優先級
5); // 時間片長度
/* 如果獲得線程控制塊,啓動這個線程 */
if (iwdg_tid != RT_NULL)
rt_thread_startup(iwdg_tid);
return 0;
}
INIT_DEVICE_EXPORT(iwdg_init); // 自動初始化
可以看到每5次200ms,單片機就會重啓。