diff --git a/components/esp32/brownout.c b/components/esp32/brownout.c index 97846ae8c..1dcde078e 100644 --- a/components/esp32/brownout.c +++ b/components/esp32/brownout.c @@ -24,7 +24,7 @@ #if CONFIG_BROWNOUT_DET /* -This file ins included in esp-idf, but the menuconfig option for this is disabled because a silicon bug +This file is included in esp-idf, but the menuconfig option for this is disabled because a silicon bug prohibits the brownout detector from functioning correctly on the ESP32. */ diff --git a/components/esp32/include/esp_brownout.h b/components/esp32/include/esp_brownout.h index acce05e0d..5a0b1aec0 100644 --- a/components/esp32/include/esp_brownout.h +++ b/components/esp32/include/esp_brownout.h @@ -1,5 +1,20 @@ -#ifndef BROWNOUT_H -#define BROWNOUT_H +// 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. + + +#ifndef __ESP_BROWNOUT_H +#define __ESP_BROWNOUT_H void esp_brownout_init(); diff --git a/components/esp32/include/esp_int_wdt.h b/components/esp32/include/esp_int_wdt.h index 81404e305..dc5bd0dda 100644 --- a/components/esp32/include/esp_int_wdt.h +++ b/components/esp32/include/esp_int_wdt.h @@ -1,6 +1,46 @@ -#ifndef INT_WDT_H -#define INT_WDT_H +// 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. + +#ifndef __ESP_INT_WDT_H +#define __ESP_INT_WDT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** @addtogroup Watchdog_APIs + * @{ + */ + +/** + * @brief Initialize the interrupt watchdog. This is called in the init code, no need to + * call it explicitly. + * + * @param null + * + * @return null + */ void int_wdt_init(); + +/** + * @} + */ + + +#ifdef __cplusplus +} +#endif + #endif \ No newline at end of file diff --git a/components/esp32/include/esp_task_wdt.h b/components/esp32/include/esp_task_wdt.h index 9163e6907..c050616af 100644 --- a/components/esp32/include/esp_task_wdt.h +++ b/components/esp32/include/esp_task_wdt.h @@ -1,8 +1,73 @@ -#ifndef TASK_WDT_H -#define TASK_WDT_H +// 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 -void task_wdt_feed(); -void task_wdt_delete(); +// 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. + +#ifndef __ESP_TASK_WDT_H +#define __ESP_TASK_WDT_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \defgroup Watchdog_APIs Watchdog APIs + * @brief Watchdog APIs + */ + +/** @addtogroup Watchdog_APIs + * @{ + */ + +/** + * @brief Initialize the task watchdog. This is called in the init code, no need to + * call it explicitly. + * + * @param null + * + * @return null + */ void task_wdt_init(); +/** + * @brief Feed the watchdog. After the first feeding session, the watchdog will expect the calling + * task to keep feeding the watchdog until task_wdt_delete() is called. + * + * @param null + * + * @return null + */ + +void task_wdt_feed(); + + +/** + * @brief Delete the watchdog for the current task. + * + * @param null + * + * @return null + */ +void task_wdt_delete(); + +/** + * @} + */ + + +#ifdef __cplusplus +} +#endif + + + #endif \ No newline at end of file diff --git a/components/esp32/int_wdt.c b/components/esp32/int_wdt.c index 8eadbeb8d..8c506bde1 100644 --- a/components/esp32/int_wdt.c +++ b/components/esp32/int_wdt.c @@ -48,28 +48,28 @@ This uses the TIMERG1 WDT. #define WDT_WRITE_KEY 0x50D83AA1 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 - //The timer configs initially are set to 5 seconds, to make sure the CPU can start up. The tick hook sets - //it to their actual value. - TIMERG1.wdt_config2=10000; - TIMERG1.wdt_config3=10000; - TIMERG1.wdt_config0.en=1; - TIMERG1.wdt_feed=1; - TIMERG1.wdt_wprotect=0; - TIMERG1.int_clr_timers.wdt=1; - TIMERG1.int_ena.wdt=1; - ESP_INTR_DISABLE(WDT_INT_NUM); - intr_matrix_set(xPortGetCoreID(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, WDT_INT_NUM); - //We do not register a handler for the interrupt because it is interrupt level 4 which - //is not servicable from C. Instead, xtensa_vectors.S has a call to the panic handler for - //this interrupt. - ESP_INTR_ENABLE(WDT_INT_NUM); + 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 + //The timer configs initially are set to 5 seconds, to make sure the CPU can start up. The tick hook sets + //it to their actual value. + TIMERG1.wdt_config2=10000; + TIMERG1.wdt_config3=10000; + TIMERG1.wdt_config0.en=1; + TIMERG1.wdt_feed=1; + TIMERG1.wdt_wprotect=0; + TIMERG1.int_clr_timers.wdt=1; + TIMERG1.int_ena.wdt=1; + ESP_INTR_DISABLE(WDT_INT_NUM); + intr_matrix_set(xPortGetCoreID(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, WDT_INT_NUM); + //We do not register a handler for the interrupt because it is interrupt level 4 which + //is not servicable from C. Instead, xtensa_vectors.S has a call to the panic handler for + //this interrupt. + ESP_INTR_ENABLE(WDT_INT_NUM); } @@ -78,28 +78,28 @@ void int_wdt_init() { bool int_wdt_app_cpu_ticked=false; void vApplicationTickHook(void) { - if (xPortGetCoreID()!=0) { - int_wdt_app_cpu_ticked=true; - } else { - //Only feed wdt if app cpu also ticked. - if (int_wdt_app_cpu_ticked) { - TIMERG1.wdt_wprotect=WDT_WRITE_KEY; - 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_feed=1; - TIMERG1.wdt_wprotect=0; - int_wdt_app_cpu_ticked=false; - } - } + if (xPortGetCoreID()!=0) { + int_wdt_app_cpu_ticked=true; + } else { + //Only feed wdt if app cpu also ticked. + if (int_wdt_app_cpu_ticked) { + TIMERG1.wdt_wprotect=WDT_WRITE_KEY; + 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_feed=1; + TIMERG1.wdt_wprotect=0; + int_wdt_app_cpu_ticked=false; + } + } } #else void vApplicationTickHook(void) { - if (xPortGetCoreID()!=0) return; - TIMERG1.wdt_wprotect=WDT_WRITE_KEY; - 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_feed=1; - TIMERG1.wdt_wprotect=0; + if (xPortGetCoreID()!=0) return; + TIMERG1.wdt_wprotect=WDT_WRITE_KEY; + 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_feed=1; + TIMERG1.wdt_wprotect=0; } #endif diff --git a/components/esp32/task_wdt.c b/components/esp32/task_wdt.c index 6da0901fb..e6a462088 100644 --- a/components/esp32/task_wdt.c +++ b/components/esp32/task_wdt.c @@ -45,9 +45,9 @@ 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; + TaskHandle_t task_handle; + bool fed_watchdog; + wdt_task_t *next; }; static wdt_task_t *wdt_task_list=NULL; @@ -58,127 +58,127 @@ static wdt_task_t *wdt_task_list=NULL; #define WDT_WRITE_KEY 0x50D83AA1 static void IRAM_ATTR task_wdt_isr(void *arg) { - wdt_task_t *wdttask; - const char *cpu; - //Feed the watchdog so we do not reset - TIMERG0.wdt_wprotect=WDT_WRITE_KEY; - TIMERG0.wdt_feed=1; - TIMERG0.wdt_wprotect=0; - //Ack interrupt - TIMERG0.int_clr_timers.wdt=1; - //Watchdog got triggered because at least one task did not report in. - ets_printf("Task watchdog got triggered. The following tasks did not feed the watchdog in time:\n"); - for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) { - if (!wdttask->fed_watchdog) { - cpu=xTaskGetAffinity(wdttask->task_handle)==0?"CPU 0":"CPU 1"; - if (xTaskGetAffinity(wdttask->task_handle)==tskNO_AFFINITY) cpu="CPU 0/1"; - printf(" - %s (%s)\n", pcTaskGetTaskName(wdttask->task_handle), cpu); - } - } - ets_printf("Tasks currently running:\n"); - for (int x=0; xnext) { + if (!wdttask->fed_watchdog) { + cpu=xTaskGetAffinity(wdttask->task_handle)==0?"CPU 0":"CPU 1"; + if (xTaskGetAffinity(wdttask->task_handle)==tskNO_AFFINITY) cpu="CPU 0/1"; + printf(" - %s (%s)\n", pcTaskGetTaskName(wdttask->task_handle), cpu); + } + } + ets_printf("Tasks currently running:\n"); + for (int x=0; xnext) { - //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; - } - - 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 { - for (wdttask=wdt_task_list; wdttask->next!=NULL; wdttask=wdttask->next) ; - wdttask->next=newtask; - } - } - 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; - //Reset fed_watchdog status - for (wdttask=wdt_task_list; wdttask->next!=NULL; wdttask=wdttask->next) wdttask->fed_watchdog=false; - } + 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. + for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) { + //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; + } + + 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 { + for (wdttask=wdt_task_list; wdttask->next!=NULL; wdttask=wdttask->next) ; + wdttask->next=newtask; + } + } + 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; + //Reset fed_watchdog status + for (wdttask=wdt_task_list; wdttask->next!=NULL; wdttask=wdttask->next) wdttask->fed_watchdog=false; + } } 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); - } + 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_TG0_WDT_LEVEL_INTR_SOURCE, ETS_T0_WDT_INUM); - xt_set_interrupt_handler(ETS_T0_WDT_INUM, task_wdt_isr, NULL); - TIMERG0.int_clr_timers.wdt=1; - TIMERG0.int_ena.wdt=1; - ESP_INTR_ENABLE(ETS_T0_WDT_INUM); + 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_TG0_WDT_LEVEL_INTR_SOURCE, ETS_T0_WDT_INUM); + xt_set_interrupt_handler(ETS_T0_WDT_INUM, task_wdt_isr, NULL); + TIMERG0.int_clr_timers.wdt=1; + TIMERG0.int_ena.wdt=1; + ESP_INTR_ENABLE(ETS_T0_WDT_INUM); } #if CONFIG_TASK_WDT_CHECK_IDLE_TASK void vApplicationIdleHook(void) { #if !CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 - if (xPortGetCoreID()!=0) return; + if (xPortGetCoreID()!=0) return; #endif - task_wdt_feed(); + task_wdt_feed(); } #endif