天天看點

【IoT】ESP32 Arduino 超低功耗模式 Deep-sleep

背景:

低功耗是對 IoT 産品的最基本要求,也是一款好産品走向市場的基礎,功耗評估顯得尤為重要。

一、基礎資源簡析

ESP32 支援 Deep-sleep 低功耗模式,通過配置 RTC 外設和 ULP 協處理器的工作模式,可以滿足多種應用場景下的低功耗需求。

在 Deep-sleep 模式時,所有由 APB_CLK 驅動的外設、CPU 和 RAM 将掉電,RTC_CLK 繼續工作;

RTC 控制器、RTC 外設、ULP 協處理器、RTC 快速記憶體和 RTC 慢速記憶體可以不掉電,具體取決于應用程式中的喚醒源設定。

硬體資源:

RTC 外設     – 片上溫度傳感器、ADC、RTC GPIO 和 touchpad

ULP 協處理器 – 可在 Deep-sleep 模式下,進行簡單的資料采集或作為一種喚醒源,協處理器可以通路 RTC 慢速記憶體和 RTC 寄存器

RTC 快速記憶體 – 晶片從 Deep-sleep 模式下喚醒後不會馬上執行 bootloader,而是會先執行存放在 RTC 快速記憶體中的 esp_wake_deep_sleep() 函數

RTC 慢速記憶體 – 存放 ULP 協處理器和 wake stub 代碼通路的資料

Deep-sleep 模式下支援的喚醒源包括:

1、定時器

2、touchpad

3、Ext(0):RTC IO 中某個指定 GPIO 滿足指定電平即喚醒

4、Ext(1):RTC IO 中某些指定 GPIO 同時滿足指定電平即喚醒

5、ULP 協處理器

二、示例

1、定時器喚醒:6uA 左右

調用 esp_deep_sleep_enable_timer_wakeup(sleep_time_us) 函數,設定 Deep-sleep 時間

調用 esp_deep_sleep_start() 函數,進入 Deep-sleep 模式

此時需要周期性喚醒 ESP32,不能充分利用 ESP32 的低功耗性能,但可以進行複雜的傳感器資料采集

/*
Simple Deep Sleep with Timer Wake Up
=====================================
ESP32 offers a deep sleep mode for effective power
saving as power is an important factor for IoT
applications. In this mode CPUs, most of the RAM,
and all the digital peripherals which are clocked
from APB_CLK are powered off. The only parts of
the chip which can still be powered on are:
RTC controller, RTC peripherals ,and RTC memories

This code displays the most basic deep sleep with
a timer to wake it up and how to store data in
RTC memory to use it over reboots

This code is under Public Domain License.

Author:
Pranav Cherukupalli <[email protected]>
*/

#define uS_TO_S_FACTOR 1000000  /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP  5        /* Time ESP32 will go to sleep (in seconds) */

RTC_DATA_ATTR int bootCount = 0;

/*
Method to print the reason by which ESP32
has been awaken from sleep
*/
void print_wakeup_reason(){
  esp_deep_sleep_wakeup_cause_t wakeup_reason;

  wakeup_reason = esp_deep_sleep_get_wakeup_cause();

  switch(wakeup_reason)
  {
    case 1  : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
    case 2  : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
    case 3  : Serial.println("Wakeup caused by timer"); break;
    case 4  : Serial.println("Wakeup caused by touchpad"); break;
    case 5  : Serial.println("Wakeup caused by ULP program"); break;
    default : Serial.println("Wakeup was not caused by deep sleep"); break;
  }
}

void setup(){
  Serial.begin(115200);
  delay(1000); //Take some time to open up the Serial Monitor

  //Increment boot number and print it every reboot
  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));

  //Print the wakeup reason for ESP32
  print_wakeup_reason();

  /*
  First we configure the wake up source
  We set our ESP32 to wake up every 5 seconds
  */
  esp_deep_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR*2);
  Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) +
  " Seconds");

  /*
  Next we decide what all peripherals to shut down/keep on
  By default, ESP32 will automatically power down the peripherals
  not needed by the wakeup source, but if you want to be a poweruser
  this is for you. Read in detail at the API docs
  http://esp-idf.readthedocs.io/en/latest/api-reference/system/deep_sleep.html
  Left the line commented as an example of how to configure peripherals.
  The line below turns off all RTC peripherals in deep sleep.
  */
  //esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF);
  //Serial.println("Configured all RTC Peripherals to be powered down in sleep");

  /*
  Now that we have setup a wake cause and if needed setup the
  peripherals state in deep sleep, we can now start going to
  deep sleep.
  In the case that no wake up sources were provided but deep
  sleep was started, it will sleep forever unless hardware
  reset occurs.
  */
  Serial.println("Going to sleep now");
  esp_deep_sleep_start();
  Serial.println("This will never be printed");
}

