Merge branch 'feature/enable_brownout_detector' into 'master'

Enable brownout detector

See merge request !811
This commit is contained in:
Ivan Grokhotkov
2017-07-13 11:40:37 +08:00
10 changed files with 232 additions and 54 deletions

View File

@@ -0,0 +1,48 @@
// Copyright 2016-2017 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.
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "esp_intr_alloc.h"
/**
* @brief Register a handler for specific RTC_CNTL interrupts
*
* Multiple handlers can be registered using this function. Whenever an
* RTC interrupt happens, all handlers with matching rtc_intr_mask values
* will be called.
*
* @param handler handler function to call
* @param handler_arg argument to be passed to the handler
* @param rtc_intr_mask combination of RTC_CNTL_*_INT_ENA bits indicating the
* sources to call the handler for
* @return
* - ESP_OK on success
* - ESP_ERR_NO_MEM not enough memory to allocate handler structure
* - other errors returned by esp_intr_alloc
*/
esp_err_t rtc_isr_register(intr_handler_t handler, void* handler_arg,
uint32_t rtc_intr_mask);
/**
* @brief Deregister the handler previously registered using rtc_isr_register
* @param handler handler function to call (as passed to rtc_isr_register)
* @param handler_arg argument of the handler (as passed to rtc_isr_register)
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_STATE if a handler matching both handler and
* handler_arg isn't registered
*/
esp_err_t rtc_isr_deregister(intr_handler_t handler, void* handler_arg);

View File

@@ -24,6 +24,16 @@
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/xtensa_api.h" #include "freertos/xtensa_api.h"
#include "freertos/semphr.h" #include "freertos/semphr.h"
#include "esp_intr_alloc.h"
#include "sys/lock.h"
#include "driver/rtc_cntl.h"
#ifndef NDEBUG
// Enable built-in checks in queue.h in debug builds
#define INVARIANTS
#endif
#include "rom/queue.h"
static const char *RTC_MODULE_TAG = "RTC_MODULE"; static const char *RTC_MODULE_TAG = "RTC_MODULE";
@@ -792,3 +802,99 @@ int hall_sensor_read()
adc1_config_channel_atten(ADC1_CHANNEL_3, ADC_ATTEN_0db); adc1_config_channel_atten(ADC1_CHANNEL_3, ADC_ATTEN_0db);
return hall_sensor_get_value(); return hall_sensor_get_value();
} }
/*---------------------------------------------------------------
INTERRUPT HANDLER
---------------------------------------------------------------*/
typedef struct rtc_isr_handler_ {
uint32_t mask;
intr_handler_t handler;
void* handler_arg;
SLIST_ENTRY(rtc_isr_handler_) next;
} rtc_isr_handler_t;
static SLIST_HEAD(rtc_isr_handler_list_, rtc_isr_handler_) s_rtc_isr_handler_list =
SLIST_HEAD_INITIALIZER(s_rtc_isr_handler_list);
portMUX_TYPE s_rtc_isr_handler_list_lock = portMUX_INITIALIZER_UNLOCKED;
static intr_handle_t s_rtc_isr_handle;
static void rtc_isr(void* arg)
{
uint32_t status = REG_READ(RTC_CNTL_INT_ST_REG);
rtc_isr_handler_t* it;
portENTER_CRITICAL(&s_rtc_isr_handler_list_lock);
SLIST_FOREACH(it, &s_rtc_isr_handler_list, next) {
if (it->mask & status) {
portEXIT_CRITICAL(&s_rtc_isr_handler_list_lock);
(*it->handler)(it->handler_arg);
portENTER_CRITICAL(&s_rtc_isr_handler_list_lock);
}
}
portEXIT_CRITICAL(&s_rtc_isr_handler_list_lock);
REG_WRITE(RTC_CNTL_INT_CLR_REG, status);
}
static esp_err_t rtc_isr_ensure_installed()
{
esp_err_t err = ESP_OK;
portENTER_CRITICAL(&s_rtc_isr_handler_list_lock);
if (s_rtc_isr_handle) {
goto out;
}
REG_WRITE(RTC_CNTL_INT_ENA_REG, 0);
REG_WRITE(RTC_CNTL_INT_CLR_REG, UINT32_MAX);
err = esp_intr_alloc(ETS_RTC_CORE_INTR_SOURCE, 0, &rtc_isr, NULL, &s_rtc_isr_handle);
if (err != ESP_OK) {
goto out;
}
out:
portEXIT_CRITICAL(&s_rtc_isr_handler_list_lock);
return err;
}
esp_err_t rtc_isr_register(intr_handler_t handler, void* handler_arg, uint32_t rtc_intr_mask)
{
esp_err_t err = rtc_isr_ensure_installed();
if (err != ESP_OK) {
return err;
}
rtc_isr_handler_t* item = malloc(sizeof(*item));
if (item == NULL) {
return ESP_ERR_NO_MEM;
}
item->handler = handler;
item->handler_arg = handler_arg;
item->mask = rtc_intr_mask;
portENTER_CRITICAL(&s_rtc_isr_handler_list_lock);
SLIST_INSERT_HEAD(&s_rtc_isr_handler_list, item, next);
portEXIT_CRITICAL(&s_rtc_isr_handler_list_lock);
return ESP_OK;
}
esp_err_t rtc_isr_deregister(intr_handler_t handler, void* handler_arg)
{
rtc_isr_handler_t* it;
rtc_isr_handler_t* prev = NULL;
bool found = false;
portENTER_CRITICAL(&s_rtc_isr_handler_list_lock);
SLIST_FOREACH(it, &s_rtc_isr_handler_list, next) {
if (it->handler == handler && it->handler_arg == handler_arg) {
if (it == SLIST_FIRST(&s_rtc_isr_handler_list)) {
SLIST_REMOVE_HEAD(&s_rtc_isr_handler_list, next);
} else {
SLIST_REMOVE_AFTER(prev, next);
}
found = true;
break;
}
prev = it;
}
portEXIT_CRITICAL(&s_rtc_isr_handler_list_lock);
return found ? ESP_OK : ESP_ERR_INVALID_STATE;
}

