ESP32 任務看門狗(TaskWDT)組件與用戶任務監控

看門狗機制用於監控嵌入式系統運行並在發生不可知的軟硬件故障時將系統復位。系統正常運行時,看門狗定時器溢出之前會被重置計數值,也就是“餵狗”。定時器溢出意味着無法“餵狗”,系統異常。

 

0:BUG

前一陣在沒事開着ESP32測試運行的時候,較長時間後會有很小的概率出現異常,Log未保存,大概是:“Task watchdog got triggered. Thefollowing tasks did not reset the watchdog in time:”,也就是說,系統內的任務看門狗未在指定時間內被複位(餵狗)。

出現這個問題以後,基本系統就不正常運行了。

因此關注ESP32的任務看門狗定時器(TaskWDT),以便於問題再出現時有些對策。

 

一些說明:

WDT:看門狗定時器

MWDT0/1:ESP32定時器0/1內置的看門狗定時器

RWDT:ESP32 RTC內置的看門狗定時器

TWDT:任務看門狗定時器,注意是ESP32 SDK軟件上實現的軟件定時器功能組件,並非硬件定時器模塊。

 

1:TWDT源碼路徑

路徑:

\esp-idf-v3.0-rc1\components\esp32\include\Esp_task_wdt.h       TWDT API頭文件

\esp-idf-v3.0-rc1\components\esp32\task_wdt.c                  TWDT源文件

 

重要API:

esp_err_t esp_task_wdt_init(uint32_ttimeout, bool panic);  初始化TWDT

esp_err_t esp_task_wdt_add(TaskHandle_thandle);          任務添加到TWDT鏈表

void esp_task_wdt_feed() __attribute__((deprecated));    餵狗

 

2:TWDT數據結構



整個TWDT的數據結構是鏈表,基本的數據類型是twdt_task_t,頭結點包含有擴展數據,類型是twdt_config_t,重要成員數據涵義如下:

TaskHandle_t task_handle:任務句柄

bool has_reset:標識位。標識該任務的是否已進行餵狗操作。

bool panic:標識位。若TWDT有溢出,置位則生成系統panic。

intr_handle_t intr_handle:函數指針,指向TWDT溢出中斷處理函數。

uint32_t timeout:TWDT計時溢出的週期,單位秒。

 

基本上從數據結構已經可以看出TWDT是如何運作的。

 

3:TWDT初始化

 

3.1esp_task_wdt_init

esp_err_t esp_task_wdt_init(uint32_ttimeout, bool panic)實現TWDT模塊的初始化。

參數timeout設定了TWDT溢出的時間,單位是秒;

參數panic設置當TWDT溢出時是否生成系統panic。

esp_task_wdt_init的代碼部分很好理解,也就是初始化TWDT的鏈表頭,填充相關數據的數據等。

TWDT是基於ESP32定時器0的WDT實現定時,不得不說這個形式的定時器是筆者見過比較複雜的(也可能是已經過氣的原因),因此特別關注一下:

 

3.2ESP32 MWDT0

文檔《esp32_technical_reference_manual_cn》Chapter19有關於ESP32定時器模塊的描述,概要如下:

<1>ESP32內有三個WDT,兩個通用定時器0/1模塊內各一個WDT(MWDT0& MWDT1),RTC模塊內一個(RWDT);

<2>MWDT與RWDT使能後循環工作,且每個循環週期可被配置爲4個階段,階段0~階段3。

每個階段的超時時間與超時動作都可以單獨配置。當軟件餵狗時,WDT重置回階段0。

<3>以下動作可以被配置爲WDT定時時間接近某個階段預設定時值時的執行的動作:

A:觸發WDT溢出中斷

B:復位指定的CPU(ESP32包含兩個CPU):

復位 MWDT0只能復位 PRO CPU,復位 MWDT1只能復位APP CPU;

根據不同配置,復位 RWDT 可以復位兩個,一個或不復位 CPU 內核。

C:復位主系統:復位CPU以及MWDT在內的外設,RTC除外。

D:復位主系統與RTC:復位CPU以及全部外設,只能通過RWDT實現。

E:Do Nothing


這裏再通過esp_task_wdt_init解析一下TWDT的配置:


