pyb.stop on REPL

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
stiege
Posts: 10
Joined: Sat Feb 04, 2017 6:22 am

pyb.stop on REPL

Post by stiege » Tue Feb 07, 2017 5:15 am

Code: Select all

MicroPython v1.8.7 on 2017-02-07; PYBv1.1 with STM32F405RG
Type "help()" for more information.
>>> def test():
...     pyb.RTC().wakeup(1000)
...     print("Before")
...     pyb.stop()
...     print("after")
...     pyb.RTC().wakeup(None)
...
>>> test()
<REPL hangs>
I would expect "Before" to be printed, then a wait of one second duration, followed by "after" being printed, and then the REPL returning. Instead it just seems to hang.

This is the minimal case; the code I'm actually writing is sleeping (pyb.stop()) between calling uart1.any() to check if there are any bytes to process, but this always seems to return 0.

The reasoning given at viewtopic.php?t=2388 indicates what I'm doing should be possible and has been tried before. I don't mind so much about the REPL not working (although it's a bit strange to me); but would be good if the UART would work.

Code: Select all

...
       self._rtc = pyb.RTC()
       self._uart = pyb.UART(1, 9600, read_buf_len=1024)
       self._rtc.wakeup(2000)
...
       while 1:
            self._led(1).on()
            if self._uart.any() < 512:
                pass  # Execution always flows through here (tested with LEDs)
            else:
                self._data += self._uart.read(self._uart.any())
                gprmc_start_loc = self._data.find(b"$GPRMC")
                gprmc_end_loc = self._data.find(b"\r\n", gprmc_start_loc)
                if gprmc_end_loc == -1:
                    self._data = b""
                else:
                    self._led(2).on()
                    with self._file() as f:
                        f.write(
                            self._data[gprmc_start_loc: gprmc_end_loc + 2])
                    self._data = self._data[gprmc_end_loc + 2:]
                    self._sync()
                    self._led(2).off()
            self._led(1).off()
            self._stop()
I should also say, I expected the UART's interrupt to make pyb.stop() return every time data was received (much more often than the 2 seconds my code is attempting), but this isn't what's happening.

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: pyb.stop on REPL

Post by dhylands » Tue Feb 07, 2017 5:33 am

The reason you don't see "Before" is because printing Before just puts it in the buffer for device-to-host data. That data won't actually get transferred to the host until it polls the device. But your code immediately goes into stop mode, so the data is never sent.

pyb.stop() puts the MCU in standby mode. There are only a few ways to get out of standby mode. From the datasheet:
WKUP pin rising edge, RTC alarm (Alarm A and Alarm B), RTC wakeup,
tamper event, time stamp event, external reset in NRST pin, IWDG reset
It sounds like what you want is pyb.wfi() (Wait for interrupt) which will wakeup whenever the next interrupt is received. The system generates an interrupt every millisecond, so you normally need to put the wfi call in a loop waiting for your condition to be satisfied.

Exiting stop mode resets the processor, so the code after pyb.stop() will never be executed.

stiege
Posts: 10
Joined: Sat Feb 04, 2017 6:22 am

Re: pyb.stop on REPL

Post by stiege » Tue Feb 07, 2017 5:41 am

Hi,

Thanks so much for your prompt response.

I am a bit confused now:
pyb.stop()
Put the pyboard in a “sleeping” state.

This reduces power consumption to less than 500 uA. To wake from this sleep state requires an external interrupt or a real-time-clock event. Upon waking execution continues where it left off. (emphasis mine)

See rtc.wakeup() to configure a real-time-clock wakeup event.
https://docs.micropython.org/en/latest/ ... p#pyb.stop

I did find the naming around stop and standby a bit confusing; but it looks like I should be able to continue execution using the stop() as I am with the rtc wakeup in my uart code at the top of the thread?

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: pyb.stop on REPL

Post by dhylands » Tue Feb 07, 2017 5:56 am

I was looking at the source code.

pyb.stop(): https://github.com/micropython/micropyt ... pyb.c#L157
machine_sleep: https://github.com/micropython/micropyt ... ine.c#L414

So that calls stop mode: https://github.com/micropython/micropyt ... ine.c#L447

I was looking at deepsleep, not sleep - my apologies. You were rightly confused.

For STOP mode, the datasheet says:
If WFI or Return from ISR was used for entry:
All EXTI lines configured in Interrupt mode (the corresponding EXTI
Interrupt vector must be enabled in the NVIC). The interrupt source can
be external interrupts or peripherals with wakeup capability. Refer to
Table 61: Vector table for STM32F405xx/07xx and STM32F415xx/17xx
on page 374.
There is a section called: "External interrupt/event line mapping" and it lists EXTI lines 16 thru 22 as possible EXTI sources. UART interrupts aren't one of the external interrupts listed.
• EXTI line 16 is connected to the PVD output
• EXTI line 17 is connected to the RTC Alarm event
• EXTI line 18 is connected to the USB OTG FS Wakeup event
• EXTI line 19 is connected to the Ethernet Wakeup event
• EXTI line 20 is connected to the USB OTG HS (configured in FS) Wakeup event
• EXTI line 21 is connected to the RTC Tamper and TimeStamp events
• EXTI line 22 is connected to the RTC Wakeup event

stiege
Posts: 10
Joined: Sat Feb 04, 2017 6:22 am

Re: pyb.stop on REPL

Post by stiege » Tue Feb 07, 2017 8:07 am

Thanks so much.

If I'm interpreting the datasheet correctly it looks like my options are:

[*] Use UART4 and configure the SYSCFG_EXTICR1 register accordingly to gate PA1 as an event
[*] Use wfi
[*] Find another way

Using wfi seems to increase current consumption for me, so I guess I'm stuck with the other options.

Cheers

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: pyb.stop on REPL

Post by dhylands » Tue Feb 07, 2017 8:25 am

I think using EXTI on the UART Rx pin should work, but I haven't tested it myself.

You could set the ExtInt callback before calling pyb.stop() and clear it after calling stop (that way it won't continue to generate interrupts once you've woken up).

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: pyb.stop on REPL

Post by pythoncoder » Tue Feb 07, 2017 8:35 am

@stiege If you're interested in the ultra low power standby mode you might like to read some docs and code samples I put together here https://github.com/peterhinch/micropython-micropower. I suspect that for your application you may need to use stop mode and accept the higher current consumption as a tradeoff for easier waking.

I have three Pyboards each running for a year on three AA cells so it can be done.
Peter Hinch
Index to my micropython libraries.

stiege
Posts: 10
Joined: Sat Feb 04, 2017 6:22 am

Re: pyb.stop on REPL

Post by stiege » Tue Feb 07, 2017 8:40 am

Thanks @pythoncoder; I've perused those already but I suspect I'll be studying them in detail soon...

stiege
Posts: 10
Joined: Sat Feb 04, 2017 6:22 am

Re: pyb.stop on REPL

Post by stiege » Tue Feb 07, 2017 9:29 pm

Thanks for your help on this.

In the end I did use pyb.standby() and went to sleep for 10 seconds at a time before waking to read just one output from the GPS. This meant that the MCU power consumption was now small when compared to the GPS constant draw of 25 - 30 mA or so, so good enough for my purposes.

I note that the STM32L4x6 reference manual has much more detail on lower power effects on the USART than the STM32F405 reference manual. Hopefully that means doing a similar task with that micro will be a tad easier.

I take it micropython in a low power run mode on the STM32L is out of the question (have to power down flash), so I think I'm planning to attack the problem by instead writing a custom module that just enters the low power run mode and executes some hard coded C. Would be great to be able to exit low power run mode and restore the micropython state as it was before I entered. Is that within the realm of possibilities? A lot of work?

Post Reply