“掉电检测器已触发”的异常处理

问题描述 投票:0回答:4

我使用 ESP-IDF 作为框架。

我知道

Brownout detector was trigerred
错误来自检测低电压发生的低电压检测器。通常当该错误发生时,MCU 会自动重启。

是的,可以设置检测器,但是我可以在软件中处理该错误吗?就像

esp-idf
如何使用约定
esp_err_t
处理错误一样?所以我可以继续在我的 MCU 中运行,而不会因此类错误而重新启动。

我的意思是处理就像如何使用

try-catch
概念进行高级编程。

esp32
4个回答
2
投票

发生此错误后CPU会复位。可能有一种方法可以在CPU重新启动时找出复位的原因。与 STM32 MCU 一样,可以读取 RCC(复位和时钟控制器)寄存器。在我的研究过程中,我找到了一个可以与 ESP32 一起使用的解决方案。

#include <rom/rtc.h>

void print_reset_reason(RESET_REASON reason)
{
  switch (reason)
  {
    /**<1, Vbat power on reset*/
    case 1 : Serial.println ("POWERON_RESET");break;          
    
    /**<3, Software reset digital core*/
    case 3 : Serial.println ("SW_RESET");break;               
    
    /**<4, Legacy watch dog reset digital core*/
    case 4 : Serial.println ("OWDT_RESET");break;             
    
    /**<5, Deep Sleep reset digital core*/
    case 5 : Serial.println ("DEEPSLEEP_RESET");break;        
    
    /**<6, Reset by SLC module, reset digital core*/
    case 6 : Serial.println ("SDIO_RESET");break;             
    
    /**<7, Timer Group0 Watch dog reset digital core*/
    case 7 : Serial.println ("TG0WDT_SYS_RESET");break;       
    
    /**<8, Timer Group1 Watch dog reset digital core*/
    case 8 : Serial.println ("TG1WDT_SYS_RESET");break;       
    
    /**<9, RTC Watch dog Reset digital core*/
    case 9 : Serial.println ("RTCWDT_SYS_RESET");break;       
    
    /**<10, Instrusion tested to reset CPU*/
    case 10 : Serial.println ("INTRUSION_RESET");break;       
    
    /**<11, Time Group reset CPU*/
    case 11 : Serial.println ("TGWDT_CPU_RESET");break;       
    
    /**<12, Software reset CPU*/
    case 12 : Serial.println ("SW_CPU_RESET");break;          
    
    /**<13, RTC Watch dog Reset CPU*/
    case 13 : Serial.println ("RTCWDT_CPU_RESET");break;      
    
    /**<14, for APP CPU, reseted by PRO CPU*/
    case 14 : Serial.println ("EXT_CPU_RESET");break;         
    
    /**<15, Reset when the vdd voltage is not stable*/
    case 15 : Serial.println ("RTCWDT_BROWN_OUT_RESET");break;
    
    /**<16, RTC Watch dog reset digital core and rtc module*/
    case 16 : Serial.println ("RTCWDT_RTC_RESET");break;      
    
    default : Serial.println ("NO_MEAN");
  }
}

void setup() {
  Serial.begin(115200);
  delay(2000);

  Serial.println("CPU0 reset reason: ");
  print_reset_reason(rtc_get_reset_reason(0));

  Serial.println("CPU1 reset reason: ");
  print_reset_reason(rtc_get_reset_reason(1));
}

void loop() {}

相关链接


2
投票

尝试“赶上”限电是没有任何意义的。

当欠压检测触发时,意味着 ESP32 没有获得足够的电力来可靠运行。如果它无法可靠地运行,则尝试捕获表明该情况的异常是没有帮助的,因为异常处理程序也无法可靠地运行。

如果您遇到此问题,有一个解决方法,那就是为您的 ESP32 以及您连接的任何电路提供足够的电源。就是这样,这就是你所做的。这意味着计算出整个项目消耗了多少电流,并使用额定电流超过该电流的电源。如果您使用的是“壁式”交流/直流适配器,请使用额定电流大得多的适配器,因为其中许多适配器无法提供其承诺的功能。


1
投票

https://www.esp32.com/viewtopic.php?t=6855 表示查看 Components/esp_system/port/brownout.c 了解如何捕获中断。设置 2.8V 高阈值,这样您就有一点时间。默认值为 2.43V 低阈值,通常没有时间打印整个约 40 个字符的消息。如果您想保存某些内容,请预擦除闪存。

使用更大的电源电容——100uF可能太小了。

此用途的一个用例可能看起来像 GPS,如果可以的话,它希望保存一些热启动数据,但又不想因为一直保存这些数据而磨损闪光灯。如果数据丢失或损坏,恢复过程只是需要更长的时间。

反模式:不要依赖掉电检测器将系统置于安全状态。随着时间的推移,那些大电容器会变得越来越小。

编辑:实际测试,4700uf 不能提供可靠的 1/30 秒,但 2 X 4700uF 可以。 (只是一个 wroom32,没有别的。)我认为 10,000uF 对于我的墙疣启动来说是一个沉重的负担。

0 idle0=12954   idle1=25465   FPS=29.9967  sampleRate=    306822.9
0 idle0=12954   idle1=25465   FPS=30.0003  sampleRate=    306836.3

2.80V Brownout...warning...
1 idle0=14692   idle1=23489   FPS=30.0012  sampleRate=    306848.5
1 idle0=14692   idle1=23489   FPS=29.9976  sampleRate=    306859.1

