Merge branch 'feature/deep_sleep_wakeup_from_touch' into 'master'
add wakeup from touch sensor, and deep sleep example - add new deep sleep wakeup mode - change documentation to explain incompatibilities between different wakeup mode, add error checks - add new ULP instructions necessary for ULP wakeup scenario - fix issues with I_WR_REG, I_SLEEP, I_END instructions - add deep sleep example, illustrating the use of timer, gpio, touch, and ULP wakeup triggers See merge request !461
This commit is contained in:
commit
66c693eebb
15 changed files with 909 additions and 29 deletions
|
@ -126,6 +126,9 @@ void IRAM_ATTR esp_deep_sleep_start()
|
||||||
if (s_config.wakeup_triggers & EXT_EVENT1_TRIG_EN) {
|
if (s_config.wakeup_triggers & EXT_EVENT1_TRIG_EN) {
|
||||||
ext1_wakeup_prepare();
|
ext1_wakeup_prepare();
|
||||||
}
|
}
|
||||||
|
if (s_config.wakeup_triggers & SAR_TRIG_EN) {
|
||||||
|
SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_WAKEUP_FORCE_EN);
|
||||||
|
}
|
||||||
// TODO: move timer wakeup configuration into a similar function
|
// TODO: move timer wakeup configuration into a similar function
|
||||||
// once rtc_sleep is opensourced.
|
// once rtc_sleep is opensourced.
|
||||||
|
|
||||||
|
@ -163,6 +166,10 @@ void system_deep_sleep(uint64_t) __attribute__((alias("esp_deep_sleep")));
|
||||||
esp_err_t esp_deep_sleep_enable_ulp_wakeup()
|
esp_err_t esp_deep_sleep_enable_ulp_wakeup()
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_ULP_COPROC_ENABLED
|
#ifdef CONFIG_ULP_COPROC_ENABLED
|
||||||
|
if(s_config.wakeup_triggers & RTC_EXT_EVENT0_TRIG_EN) {
|
||||||
|
ESP_LOGE(TAG, "Conflicting wake-up trigger: ext0");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
s_config.wakeup_triggers |= RTC_SAR_TRIG_EN;
|
s_config.wakeup_triggers |= RTC_SAR_TRIG_EN;
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
#else
|
#else
|
||||||
|
@ -177,6 +184,26 @@ esp_err_t esp_deep_sleep_enable_timer_wakeup(uint64_t time_in_us)
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_deep_sleep_enable_touchpad_wakeup()
|
||||||
|
{
|
||||||
|
if (s_config.wakeup_triggers & (RTC_EXT_EVENT0_TRIG_EN)) {
|
||||||
|
ESP_LOGE(TAG, "Conflicting wake-up trigger: ext0");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
s_config.wakeup_triggers |= RTC_TOUCH_TRIG_EN;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
touch_pad_t esp_deep_sleep_get_touchpad_wakeup_status()
|
||||||
|
{
|
||||||
|
if (esp_deep_sleep_get_wakeup_cause() != ESP_DEEP_SLEEP_WAKEUP_TOUCHPAD) {
|
||||||
|
return TOUCH_PAD_MAX;
|
||||||
|
}
|
||||||
|
uint32_t touch_mask = REG_GET_FIELD(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_MEAS_EN);
|
||||||
|
assert(touch_mask != 0 && "wakeup reason is RTC_TOUCH_TRIG_EN but SENS_TOUCH_MEAS_EN is zero");
|
||||||
|
return (touch_pad_t) (__builtin_ffs(touch_mask) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
esp_err_t esp_deep_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level)
|
esp_err_t esp_deep_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level)
|
||||||
{
|
{
|
||||||
if (level < 0 || level > 1) {
|
if (level < 0 || level > 1) {
|
||||||
|
@ -185,6 +212,10 @@ esp_err_t esp_deep_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level)
|
||||||
if (!RTC_GPIO_IS_VALID_GPIO(gpio_num)) {
|
if (!RTC_GPIO_IS_VALID_GPIO(gpio_num)) {
|
||||||
return ESP_ERR_INVALID_ARG;
|
return ESP_ERR_INVALID_ARG;
|
||||||
}
|
}
|
||||||
|
if (s_config.wakeup_triggers & (RTC_TOUCH_TRIG_EN | RTC_SAR_TRIG_EN)) {
|
||||||
|
ESP_LOGE(TAG, "Conflicting wake-up triggers: touch / ULP");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
s_config.ext0_rtc_gpio_num = rtc_gpio_desc[gpio_num].rtc_num;
|
s_config.ext0_rtc_gpio_num = rtc_gpio_desc[gpio_num].rtc_num;
|
||||||
s_config.ext0_trigger_level = level;
|
s_config.ext0_trigger_level = level;
|
||||||
s_config.wakeup_triggers |= RTC_EXT_EVENT0_TRIG_EN;
|
s_config.wakeup_triggers |= RTC_EXT_EVENT0_TRIG_EN;
|
||||||
|
@ -276,8 +307,7 @@ static void ext1_wakeup_prepare()
|
||||||
|
|
||||||
uint64_t esp_deep_sleep_get_ext1_wakeup_status()
|
uint64_t esp_deep_sleep_get_ext1_wakeup_status()
|
||||||
{
|
{
|
||||||
int wakeup_reason = REG_GET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, RTC_CNTL_WAKEUP_CAUSE);
|
if (esp_deep_sleep_get_wakeup_cause() != ESP_DEEP_SLEEP_WAKEUP_EXT1) {
|
||||||
if (wakeup_reason != RTC_EXT_EVENT1_TRIG) {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
uint32_t status = REG_GET_FIELD(RTC_CNTL_EXT_WAKEUP1_STATUS_REG, RTC_CNTL_EXT_WAKEUP1_STATUS);
|
uint32_t status = REG_GET_FIELD(RTC_CNTL_EXT_WAKEUP1_STATUS_REG, RTC_CNTL_EXT_WAKEUP1_STATUS);
|
||||||
|
@ -296,6 +326,28 @@ uint64_t esp_deep_sleep_get_ext1_wakeup_status()
|
||||||
return gpio_mask;
|
return gpio_mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_deep_sleep_wakeup_cause_t esp_deep_sleep_get_wakeup_cause()
|
||||||
|
{
|
||||||
|
if (rtc_get_reset_reason(0) != DEEPSLEEP_RESET) {
|
||||||
|
return ESP_DEEP_SLEEP_WAKEUP_UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t wakeup_cause = REG_GET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, RTC_CNTL_WAKEUP_CAUSE);
|
||||||
|
if (wakeup_cause & RTC_EXT_EVENT0_TRIG) {
|
||||||
|
return ESP_DEEP_SLEEP_WAKEUP_EXT0;
|
||||||
|
} else if (wakeup_cause & RTC_EXT_EVENT1_TRIG) {
|
||||||
|
return ESP_DEEP_SLEEP_WAKEUP_EXT1;
|
||||||
|
} else if (wakeup_cause & RTC_TIMER_EXPIRE) {
|
||||||
|
return ESP_DEEP_SLEEP_WAKEUP_TIMER;
|
||||||
|
} else if (wakeup_cause & RTC_TOUCH_TRIG) {
|
||||||
|
return ESP_DEEP_SLEEP_WAKEUP_TOUCHPAD;
|
||||||
|
} else if (wakeup_cause & RTC_SAR_TRIG) {
|
||||||
|
return ESP_DEEP_SLEEP_WAKEUP_ULP;
|
||||||
|
} else {
|
||||||
|
return ESP_DEEP_SLEEP_WAKEUP_UNDEFINED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
esp_err_t esp_deep_sleep_pd_config(esp_deep_sleep_pd_domain_t domain,
|
esp_err_t esp_deep_sleep_pd_config(esp_deep_sleep_pd_domain_t domain,
|
||||||
esp_deep_sleep_pd_option_t option)
|
esp_deep_sleep_pd_option_t option)
|
||||||
{
|
{
|
||||||
|
@ -333,13 +385,15 @@ static uint32_t get_power_down_flags()
|
||||||
s_config.pd_options[ESP_PD_DOMAIN_RTC_FAST_MEM] = ESP_PD_OPTION_ON;
|
s_config.pd_options[ESP_PD_DOMAIN_RTC_FAST_MEM] = ESP_PD_OPTION_ON;
|
||||||
}
|
}
|
||||||
|
|
||||||
// RTC_PERIPH is needed for EXT0 wakeup and for ULP.
|
// RTC_PERIPH is needed for EXT0 wakeup.
|
||||||
// If RTC_PERIPH is auto, and both EXT0 and ULP aren't enabled,
|
// If RTC_PERIPH is auto, and EXT0 isn't enabled, power down RTC_PERIPH.
|
||||||
// power down RTC_PERIPH.
|
|
||||||
if (s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] == ESP_PD_OPTION_AUTO) {
|
if (s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] == ESP_PD_OPTION_AUTO) {
|
||||||
if (s_config.wakeup_triggers &
|
if (s_config.wakeup_triggers & RTC_EXT_EVENT0_TRIG_EN) {
|
||||||
(RTC_SAR_TRIG_EN | RTC_EXT_EVENT0_TRIG_EN)) {
|
|
||||||
s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] = ESP_PD_OPTION_ON;
|
s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] = ESP_PD_OPTION_ON;
|
||||||
|
} else if (s_config.wakeup_triggers & (RTC_TOUCH_TRIG_EN | RTC_SAR_TRIG_EN)) {
|
||||||
|
// In both rev. 0 and rev. 1 of ESP32, forcing power up of RTC_PERIPH
|
||||||
|
// prevents ULP timer and touch FSMs from working correctly.
|
||||||
|
s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] = ESP_PD_OPTION_OFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/touch_pad.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -50,12 +51,28 @@ typedef enum {
|
||||||
ESP_PD_OPTION_AUTO //!< Keep power domain enabled in deep sleep, if it is needed by one of the wakeup options. Otherwise power it down.
|
ESP_PD_OPTION_AUTO //!< Keep power domain enabled in deep sleep, if it is needed by one of the wakeup options. Otherwise power it down.
|
||||||
} esp_deep_sleep_pd_option_t;
|
} esp_deep_sleep_pd_option_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Deep sleep wakeup cause
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
ESP_DEEP_SLEEP_WAKEUP_UNDEFINED, //! Wakeup was not caused by deep sleep
|
||||||
|
ESP_DEEP_SLEEP_WAKEUP_EXT0, //! Wakeup caused by external signal using RTC_IO
|
||||||
|
ESP_DEEP_SLEEP_WAKEUP_EXT1, //! Wakeup caused by external signal using RTC_CNTL
|
||||||
|
ESP_DEEP_SLEEP_WAKEUP_TIMER, //! Wakeup caused by timer
|
||||||
|
ESP_DEEP_SLEEP_WAKEUP_TOUCHPAD, //! Wakeup caused by touchpad
|
||||||
|
ESP_DEEP_SLEEP_WAKEUP_ULP, //! Wakeup caused by ULP program
|
||||||
|
} esp_deep_sleep_wakeup_cause_t;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Enable wakeup by ULP coprocessor
|
* @brief Enable wakeup by ULP coprocessor
|
||||||
|
* @note In revisions 0 and 1 of the ESP32, ULP wakeup source
|
||||||
|
* can not be used when RTC_PERIPH power domain is forced
|
||||||
|
* to be powered on (ESP_PD_OPTION_ON) or when ext0 wakeup
|
||||||
|
* source is used.
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK on success
|
* - ESP_OK on success
|
||||||
* - ESP_ERR_INVALID_STATE if ULP co-processor is not enabled.
|
* - ESP_ERR_INVALID_STATE if ULP co-processor is not enabled or if wakeup triggers conflict
|
||||||
*/
|
*/
|
||||||
esp_err_t esp_deep_sleep_enable_ulp_wakeup();
|
esp_err_t esp_deep_sleep_enable_ulp_wakeup();
|
||||||
|
|
||||||
|
@ -68,6 +85,29 @@ esp_err_t esp_deep_sleep_enable_ulp_wakeup();
|
||||||
*/
|
*/
|
||||||
esp_err_t esp_deep_sleep_enable_timer_wakeup(uint64_t time_in_us);
|
esp_err_t esp_deep_sleep_enable_timer_wakeup(uint64_t time_in_us);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enable wakeup by touch sensor
|
||||||
|
*
|
||||||
|
* @note In revisions 0 and 1 of the ESP32, touch wakeup source
|
||||||
|
* can not be used when RTC_PERIPH power domain is forced
|
||||||
|
* to be powered on (ESP_PD_OPTION_ON) or when ext0 wakeup
|
||||||
|
* source is used.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_INVALID_STATE if wakeup triggers conflict
|
||||||
|
*/
|
||||||
|
esp_err_t esp_deep_sleep_enable_touchpad_wakeup();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the touch pad which caused wakeup
|
||||||
|
*
|
||||||
|
* If wakeup was caused by another source, this function will return TOUCH_PAD_MAX;
|
||||||
|
*
|
||||||
|
* @return touch pad which caused wakeup
|
||||||
|
*/
|
||||||
|
touch_pad_t esp_deep_sleep_get_touchpad_wakeup_status();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Enable wakeup using a pin
|
* @brief Enable wakeup using a pin
|
||||||
*
|
*
|
||||||
|
@ -81,6 +121,9 @@ esp_err_t esp_deep_sleep_enable_timer_wakeup(uint64_t time_in_us);
|
||||||
* configured in esp_deep_sleep_start, immediately before
|
* configured in esp_deep_sleep_start, immediately before
|
||||||
* entering deep sleep.
|
* entering deep sleep.
|
||||||
*
|
*
|
||||||
|
* @note In revisions 0 and 1 of the ESP32, ext0 wakeup source
|
||||||
|
* can not be used together with touch or ULP wakeup sources.
|
||||||
|
*
|
||||||
* @param gpio_num GPIO number used as wakeup source. Only GPIOs which are have RTC
|
* @param gpio_num GPIO number used as wakeup source. Only GPIOs which are have RTC
|
||||||
* functionality can be used: 0,2,4,12-15,25-27,32-39.
|
* functionality can be used: 0,2,4,12-15,25-27,32-39.
|
||||||
* @param level input level which will trigger wakeup (0=low, 1=high)
|
* @param level input level which will trigger wakeup (0=low, 1=high)
|
||||||
|
@ -88,6 +131,7 @@ esp_err_t esp_deep_sleep_enable_timer_wakeup(uint64_t time_in_us);
|
||||||
* - ESP_OK on success
|
* - ESP_OK on success
|
||||||
* - ESP_ERR_INVALID_ARG if the selected GPIO is not an RTC GPIO,
|
* - ESP_ERR_INVALID_ARG if the selected GPIO is not an RTC GPIO,
|
||||||
* or the mode is invalid
|
* or the mode is invalid
|
||||||
|
* - ESP_ERR_INVALID_STATE if wakeup triggers conflict
|
||||||
*/
|
*/
|
||||||
esp_err_t esp_deep_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level);
|
esp_err_t esp_deep_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level);
|
||||||
|
|
||||||
|
@ -188,6 +232,15 @@ void esp_deep_sleep(uint64_t time_in_us) __attribute__((noreturn));
|
||||||
*/
|
*/
|
||||||
void system_deep_sleep(uint64_t time_in_us) __attribute__((noreturn, deprecated));
|
void system_deep_sleep(uint64_t time_in_us) __attribute__((noreturn, deprecated));
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the source which caused deep sleep wakeup
|
||||||
|
*
|
||||||
|
* @return wakeup cause, or ESP_DEEP_SLEEP_WAKEUP_UNDEFINED if reset reason is other than deep sleep reset.
|
||||||
|
*/
|
||||||
|
esp_deep_sleep_wakeup_cause_t esp_deep_sleep_get_wakeup_cause();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Default stub to run on wake from deep sleep.
|
* @brief Default stub to run on wake from deep sleep.
|
||||||
*
|
*
|
||||||
|
|
|
@ -240,7 +240,7 @@
|
||||||
#define RTC_CNTL_TIME_VALID_S 30
|
#define RTC_CNTL_TIME_VALID_S 30
|
||||||
|
|
||||||
/* frequency of RTC slow clock, Hz */
|
/* frequency of RTC slow clock, Hz */
|
||||||
#define RTC_CTNL_SLOWCLK_FREQ 150000
|
#define RTC_CNTL_SLOWCLK_FREQ 150000
|
||||||
|
|
||||||
#define RTC_CNTL_TIME0_REG (DR_REG_RTCCNTL_BASE + 0x10)
|
#define RTC_CNTL_TIME0_REG (DR_REG_RTCCNTL_BASE + 0x10)
|
||||||
/* RTC_CNTL_TIME_LO : RO ;bitpos:[31:0] ;default: 32'h0 ; */
|
/* RTC_CNTL_TIME_LO : RO ;bitpos:[31:0] ;default: 32'h0 ; */
|
||||||
|
|
20
components/esp32/test/test_tsens.c
Normal file
20
components/esp32/test/test_tsens.c
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "unity.h"
|
||||||
|
#include "rom/ets_sys.h"
|
||||||
|
#include "soc/rtc_cntl_reg.h"
|
||||||
|
#include "soc/sens_reg.h"
|
||||||
|
|
||||||
|
TEST_CASE("can control TSENS using registers", "[rtc][ignore]")
|
||||||
|
{
|
||||||
|
SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 3, SENS_FORCE_XPD_SAR_S);
|
||||||
|
SET_PERI_REG_BITS(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_CLK_DIV, 10, SENS_TSENS_CLK_DIV_S);
|
||||||
|
CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP);
|
||||||
|
CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_DUMP_OUT);
|
||||||
|
SET_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP_FORCE);
|
||||||
|
SET_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP);
|
||||||
|
ets_delay_us(100);
|
||||||
|
SET_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_DUMP_OUT);
|
||||||
|
ets_delay_us(5);
|
||||||
|
int res = GET_PERI_REG_BITS2(SENS_SAR_SLAVE_ADDR3_REG, SENS_TSENS_OUT, SENS_TSENS_OUT_S);
|
||||||
|
printf("res=%d\n", res);
|
||||||
|
}
|
|
@ -52,7 +52,7 @@ static uint64_t get_rtc_time_us()
|
||||||
uint64_t low = READ_PERI_REG(RTC_CNTL_TIME0_REG);
|
uint64_t low = READ_PERI_REG(RTC_CNTL_TIME0_REG);
|
||||||
uint64_t high = READ_PERI_REG(RTC_CNTL_TIME1_REG);
|
uint64_t high = READ_PERI_REG(RTC_CNTL_TIME1_REG);
|
||||||
uint64_t ticks = (high << 32) | low;
|
uint64_t ticks = (high << 32) | low;
|
||||||
return ticks * 100 / (RTC_CTNL_SLOWCLK_FREQ / 10000); // scale RTC_CTNL_SLOWCLK_FREQ to avoid overflow
|
return ticks * 100 / (RTC_CNTL_SLOWCLK_FREQ / 10000); // scale RTC_CNTL_SLOWCLK_FREQ to avoid overflow
|
||||||
}
|
}
|
||||||
#endif // WITH_RTC
|
#endif // WITH_RTC
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ extern "C" {
|
||||||
#define B_CMP_L 0 /*!< Branch if R0 is less than an immediate */
|
#define B_CMP_L 0 /*!< Branch if R0 is less than an immediate */
|
||||||
#define B_CMP_GE 1 /*!< Branch if R0 is greater than or equal to an immediate */
|
#define B_CMP_GE 1 /*!< Branch if R0 is greater than or equal to an immediate */
|
||||||
|
|
||||||
#define OPCODE_END 9 /*!< Stop executing the program (not implemented yet) */
|
#define OPCODE_END 9 /*!< Stop executing the program */
|
||||||
#define SUB_OPCODE_END 0 /*!< Stop executing the program and optionally wake up the chip */
|
#define SUB_OPCODE_END 0 /*!< Stop executing the program and optionally wake up the chip */
|
||||||
#define SUB_OPCODE_SLEEP 1 /*!< Stop executing the program and run it again after selected interval */
|
#define SUB_OPCODE_SLEEP 1 /*!< Stop executing the program and run it again after selected interval */
|
||||||
|
|
||||||
|
@ -222,7 +222,7 @@ typedef union {
|
||||||
struct {
|
struct {
|
||||||
uint32_t dreg : 2; /*!< Register where to store temperature measurement result */
|
uint32_t dreg : 2; /*!< Register where to store temperature measurement result */
|
||||||
uint32_t wait_delay: 14; /*!< Cycles to wait after measurement is done */
|
uint32_t wait_delay: 14; /*!< Cycles to wait after measurement is done */
|
||||||
uint32_t cycles: 12; /*!< Cycles used to perform measurement */
|
uint32_t reserved: 12; /*!< Reserved, set to 0 */
|
||||||
uint32_t opcode: 4; /*!< Opcode (OPCODE_TSENS) */
|
uint32_t opcode: 4; /*!< Opcode (OPCODE_TSENS) */
|
||||||
} tsens; /*!< Format of TSENS instruction */
|
} tsens; /*!< Format of TSENS instruction */
|
||||||
|
|
||||||
|
@ -271,7 +271,12 @@ _Static_assert(sizeof(ulp_insn_t) == 4, "ULP coprocessor instruction size should
|
||||||
.cycles = cycles_ } }
|
.cycles = cycles_ } }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Halt the coprocessor
|
* Halt the coprocessor.
|
||||||
|
*
|
||||||
|
* This instruction halts the coprocessor, but keeps ULP timer active.
|
||||||
|
* As such, ULP program will be restarted again by timer.
|
||||||
|
* To stop the program and prevent the timer from restarting the program,
|
||||||
|
* use I_END(0) instruction.
|
||||||
*/
|
*/
|
||||||
#define I_HALT() { .halt = {\
|
#define I_HALT() { .halt = {\
|
||||||
.unused = 0, \
|
.unused = 0, \
|
||||||
|
@ -307,7 +312,7 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) {
|
||||||
* This instruction can access RTC_CNTL_, RTC_IO_, and SENS_ peripheral registers.
|
* This instruction can access RTC_CNTL_, RTC_IO_, and SENS_ peripheral registers.
|
||||||
*/
|
*/
|
||||||
#define I_WR_REG(reg, low_bit, high_bit, val) {.wr_reg = {\
|
#define I_WR_REG(reg, low_bit, high_bit, val) {.wr_reg = {\
|
||||||
.addr = reg & 0xff, \
|
.addr = (reg & 0xff) / sizeof(uint32_t), \
|
||||||
.periph_sel = SOC_REG_TO_ULP_PERIPH_SEL(reg), \
|
.periph_sel = SOC_REG_TO_ULP_PERIPH_SEL(reg), \
|
||||||
.data = val, \
|
.data = val, \
|
||||||
.low = low_bit, \
|
.low = low_bit, \
|
||||||
|
@ -320,8 +325,8 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) {
|
||||||
* R0 = reg[high_bit : low_bit]
|
* R0 = reg[high_bit : low_bit]
|
||||||
* This instruction can access RTC_CNTL_, RTC_IO_, and SENS_ peripheral registers.
|
* This instruction can access RTC_CNTL_, RTC_IO_, and SENS_ peripheral registers.
|
||||||
*/
|
*/
|
||||||
#define I_RD_REG(reg, low_bit, high_bit, val) {.wr_reg = {\
|
#define I_RD_REG(reg, low_bit, high_bit) {.rd_reg = {\
|
||||||
.addr = reg & 0xff, \
|
.addr = (reg & 0xff) / sizeof(uint32_t), \
|
||||||
.periph_sel = SOC_REG_TO_ULP_PERIPH_SEL(reg), \
|
.periph_sel = SOC_REG_TO_ULP_PERIPH_SEL(reg), \
|
||||||
.unused = 0, \
|
.unused = 0, \
|
||||||
.low = low_bit, \
|
.low = low_bit, \
|
||||||
|
@ -329,25 +334,105 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) {
|
||||||
.opcode = OPCODE_RD_REG } }
|
.opcode = OPCODE_RD_REG } }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* End program.
|
* Set or clear a bit in the peripheral register.
|
||||||
*
|
*
|
||||||
* If wake == 1, wake up main CPU.
|
* Sets bit (1 << shift) of register reg to value val.
|
||||||
|
* This instruction can access RTC_CNTL_, RTC_IO_, and SENS_ peripheral registers.
|
||||||
*/
|
*/
|
||||||
#define I_END(wake) { .end = { \
|
#define I_WR_REG_BIT(reg, shift, val) I_WR_REG(reg, shift, shift, val)
|
||||||
.wakeup = wake, \
|
|
||||||
|
/**
|
||||||
|
* Wake the SoC from deep sleep.
|
||||||
|
*
|
||||||
|
* This instruction initiates wake up from deep sleep.
|
||||||
|
* Use esp_deep_sleep_enable_ulp_wakeup to enable deep sleep wakeup
|
||||||
|
* triggered by the ULP before going into deep sleep.
|
||||||
|
* Note that ULP program will still keep running until the I_HALT
|
||||||
|
* instruction, and it will still be restarted by timer at regular
|
||||||
|
* intervals, even when the SoC is woken up.
|
||||||
|
*
|
||||||
|
* To stop the ULP program, use I_HALT instruction.
|
||||||
|
*
|
||||||
|
* To disable the timer which start ULP program, use I_END()
|
||||||
|
* instruction. I_END instruction clears the
|
||||||
|
* RTC_CNTL_ULP_CP_SLP_TIMER_EN_S bit of RTC_CNTL_STATE0_REG
|
||||||
|
* register, which controls the ULP timer.
|
||||||
|
*/
|
||||||
|
#define I_WAKE() { .end = { \
|
||||||
|
.wakeup = 1, \
|
||||||
.unused = 0, \
|
.unused = 0, \
|
||||||
.sub_opcode = SUB_OPCODE_END, \
|
.sub_opcode = SUB_OPCODE_END, \
|
||||||
.opcode = OPCODE_END } }
|
.opcode = OPCODE_END } }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop ULP program timer.
|
||||||
|
*
|
||||||
|
* This is a convenience macro which disables the ULP program timer.
|
||||||
|
* Once this instruction is used, ULP program will not be restarted
|
||||||
|
* anymore until ulp_run function is called.
|
||||||
|
*
|
||||||
|
* ULP program will continue running after this instruction. To stop
|
||||||
|
* the currently running program, use I_HALT().
|
||||||
|
*/
|
||||||
|
#define I_END() \
|
||||||
|
I_WR_REG_BIT(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN_S, 0)
|
||||||
|
/**
|
||||||
|
* Select the time interval used to run ULP program.
|
||||||
|
*
|
||||||
|
* This instructions selects which of the SENS_SLEEP_CYCLES_Sx
|
||||||
|
* registers' value is used by the ULP program timer.
|
||||||
|
* When the ULP program stops at I_HALT instruction, ULP program
|
||||||
|
* timer start counting. When the counter reaches the value of
|
||||||
|
* the selected SENS_SLEEP_CYCLES_Sx register, ULP program
|
||||||
|
* start running again from the start address (passed to the ulp_run
|
||||||
|
* function).
|
||||||
|
* There are 5 SENS_SLEEP_CYCLES_Sx registers, so 0 <= timer_idx < 5.
|
||||||
|
*
|
||||||
|
* By default, SENS_SLEEP_CYCLES_S0 register is used by the ULP
|
||||||
|
* program timer.
|
||||||
|
*/
|
||||||
|
#define I_SLEEP_CYCLE_SEL(timer_idx) { .sleep = { \
|
||||||
|
.cycle_sel = timer_idx, \
|
||||||
|
.unused = 0, \
|
||||||
|
.sub_opcode = SUB_OPCODE_SLEEP, \
|
||||||
|
.opcode = OPCODE_END } }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform temperature sensor measurement and store it into reg_dest.
|
||||||
|
*
|
||||||
|
* Delay can be set between 1 and ((1 << 14) - 1). Higher values give
|
||||||
|
* higher measurement resolution.
|
||||||
|
*/
|
||||||
|
#define I_TSENS(reg_dest, delay) { .tsens = { \
|
||||||
|
.dreg = reg_dest, \
|
||||||
|
.wait_delay = delay, \
|
||||||
|
.reserved = 0, \
|
||||||
|
.opcode = OPCODE_TSENS } }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform ADC measurement and store result in reg_dest.
|
||||||
|
*
|
||||||
|
* adc_idx selects ADC (0 or 1).
|
||||||
|
* pad_idx selects ADC pad (0 - 7).
|
||||||
|
*/
|
||||||
|
#define I_ADC(reg_dest, adc_idx, pad_idx) { .adc = {\
|
||||||
|
.dreg = reg_dest, \
|
||||||
|
.mux = pad_idx + 1, \
|
||||||
|
.sar_sel = adc_idx, \
|
||||||
|
.unused1 = 0, \
|
||||||
|
.cycles = 0, \
|
||||||
|
.unused2 = 0, \
|
||||||
|
.opcode = OPCODE_ADC } }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store value from register reg_val into RTC memory.
|
* Store value from register reg_val into RTC memory.
|
||||||
*
|
*
|
||||||
* The value is written to an offset calculated by adding value of
|
* The value is written to an offset calculated by adding value of
|
||||||
* reg_addr register and offset_ field (this offset is expressed in 32-bit words).
|
* reg_addr register and offset_ field (this offset is expressed in 32-bit words).
|
||||||
* 32 bits written to RTC memory are built as follows:
|
* 32 bits written to RTC memory are built as follows:
|
||||||
* - 5 MSBs are zero
|
* - bits [31:21] hold the PC of current instruction, expressed in 32-bit words
|
||||||
* - next 11 bits hold the PC of current instruction, expressed in 32-bit words
|
* - bits [20:16] = 5'b1
|
||||||
* - next 16 bits hold the actual value to be written
|
* - bits [15:0] are assigned the contents of reg_val
|
||||||
*
|
*
|
||||||
* RTC_SLOW_MEM[addr + offset_] = { 5'b0, insn_PC[10:0], val[15:0] }
|
* RTC_SLOW_MEM[addr + offset_] = { 5'b0, insn_PC[10:0], val[15:0] }
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -112,7 +112,9 @@ TEST_CASE("ulp wakeup test", "[ulp][ignore]")
|
||||||
I_MOVI(R2, 42),
|
I_MOVI(R2, 42),
|
||||||
I_MOVI(R3, 15),
|
I_MOVI(R3, 15),
|
||||||
I_ST(R2, R3, 0),
|
I_ST(R2, R3, 0),
|
||||||
I_END(1)
|
I_WAKE(),
|
||||||
|
I_END(),
|
||||||
|
I_HALT()
|
||||||
};
|
};
|
||||||
size_t size = sizeof(program)/sizeof(ulp_insn_t);
|
size_t size = sizeof(program)/sizeof(ulp_insn_t);
|
||||||
ulp_process_macros_and_load(0, program, &size);
|
ulp_process_macros_and_load(0, program, &size);
|
||||||
|
@ -121,6 +123,100 @@ TEST_CASE("ulp wakeup test", "[ulp][ignore]")
|
||||||
esp_deep_sleep_start();
|
esp_deep_sleep_start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ulp can write and read peripheral registers", "[ulp]")
|
||||||
|
{
|
||||||
|
assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 260 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig");
|
||||||
|
CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN);
|
||||||
|
memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM);
|
||||||
|
REG_WRITE(RTC_CNTL_STORE1_REG, 0x89abcdef);
|
||||||
|
|
||||||
|
const ulp_insn_t program[] = {
|
||||||
|
I_MOVI(R1, 64),
|
||||||
|
I_RD_REG(RTC_CNTL_STORE1_REG, 0, 15),
|
||||||
|
I_ST(R0, R1, 0),
|
||||||
|
I_RD_REG(RTC_CNTL_STORE1_REG, 4, 11),
|
||||||
|
I_ST(R0, R1, 1),
|
||||||
|
I_RD_REG(RTC_CNTL_STORE1_REG, 16, 31),
|
||||||
|
I_ST(R0, R1, 2),
|
||||||
|
I_RD_REG(RTC_CNTL_STORE1_REG, 20, 27),
|
||||||
|
I_ST(R0, R1, 3),
|
||||||
|
I_WR_REG(RTC_CNTL_STORE0_REG, 0, 7, 0x89),
|
||||||
|
I_WR_REG(RTC_CNTL_STORE0_REG, 8, 15, 0xab),
|
||||||
|
I_WR_REG(RTC_CNTL_STORE0_REG, 16, 23, 0xcd),
|
||||||
|
I_WR_REG(RTC_CNTL_STORE0_REG, 24, 31, 0xef),
|
||||||
|
I_LD(R0, R1, 4),
|
||||||
|
I_ADDI(R0, R0, 1),
|
||||||
|
I_ST(R0, R1, 4),
|
||||||
|
I_END(),
|
||||||
|
I_HALT()
|
||||||
|
};
|
||||||
|
size_t size = sizeof(program)/sizeof(ulp_insn_t);
|
||||||
|
TEST_ESP_OK(ulp_process_macros_and_load(0, program, &size));
|
||||||
|
TEST_ESP_OK(ulp_run(0));
|
||||||
|
vTaskDelay(100/portTICK_PERIOD_MS);
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL_HEX32(0xefcdab89, REG_READ(RTC_CNTL_STORE0_REG));
|
||||||
|
TEST_ASSERT_EQUAL_HEX16(0xcdef, RTC_SLOW_MEM[64] & 0xffff);
|
||||||
|
TEST_ASSERT_EQUAL_HEX16(0xde, RTC_SLOW_MEM[65] & 0xffff);
|
||||||
|
TEST_ASSERT_EQUAL_HEX16(0x89ab, RTC_SLOW_MEM[66] & 0xffff);
|
||||||
|
TEST_ASSERT_EQUAL_HEX16(0x9a, RTC_SLOW_MEM[67] & 0xffff);
|
||||||
|
TEST_ASSERT_EQUAL_HEX32(1 | (15 << 21) | (1 << 16), RTC_SLOW_MEM[68]);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ULP I_WR_REG instruction test", "[ulp]")
|
||||||
|
{
|
||||||
|
assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 260 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig");
|
||||||
|
memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM);
|
||||||
|
typedef struct {
|
||||||
|
int low;
|
||||||
|
int width;
|
||||||
|
} wr_reg_test_item_t;
|
||||||
|
|
||||||
|
const wr_reg_test_item_t test_items[] = {
|
||||||
|
{0, 1}, {0, 2}, {0, 3}, {0, 4}, {0, 5}, {0, 6}, {0, 7}, {0, 8},
|
||||||
|
{3, 1}, {3, 2}, {3, 3}, {3, 4}, {3, 5}, {3, 6}, {3, 7}, {3, 8},
|
||||||
|
{15, 1}, {15, 2}, {15, 3}, {15, 4}, {15, 5}, {15, 6}, {15, 7}, {15, 8},
|
||||||
|
{16, 1}, {16, 2}, {16, 3}, {16, 4}, {16, 5}, {16, 6}, {16, 7}, {16, 8},
|
||||||
|
{18, 1}, {18, 2}, {18, 3}, {18, 4}, {18, 5}, {18, 6}, {18, 7}, {18, 8},
|
||||||
|
{24, 1}, {24, 2}, {24, 3}, {24, 4}, {24, 5}, {24, 6}, {24, 7}, {24, 8},
|
||||||
|
};
|
||||||
|
|
||||||
|
const size_t test_items_count =
|
||||||
|
sizeof(test_items)/sizeof(test_items[0]);
|
||||||
|
for (size_t i = 0; i < test_items_count; ++i) {
|
||||||
|
const uint32_t mask = (uint32_t) (((1ULL << test_items[i].width) - 1) << test_items[i].low);
|
||||||
|
const uint32_t not_mask = ~mask;
|
||||||
|
printf("#%2d: low: %2d width: %2d mask: %08x expected: %08x ", i,
|
||||||
|
test_items[i].low, test_items[i].width,
|
||||||
|
mask, not_mask);
|
||||||
|
|
||||||
|
REG_WRITE(RTC_CNTL_STORE0_REG, 0xffffffff);
|
||||||
|
REG_WRITE(RTC_CNTL_STORE1_REG, 0x00000000);
|
||||||
|
const ulp_insn_t program[] = {
|
||||||
|
I_WR_REG(RTC_CNTL_STORE0_REG,
|
||||||
|
test_items[i].low,
|
||||||
|
test_items[i].low + test_items[i].width - 1,
|
||||||
|
0),
|
||||||
|
I_WR_REG(RTC_CNTL_STORE1_REG,
|
||||||
|
test_items[i].low,
|
||||||
|
test_items[i].low + test_items[i].width - 1,
|
||||||
|
0xff & ((1 << test_items[i].width) - 1)),
|
||||||
|
I_END(),
|
||||||
|
I_HALT()
|
||||||
|
};
|
||||||
|
size_t size = sizeof(program)/sizeof(ulp_insn_t);
|
||||||
|
ulp_process_macros_and_load(0, program, &size);
|
||||||
|
ulp_run(0);
|
||||||
|
vTaskDelay(10/portTICK_PERIOD_MS);
|
||||||
|
uint32_t clear = REG_READ(RTC_CNTL_STORE0_REG);
|
||||||
|
uint32_t set = REG_READ(RTC_CNTL_STORE1_REG);
|
||||||
|
printf("clear: %08x set: %08x\n", clear, set);
|
||||||
|
TEST_ASSERT_EQUAL_HEX32(not_mask, clear);
|
||||||
|
TEST_ASSERT_EQUAL_HEX32(mask, set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE("ulp controls RTC_IO", "[ulp][ignore]")
|
TEST_CASE("ulp controls RTC_IO", "[ulp][ignore]")
|
||||||
{
|
{
|
||||||
assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 260 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig");
|
assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 260 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig");
|
||||||
|
@ -149,7 +245,9 @@ TEST_CASE("ulp controls RTC_IO", "[ulp][ignore]")
|
||||||
M_LABEL(5),
|
M_LABEL(5),
|
||||||
M_BX(4),
|
M_BX(4),
|
||||||
M_LABEL(6),
|
M_LABEL(6),
|
||||||
I_END(1) // wake up the SoC
|
I_WAKE(), // wake up the SoC
|
||||||
|
I_END(), // stop ULP program timer
|
||||||
|
I_HALT()
|
||||||
};
|
};
|
||||||
const gpio_num_t led_gpios[] = {
|
const gpio_num_t led_gpios[] = {
|
||||||
GPIO_NUM_2,
|
GPIO_NUM_2,
|
||||||
|
@ -168,3 +266,198 @@ TEST_CASE("ulp controls RTC_IO", "[ulp][ignore]")
|
||||||
esp_deep_sleep_start();
|
esp_deep_sleep_start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ulp power consumption in deep sleep", "[ulp]")
|
||||||
|
{
|
||||||
|
assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 4 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig");
|
||||||
|
ulp_insn_t insn = I_HALT();
|
||||||
|
RTC_SLOW_MEM[0] = *(uint32_t*) &insn;
|
||||||
|
|
||||||
|
REG_WRITE(SENS_ULP_CP_SLEEP_CYC0_REG, 0x8000);
|
||||||
|
|
||||||
|
ulp_run(0);
|
||||||
|
|
||||||
|
esp_deep_sleep_enable_ulp_wakeup();
|
||||||
|
esp_deep_sleep_enable_timer_wakeup(10 * 1000000);
|
||||||
|
esp_deep_sleep_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ulp timer setting", "[ulp]")
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Run a simple ULP program which increments the counter, for one second.
|
||||||
|
* Program calls I_HALT each time and gets restarted by the timer.
|
||||||
|
* Compare the expected number of times the program runs with the actual.
|
||||||
|
*/
|
||||||
|
assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 32 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig");
|
||||||
|
memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM);
|
||||||
|
|
||||||
|
const int offset = 6;
|
||||||
|
const ulp_insn_t program[] = {
|
||||||
|
I_MOVI(R1, offset), // r1 <- offset
|
||||||
|
I_LD(R2, R1, 0), // load counter
|
||||||
|
I_ADDI(R2, R2, 1), // counter += 1
|
||||||
|
I_ST(R2, R1, 0), // save counter
|
||||||
|
I_HALT(),
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t size = sizeof(program)/sizeof(ulp_insn_t);
|
||||||
|
TEST_ESP_OK(ulp_process_macros_and_load(0, program, &size));
|
||||||
|
assert(offset >= size && "data offset needs to be greater or equal to program size");
|
||||||
|
TEST_ESP_OK(ulp_run(0));
|
||||||
|
// disable the ULP program timer — we will enable it later
|
||||||
|
CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN);
|
||||||
|
|
||||||
|
const uint32_t cycles_to_test[] = {0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000};
|
||||||
|
const size_t tests_count = sizeof(cycles_to_test) / sizeof(cycles_to_test[0]);
|
||||||
|
for (size_t i = 0; i < tests_count; ++i) {
|
||||||
|
// zero out the counter
|
||||||
|
RTC_SLOW_MEM[offset] = 0;
|
||||||
|
// set the number of slow clock cycles
|
||||||
|
REG_WRITE(SENS_ULP_CP_SLEEP_CYC0_REG, cycles_to_test[i]);
|
||||||
|
// enable the timer and wait for a second
|
||||||
|
SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN);
|
||||||
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||||
|
// get the counter value and stop the timer
|
||||||
|
uint32_t counter = RTC_SLOW_MEM[offset] & 0xffff;
|
||||||
|
CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN);
|
||||||
|
// compare the actual and expected numbers of iterations of ULP program
|
||||||
|
float expected_period = (cycles_to_test[i] + 16) / (float) RTC_CNTL_SLOWCLK_FREQ + 5 / 8e6f;
|
||||||
|
float error = 1.0f - counter * expected_period;
|
||||||
|
printf("%u\t%u\t%.01f\t%.04f\n", cycles_to_test[i], counter, 1.0f / expected_period, error);
|
||||||
|
// Should be within 15%
|
||||||
|
TEST_ASSERT_INT_WITHIN(15, 0, (int) error * 100);
|
||||||
|
// Note: currently RTC_CNTL_SLOWCLK_FREQ is ballpark value — we need to determine it
|
||||||
|
// Precisely by running calibration similar to the one done in deep sleep.
|
||||||
|
// This may cause the test to fail on some chips which have the slow clock frequency
|
||||||
|
// way off.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ulp can use TSENS in deep sleep", "[ulp][ignore]")
|
||||||
|
{
|
||||||
|
assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 260 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig");
|
||||||
|
|
||||||
|
hexdump(RTC_SLOW_MEM, CONFIG_ULP_COPROC_RESERVE_MEM / 4);
|
||||||
|
printf("\n\n");
|
||||||
|
memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM);
|
||||||
|
|
||||||
|
// Allow TSENS to be controlled by the ULP
|
||||||
|
SET_PERI_REG_BITS(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_CLK_DIV, 10, SENS_TSENS_CLK_DIV_S);
|
||||||
|
SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 3, SENS_FORCE_XPD_SAR_S);
|
||||||
|
CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP);
|
||||||
|
CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_DUMP_OUT);
|
||||||
|
CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP_FORCE);
|
||||||
|
|
||||||
|
// data start offset
|
||||||
|
size_t offset = 20;
|
||||||
|
// number of samples to collect
|
||||||
|
RTC_SLOW_MEM[offset] = (CONFIG_ULP_COPROC_RESERVE_MEM) / 4 - offset - 8;
|
||||||
|
// sample counter
|
||||||
|
RTC_SLOW_MEM[offset + 1] = 0;
|
||||||
|
|
||||||
|
const ulp_insn_t program[] = {
|
||||||
|
I_MOVI(R1, offset), // r1 <- offset
|
||||||
|
I_LD(R2, R1, 1), // r2 <- counter
|
||||||
|
I_LD(R3, R1, 0), // r3 <- length
|
||||||
|
I_SUBI(R3, R3, 1), // end = length - 1
|
||||||
|
I_SUBR(R3, R3, R2), // r3 = length - counter
|
||||||
|
M_BXF(1), // if overflow goto 1:
|
||||||
|
I_WR_REG(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR_S, SENS_FORCE_XPD_SAR_S + 1, 3),
|
||||||
|
I_TSENS(R0, 16383), // r0 <- tsens
|
||||||
|
I_WR_REG(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR_S, SENS_FORCE_XPD_SAR_S + 1, 0),
|
||||||
|
I_ST(R0, R2, offset + 4),
|
||||||
|
I_ADDI(R2, R2, 1), // counter += 1
|
||||||
|
I_ST(R2, R1, 1), // save counter
|
||||||
|
I_HALT(), // enter sleep
|
||||||
|
M_LABEL(1), // done with measurements
|
||||||
|
I_END(), // stop ULP timer
|
||||||
|
I_WAKE(), // initiate wakeup
|
||||||
|
I_HALT()
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t size = sizeof(program)/sizeof(ulp_insn_t);
|
||||||
|
TEST_ESP_OK(ulp_process_macros_and_load(0, program, &size));
|
||||||
|
assert(offset >= size);
|
||||||
|
|
||||||
|
TEST_ESP_OK(ulp_run(0));
|
||||||
|
esp_deep_sleep_enable_timer_wakeup(4000000);
|
||||||
|
esp_deep_sleep_enable_ulp_wakeup();
|
||||||
|
esp_deep_sleep_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("can use ADC in deep sleep", "[ulp][ignore]")
|
||||||
|
{
|
||||||
|
assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 260 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig");
|
||||||
|
|
||||||
|
hexdump(RTC_SLOW_MEM, CONFIG_ULP_COPROC_RESERVE_MEM / 4);
|
||||||
|
printf("\n\n");
|
||||||
|
memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM);
|
||||||
|
|
||||||
|
SET_PERI_REG_BITS(SENS_SAR_START_FORCE_REG, SENS_SAR1_BIT_WIDTH, 3, SENS_SAR1_BIT_WIDTH_S);
|
||||||
|
SET_PERI_REG_BITS(SENS_SAR_START_FORCE_REG, SENS_SAR2_BIT_WIDTH, 3, SENS_SAR2_BIT_WIDTH_S);
|
||||||
|
|
||||||
|
SET_PERI_REG_BITS(SENS_SAR_READ_CTRL_REG, SENS_SAR1_SAMPLE_BIT, 0x3, SENS_SAR1_SAMPLE_BIT_S);
|
||||||
|
SET_PERI_REG_BITS(SENS_SAR_READ_CTRL2_REG, SENS_SAR2_SAMPLE_BIT, 0x3, SENS_SAR2_SAMPLE_BIT_S);
|
||||||
|
|
||||||
|
CLEAR_PERI_REG_MASK(SENS_SAR_MEAS_START2_REG, SENS_MEAS2_START_FORCE);
|
||||||
|
CLEAR_PERI_REG_MASK(SENS_SAR_MEAS_START1_REG, SENS_MEAS1_START_FORCE);
|
||||||
|
|
||||||
|
SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 0, SENS_FORCE_XPD_SAR_S);
|
||||||
|
SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_AMP, 2, SENS_FORCE_XPD_AMP_S);
|
||||||
|
|
||||||
|
// SAR1 invert result
|
||||||
|
SET_PERI_REG_MASK(SENS_SAR_READ_CTRL_REG, SENS_SAR1_DATA_INV);
|
||||||
|
SET_PERI_REG_MASK(SENS_SAR_READ_CTRL_REG, SENS_SAR2_DATA_INV);
|
||||||
|
|
||||||
|
|
||||||
|
// const int adc = 1;
|
||||||
|
// const int channel = 1;
|
||||||
|
// const int atten = 3;
|
||||||
|
// const int gpio_num = 0;
|
||||||
|
|
||||||
|
const int adc = 0;
|
||||||
|
const int channel = 0;
|
||||||
|
const int atten = 0;
|
||||||
|
const int gpio_num = 36;
|
||||||
|
|
||||||
|
rtc_gpio_init(gpio_num);
|
||||||
|
|
||||||
|
CLEAR_PERI_REG_MASK(SENS_SAR_MEAS_START1_REG, SENS_SAR1_EN_PAD_FORCE_M);
|
||||||
|
CLEAR_PERI_REG_MASK(SENS_SAR_MEAS_START2_REG, SENS_SAR2_EN_PAD_FORCE_M);
|
||||||
|
|
||||||
|
SET_PERI_REG_BITS(SENS_SAR_ATTEN1_REG, 3, atten, 2 * channel); //set SAR1 attenuation
|
||||||
|
SET_PERI_REG_BITS(SENS_SAR_ATTEN2_REG, 3, atten, 2 * channel); //set SAR2 attenuation
|
||||||
|
|
||||||
|
// data start offset
|
||||||
|
size_t offset = 20;
|
||||||
|
// number of samples to collect
|
||||||
|
RTC_SLOW_MEM[offset] = (CONFIG_ULP_COPROC_RESERVE_MEM) / 4 - offset - 8;
|
||||||
|
// sample counter
|
||||||
|
RTC_SLOW_MEM[offset + 1] = 0;
|
||||||
|
|
||||||
|
const ulp_insn_t program[] = {
|
||||||
|
I_MOVI(R1, offset), // r1 <- offset
|
||||||
|
I_LD(R2, R1, 1), // r2 <- counter
|
||||||
|
I_LD(R3, R1, 0), // r3 <- length
|
||||||
|
I_SUBI(R3, R3, 1), // end = length - 1
|
||||||
|
I_SUBR(R3, R3, R2), // r3 = length - counter
|
||||||
|
M_BXF(1), // if overflow goto 1:
|
||||||
|
I_ADC(R0, adc, channel), // r0 <- ADC
|
||||||
|
I_ST(R0, R2, offset + 4),
|
||||||
|
I_ADDI(R2, R2, 1), // counter += 1
|
||||||
|
I_ST(R2, R1, 1), // save counter
|
||||||
|
I_HALT(),
|
||||||
|
M_LABEL(1), // done with measurements
|
||||||
|
I_END(), // stop ULP program timer
|
||||||
|
I_HALT()
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t size = sizeof(program)/sizeof(ulp_insn_t);
|
||||||
|
TEST_ESP_OK(ulp_process_macros_and_load(0, program, &size));
|
||||||
|
assert(offset >= size);
|
||||||
|
|
||||||
|
TEST_ESP_OK(ulp_run(0));
|
||||||
|
esp_deep_sleep_enable_timer_wakeup(4000000);
|
||||||
|
esp_deep_sleep_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -265,12 +265,16 @@ esp_err_t ulp_run(uint32_t entry_point)
|
||||||
{
|
{
|
||||||
// disable ULP timer
|
// disable ULP timer
|
||||||
CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN);
|
CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN);
|
||||||
|
// wait for at least 1 RTC_SLOW_CLK cycle
|
||||||
|
ets_delay_us(10);
|
||||||
// set entry point
|
// set entry point
|
||||||
SET_PERI_REG_BITS(SENS_SAR_START_FORCE_REG, SENS_PC_INIT_V, entry_point, SENS_PC_INIT_S);
|
SET_PERI_REG_BITS(SENS_SAR_START_FORCE_REG, SENS_PC_INIT_V, entry_point, SENS_PC_INIT_S);
|
||||||
// disable force start
|
// disable force start
|
||||||
CLEAR_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_ULP_CP_FORCE_START_TOP_M);
|
CLEAR_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_ULP_CP_FORCE_START_TOP_M);
|
||||||
// make sure voltage is raised when RTC 8MCLK is enabled
|
// make sure voltage is raised when RTC 8MCLK is enabled
|
||||||
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_I2C_FOLW_8M);
|
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_I2C_FOLW_8M);
|
||||||
|
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_CORE_FOLW_8M);
|
||||||
|
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_SLEEP_FOLW_8M);
|
||||||
// enable ULP timer
|
// enable ULP timer
|
||||||
SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN);
|
SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
|
|
|
@ -26,6 +26,15 @@ The following function can be used to enable deep sleep wakeup using a timer.
|
||||||
|
|
||||||
.. doxygenfunction:: esp_deep_sleep_enable_timer_wakeup
|
.. doxygenfunction:: esp_deep_sleep_enable_timer_wakeup
|
||||||
|
|
||||||
|
Touch pad
|
||||||
|
^^^^^^^^^
|
||||||
|
|
||||||
|
RTC IO module contains logic to trigger wakeup when a touch sensor interrupt occurs. You need to configure the touch pad interrupt before the chip starts deep sleep.
|
||||||
|
|
||||||
|
Revisions 0 and 1 of the ESP32 only support this wakeup mode when RTC peripherals are not forced to be powered on (i.e. ESP_PD_DOMAIN_RTC_PERIPH should be set to ESP_PD_OPTION_AUTO).
|
||||||
|
|
||||||
|
.. doxygenfunction:: esp_deep_sleep_enable_touchpad_wakeup
|
||||||
|
|
||||||
|
|
||||||
External wakeup (ext0)
|
External wakeup (ext0)
|
||||||
^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -34,6 +43,8 @@ RTC IO module contains logic to trigger wakeup when one of RTC GPIOs is set to a
|
||||||
|
|
||||||
Because RTC IO module is enabled in this mode, internal pullup or pulldown resistors can also be used. They need to be configured by the application using ``rtc_gpio_pullup_en`` and ``rtc_gpio_pulldown_en`` functions, before calling ``esp_deep_sleep_start``.
|
Because RTC IO module is enabled in this mode, internal pullup or pulldown resistors can also be used. They need to be configured by the application using ``rtc_gpio_pullup_en`` and ``rtc_gpio_pulldown_en`` functions, before calling ``esp_deep_sleep_start``.
|
||||||
|
|
||||||
|
In revisions 0 and 1 of the ESP32, this wakeup source is incompatible with ULP and touch wakeup sources.
|
||||||
|
|
||||||
.. doxygenfunction:: esp_deep_sleep_enable_ext0_wakeup
|
.. doxygenfunction:: esp_deep_sleep_enable_ext0_wakeup
|
||||||
|
|
||||||
External wakeup (ext1)
|
External wakeup (ext1)
|
||||||
|
@ -41,8 +52,8 @@ External wakeup (ext1)
|
||||||
|
|
||||||
RTC controller contains logic to trigger wakeup using multiple RTC GPIOs. One of the two logic functions can be used to trigger wakeup:
|
RTC controller contains logic to trigger wakeup using multiple RTC GPIOs. One of the two logic functions can be used to trigger wakeup:
|
||||||
|
|
||||||
- wake up if any of the selected pins is low
|
- wake up if any of the selected pins is high (``ESP_EXT1_WAKEUP_ANY_HIGH``)
|
||||||
- wake up if all the selected pins are high
|
- wake up if all the selected pins are low (``ESP_EXT1_WAKEUP_ALL_LOW``)
|
||||||
|
|
||||||
This wakeup source is implemented by the RTC controller. As such, RTC peripherals and RTC memories can be powered off in this mode. However, if RTC peripherals are powered down, internal pullup and pulldown resistors will be disabled. To use internal pullup or pulldown resistors, request RTC peripherals power domain to be kept on during deep sleep, and configure pullup/pulldown resistors using ``rtc_gpio_`` functions, before entering deep sleep::
|
This wakeup source is implemented by the RTC controller. As such, RTC peripherals and RTC memories can be powered off in this mode. However, if RTC peripherals are powered down, internal pullup and pulldown resistors will be disabled. To use internal pullup or pulldown resistors, request RTC peripherals power domain to be kept on during deep sleep, and configure pullup/pulldown resistors using ``rtc_gpio_`` functions, before entering deep sleep::
|
||||||
|
|
||||||
|
@ -60,7 +71,9 @@ The following function can be used to enable this wakeup mode:
|
||||||
ULP coprocessor wakeup
|
ULP coprocessor wakeup
|
||||||
^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
ULP coprocessor can run while the chip is in deep sleep, and may be used to poll sensors, monitor ADC or touch sensor values, and wake up the chip when a specific event is detected. ULP coprocessor is part of RTC peripherals power domain, and it runs the program stored in RTC slow memeory. Therefore RTC peripherals and RTC slow memory will be powered on during deep sleep if this wakeup mode is requested.
|
ULP coprocessor can run while the chip is in deep sleep, and may be used to poll sensors, monitor ADC or touch sensor values, and wake up the chip when a specific event is detected. ULP coprocessor is part of RTC peripherals power domain, and it runs the program stored in RTC slow memeory. RTC slow memory will be powered on during deep sleep if this wakeup mode is requested. RTC peripherals will be automatically powered on before ULP coprocessor starts running the program; once the program stops running, RTC peripherals are automatically powered down again.
|
||||||
|
|
||||||
|
Revisions 0 and 1 of the ESP32 only support this wakeup mode when RTC peripherals are not forced to be powered on (i.e. ESP_PD_DOMAIN_RTC_PERIPH should be set to ESP_PD_OPTION_AUTO).
|
||||||
|
|
||||||
The following function can be used to enable this wakeup mode:
|
The following function can be used to enable this wakeup mode:
|
||||||
|
|
||||||
|
@ -71,7 +84,7 @@ Power-down of RTC peripherals and memories
|
||||||
|
|
||||||
By default, ``esp_deep_sleep_start`` function will power down all RTC power domains which are not needed by the enabled wakeup sources. To override this behaviour, the following function is provided:
|
By default, ``esp_deep_sleep_start`` function will power down all RTC power domains which are not needed by the enabled wakeup sources. To override this behaviour, the following function is provided:
|
||||||
|
|
||||||
Note: on the first revision of the ESP32, RTC fast memory will always be kept enabled in deep sleep, so that the deep sleep stub can run after reset. This can be overriden, if the application doesn't need clean reset behaviour after deep sleep.
|
Note: in revision 0 of the ESP32, RTC fast memory will always be kept enabled in deep sleep, so that the deep sleep stub can run after reset. This can be overriden, if the application doesn't need clean reset behaviour after deep sleep.
|
||||||
|
|
||||||
If some variables in the program are placed into RTC slow memory (for example, using ``RTC_DATA_ATTR`` attribute), RTC slow memory will be kept powered on by default. This can be overriden using ``esp_deep_sleep_pd_config`` function, if desired.
|
If some variables in the program are placed into RTC slow memory (for example, using ``RTC_DATA_ATTR`` attribute), RTC slow memory will be kept powered on by default. This can be overriden using ``esp_deep_sleep_pd_config`` function, if desired.
|
||||||
|
|
||||||
|
@ -87,8 +100,20 @@ The following function can be used to enter deep sleep once wakeup sources are c
|
||||||
|
|
||||||
.. doxygenfunction:: esp_deep_sleep_start
|
.. doxygenfunction:: esp_deep_sleep_start
|
||||||
|
|
||||||
|
Checking deep sleep wakeup cause
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
The following function can be used to check which wakeup source has triggered wakeup from deep sleep mode. For touch pad and ext1 wakeup sources, it is possible to identify pin or touch pad which has caused wakeup.
|
||||||
|
|
||||||
|
.. doxygenfunction:: esp_deep_sleep_get_wakeup_cause
|
||||||
|
.. doxygenenum:: esp_deep_sleep_wakeup_cause_t
|
||||||
|
.. doxygenfunction:: esp_deep_sleep_get_touchpad_wakeup_status
|
||||||
|
.. doxygenfunction:: esp_deep_sleep_get_ext1_wakeup_status
|
||||||
|
|
||||||
|
|
||||||
Application Example
|
Application Example
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
Implementation of basic functionality of deep sleep is shown in :example:`protocols/sntp` example, where ESP module is periodically waken up to retrive time from NTP server.
|
Implementation of basic functionality of deep sleep is shown in :example:`protocols/sntp` example, where ESP module is periodically waken up to retrive time from NTP server.
|
||||||
|
|
||||||
|
More extensive example in :example:`system/deep_sleep` illustrates usage of various deep sleep wakeup triggers and ULP coprocessor programming.
|
||||||
|
|
9
examples/system/deep_sleep/Makefile
Normal file
9
examples/system/deep_sleep/Makefile
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#
|
||||||
|
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||||
|
# project subdirectory.
|
||||||
|
#
|
||||||
|
|
||||||
|
PROJECT_NAME := deep_sleep
|
||||||
|
|
||||||
|
include $(IDF_PATH)/make/project.mk
|
||||||
|
|
11
examples/system/deep_sleep/README.md
Normal file
11
examples/system/deep_sleep/README.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Example: deep sleep
|
||||||
|
|
||||||
|
This example illustrates usage of deep sleep mode and various wakeup sources.
|
||||||
|
|
||||||
|
The following wake up sources are configured:
|
||||||
|
|
||||||
|
- Timer: wake up the chip in 20 seconds
|
||||||
|
- EXT1: wake up the chip if any of the two buttons are pressed (GPIO25, GPIO26)
|
||||||
|
- Touch: wake up the chip if any of the touch pads are pressed (GPIO32, GPIO33)
|
||||||
|
- ULP: wake up when the chip temperature changes by more than ~5 degrees Celsius (this value hasn't been characterized exactly yet).
|
||||||
|
|
20
examples/system/deep_sleep/main/Kconfig.projbuild
Normal file
20
examples/system/deep_sleep/main/Kconfig.projbuild
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
menu "Example Configuration"
|
||||||
|
|
||||||
|
config ENABLE_TOUCH_WAKEUP
|
||||||
|
bool "Enable touch wake up"
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
This option enables wake up from deep sleep using touch pads
|
||||||
|
TOUCH8 and TOUCH9, which correspond to GPIO33 and GPIO32.
|
||||||
|
|
||||||
|
config ENABLE_ULP_TEMPERATURE_WAKEUP
|
||||||
|
bool "Enable temperature monitoring by ULP"
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
This option enables wake up from deep sleep using ULP.
|
||||||
|
ULP program monitors the on-chip temperature sensor and
|
||||||
|
wakes up the chip when the temperature goes outside of
|
||||||
|
the window defined by the initial temperature and a threshold
|
||||||
|
around it.
|
||||||
|
|
||||||
|
endmenu
|
3
examples/system/deep_sleep/main/component.mk
Normal file
3
examples/system/deep_sleep/main/component.mk
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#
|
||||||
|
# Main Makefile. This is basically the same as a component makefile.
|
||||||
|
#
|
296
examples/system/deep_sleep/main/deep_sleep_wakeup.c
Normal file
296
examples/system/deep_sleep/main/deep_sleep_wakeup.c
Normal file
|
@ -0,0 +1,296 @@
|
||||||
|
/* Deep sleep wake up example
|
||||||
|
|
||||||
|
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, this
|
||||||
|
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "esp_deep_sleep.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp32/ulp.h"
|
||||||
|
#include "driver/touch_pad.h"
|
||||||
|
#include "driver/adc.h"
|
||||||
|
#include "driver/rtc_io.h"
|
||||||
|
#include "soc/rtc_cntl_reg.h"
|
||||||
|
#include "soc/sens_reg.h"
|
||||||
|
|
||||||
|
static RTC_DATA_ATTR struct timeval sleep_enter_time;
|
||||||
|
|
||||||
|
#ifdef CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Offset (in 32-bit words) in RTC Slow memory where the data is placed
|
||||||
|
* by the ULP coprocessor. It can be chosen to be any value greater or equal
|
||||||
|
* to ULP program size, and less than the CONFIG_ULP_COPROC_RESERVE_MEM/4 - 6,
|
||||||
|
* where 6 is the number of words used by the ULP coprocessor.
|
||||||
|
*/
|
||||||
|
#define ULP_DATA_OFFSET 36
|
||||||
|
|
||||||
|
_Static_assert(ULP_DATA_OFFSET < CONFIG_ULP_COPROC_RESERVE_MEM/4 - 6,
|
||||||
|
"ULP_DATA_OFFSET is set too high, or CONFIG_ULP_COPROC_RESERVE_MEM is not sufficient");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start ULP temperature monitoring program
|
||||||
|
*
|
||||||
|
* This function loads a program into the RTC Slow memory and starts up the ULP.
|
||||||
|
* The program monitors on-chip temperature sensor and wakes up the SoC when
|
||||||
|
* the temperature goes lower or higher than certain thresholds.
|
||||||
|
*/
|
||||||
|
static void start_ulp_temperature_monitoring();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Utility function which reads data written by ULP program
|
||||||
|
*
|
||||||
|
* @param offset offset from ULP_DATA_OFFSET in RTC Slow memory, in words
|
||||||
|
* @return lower 16-bit part of the word writable by the ULP
|
||||||
|
*/
|
||||||
|
static inline uint16_t ulp_data_read(size_t offset)
|
||||||
|
{
|
||||||
|
return RTC_SLOW_MEM[ULP_DATA_OFFSET + offset] & 0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Utility function which writes data to be ready by ULP program
|
||||||
|
*
|
||||||
|
* @param offset offset from ULP_DATA_OFFSET in RTC Slow memory, in words
|
||||||
|
* @param value lower 16-bit part of the word to be stored
|
||||||
|
*/
|
||||||
|
static inline void ulp_data_write(size_t offset, uint16_t value)
|
||||||
|
{
|
||||||
|
RTC_SLOW_MEM[ULP_DATA_OFFSET + offset] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP
|
||||||
|
|
||||||
|
#ifdef CONFIG_ENABLE_TOUCH_WAKEUP
|
||||||
|
static void calibrate_touch_pad(touch_pad_t pad);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void app_main()
|
||||||
|
{
|
||||||
|
struct timeval now;
|
||||||
|
gettimeofday(&now, NULL);
|
||||||
|
int sleep_time_ms = (now.tv_sec - sleep_enter_time.tv_sec) * 1000 + (now.tv_usec - sleep_enter_time.tv_usec) / 1000;
|
||||||
|
|
||||||
|
switch (esp_deep_sleep_get_wakeup_cause()) {
|
||||||
|
case ESP_DEEP_SLEEP_WAKEUP_EXT1: {
|
||||||
|
uint64_t wakeup_pin_mask = esp_deep_sleep_get_ext1_wakeup_status();
|
||||||
|
if (wakeup_pin_mask != 0) {
|
||||||
|
int pin = __builtin_ffsll(wakeup_pin_mask) - 1;
|
||||||
|
printf("Wake up from GPIO %d\n", pin);
|
||||||
|
} else {
|
||||||
|
printf("Wake up from GPIO\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ESP_DEEP_SLEEP_WAKEUP_TIMER: {
|
||||||
|
printf("Wake up from timer. Time spent in deep sleep: %dms\n", sleep_time_ms);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#ifdef CONFIG_ENABLE_TOUCH_WAKEUP
|
||||||
|
case ESP_DEEP_SLEEP_WAKEUP_TOUCHPAD: {
|
||||||
|
printf("Wake up from touch on pad %d\n", esp_deep_sleep_get_touchpad_wakeup_status());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif // CONFIG_ENABLE_TOUCH_WAKEUP
|
||||||
|
#ifdef CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP
|
||||||
|
case ESP_DEEP_SLEEP_WAKEUP_ULP: {
|
||||||
|
printf("Wake up from ULP\n");
|
||||||
|
int16_t diff_high = (int16_t) ulp_data_read(3);
|
||||||
|
int16_t diff_low = (int16_t) ulp_data_read(4);
|
||||||
|
if (diff_high < 0) {
|
||||||
|
printf("High temperature alarm was triggered\n");
|
||||||
|
} else if (diff_low < 0) {
|
||||||
|
printf("Low temperature alarm was triggered\n");
|
||||||
|
} else {
|
||||||
|
assert(false && "temperature has stayed within limits, but got ULP wakeup\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif // CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP
|
||||||
|
case ESP_DEEP_SLEEP_WAKEUP_UNDEFINED:
|
||||||
|
default:
|
||||||
|
printf("Not a deep sleep reset\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP
|
||||||
|
if (esp_deep_sleep_get_wakeup_cause() != ESP_DEEP_SLEEP_WAKEUP_UNDEFINED) {
|
||||||
|
printf("ULP did %d temperature measurements in %d ms\n", ulp_data_read(1), sleep_time_ms);
|
||||||
|
printf("Initial T=%d, latest T=%d\n", ulp_data_read(0), ulp_data_read(2));
|
||||||
|
}
|
||||||
|
#endif // CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP
|
||||||
|
|
||||||
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
|
const int wakeup_time_sec = 20;
|
||||||
|
printf("Enabling timer wakeup, %ds\n", wakeup_time_sec);
|
||||||
|
esp_deep_sleep_enable_timer_wakeup(wakeup_time_sec * 1000000);
|
||||||
|
|
||||||
|
const int ext_wakeup_pin_1 = 25;
|
||||||
|
const uint64_t ext_wakeup_pin_1_mask = 1ULL << ext_wakeup_pin_1;
|
||||||
|
const int ext_wakeup_pin_2 = 26;
|
||||||
|
const uint64_t ext_wakeup_pin_2_mask = 1ULL << ext_wakeup_pin_2;
|
||||||
|
|
||||||
|
printf("Enabling EXT1 wakeup on pins GPIO%d, GPIO%d\n", ext_wakeup_pin_1, ext_wakeup_pin_2);
|
||||||
|
esp_deep_sleep_enable_ext1_wakeup(ext_wakeup_pin_1_mask | ext_wakeup_pin_2_mask, ESP_EXT1_WAKEUP_ANY_HIGH);
|
||||||
|
|
||||||
|
#ifdef CONFIG_ENABLE_TOUCH_WAKEUP
|
||||||
|
touch_pad_init();
|
||||||
|
calibrate_touch_pad(TOUCH_PAD_NUM8);
|
||||||
|
calibrate_touch_pad(TOUCH_PAD_NUM9);
|
||||||
|
printf("Enabling touch pad wakeup\n");
|
||||||
|
esp_deep_sleep_enable_touchpad_wakeup();
|
||||||
|
#endif // CONFIG_ENABLE_TOUCH_WAKEUP
|
||||||
|
|
||||||
|
#ifdef CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP
|
||||||
|
printf("Enabling ULP wakeup\n");
|
||||||
|
esp_deep_sleep_enable_ulp_wakeup();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
printf("Entering deep sleep\n");
|
||||||
|
gettimeofday(&sleep_enter_time, NULL);
|
||||||
|
|
||||||
|
#ifdef CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP
|
||||||
|
start_ulp_temperature_monitoring();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
esp_deep_sleep_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_ENABLE_TOUCH_WAKEUP
|
||||||
|
static void calibrate_touch_pad(touch_pad_t pad)
|
||||||
|
{
|
||||||
|
touch_pad_config(pad, 1000);
|
||||||
|
|
||||||
|
int avg = 0;
|
||||||
|
const size_t calibration_count = 128;
|
||||||
|
for (int i = 0; i < calibration_count; ++i) {
|
||||||
|
uint16_t val;
|
||||||
|
touch_pad_read(pad, &val);
|
||||||
|
avg += val;
|
||||||
|
}
|
||||||
|
avg /= calibration_count;
|
||||||
|
const int min_reading = 300;
|
||||||
|
if (avg < min_reading) {
|
||||||
|
printf("Touch pad #%d average reading is too low: %d (expecting at least %d). "
|
||||||
|
"Not using for deep sleep wakeup.\n", pad, avg, min_reading);
|
||||||
|
touch_pad_config(pad, 0);
|
||||||
|
} else {
|
||||||
|
int threshold = avg - 100;
|
||||||
|
printf("Touch pad #%d average: %d, wakeup threshold set to %d.\n", pad, avg, threshold);
|
||||||
|
touch_pad_config(pad, threshold);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // CONFIG_ENABLE_TOUCH_WAKEUP
|
||||||
|
|
||||||
|
#ifdef CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP
|
||||||
|
static void start_ulp_temperature_monitoring()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This ULP program monitors the on-chip temperature sensor and wakes the chip up when
|
||||||
|
* the temperature goes outside of certain window.
|
||||||
|
* When the program runs for the first time, it saves the temperature measurement,
|
||||||
|
* it is considered initial temperature (T0).
|
||||||
|
*
|
||||||
|
* On each subsequent run, temperature measured and compared to T0.
|
||||||
|
* If the measured value is higher than T0 + max_temp_diff or lower than T0 - max_temp_diff,
|
||||||
|
* the chip is woken up from deep sleep.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Temperature difference threshold which causes wakeup
|
||||||
|
* With settings here (TSENS_CLK_DIV=2, 8000 cycles),
|
||||||
|
* TSENS measurement is done in units of 0.73 degrees Celsius.
|
||||||
|
* Therefore, max_temp_diff below is equivalent to ~2.2 degrees Celsius.
|
||||||
|
*/
|
||||||
|
const int16_t max_temp_diff = 3;
|
||||||
|
|
||||||
|
// Number of measurements ULP should do per second
|
||||||
|
const uint32_t measurements_per_sec = 5;
|
||||||
|
|
||||||
|
// Allow TSENS to be controlled by the ULP
|
||||||
|
SET_PERI_REG_BITS(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_CLK_DIV, 2, SENS_TSENS_CLK_DIV_S);
|
||||||
|
SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 3, SENS_FORCE_XPD_SAR_S);
|
||||||
|
CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP);
|
||||||
|
CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_DUMP_OUT);
|
||||||
|
CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP_FORCE);
|
||||||
|
|
||||||
|
// Clear the part of RTC_SLOW_MEM reserved for the ULP. Makes debugging easier.
|
||||||
|
memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM);
|
||||||
|
|
||||||
|
// The first word of memory (at data offset) is used to store the initial temperature (T0)
|
||||||
|
// Zero it out here, then ULP will update it on the first run.
|
||||||
|
ulp_data_write(0, 0);
|
||||||
|
// The second word is used to store measurement count, zero it out as well.
|
||||||
|
ulp_data_write(1, 0);
|
||||||
|
|
||||||
|
const ulp_insn_t program[] = {
|
||||||
|
// load data offset into R2
|
||||||
|
I_MOVI(R2, ULP_DATA_OFFSET),
|
||||||
|
// load/increment/store measurement counter using R1
|
||||||
|
I_LD(R1, R2, 1),
|
||||||
|
I_ADDI(R1, R1, 1),
|
||||||
|
I_ST(R1, R2, 1),
|
||||||
|
// enable temperature sensor
|
||||||
|
I_WR_REG(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR_S, SENS_FORCE_XPD_SAR_S + 1, 3),
|
||||||
|
// do temperature measurement and store result in R3
|
||||||
|
I_TSENS(R3, 8000),
|
||||||
|
// disable temperature sensor
|
||||||
|
I_WR_REG(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR_S, SENS_FORCE_XPD_SAR_S + 1, 0),
|
||||||
|
// Save current measurement at offset+2
|
||||||
|
I_ST(R3, R2, 2),
|
||||||
|
// load initial value into R0
|
||||||
|
I_LD(R0, R2, 0),
|
||||||
|
// if threshold value >=1 (i.e. initialized), goto 1
|
||||||
|
M_BGE(1, 1),
|
||||||
|
// otherwise, save the current value as initial (T0)
|
||||||
|
I_MOVR(R0, R3),
|
||||||
|
I_ST(R0, R2, 0),
|
||||||
|
M_LABEL(1),
|
||||||
|
// check if the temperature is greater or equal (T0 + max_temp_diff)
|
||||||
|
// uses R1 as scratch register, difference is saved at offset + 3
|
||||||
|
I_ADDI(R1, R0, max_temp_diff - 1),
|
||||||
|
I_SUBR(R1, R1, R3),
|
||||||
|
I_ST(R1, R2, 3),
|
||||||
|
M_BXF(2),
|
||||||
|
// check if the temperature is less or equal (T0 - max_temp_diff)
|
||||||
|
// uses R1 as scratch register, difference is saved at offset + 4
|
||||||
|
I_SUBI(R1, R0, max_temp_diff - 1),
|
||||||
|
I_SUBR(R1, R3, R1),
|
||||||
|
I_ST(R1, R2, 4),
|
||||||
|
M_BXF(2),
|
||||||
|
// temperature is within (T0 - max_temp_diff; T0 + max_temp_diff)
|
||||||
|
// stop ULP until the program timer starts it again
|
||||||
|
I_HALT(),
|
||||||
|
M_LABEL(2),
|
||||||
|
// temperature is out of bounds
|
||||||
|
// disable ULP program timer
|
||||||
|
I_WR_REG_BIT(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN_S, 0),
|
||||||
|
// initiate wakeup of the SoC
|
||||||
|
I_WAKE(),
|
||||||
|
// stop the ULP program
|
||||||
|
I_HALT()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load ULP program into RTC_SLOW_MEM, at offset 0
|
||||||
|
size_t size = sizeof(program)/sizeof(ulp_insn_t);
|
||||||
|
ESP_ERROR_CHECK( ulp_process_macros_and_load(0, program, &size) );
|
||||||
|
assert(size < ULP_DATA_OFFSET && "ULP_DATA_OFFSET needs to be greater or equal to the program size");
|
||||||
|
|
||||||
|
// Set ULP wakeup period
|
||||||
|
const uint32_t sleep_cycles = RTC_CNTL_SLOWCLK_FREQ / measurements_per_sec;
|
||||||
|
REG_WRITE(SENS_ULP_CP_SLEEP_CYC0_REG, sleep_cycles);
|
||||||
|
|
||||||
|
// Start ULP
|
||||||
|
ESP_ERROR_CHECK( ulp_run(0) );
|
||||||
|
}
|
||||||
|
#endif // CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP
|
||||||
|
|
7
examples/system/deep_sleep/sdkconfig.defaults
Normal file
7
examples/system/deep_sleep/sdkconfig.defaults
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
CONFIG_ESP32_DEFAULT_CPU_FREQ_80=y
|
||||||
|
CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=80
|
||||||
|
CONFIG_ULP_COPROC_ENABLED=y
|
||||||
|
CONFIG_ULP_COPROC_RESERVE_MEM=512
|
||||||
|
CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y
|
||||||
|
CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y
|
||||||
|
CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=500
|
Loading…
Reference in a new issue