-
-
Notifications
You must be signed in to change notification settings - Fork 8.3k
stm32/powerctrl: Add USB support to lightsleep. #8304
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
base: master
Are you sure you want to change the base?
Conversation
This is a good fix to the USB issue in low-power. I see a different behavior with stm32 boards on Linux, the device disconnects and reconnects eventually, however it seems the OS thinks it's broken, and logs a lot of errors, like these: [13891.998002] usb 4-2: reset full-speed USB device number 33 using xhci_hcd
[13892.129044] usb 4-2: device descriptor read/64, error -71
[13892.365742] usb 4-2: device descriptor read/64, error -71
....
[13895.324991] usb 4-2: New USB device found, idVendor=f055, idProduct=9800, bcdDevice= 2.00
[13895.325003] usb 4-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[13895.325006] usb 4-2: Product: Pyboard Virtual Comm Port in FS Mode
[13895.325009] usb 4-2: Manufacturer: MicroPython With your patch the OS detects that the device is disconnected: [14491.508322] usb 4-2: USB disconnect, device number 40
[14496.736220] usb 4-2: new full-speed USB device number 41 using xhci_hcd
[14496.911856] usb 4-2: New USB device found, idVendor=f055, idProduct=9800, bcdDevice= 2.00
[14496.911868] usb 4-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[14496.911871] usb 4-2: Product: Pyboard Virtual Comm Port in FS Mode
[14496.911874] usb 4-2: Manufacturer: MicroPython |
That's a shame it's not re-connecting in linux, could you test it a couple more times to be sure? Perhaps a more thorough reconfiguration of usb is warranted, though I was trying to avoid that. |
But it does reconnect, with or without this patch. However, with this patch it's more consistent and the OS detects the disconnection so it doesn't log any errors before reconnecting.
I tested it a few times, on Linux, and it seems to be working great, much better than before for sure. |
Oh, I thought from your message that on linux it was stuck disconnected, but I looked again at the log messages and yes, that looks better; explicit disconnect then reconnect. |
This looks like a good fix, because But testing it now, it looks like Linux has improved the USB driver behaviour, because without this patch I'm able to run Then testing with this PR, doing the So... not sure what to think about this. Maybe it's just the host USB driver that is the issue and now it's fixed (on Linux at least)? How do things behave on Windows? |
On windows for me the lightsleep() call has always caused the USB connection to stop responding. After sleep, I get no data in or out on repl. The device always appears to be working otherwise though, lights flash as expected / responds to buttons etc. This has been a long term issue on a couple of projects of mine on Windows, I've had multiple stm32 micropython platforms with the issue, on different windows computers / versions. That's interesting that Linux is working better for you, in that sense this patch is a regression in behaviour - I'd much prefer the existing connection continue. I wonder if there's any signal that could be sent on USB to "ping" the computer. I've briefly looked into the USB suspend/resume signalling, though I think that's mostly supposed to be host initiated. Perhaps something like this could work better though. |
Yes this PR is a regression for me, but not to the extent that it blocks this PR. The approach here might still be a good compromise. But the fact that it works ok for me on Linux means the ST USB peripheral can handle STOP mode without any issue, in particular without re-enumerating. Note that if the board is using an external PHY (eg HS PHY) then I think the link with the host PC is maintained during STOP mode, so this PR should perhaps only disconnect if the PHY is internal. |
if (usb_connected) { | ||
pyb_usb_dev_disconnect(); | ||
} | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be called at the very start of this function, before IRQs are disabled. It's a high level operation.
pyb_usb_dev_connect(); | ||
} | ||
#endif | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be called after IRQs are enabled, to mirror the disconnect.
No not really, and I think the host driver is supposed to do this because the device just stops responding abruptly, so from the host's point of view the device could be malfunctioning. I'm now on Linux
With this patch, this doesn't happen on long sleep. When the device wakes up it just reconnects (note the timestamp)
This is a very good point, I have a board with external HS phy, and in my code I have ST low-level ULPI code that I use to switch the phy to low-power mode on entry to stop mode, and out of low-power mode on exit from stop mode, so this patch is not needed for this board and similar boards with external phy which should do the same for the lowest current. So I think code in this patch (on entry/exit to stop mode) should just be in the board specific Also worth mentioning, when switching the external phy to low-power/normal mode it seems to disconnect/reconnect the device, just like this patch does, which may indicate that this is the right way to do it. H7 + HS Phy in low-power + stop mode, sleep 15 seconds: [ 3928.154385] usb 4-1: USB disconnect, device number 2
[ 3943.701293] usb 4-1: new high-speed USB device number 3 using xhci_hcd
[ 3943.842729] usb 4-1: config 1 interface 0 altsetting 0 endpoint 0x82 has an invalid bInterval 32, changing to 9
[ 3943.842741] usb 4-1: config 1 interface 1 altsetting 0 bulk endpoint 0x1 has invalid maxpacket 64
[ 3943.842746] usb 4-1: config 1 interface 1 altsetting 0 bulk endpoint 0x81 has invalid maxpacket 64
[ 3943.844700] usb 4-1: New USB device found, idVendor=2341, idProduct=005b, bcdDevice= 2.00
[ 3943.844706] usb 4-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 Side note, I see some USB config issues, not related to stop mode/low-power. |
I've found a number of online references to similar issues on a number of embedded devices, particularly for usb-cdc. The host always sends regular USB requests to a CDC device, of they're not ack/nak'd you run into trouble. Suspend/resume is often used to trigger sleep on embedded devices, but can only been host initiated, not device. Perhaps a possible better solution for lightsleep (stop1) would be to enable usb interrupt wake so the USB IRQ/connection is serviced. The lightsleep function can query the wake-up source (WUF: Wakeup flag) and if it was USB, go back to sleep for remainder of set time (if rtc is desired timeout) or until other external wake-up trigger etc. |
The code might need to check at runtime if the USB has an external PHY or not, because there may be multiple USB ports active and only those with internal PHYs need to be disabled.
That sounds good but I don't think it works, because I think the USB wake interrupt is for when the host puts the device to sleep and then wakes it up again. I have never seen this interrupt fire (maybe because the USB devices we define don't indicate that they can go to sleep...). But the bigger question is: why does def my_lightsleep(t):
pyb.usb_mode(None)
machine.lightsleep(t)
pyb.usb_mode("VCP") |
Mainly for development and testing, but a board that's connected to an SBC via USB maybe ? |
Others online have pointed to when running from a battery powered host, wanting to reduce power. It's also just a gotcha for new users - we ran into a problem with a project where our usb serial logging would just fail after a while, it took a long time before we found it was caused by lightsleep(). |
3e8cf44
to
9cb3d30
Compare
9cb3d30
to
e4ef97d
Compare
Fixed compilation error when FF_MAX_SS != FF_MIN_SS
Coming back to this discussion after a few years (!!), the rp2 port handles this situation differently and keeps USB alive during |
Oh that is interesting, I'm not sure if some/all stm can support this but it's worth looking into certainly. |
At the very least I think we should make the stm32 port match rp2 from a users perspective, in the sense that it should be possible to run There's a lightsleep test for rp2 in |
A quick read of the a basic F4 reference manual does not look overly promising. In summary, USB (along with any other 48Mhz peripherals) is clocked by PLL48CLK which is derived from HSE or HSI.
An alternative might be to check if USB is in use, if so swap out STOP mode for:
Which has two entry modes, basic
I tried a quick / naieve hack // HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
__SEV();
__WFE(); But not surprisingly
exits back to repl immediately. So as expected WFE would need some other careful management to disable/enable undesired wake triggers before/after the call. |
Currently on stm32, if usb cdc repl is in use when
machine.lightsleep()
is run, the reply locks up and never recovers.The micro itself will wake up and continue on with the code, but no data can be read/written to USB.
The PC thinks the USB is still connected, however no data is transferred.
If the USB is unplugged / replugged the connection recovers - assuming the device is self-powered it continues working with a normal re-connection of the USB, a reboot is not required.
This PR fixes the issue by explicitely disconnecting / reconnecting the USB in code before and after the STOP call.
While it would be nicer to not need the PC connection to disconnect / reconnect I'm not sure this is possible.