-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Requesting a WAKE example #75
Comments
Interesting case :) The ULP code looks correct. (Except if you also want to stop the ULP timer from starting the ULP program again: as per here - but anyway, not related to your issue.) So my assumption is that ULP wakeup is not enabled. I see that you attempt to enable it with Looking at how Micropython's As I now look at the very latest MicroPython code, I see that support for waking up the main CPU by ULP was just added 5 days ago :) - (commit). But... until this is released in the next MicroPython version, I would try one thing (my go-to solution for everything of the ESP32 that MicroPython does not support): Try using the ULP to set the necessary register, i.e. do the This works for pretty much anything that I have tried so far (setting RTC GPIOs to the right mode, setting power configuration for the hall and temp sensor, configuring adc's, etc)... so it might work here too. Of course it might also not, because the register value at the time of going to sleep might impact some state elsewhere. But I think it's worth trying. I would guess the ULP code would be:
but I also see some other approach here in the ESP-IDF, that you can also try, if the above does not work:
|
I looked at the deepsleep() implementation, too, and convinced myself that it was only setting bits, not wiping out the whole wake up enable register ... but I find that code a little hard to follow so I'm not sure. I'm a little concerned my hardware might be too old for this. A few things I have come across seem to suggest wake on ULP isn't super reliable, and I'm not sure why. The chip reports itself as:
which I think might be pretty old. I am not certain that matters. Trying to set that flag from the ULP program is a really good idea, I'll try that next. The other thing I thought of is can I tell the python side to deepsleep(50000) then have the ULP program continuously update that number so it doesn't reach the timeout until the ULP halts. I guess my third workaround is to wire a pin from the ULP back to RST but that seems hacky. Will report back. |
I'm not proficient at this yet but I tried:
which I think is right, and it didn't change the result. I wonder if my chip is just too old. |
About the age of the chip - I am not sure it matters that much. Our assembler only supports the original ESP32 anyway (not the newer S2, C3, etc models), and the only change I am ware of in the ESP32 is that they had some revision 1, that fixed a few bugs. If I use So assuming it does not matter, what I am wondering about the code you tried is, whether it runs too early (before the main processor has entered sleep (and effectively overwritten the register content). Then again, your ULP program might re-run many times, so it should eventually get the right value into that register. So probably a wrong assumption. Perhaps try that "force" approach I mentioned last in my first comment? (also making sure it gets to run after the main processor is in deepsleep). If that does not work, then maybe the a wire to the RST pin is not too bad a solution (it should work), but it would prevent you from determining the correct wakeup reason. Regarding updating the deepsleep timeout value - interesting idea. Have you seen anywhere, where one can write/update that value? Lastly, what MicroPython version are you using? I have a few ESP32s (all are rev 1) with different MicroPython versions, so I could try your code on one of them to see if it works for me. |
I'm trying to keep the test as simple as possible, so what it does is calls WAIT 40000 (5 msec, assuming an 8 MHz clock) in a loop 1000 times (so 5 seconds total). I'll give you the short version. What I expect this to do is to put say "going to sleep" then reset the processor 5 seconds later. #define RTC_CNTL_LOW_POWER_ST_REG 0x3FF480C0
#define RTC_CNTL_WAKEUP_STATE_REG 0x3FF48038
#define RTC_CNTL_RDY_FOR_WAKEUP BIT(19)
entry:
// try forcing wake-on-ULP bit to enabled
WRITE_RTC_REG(RTC_CNTL_WAKEUP_STATE_REG, 21, 11, 0x200);
// Sleep loop initialization
MOVE r0, 1000
loop:
WAIT 40000 // at 8 MHz this should take 5 msec
SUB r0, r0, 1
jump wake_up, eq
jump loop
wake_up:
READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP)
AND r0, r0, 1
JUMP wake_up, eq // Retry until the bit is set
WAKE // Trigger wake up
HALT // Stop the ULP program then invoke it as:
|
Oh ... no. I haven't seen anything saying it's OK to do that. It's a multi-value register, there's no indication in the documentation about how safe it is for concurrent access and it's multiple registers, so I'm a little concerned this is a risky idea haha |
Deep sleep is entered here which calls this but I think code-wise this is a dead end, as indicated by this comment:
Because that function seems to take the wake up sources in as a parameter I think the only option (for now) is what you suggested: enabling ULP wakeup at the start of the ULP code itself. |
I added a few simple things to make sure at least my ULP program is running (it is).
I just slept for 1 second before dumping them out, then slept: ULP_MEM_BASE = 0x50000000
sleep(1)
print(hex(mem32[ULP_MEM_BASE+4] & 0xFFFFFFFF), hex(mem32[ULP_MEM_BASE+8] & 0xFFFFFFFF))
sleep(1)
print("going to sleep")
machine.deepsleep(0) I knew from your counter example that would work but I just wanted to try it. So that process takes 2 seconds and the ULP should wake it up 3 seconds after that (it still doesn't) |
Did I even tell you what my use case is? I'm making a wireless panic button, running off of a CR123A. That's why power consumption has to be minimal. A button wakes it up, but I want to use the ULP to debounce the button, and I also want to force a wake up once every hour or two just to report that the device is still communicating. |
OOOKAY I'm going to go ahead and close this now. I installed the latest NIGHTLY BUILD of MicroPython and it works. I don't understand why enabling wake-on-ulp in the ULP code didn't work. I think it should have. But regardless, it works. I still think we should add an example. This is your code, so let me know how to do it:
I definitely think this deserves an example, though It really enables a lot of battery-powered possibilities even if you want to stick to the micropython world. And figuring this out confused me, because while I'm an experienced microcontroller guy I haven't taken the time to dig into the esp32 - that's partly why I'm using micropython in the first place. So recipes help :) Let me know what you'd like to do. #
# Example of putting the main processor to sleep then waking it up
# via the ULP after a while
#
from esp32 import ULP
from machine import mem32
import machine
import esp32
from esp32_ulp import src_to_binary
source = """\
#define RTC_CNTL_LOW_POWER_ST_REG 0x3FF480C0
#define RTC_CNTL_WAKEUP_STATE_REG 0x3FF48038
#define RTC_CNTL_RDY_FOR_WAKEUP BIT(19)
#############################################################################
entry:
jump start
#############################################################################
#
# Declare ram usage here if desired
#
#############################################################################
#############################################################################
start:
MOVE r0, 2000
loop:
WAIT 40000 // at 8 MHz this is 5 msec, if we do this 1000 times that's 5 seconds
SUB r0, r0, 1
jump wake_up, eq
jump loop
wake_up:
READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP)
AND r0, r0, 1
JUMP wake_up, eq // Retry until the bit is set
WAKE // Trigger wake up
HALT // Stop the ULP program
"""
#
# Compile the ULP code
#
binary = src_to_binary(source)
# load_addr is where to start the program, entry_addr is an offset to
# the start point for cases where some memory (variables) are
# declared before the executable code begins
load_addr, entry_addr = 0, 0
ulp = ULP()
ulp.load_binary(load_addr, binary)
ulp.run(entry_addr)
esp32.wake_on_ulp(True)
print("going to sleep")
machine.deepsleep() |
Ok. Good to know the latest nightly build works. I am still curious however, if we could solve it another way. One thing I noticed is you were pointing to references in the pycom/esp-idf-2.0 repository. (Unless you were using that somewhere) the best place to look is in espressif's repository and look at version 4.x which is what MicroPython is built against these days. So the comment Now looking at your code, where you introduced the 5 second wait, I do have the feeling there is a race there between the ULP and the main processor exactly between the "ulp.run()" call and the "machine.deepsleep()" call. The The other thing I noticed is that this instruction is wrong:
The parameters would work for the
So in your code, the resulting processor instruction would have a low_bit of 21 (incorrect) and a high bit of 21+11-1 = 32 (not possible). So perhaps try
(which results in Of course, you got it working now with official support in the latest nightly MicroPython - so try the above only if you feel like it (it would be nice to know). Otherwise I will eventually get to trying it out, because I also have this need in one of my projects (just haven't gotten to it yet, to notice the missing |
Oh okay, I goofed up that macro for sure. I think you're right about that race condition. There's another way that might work, too, which is to have the ULP program start by spinning waiting for the READY TO WAKE bit to go active, where I check this:
or, possibly this other bit RTC_CNTL_MAIN_STATE_IDLE that I found described HERE - but that one is a bit murkier (it's in a reserved section). Oops about linking to the wrong code, doh. Still feeling my way around this ecosystem! Thanks for the correct pointer. |
I would like an example of how to use WAKE because I'm not quite sure how it's supposed to work. I'll contribute one if I can, but right now it doesn't work. Should it?
The text was updated successfully, but these errors were encountered: