Initial addition of wdt and brownout code
This commit is contained in:
parent
922839d131
commit
53146799a0
|
@ -140,4 +140,117 @@ config ULP_COPROC_RESERVE_MEM
|
|||
default 0
|
||||
depends on !ULP_COPROC_ENABLED
|
||||
|
||||
menu "Watchdogs & brown-out detection"
|
||||
|
||||
config INT_WDT
|
||||
bool "Interrupt watchdog"
|
||||
default y
|
||||
help
|
||||
This watchdog timer can detect if the FreeRTOS tick interrupt has not been called for a certain time,
|
||||
either because a task turned off interrupts and did not turn them on for a long time, or because an
|
||||
interrupt handler did not return. It will try to invoke the panic handler first and failing that
|
||||
reset the SoC.
|
||||
|
||||
config INT_WDT_TIMEOUT_MS_MIN
|
||||
default (2000/CONFIG_FREERTOS_HZ)
|
||||
|
||||
config INT_WDT_TIMEOUT_MS
|
||||
int "Interrupt watchdog timeout (ms)"
|
||||
depends on INT_WDT
|
||||
default 100
|
||||
range INT_WDT_TIMEOUT_MS_MIN 10000
|
||||
help
|
||||
The timeout of the watchdog, in miliseconds.
|
||||
|
||||
config TASK_WDT
|
||||
bool "Task watchdog"
|
||||
default y
|
||||
help
|
||||
This watchdog timer can be used to make sure individual tasks are still running.
|
||||
|
||||
config TASK_WDT_PANIC
|
||||
bool "Invoke panic handler when Task Watchdog is triggered"
|
||||
depends on TASK_WDT
|
||||
default n
|
||||
help
|
||||
Normally, the Task Watchdog will only print out a warning if it detects it has not
|
||||
been fed. If this is enabled, it will invoke the panic handler instead, which
|
||||
can then halt or reboot the chip.
|
||||
|
||||
config TASK_WDT_TIMEOUT_S
|
||||
int "Task watchdog timeout (seconds)"
|
||||
depends on TASK_WDT
|
||||
range 1 60
|
||||
default 5
|
||||
help
|
||||
Timeout for the task WDT, in seconds.
|
||||
|
||||
config TASK_WDT_CHECK_IDLE_TASK
|
||||
bool "Task watchdog watches idle tasks"
|
||||
depends on TASK_WDT
|
||||
default y
|
||||
help
|
||||
With this turned on, the task WDT can detect if the idle task is not called within the task
|
||||
watchdog timeout period. The idle task not being called usually is a symptom of another
|
||||
task hoarding the CPU. It is also a bad thing because FreeRTOS household tasks depend on the
|
||||
idle task getting some runtime every now and then.
|
||||
|
||||
config BROWNOUT_DET
|
||||
bool "Hardware brownout detect & reset"
|
||||
default y
|
||||
help
|
||||
The ESP32 has a built-in brownout detector which can detect if the voltage is lower than
|
||||
a specific value. If this happens, it will reset the chip in order to prevent unintended
|
||||
behaviour.
|
||||
|
||||
choice BROWNOUT_DET_LVL_SEL
|
||||
prompt "Brownout voltage level"
|
||||
depends on BROWNOUT_DET
|
||||
default BROWNOUT_DET_LVL_SEL_25
|
||||
help
|
||||
The brownout detector will reset the chip when the supply voltage is below this level.
|
||||
|
||||
config BROWNOUT_DET_LVL_SEL_0
|
||||
bool "2.1V"
|
||||
config BROWNOUT_DET_LVL_SEL_1
|
||||
bool "2.2V"
|
||||
config BROWNOUT_DET_LVL_SEL_2
|
||||
bool "2.3V"
|
||||
config BROWNOUT_DET_LVL_SEL_3
|
||||
bool "2.4V"
|
||||
config BROWNOUT_DET_LVL_SEL_4
|
||||
bool "2.5V"
|
||||
config BROWNOUT_DET_LVL_SEL_5
|
||||
bool "2.6V"
|
||||
config BROWNOUT_DET_LVL_SEL_6
|
||||
bool "2.7V"
|
||||
config BROWNOUT_DET_LVL_SEL_7
|
||||
bool "2.8V"
|
||||
endchoice
|
||||
|
||||
config BROWNOUT_DET_LVL
|
||||
int
|
||||
default 0 if BROWNOUT_DET_LVL_SEL_0
|
||||
default 1 if BROWNOUT_DET_LVL_SEL_1
|
||||
default 2 if BROWNOUT_DET_LVL_SEL_2
|
||||
default 3 if BROWNOUT_DET_LVL_SEL_3
|
||||
default 4 if BROWNOUT_DET_LVL_SEL_4
|
||||
default 5 if BROWNOUT_DET_LVL_SEL_5
|
||||
default 6 if BROWNOUT_DET_LVL_SEL_6
|
||||
default 7 if BROWNOUT_DET_LVL_SEL_7
|
||||
|
||||
|
||||
config BROWNOUT_DET_RESETDELAY
|
||||
int "Brownout reset delay (in uS)"
|
||||
depends on BROWNOUT_DET
|
||||
range 0 6820
|
||||
default 1000
|
||||
help
|
||||
The brownout detector can reset the chip after a certain delay, in order to make sure e.g. a voltage dip has entirely passed
|
||||
before trying to restart the chip. You can set the delay here.
|
||||
|
||||
endmenu
|
||||
|
||||
|
||||
|
||||
endmenu
|
||||
|
|
37
components/esp32/brownout.c
Normal file
37
components/esp32/brownout.c
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "soc/soc.h"
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
|
||||
|
||||
void esp_brownout_init() {
|
||||
// WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG,
|
||||
// RTC_CNTL_BROWN_OUT_ENA | (CONFIG_BROWNOUT_DET_LVL << RTC_CNTL_DBROWN_OUT_THRES_S) |
|
||||
// RTC_CNTL_BROWN_OUT_RST_ENA | (((CONFIG_BROWNOUT_DET_RESETDELAY*150)/1000) << RTC_CNTL_BROWN_OUT_RST_WAIT_S) |
|
||||
// RTC_CNTL_BROWN_OUT_PD_RF_ENA|RTC_CNTL_BROWN_OUT_CLOSE_FLASH_ENA);
|
||||
|
||||
|
||||
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG,
|
||||
RTC_CNTL_BROWN_OUT_ENA | (4 << RTC_CNTL_DBROWN_OUT_THRES_S) |
|
||||
RTC_CNTL_BROWN_OUT_RST_ENA | (0x3FF << RTC_CNTL_BROWN_OUT_RST_WAIT_S) |
|
||||
RTC_CNTL_BROWN_OUT_PD_RF_ENA | RTC_CNTL_BROWN_OUT_CLOSE_FLASH_ENA);
|
||||
|
||||
}
|
|
@ -43,6 +43,8 @@
|
|||
#include "esp_ipc.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "esp_brownout.h"
|
||||
|
||||
void start_cpu0(void) __attribute__((weak, alias("start_cpu0_default")));
|
||||
void start_cpu0_default(void) IRAM_ATTR;
|
||||
#if !CONFIG_FREERTOS_UNICORE
|
||||
|
@ -137,6 +139,16 @@ void start_cpu0_default(void)
|
|||
do_global_ctors();
|
||||
esp_ipc_init();
|
||||
spi_flash_init();
|
||||
#if CONFIG_BROWNOUT_DET
|
||||
esp_brownout_init();
|
||||
#endif
|
||||
#if CONFIG_INT_WDT
|
||||
int_wdt_init()
|
||||
#endif
|
||||
#if CONFIG_TASK_WDT
|
||||
task_wdt_init()
|
||||
#endif
|
||||
|
||||
xTaskCreatePinnedToCore(&main_task, "main",
|
||||
ESP_TASK_MAIN_STACK, NULL,
|
||||
ESP_TASK_MAIN_PRIO, NULL, 0);
|
||||
|
|
6
components/esp32/include/esp_brownout.h
Normal file
6
components/esp32/include/esp_brownout.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#ifndef BROWNOUT_H
|
||||
#define BROWNOUT_H
|
||||
|
||||
void esp_brownout_init();
|
||||
|
||||
#endif
|
6
components/esp32/include/esp_int_wdt.h
Normal file
6
components/esp32/include/esp_int_wdt.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#ifndef INT_WDT_H
|
||||
#define INT_WDT_H
|
||||
|
||||
void int_wdt_init();
|
||||
|
||||
#endif
|
8
components/esp32/include/esp_task_wdt.h
Normal file
8
components/esp32/include/esp_task_wdt.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#ifndef TASK_WDT_H
|
||||
#define TASK_WDT_H
|
||||
|
||||
void task_wdt_feed();
|
||||
void task_wdt_delete();
|
||||
void task_wdt_init();
|
||||
|
||||
#endif
|
82
components/esp32/int_wdt.c
Normal file
82
components/esp32/int_wdt.c
Normal file
|
@ -0,0 +1,82 @@
|
|||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
/*
|
||||
This routine enables a watchdog to catch instances of processes disabling
|
||||
interrupts for too long, or code within interrupt handlers taking too long.
|
||||
It does this by setting up a watchdog which gets fed from the FreeRTOS
|
||||
task switch interrupt. When this watchdog times out, initially it will call
|
||||
a high-level interrupt routine that will panic FreeRTOS in order to allow
|
||||
for forensic examination of the state of the CPU. When this interrupt
|
||||
handler is not called and the watchdog times out a second time, it will
|
||||
reset the SoC.
|
||||
|
||||
This uses the TIMERG1 WDT.
|
||||
*/
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include <esp_types.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_intr.h"
|
||||
#include "soc/timer_group_struct.h"
|
||||
|
||||
#include "esp_int_wdt.h"
|
||||
|
||||
#if CONFIG_INT_WDT
|
||||
|
||||
|
||||
#define WDT_INT_NUM 24
|
||||
|
||||
|
||||
#define WDT_WRITE_KEY 0x50D83AA1
|
||||
|
||||
static void esp_int_wdt_isr(void *arg) {
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
void int_wdt_init() {
|
||||
TIMERG1.wdt_wprotect=WDT_WRITE_KEY;
|
||||
TIMERG1.wdt_config0.sys_reset_length=7; //3.2uS
|
||||
TIMERG1.wdt_config0.cpu_reset_length=7; //3.2uS
|
||||
TIMERG1.wdt_config0.level_int_en=1;
|
||||
TIMERG1.wdt_config0.stg0=1; //1st stage timeout: interrupt
|
||||
TIMERG1.wdt_config0.stg1=3; //2nd stage timeout: reset system
|
||||
TIMERG1.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS
|
||||
TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt
|
||||
TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset
|
||||
TIMERG1.wdt_config0.en=1;
|
||||
TIMERG1.wdt_feed=1;
|
||||
TIMERG1.wdt_wprotect=0;
|
||||
ESP_INTR_DISABLE(WDT_INT_NUM);
|
||||
intr_matrix_set(xPortGetCoreID(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, WDT_INT_NUM);
|
||||
xt_set_interrupt_handler(WDT_INT_NUM, int_wdt_isr, NULL);
|
||||
ESP_INTR_ENABLE(WDT_INT_NUM);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void vApplicationTickHook(void) {
|
||||
TIMERG1.wdt_wprotect=WDT_WRITE_KEY;
|
||||
TIMERG1.wdt_feed=1;
|
||||
TIMERG1.wdt_wprotect=0;
|
||||
}
|
||||
|
||||
#endif
|
158
components/esp32/task_wdt.c
Normal file
158
components/esp32/task_wdt.c
Normal file
|
@ -0,0 +1,158 @@
|
|||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
/*
|
||||
This routine enables a more general-purpose task watchdog: tasks can individually
|
||||
feed the watchdog and the watchdog will bark if one or more tasks haven't fed the
|
||||
watchdog within the specified time. Optionally, the idle tasks can also configured
|
||||
to feed the watchdog in a similar fashion, to detect CPU starvation.
|
||||
|
||||
This uses the TIMERG0 WDT.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include <esp_types.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_intr.h"
|
||||
#include "soc/timer_group_struct.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "esp_task_wdt.h"
|
||||
|
||||
#if CONFIG_TASK_WDT
|
||||
|
||||
static const char* TAG = "task_wdt";
|
||||
|
||||
|
||||
typedef struct wdt_task_t wdt_task_t;
|
||||
struct wdt_task_t {
|
||||
TaskHandle_t task_handle;
|
||||
bool fed_watchdog;
|
||||
wdt_task_t *next;
|
||||
};
|
||||
|
||||
|
||||
static wdt_task_t *wdt_task_list=NULL;
|
||||
|
||||
#define WDT_INT_NUM 24
|
||||
|
||||
|
||||
#define WDT_WRITE_KEY 0x50D83AA1
|
||||
|
||||
static void task_wdt_isr(void *arg) {
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
void task_wdt_feed() {
|
||||
wdt_task_t *wdttask=wdt_task_list;
|
||||
bool found_task=false, do_feed_wdt=true;
|
||||
TaskHandle_t handle=xTaskGetCurrentTaskHandle();
|
||||
//Walk the linked list of wdt tasks to find this one, as well as see if we need to feed
|
||||
//the real watchdog timer.
|
||||
while (wdttask!=NULL) {
|
||||
//See if we are at the current task.
|
||||
if (wdttask->task_handle == handle) {
|
||||
wdttask->fed_watchdog=true;
|
||||
found_task=true;
|
||||
}
|
||||
//If even one task in the list doesn't have the do_feed_wdt var set, we do not feed the watchdog.
|
||||
if (!wdttask->fed_watchdog) do_feed_wdt=false;
|
||||
//Next entry.
|
||||
wdttask=wdttask->next;
|
||||
}
|
||||
|
||||
if (!found_task) {
|
||||
//This is the first time the task calls the task_wdt_feed function. Create a new entry for it in
|
||||
//the linked list.
|
||||
wdt_task_t *newtask=malloc(sizeof(wdt_task_t));
|
||||
memset(newtask, 0, sizeof(wdt_task_t));
|
||||
newtask->task_handle=handle;
|
||||
newtask->fed_watchdog=true;
|
||||
if (wdt_task_list == NULL) {
|
||||
wdt_task_list=newtask;
|
||||
} else {
|
||||
wdttask=wdt_task_list;
|
||||
while (!(wdttask->next == NULL)) wdttask=wdttask->next;
|
||||
wdttask->next=wdttask;
|
||||
}
|
||||
}
|
||||
if (do_feed_wdt) {
|
||||
//All tasks have checked in; time to feed the hw watchdog.
|
||||
TIMERG0.wdt_wprotect=WDT_WRITE_KEY;
|
||||
TIMERG0.wdt_feed=1;
|
||||
TIMERG0.wdt_wprotect=0;
|
||||
}
|
||||
}
|
||||
|
||||
void task_wdt_delete() {
|
||||
TaskHandle_t handle=xTaskGetCurrentTaskHandle();
|
||||
wdt_task_t *wdttask=wdt_task_list;
|
||||
//Wdt task list can't be empty
|
||||
if (!wdt_task_list) {
|
||||
ESP_LOGE(TAG, "task_wdt_delete: No tasks in list?");
|
||||
return;
|
||||
}
|
||||
if (handle==wdt_task_list) {
|
||||
//Current task is first on list.
|
||||
wdt_task_list=wdt_task_list->next;
|
||||
free(wdttask);
|
||||
} else {
|
||||
//Find current task in list
|
||||
while (wdttask->next!=NULL && wdttask->next->task_handle!=handle) wdttask=wdttask->next;
|
||||
if (!wdttask->next) {
|
||||
ESP_LOGE(TAG, "task_wdt_delete: Task never called task_wdt_feed!");
|
||||
return;
|
||||
}
|
||||
wdt_task_t *freeme=wdttask->next;
|
||||
wdttask->next=wdttask->next->next;
|
||||
free(freeme);
|
||||
}
|
||||
}
|
||||
|
||||
void task_wdt_init() {
|
||||
TIMERG0.wdt_wprotect=WDT_WRITE_KEY;
|
||||
TIMERG0.wdt_config0.sys_reset_length=7; //3.2uS
|
||||
TIMERG0.wdt_config0.cpu_reset_length=7; //3.2uS
|
||||
TIMERG0.wdt_config0.level_int_en=1;
|
||||
TIMERG0.wdt_config0.stg0=1; //1st stage timeout: interrupt
|
||||
TIMERG0.wdt_config0.stg1=3; //2nd stage timeout: reset system
|
||||
TIMERG0.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS
|
||||
TIMERG0.wdt_config2=CONFIG_TASK_WDT_TIMEOUT_S*2000; //Set timeout before interrupt
|
||||
TIMERG0.wdt_config3=CONFIG_TASK_WDT_TIMEOUT_S*4000; //Set timeout before reset
|
||||
TIMERG0.wdt_config0.en=1;
|
||||
TIMERG0.wdt_feed=1;
|
||||
TIMERG0.wdt_wprotect=0;
|
||||
ESP_INTR_DISABLE(ETS_T0_WDT_INUM);
|
||||
intr_matrix_set(xPortGetCoreID(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, ETS_T0_WDT_INUM);
|
||||
xt_set_interrupt_handler(ETS_T0_WDT_INUM, task_wdt_isr, NULL);
|
||||
ESP_INTR_ENABLE(ETS_T0_WDT_INUM);
|
||||
}
|
||||
|
||||
|
||||
#if CONFIG_TASK_WDT_CHECK_IDLE_TASK
|
||||
void vApplicationIdleHook(void) {
|
||||
task_wdt_feed();
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -152,9 +152,9 @@
|
|||
*----------------------------------------------------------*/
|
||||
|
||||
#define configUSE_PREEMPTION 1
|
||||
#define configUSE_IDLE_HOOK 0
|
||||
#define configUSE_IDLE_HOOK ( CONFIG_TASK_WDT_CHECK_IDLE_TASK )
|
||||
|
||||
#define configUSE_TICK_HOOK 0
|
||||
#define configUSE_TICK_HOOK ( CONFIG_INT_WDT )
|
||||
|
||||
#define configTICK_RATE_HZ ( CONFIG_FREERTOS_HZ )
|
||||
|
||||
|
|
Loading…
Reference in a new issue