【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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章