void loop(){
  //This is not going to be called
}
           

2、Touchpad:36uA 左右

設定作為喚醒源的 touchpad

調用 esp_deep_sleep_enable_touchpad_wakeup() 函數使能 touchpad 喚醒,然後調用 esp_deep_sleep_start() 函數進入 Deep-sleep 模式

/*
Deep Sleep with Touch Wake Up
=====================================
This code displays how to use deep sleep with
a touch as a wake up source and how to store data in
RTC memory to use it over reboots

This code is under Public Domain License.

Author:
Pranav Cherukupalli <[email protected]>
*/

#define Threshold 40 /* Greater the value, more the sensitivity */

RTC_DATA_ATTR int bootCount = 0;
touch_pad_t touchPin;
/*
Method to print the reason by which ESP32
has been awaken from sleep
*/
void print_wakeup_reason(){
  esp_deep_sleep_wakeup_cause_t wakeup_reason;

  wakeup_reason = esp_deep_sleep_get_wakeup_cause();

  switch(wakeup_reason)
  {
    case 1  : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
    case 2  : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
    case 3  : Serial.println("Wakeup caused by timer"); break;
    case 4  : Serial.println("Wakeup caused by touchpad"); break;
    case 5  : Serial.println("Wakeup caused by ULP program"); break;
    default : Serial.println("Wakeup was not caused by deep sleep"); break;
  }
}

/*
Method to print the touchpad by which ESP32
has been awaken from sleep
*/
void print_wakeup_touchpad(){
  touch_pad_t pin;

  touchPin = esp_deep_sleep_get_touchpad_wakeup_status();

  switch(touchPin)
  {
    case 0  : Serial.println("Touch detected on GPIO 4"); break;
    case 1  : Serial.println("Touch detected on GPIO 0"); break;
    case 2  : Serial.println("Touch detected on GPIO 2"); break;
    case 3  : Serial.println("Touch detected on GPIO 15"); break;
    case 4  : Serial.println("Touch detected on GPIO 13"); break;
    case 5  : Serial.println("Touch detected on GPIO 12"); break;
    case 6  : Serial.println("Touch detected on GPIO 14"); break;
    case 7  : Serial.println("Touch detected on GPIO 27"); break;
    case 8  : Serial.println("Touch detected on GPIO 33"); break;
    case 9  : Serial.println("Touch detected on GPIO 32"); break;
    default : Serial.println("Wakeup not by touchpad"); break;
  }
}

void callback(){
  //placeholder callback function
}

void setup(){
  Serial.begin(115200);
  delay(1000); //Take some time to open up the Serial Monitor

  //Increment boot number and print it every reboot
  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));

  //Print the wakeup reason for ESP32 and touchpad too
  print_wakeup_reason();
  print_wakeup_touchpad();

  //Setup interrupt on Touch Pad 3 (GPIO15)
  touchAttachInterrupt(T3, callback, Threshold);

  //Configure Touchpad as wakeup source
  esp_deep_sleep_enable_touchpad_wakeup();

  //Go to sleep now
  Serial.println("Going to sleep now");
  esp_deep_sleep_start();
  Serial.println("This will never be printed");
}

void loop(){
  //This will never be reached
}
           

3、GPIO 喚醒:6uA 左右

調用 rtc_gpio_pulldown_en(MY_RTC_WAKEUP_IO) 函數或 rtc_gpio_pullup_en(MY_RTC_WAKEUP_IO) 函數,設定内部下拉或上拉類型

調用 esp_deep_sleep_enable_ext0_wakeup(MY_RTC_WAKEUP_IO, WAKEUP_IO_LEVEL) 函數或 esp_deep_sleep_enable_ext1_wakeup(WAKEUP_PIN_MASK, WAKEUP_TYPE) 函數,設定從 Deep-sleep 模式下喚醒的 RTC GPIO 電壓條件

