我使用 ESP-IDF 作为框架。
我知道
Brownout detector was trigerred
错误来自检测低电压发生的低电压检测器。通常当该错误发生时,MCU 会自动重启。
是的,可以设置检测器,但是我可以在软件中处理该错误吗?就像
esp-idf
如何使用约定esp_err_t
处理错误一样?所以我可以继续在我的 MCU 中运行,而不会因此类错误而重新启动。
我的意思是处理就像如何使用
try-catch
概念进行高级编程。
发生此错误后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() {}
尝试“赶上”限电是没有任何意义的。
当欠压检测触发时,意味着 ESP32 没有获得足够的电力来可靠运行。如果它无法可靠地运行,则尝试捕获表明该情况的异常是没有帮助的,因为异常处理程序也无法可靠地运行。
如果您遇到此问题,有一个解决方法,那就是为您的 ESP32 以及您连接的任何电路提供足够的电源。就是这样,这就是你所做的。这意味着计算出整个项目消耗了多少电流,并使用额定电流超过该电流的电源。如果您使用的是“壁式”交流/直流适配器,请使用额定电流大得多的适配器,因为其中许多适配器无法提供其承诺的功能。
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
}
我遇到了同样的问题,然后发现问题是由于焊接不良造成的。