2.43V Brownout...restart...
ets Jul 29 2019 12:21:46

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:6664
load:0x40078000,len:14848
load:0x40080400,len:3792
0x40080400: _init at ??:?

entry 0x40080694
I (27) boot: ESP-IDF v4.4.3-dirty 2nd stage bootloader
I (27) boot: compile time 07:37:04
I (27) boot: chip revision: 3
I (30) boot_comm: chip revision: 3, min. bootload�

2 X 4700uF 使 CPU 持续重启。

这是快速而肮脏的测试代码;它需要一些思考,如果出现暂时的停电但不会触发重置,会发生什么,并且可能需要更多地了解如何从中断返回。在我在 ISR 中设置较低电压之前,它立即重新触发。调用代码可以在30 FPS循环中测试静态计数器;其上方是行首的 0 或 1。

// modified from components/esp_system/port/brownout.c

// 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 "esp_private/system_internal.h"
#include "driver/rtc_cntl.h"

#include "esp_rom_sys.h"

#include "soc/soc.h"
#include "soc/cpu.h"
#include "soc/rtc_periph.h"
#include "hal/cpu_hal.h"

#include "hal/brownout_hal.h"

#include "sdkconfig.h"

#if defined(CONFIG_ESP32_BROWNOUT_DET_LVL)
#define BROWNOUT_DET_LVL CONFIG_ESP32_BROWNOUT_DET_LVL
#elif defined(CONFIG_ESP32S2_BROWNOUT_DET_LVL)
#define BROWNOUT_DET_LVL CONFIG_ESP32S2_BROWNOUT_DET_LVL
#elif defined(CONFIG_ESP32S3_BROWNOUT_DET_LVL)
#define BROWNOUT_DET_LVL CONFIG_ESP32S3_BROWNOUT_DET_LVL
#elif defined(CONFIG_ESP32C3_BROWNOUT_DET_LVL)
#define BROWNOUT_DET_LVL CONFIG_ESP32C3_BROWNOUT_DET_LVL
#elif defined(CONFIG_ESP32H2_BROWNOUT_DET_LVL)
#define BROWNOUT_DET_LVL CONFIG_ESP32H2_BROWNOUT_DET_LVL
#else
#define BROWNOUT_DET_LVL 0
#endif

#if SOC_BROWNOUT_RESET_SUPPORTED
#define BROWNOUT_RESET_EN true
#else
#define BROWNOUT_RESET_EN false
#endif // SOC_BROWNOUT_RESET_SUPPORTED

int sBrownOut = 0;
extern void esp_brownout_disable(void);
extern void my_esp_brownout_disable(void);

#ifndef SOC_BROWNOUT_RESET_SUPPORTED
static void my_rtc_brownout_isr_handler(void *arg)
{
    sBrownOut++;
    brownout_hal_intr_clear();
    // change to level 0...prevents immediate retrigger...
    brownout_hal_config_t cfg = {
        .threshold = 0,//BROWNOUT_DET_LVL, 
        .enabled = true,
        .reset_enabled = BROWNOUT_RESET_EN,
        .flash_power_down = true,
        .rf_power_down = true,
    };

    brownout_hal_config(&cfg);
    if(sBrownOut==1){
        esp_rom_printf("\r\n2.80V Brownout...warning...\r\n");
    }
    else{
        esp_cpu_stall(!cpu_hal_get_core_id());
        esp_reset_reason_set_hint(ESP_RST_BROWNOUT);
        esp_rom_printf("\r\n2.43V Brownout...restart...\r\n");
        esp_restart_noos();
    }
    return; // dead code follows...

    /* 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.
     */
    brownout_hal_intr_clear();
    /* Stall the other CPU to make sure the code running there doesn't use UART
     * at the same time as the following esp_rom_printf.
     */
    esp_cpu_stall(!cpu_hal_get_core_id());
    esp_reset_reason_set_hint(ESP_RST_BROWNOUT);
    esp_rom_printf("\r\n***Brownout detector was triggered\r\n\r\n");
    esp_restart_noos();
}
#endif // not SOC_BROWNOUT_RESET_SUPPORTED

void my_esp_brownout_init(void)
{
    esp_brownout_disable();
    brownout_hal_config_t cfg = {
        .threshold = 7,//BROWNOUT_DET_LVL, // level 7 is the highest voltage, earliest possible warning, most time left...
        .enabled = true,
        .reset_enabled = BROWNOUT_RESET_EN,
        .flash_power_down = false, // if this does what it says,
        .rf_power_down = false, // probably don't want it first time
    };

    brownout_hal_config(&cfg);


#ifndef SOC_BROWNOUT_RESET_SUPPORTED
    rtc_isr_register(my_rtc_brownout_isr_handler, NULL, RTC_CNTL_BROWN_OUT_INT_ENA_M);

    brownout_hal_intr_enable(true);
#endif // not SOC_BROWNOUT_RESET_SUPPORTED
}

void my_esp_brownout_disable(void)
{
    brownout_hal_config_t cfg = {
        .enabled = false,
    };

    brownout_hal_config(&cfg);

#ifndef SOC_BROWNOUT_RESET_SUPPORTED
    brownout_hal_intr_enable(false);

    rtc_isr_deregister(my_rtc_brownout_isr_handler, NULL);
#endif // not SOC_BROWNOUT_RESET_SUPPORTED
}

0
投票

我遇到了同样的问题,然后发现问题是由于焊接不良造成的。

© www.soinside.com 2019 - 2024. All rights reserved.