調用 esp_deep_sleep_start() 函數進入 Deep-sleep 模式

/*
Deep Sleep with External Wake Up
=====================================
This code displays how to use deep sleep with
an external trigger as a wake up source and how
to store data in RTC memory to use it over reboots

This code is under Public Domain License.

Hardware Connections
======================
Push Button to GPIO 33 pulled down with a 10K Ohm
resistor

NOTE:
======
Only RTC IO can be used as a source for external wake
source. They are pins: 0,2,4,12-15,25-27,32-39.

Author:
Pranav Cherukupalli <[email protected]>
*/

#define BUTTON_PIN_BITMASK 0x200000000 // 2^33 in hex

RTC_DATA_ATTR int bootCount = 0;

/*
Method to print the reason by which ESP32
has been awaken from sleep
*/
void print_wakeup_reason(){
  esp_sleep_wakeup_cause_t wakeup_reason;

  wakeup_reason = esp_sleep_get_wakeup_cause();

  switch(wakeup_reason)
  {
    case 1  : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
    case 2  : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
    case 3  : Serial.println("Wakeup caused by timer"); break;
    case 4  : Serial.println("Wakeup caused by touchpad"); break;
    case 5  : Serial.println("Wakeup caused by ULP program"); break;
    default : Serial.println("Wakeup was not caused by deep sleep"); break;
  }
}

void setup(){
  Serial.begin(115200);
  delay(1000); //Take some time to open up the Serial Monitor

  //Increment boot number and print it every reboot
  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));

  Serial.printf("Boot number: %d ", bootCount);

  //Print the wakeup reason for ESP32
  print_wakeup_reason();

  /*
  First we configure the wake up source
  We set our ESP32 to wake up for an external trigger.
  There are two types for ESP32, ext0 and ext1 .
  ext0 uses RTC_IO to wakeup thus requires RTC peripherals
  to be on while ext1 uses RTC Controller so doesnt need
  peripherals to be powered on.
  Note that using internal pullups/pulldowns also requires
  RTC peripherals to be turned on.
  */
  esp_sleep_enable_ext0_wakeup(GPIO_NUM_33,1); //1 = High, 0 = Low

  //If you were to use ext1, you would use it like
  //esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);

  //Go to sleep now
  Serial.println("Going to sleep now");
  esp_deep_sleep_start();
  Serial.println("This will never be printed");
}

void loop(){
  //This is not going to be called
}
           

4、ULP 協處理器

使用者根據 ULP 指令集,自行編寫需要 ULP 協處理器在 Deep-sleep 模式下執行的彙編代碼,完整流程如下:

晶片 boot 啟動後,從 RTC_SLOW_MEMORY 讀取晶片在 Deep-sleep 模式期間 ULP 協處理器采集的資料,并上傳資料

調用 ulp_process_macros_and_load() 函數,将彙程式設計式代碼拷貝至 RTC_SLOW_MEMORY

調用 ulp_run(ADDRESS) 函數啟動 ULP 協處理器,執行 RTC_SLOW_MEMORY 中的代碼

調用 esp_deep_sleep_start() 函數,進入 Deep-sleep 模式

為了便于使用者使用 ULP 協處理器進行資料采集與存儲,IoT Solution 中增加了 ulp_monitor 子產品,可直接調用 C 函數運作協處理器

ulp_monitor 子產品的使用流程如下:

晶片 boot 啟動後,從 RTC_SLOW_MEMORY 讀取 ULP 協處理器在晶片 Deep-sleep 模式期間采集的資料,并上傳資料

調用 ulp_monitor_init(ULP_PROGRAM_ADDR, ULP_DATA_ADDR) 函數,設定 ULP 協處理器的程式運作位址與資料儲存位址

調用 ulp_add_adc_monitor 函數或 ulp_add_temprature_monitor 函數,添加 ULP 協處理器采集的資料類型和喚醒條件(可同時添加)

調用 ulp_monitor_start 函數設定測量頻率,并啟動 ULP 協處理器

調用 esp_deep_sleep_start() 函數,進入 Deep-sleep 模式。目前,ULP 協處理隻支援片上溫度傳感器和 ADC 資料的采集

該方式可以在低功耗情況下頻繁地采集資料,進而降低對傳感器的要求

refer:

https://blog.csdn.net/espressif/article/details/79360365

繼續閱讀