View File

@@ -407,7 +407,6 @@ config TASK_WDT_CHECK_IDLE_TASK_CPU1
config BROWNOUT_DET config BROWNOUT_DET
bool "Hardware brownout detect & reset" bool "Hardware brownout detect & reset"
default y default y
depends on NEEDS_ESP32_NEW_SILICON_REV
help help
The ESP32 has a built-in brownout detector which can detect if the voltage is lower than 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 a specific value. If this happens, it will reset the chip in order to prevent unintended
@@ -452,16 +451,6 @@ config BROWNOUT_DET_LVL
default 7 if BROWNOUT_DET_LVL_SEL_7 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.
choice ESP32_TIME_SYSCALL choice ESP32_TIME_SYSCALL
prompt "Timers used for gettimeofday function" prompt "Timers used for gettimeofday function"
default ESP32_TIME_SYSCALL_USE_RTC_FRC1 default ESP32_TIME_SYSCALL_USE_RTC_FRC1

View File

@@ -0,0 +1,61 @@
// Copyright 2015-2017 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/cpu.h"
#include "soc/rtc_cntl_reg.h"
#include "rom/ets_sys.h"
#include "esp_system.h"
#include "driver/rtc_cntl.h"
#include "freertos/FreeRTOS.h"
#ifdef CONFIG_BROWNOUT_DET_LVL
#define BROWNOUT_DET_LVL CONFIG_BROWNOUT_DET_LVL
#else
#define BROWNOUT_DET_LVL 0
#endif //CONFIG_BROWNOUT_DET_LVL
static void rtc_brownout_isr_handler()
{
/* Normally RTC ISR clears the interrupt flag after the application-supplied
* handler returns. Since restart is called here, the flag needs to be
* cleared manually.
*/
REG_WRITE(RTC_CNTL_INT_CLR_REG, RTC_CNTL_BROWN_OUT_INT_CLR);
/* Stall the other CPU to make sure the code running there doesn't use UART
* at the same time as the following ets_printf.
*/
esp_cpu_stall(!xPortGetCoreID());
ets_printf("\r\nBrownout detector was triggered\r\n\r\n");
esp_restart_noos();
}
void esp_brownout_init()
{
REG_WRITE(RTC_CNTL_BROWN_OUT_REG,
RTC_CNTL_BROWN_OUT_ENA /* Enable BOD */
| RTC_CNTL_BROWN_OUT_PD_RF_ENA /* Automatically power down RF */
/* Reset timeout must be set to >1 even if BOR feature is not used */
| (2 << RTC_CNTL_BROWN_OUT_RST_WAIT_S)
| (BROWNOUT_DET_LVL << RTC_CNTL_DBROWN_OUT_THRES_S));
ESP_ERROR_CHECK( rtc_isr_register(rtc_brownout_isr_handler, NULL, RTC_CNTL_BROWN_OUT_INT_ENA_M) );
REG_SET_BIT(RTC_CNTL_INT_ENA_REG, RTC_CNTL_BROWN_OUT_INT_ENA_M);
}

View File