TIMERG0.wdt_config0.sys_reset_length=7;

CPU復位長度信號選擇  7:3.2us

TIMERG0.wdt_config0.cpu_reset_length=7;       

系統復位長度信號選擇 7:3.2us     

TIMERG0.wdt_config1.clk_prescale=80*500;

定時器0預分頻器值,分辨率 = 12.5ns* 80 * 500 = 500000ns = 0.5ms

TIMERG0.wdt_config2=twdt_config->timeout*2000; 

時鐘週期0超時時間:timeout * 2000* 0.5ms = timeout(S)

TIMERG0.wdt_config3=twdt_config->timeout*4000;     

時鐘週期1超時時間:timeout * 4000* 0.5ms = timeout * 2(S)

 

可見,MWDT0每個計時週期被設置爲兩個階段:

階段0:計時時間0 - timeout(S),若期間MWDT被餵狗,則重新計時;若超時,則觸發WDT超時中斷。

階段1:計時時間timeout(S)–2*timeout(S),若期間MWDT被餵狗,則重新回到階段0;若超時,異常,復位主系統。

 

3.3階段與超時動作

 

MWDT0的計時週期被分爲階段0與階段1。

階段1超時的處理是復位主系統,因此無需多說;

階段0超時觸發的中斷則是在      

ESP_ERROR_CHECK(esp_intr_alloc(ETS_TG0_WDT_LEVEL_INTR_SOURCE,0, task_wdt_isr, NULL, &twdt_config->intr_handle))關聯到了函數task_wdt_isr。

 

task_wdt_isr做的事情其實很簡單,就是在TWDT鏈表內遍歷,找到位於鏈表內但未進行餵狗操作的任務打印出來。另外,若esp_task_wdt_init的panic參數被設置爲true,則產生一個系統panic:

這裏也就是爲本文開頭所述BUG出現的地方了。必然是系統內有任務被阻塞到無法餵狗,TWDT超時,最後因爲配置了panic,設備無法正常運行。

 

4:向TWDT添加任務與任務餵狗

 

4.1函數接口

TWDT完成初始化時,其鏈表內並沒有任務數據。這時用戶要把需要被TWDT監控的任務添加到TWDT鏈表內,其接口是:esp_task_wdt_add

與此同時,用戶要在自己的任務處理函數中進行餵狗操作,其接口時esp_task_wdt_feed

這裏截取一部分esp_task_wdt_feed代碼:

很顯然,用戶任務調用esp_task_wdt_feed餵狗時,esp_task_wdt_feed會找到當前運行的任務,也就是執行餵狗操作的任務。若該任務不存在於TWDT的鏈表,說明這個任務未被TWDT監控,直接返回;否則的話,所謂的餵狗操作就是簡單的設置了該任務的餵狗標識位。

值得注意的是藍色部分,當任務鏈表內的所有任務的餵狗標識位都被設置,會復位MWDT0並且將TWDT內所有的任務的餵狗標識位重新設置爲“未餵狗”狀態。

 

4.2ESP32 SDK默認TWDT監控系統IDLE任務

搜索esp_task_wdt_feed卻並未發現有任何任務進行餵狗操作。那麼爲何導致本文開頭所述的BUG?

原因是ESP32 SDK通過esp_task_wdt_add將FreeRTOS的空閒任務加入了TWDT的監控鏈表,並且還通過esp_register_freertos_idle_hook_for_cpu將函數idle_hook_cb註冊爲空閒任務的任務處理函數。而idle_hook_cb所做的事情已經沒有懸念了,就是餵狗咯。

 

這也就意味着,出現本文開頭所述的BUG時,當時系統在TWDT溢出時間內從未讓出過CPU讓IDLE任務得以執行。這裏隨手看一下正常運行時任務CPU佔有率(兩個IDLE任務是因爲ESP32雙核):


正常情況下,整個系統98%的時間基本都是IDLE的。

所以這個BUG出現時到底發生了什麼,比較可怕。只能等下一次出現。

 

5:ESP32 SDK TWDT的配置

ESP32 SDK的TWDT組件是可以配置的:

Make menuconfig後按照路徑找:



當前設置超時週期5S。

 

6:使用TWDT監控重要任務

TBD



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章