Touch FSM measures different values on deep sleep on ESP32 (IDFGH-15276)
Answers checklist.
- [x] I have read the documentation ESP-IDF Programming Guide and the issue is not addressed there.
- [x] I have updated my IDF branch (master or release) to the latest version and checked that the issue is present there.
- [x] I have searched the issue tracker for a similar issue and not found a similar issue.
General issue report
I have a piece of code fully working to set up the Touch Wakeup from deep sleep. The code works if the threshold values are hardcoded manually, however when trying to read the sensor and setting it up that way, the value is completely different from the deep sleep one.
For example:
- The values using touchRead() for the 4 pads:
- 50,45,57,57
- When touching them: 30, 25, 37, 35
- The values read by using touch_ll_read_raw_data() just after deep sleep:
- 333, 257, 382, 416
- When touching them: 160, 100, 150, 130
The actual threshold values that wake up the device from deep sleep are approximately 200.
The device uses CONFIG_RTC_CLK_SRC_EXT_OSC=y, my theory is that the FSM timer CLK is different while the chip is on, or is in deep sleep, and because of that it takes a different amount of time, and measures a different value.
Example code I am using:
touch_pad_init();
touch_pad_set_voltage(TOUCH_HVOLT_2V4, TOUCH_LVOLT_0V5, TOUCH_HVOLT_ATTEN_1V);
touch_ll_set_meas_time(1024); //31ms measuring
touch_pad_set_measurement_interval(8 * 1024); // Every 250ms I guess, since 32k crystal
esp_sleep_enable_touchpad_wakeup();
touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER);
auto setTouchPad = [&](auto&& v) {
auto pad = (touch_pad_t)HW::Touch::Pad[mSettings.mMap[v]];
auto& m = mSettings.mMaxMeasures[mSettings.mMap[v]];
touch_pad_config(pad, m - 100);
};
// For all touch pads
setTouchPad(...);
...
deepsleep()
I am 99% sure this issue is due to a change in the reference clock during deep sleep.
I set my code to do:
// ..... wakeup
LOG(mTouch.readAll());
delay(3000); // The rate of measurement of the FSM is set to 1s, therefore a new measurement should be ready
LOG(mTouch.readAll());
// Set timer for deep sleep for 1s (no touch wakeup)
deep_sleep();
Therefore the device is deep sleeping "1s", Light sleeping "3s" and active around a few milliseconds.
I can see this graph when pluging an oscilloscope to the Touch pad:
This clearly shows 3 fast readings, and 1 slow reading. The slow reading is exactly 31ms.
That is the value I set in the touch (1024 / 32.768 Khz = 1/32 s = 31.25ms).
(And it matches the 100-300ish values, cycles)
But the other readings are much much faster:
Around 0.125ms, which would match with a clock of (1024 / 8MHz = 0.125ms).
Looks to me that the touch pad is using 8MHz RTC_FAST_CLK not in deep sleep and 32.768 KHz (external clock reference from my RTC clock) the rest of the time. How could I fix this?
I found this in the file a line that says the touch uses RTC_SLOW_CLK, this contradicts the ESP32 technical document saying that it uses RTC_FAST_CLK (8MHz): /components/hal/esp32/include/hal/touch_sensor_ll.h
/**
* Set touch sensor measurement time.
*
* @param meas_time The duration of the touch sensor measurement.
* t_meas = meas_time / (8MHz), the maximum measure time is 0xffff / 8M = 8.19 ms.
*/
static inline void touch_ll_set_meas_time(uint16_t meas_time)
{
//touch sensor measure time= meas_cycle / 8Mhz
HAL_FORCE_MODIFY_U32_REG_FIELD(SENS.sar_touch_ctrl1, touch_meas_delay, meas_time);
//the waiting cycles (in 8MHz) between TOUCH_START and TOUCH_XPD
HAL_FORCE_MODIFY_U32_REG_FIELD(SENS.sar_touch_ctrl1, touch_xpd_wait, TOUCH_LL_PAD_MEASURE_WAIT_MAX);
}
...
/**
* Set touch sensor sleep time (interval of measurement).
*
* @param sleep_time The touch sensor will sleep after each measurement.
* sleep_cycle decide the interval between each measurement.
* t_sleep = sleep_cycle / (RTC_SLOW_CLK frequency).
* The approximate frequency value of RTC_SLOW_CLK can be obtained using `rtc_clk_slow_freq_get_hz` function.
*/
static inline void touch_ll_set_sleep_time(uint16_t sleep_time)
{
//touch sensor sleep cycle Time = sleep_cycle / RTC_SLOW_CLK( can be 150k or 32k depending on the options)
HAL_FORCE_MODIFY_U32_REG_FIELD(SENS.sar_touch_ctrl2, touch_sleep_cycles, sleep_time);
}
I downgraded to v5.1.5 (I was originally on v5.4) and now everything is working fine, I will try to pinpoint the offending commit.
EDIT: After doing some git bisect I found out the ofending commit that after that the issue starts happenning: dd2bde0847592af3661ad276dc5c237cdc797c50: fix(esp_hw_support): fix lightsleep destroys deepsleep rtc parameters
This commit seems to alter the deepslepp RTC 8M clock parameters, after it was merged in v5.1 it started hapening. I think the same change was also added to v5.2/v5.3/v5.4/master, and is why it also occurs there.
@esp-wzh Can i draw your attention to this?
I am working on my own mirror of IDF, so i can cherry pick the revert of the commit, but lest see if we can properly fix it.
In theory, my change should not affect Touch function. Can you help me identify which register change in this commit affects your function? RTC_CNTL_PLL_BUF_WAIT, RTC_CNTL_XTL_BUF_WAIT or RTC_CNTL_CK8M_WAIT?
Maybe this errata is related to the problem you are encountering, FYI.
In theory, my change should not affect Touch function. Can you help me identify which register change in this commit affects your function?
RTC_CNTL_PLL_BUF_WAIT,RTC_CNTL_XTL_BUF_WAITorRTC_CNTL_CK8M_WAIT?
I will pinpoint the exact register and come back here. (I do not work on this as my main work so I need to do it on my spare time).
Maybe this errata is related to the problem you are encountering, FYI.
Very likely, yes, This matches my measurements, where the ULP/Touch (according to documentation are driven by RTC_FAST) are actually driven temporarily/permanently in deepsleep by RTC_SLOW , this causes the measurements to be completely different for a brief period of time.
However, I do not have the PD enabled, as per the errata, and I am not sure how this is linked with the issue, since the errata says "boot", but the issue I see is quite consistent (deep sleep always ~300 values, and boot always ~50 values) (roughly matching a change from 32Khz -> 150Khz (6x) ).
In theory, my change should not affect Touch function. Can you help me identify which register change in this commit affects your function?
RTC_CNTL_PLL_BUF_WAIT,RTC_CNTL_XTL_BUF_WAITorRTC_CNTL_CK8M_WAIT?
Seems like RTC_CNTL_CK8M_WAIT is the issue one. The other 2 values have no effect at all.
Replacing the line to use RTC_CNTL_CK8M_WAIT_SLP_CYCLES fixes the issue:
REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_CK8M_WAIT, RTC_CNTL_CK8M_WAIT_SLP_CYCLES);
I was trying to debug the issue myself, "RTC_CNTL_TIMER1_REG" seems undocumented in the technical manual.
@esp-wzh My current standing theory, is that reconfiguring the RTC_CNTL_CK8M_WAIT to "20" just before deep sleep, makes the timer wait "too much", making the 8M clock not enabled during deep sleep. Since the 8M is not enabled seems the Touch uses another clock (probably the Ext reference 32k).
This makes the deep sleep Touch values be much higher in comparison to the lightsleep/active state.
I have not tried but will try values between 5-19, or simply reordering the fields and report back.
Hi @DarkZeros
Did you call ESP_ERROR_CHECK(esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON)); to keep the RTC_PERIPH clock working during the deep sleep? Deleting this function might help according to the errata.
Hi @DarkZeros
Did you call
ESP_ERROR_CHECK(esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON));to keep the RTC_PERIPH clock working during the deep sleep? Deleting this function might help according to the errata.
Hi @L-KAYA I tried that a while back, without success, same issue.
According to the errata, (If I am understanding it correctly), is that the "ESP_PD_DOMAIN_RTC_PERIPH" must remain OFF, if it is ON (ie: using EXT0 wakeup), then the clock of TOUCH / ULP will be running on SLOW_CLK for brief period of time.
In my case it seems the clock is always wrong, rather than just a brief period of time. I am also not using EXT0 wakeup.
Can you add print esp_rom_printf("pd_flags:0x%08x\n",pd_flags); at esp_sleep_start to check if RTC_PERIPH is really powered down during sleep? @DarkZeros
Can you add print
esp_rom_printf("pd_flags:0x%08x\n",pd_flags);at esp_sleep_start to check if RTC_PERIPH is really powered down during sleep? @DarkZeros
I get 0x00000180 with my revert of the commit applied (everything working fine). -> REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_CK8M_WAIT, RTC_CNTL_CK8M_WAIT_SLP_CYCLES);
When I go back to the master/release branches I get: 0x00040180 Then, the issue starts.
@DarkZeros Thanks for feedback, BIT(1) is the control bit for power-down RTC_PERIPH power domain, so in both of your versions the RTC_PERIPH power domain is not powered down, you can trace back to see if there is code or driver in your project that requests to keep the RTC_PERIPH power domain during sleep via esp_sleep_pd_config, or you can try to set BIT(1) of pd_flags directly to force it to power off.
@DarkZeros Thanks for feedback, BIT(1) is the control bit for power-down RTC_PERIPH power domain, so in both of your versions the RTC_PERIPH power domain is not powered down, you can trace back to see if there is code or driver in your project that requests to keep the RTC_PERIPH power domain during sleep via
esp_sleep_pd_config, or you can try to set BIT(1) ofpd_flagsdirectly to force it to power off.
Thanks! Indeed this is the root of the problem, if I manually change the get_pd_flags() to return BIT(1) set, then the issue disappear, with both versions (my changed one and the upstream one).
However, I am unable to pinpoint why this is set, I have no piece of code or library in my project that sets it to true. In my problem I only briefly enable GPIO wakeup for lightsleep, lightsleep, disable the GPIO wakeup, then set the touch and go to sleep. Can that be the reason it is setting it to "ON" forcibly due to some previous sleep parameters ?
In order to disable this before deep sleep I tried:
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF);
esp_deep_sleep_start(); // pd_flags:0x00000180 // Still does not turn RTC_PERIPH down...
I had to set it to "AUTO" in order to turn off the domain, which is so strange:
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_AUTO);
esp_deep_sleep_start(); pd_flags:0x00000182 // yai!
To sum it up, yes, I was falling in the errata. However I tested the EXT0 + TOUCH + RTC_PERIPH on, and it seems the errata only applies to a small 1-2ms glitch issue during boot, It might affect heavily the ULP (since is a processor), but the TOUCH does not seem heavily affected, specially if the trigger comes from the TOUCH itself (and the next FSM measure is in 500ms+).
In my development I might just keep this small 1 lines fix to be able to have both at the same time for my use case. Since I was planing to use EXT0 wakeup in the future (I can use EXT1, but is more problematic to have 2 wakeups for EXT1 (one high and 1 low))
That makes sense, I guess it's because of the logic here,
The necessary conditions for this workaround to take effect are as follows:
- pd_option for RTC_PERIPH domain is ESP_PD_OPTION_AUTO.
- This deepsleep does not enable EXT0 or GPIO wakeup source.
- This deepsleep enabled TOUCH or ULP wakeup source.
So you need to make a trade-off between using the EXT0 wake-up function and the ULP coprocessor and touch sensor functionalities 😿
Thanks for all the help provided to pinpoint the issue. @esp-wzh @L-KAYA
I think the original confusion comes from the fact that setting RTC_PERIPH pd_option OFF does not turn it off. It has to be set as AUTO in order to be turned OFF. Is this intended?
To give more context: In my project i am using GPIO wakeup (for light sleep) and TOUCH wakeup (from deep sleep), but i was not modifying the pd_option manually.
It might be the RTC_PERIPH is left in a wrong state (Force ON) after the GPIO wakeup is used once.
And even though i was setting it manually to OFF , still was on in the end (since AUTO was needed).
I think the original confusion comes from the fact that setting RTC_PERIPH pd_option OFF does not turn it off. It has to be set as AUTO in order to be turned OFF. Is this intended?
I guess some other module requested to turn on the RTC_PERIPH power domain. The esp_sleep_pd_config API is used to maintain the config reference count of each power domain. You can add log print in it to figure out which module requested to turn it on. https://github.com/espressif/esp-idf/blob/67c1de1eebe095d554d281952fde63c16ee2dca0/components/esp_hw_support/sleep_modes.c#L2226