@@ -59,6 +59,17 @@ void system_restore(void) __attribute__ ((deprecated));
*/ */
void esp_restart(void) __attribute__ ((noreturn)); void esp_restart(void) __attribute__ ((noreturn));
/**
* @brief Internal function to restart PRO and APP CPUs.
*
* @note This function should not be called from FreeRTOS applications.
* Use esp_restart instead.
*
* This is an internal function called by esp_restart. It is called directly
* by the panic handler and brownout detector interrupt.
*/
void esp_restart_noos() __attribute__ ((noreturn));
/** /**
* @brief Restart system. * @brief Restart system.
* *

View File

@@ -39,6 +39,7 @@
#include "esp_spi_flash.h" #include "esp_spi_flash.h"
#include "esp_cache_err_int.h" #include "esp_cache_err_int.h"
#include "esp_app_trace.h" #include "esp_app_trace.h"
#include "esp_system.h"
#if CONFIG_SYSVIEW_ENABLE #if CONFIG_SYSVIEW_ENABLE
#include "SEGGER_RTT.h" #include "SEGGER_RTT.h"
#endif #endif
@@ -409,8 +410,6 @@ static void doBacktrace(XtExcFrame *frame)
panicPutStr("\r\n\r\n"); panicPutStr("\r\n\r\n");
} }
void esp_restart_noos() __attribute__ ((noreturn));
/* /*
We arrive here after a panic or unhandled exception, when no OCD is detected. Dump the registers to the We arrive here after a panic or unhandled exception, when no OCD is detected. Dump the registers to the
serial port and either jump to the gdb stub, halt the CPU or reboot. serial port and either jump to the gdb stub, halt the CPU or reboot.

View File

@@ -265,13 +265,15 @@ void IRAM_ATTR esp_restart_noos()
esp_dport_access_int_deinit(); esp_dport_access_int_deinit();
// We need to disable TG0/TG1 watchdogs // We need to disable TG0/TG1 watchdogs
// First enable RTC watchdog to be on the safe side // First enable RTC watchdog for 1 second
REG_WRITE(RTC_CNTL_WDTWPROTECT_REG, RTC_CNTL_WDT_WKEY_VALUE); REG_WRITE(RTC_CNTL_WDTWPROTECT_REG, RTC_CNTL_WDT_WKEY_VALUE);
REG_WRITE(RTC_CNTL_WDTCONFIG0_REG, REG_WRITE(RTC_CNTL_WDTCONFIG0_REG,
RTC_CNTL_WDT_FLASHBOOT_MOD_EN_M | RTC_CNTL_WDT_FLASHBOOT_MOD_EN_M |
(RTC_WDT_STG_SEL_RESET_SYSTEM << RTC_CNTL_WDT_STG0_S) |
(RTC_WDT_STG_SEL_RESET_RTC << RTC_CNTL_WDT_STG1_S) |
(1 << RTC_CNTL_WDT_SYS_RESET_LENGTH_S) | (1 << RTC_CNTL_WDT_SYS_RESET_LENGTH_S) |
(1 << RTC_CNTL_WDT_CPU_RESET_LENGTH_S) ); (1 << RTC_CNTL_WDT_CPU_RESET_LENGTH_S) );
REG_WRITE(RTC_CNTL_WDTCONFIG1_REG, 128000); REG_WRITE(RTC_CNTL_WDTCONFIG1_REG, rtc_clk_slow_freq_get_hz() * 1);
// Disable TG0/TG1 watchdogs // Disable TG0/TG1 watchdogs
TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;

View File

@@ -1,39 +0,0 @@
// 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"
#if CONFIG_BROWNOUT_DET
/*
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.
*/
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);
}
#endif

View File

@@ -1718,6 +1718,7 @@
#define RTC_WDT_STG_SEL_INT 1 #define RTC_WDT_STG_SEL_INT 1
#define RTC_WDT_STG_SEL_RESET_CPU 2 #define RTC_WDT_STG_SEL_RESET_CPU 2
#define RTC_WDT_STG_SEL_RESET_SYSTEM 3 #define RTC_WDT_STG_SEL_RESET_SYSTEM 3
#define RTC_WDT_STG_SEL_RESET_RTC 4
#define RTC_CNTL_WDTCONFIG1_REG (DR_REG_RTCCNTL_BASE + 0x90) #define RTC_CNTL_WDTCONFIG1_REG (DR_REG_RTCCNTL_BASE + 0x90)
/* RTC_CNTL_WDT_STG0_HOLD : R/W ;bitpos:[31:0] ;default: 32'd128000 ; */ /* RTC_CNTL_WDT_STG0_HOLD : R/W ;bitpos:[31:0] ;default: 32'd128000 ; */