Skip to content

Refactor USB pullup handling #997

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

Merged
merged 1 commit into from
Mar 30, 2020

Conversation

matthijskooijman
Copy link
Contributor

Previously, variants could define USB_DISC_PIN when they had an USB
attach pullup that was disabled by default and needed a pin to
be written LOW to enable it. Other hardware configurations could only
overwrite the USBD_reenumerate function, like the M200 board did.

This commit makes the pullup configuration more flexible. By defining
the appropriate macros, enabled-by-default and disabled-by-default
pullups are both supported. The output level of the pin can also be
configured. Finally, for disabled-by-default, the output mode for the
enabled state can also be configured (INPUT to leave the pin floating
and let any external transistor gate pullup enable the USB pullup, or
OUTPUT to keep the pin as OUTPUT an actively drive the transistor gate).

The new macros also take PinName constants rather than pin numbers. This
allows merging the code that controls an external pullup with the code
that writes directly to the USB DP pin to fake a disabled pullup (this
code only knows the pin name of the DP pin, not the number).

Finally, for CPUs that have internal USB pullup (as indicated by the
presence of a SDIS config bit), the write-to-DP-trick is now not
performed (since the pullup is automatically managed by the USB hardware
already).

This fixes #885, also see that issue for discussion leading up to this
change.

@matthijskooijman matthijskooijman force-pushed the usb-pullup-configuration branch from 705d9b6 to fd33f2c Compare March 19, 2020 17:08
@fpistm fpistm requested a review from ABOSTM March 19, 2020 17:14
@fpistm fpistm added the enhancement New feature or request label Mar 19, 2020
@matthijskooijman matthijskooijman force-pushed the usb-pullup-configuration branch 2 times, most recently from 45e645f to 9029509 Compare March 19, 2020 17:18
@matthijskooijman
Copy link
Contributor Author

matthijskooijman commented Mar 19, 2020

I've only done limited testing on this PR so far. I only have access to an F4 board with native USB, which has internal pullups and works well. I did do compile testing for a few other boards.

I've ordered a blue pill and black pill to test, but it would be good to also test on a maple mini board (since that has external pullup) and the MALYAN M200 V1 (since that used to have special pullup handling). @xC0000005 maybe you could do the latter?

(I also did a few force pushes to fix style errors, should be good now).

@matthijskooijman matthijskooijman force-pushed the usb-pullup-configuration branch from 9029509 to 0f8b172 Compare March 19, 2020 17:24
@xC0000005
Copy link
Contributor

xC0000005 commented Mar 19, 2020 via email

@fpistm
Copy link
Member

fpistm commented Mar 19, 2020

And I will test on Maple. 😉

@matthijskooijman
Copy link
Contributor Author

#define USB_DISC_PIN PB9
// USB, pull this pin low to enable the USB attach pullup
#define USBD_ATTACH_PINNAME PB_9
#define USBD_ATTACH_LEVEL LOW
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not entirely sure how this works, but shouldn't one of these be low and the other high?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean? The PINNAME indicates the pin to control the pullup, and the LEVEL indicates the level it needs to have to ATTACH. What should be HIGH here? Also see the comments in variants/board_template/variant.h.

@matthijskooijman
Copy link
Contributor Author

Sorry for the short comment earlier, I was just on my way out.

Turns out with my earlier compile-testing, I had forgotten to enable USB (for our custom board we have it enabled by default, didn't realize that the default boards did not). I did that now and found a dozen other preprocessor typos, I messed up the _PIN to _PINNAME rename and some other stuff. I should really have tested this more thoroughly, sorry.

Anyway, I pushed a fixup commit that at least compiles properly for the bluepill, maple mini and M200, so that should make testing easier.

Thanks!

@xC0000005
Copy link
Contributor

xC0000005 commented Mar 19, 2020 via email

@matthijskooijman matthijskooijman force-pushed the usb-pullup-configuration branch from e770db4 to 9505ec4 Compare March 20, 2020 08:21
@matthijskooijman
Copy link
Contributor Author

matthijskooijman commented Mar 20, 2020

Thanks for testing :-)

I've done another push, to squash the fixup commit I pushed yesterday (only changes history, not the actual code).

Copy link
Contributor

@ABOSTM ABOSTM left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @matthijskooijman
Thanks for this PR.
There is a remaining definition of USB_DISC_PIN in variant: Generic_F401Rx
Even if you handled compatibility with this definition (That is smart) it is better to use new implementation.

@fpistm
Copy link
Member

fpistm commented Mar 20, 2020

Tested with a Maple mini and works as expected.

@matthijskooijman
Copy link
Contributor Author

Thanks for the testing and reviews. I've also tested on a bluepill, which works as expected too.

There is a remaining definition of USB_DISC_PIN in variant: Generic_F401Rx

Ah, I started working on top of an older branch where that variant did not exist yet. I'll convert it.

The new macros also take PinName constants rather than pin numbers. This
allows merging the code that controls an external pullup with the code
that writes directly to the USB DP pin to fake a disabled pullup (this
code only knows the pin name of the DP pin, not the number).

I thought a bit more on this, but I think it might be nicer to specify pin numbers rather than pin names in the variant, which is more consistent and thus also less likely to cause mistakes. In terms of code that just means the PIN constants must be redefined as PINNAME constants in usbd_if.c, which should just be a few more lines but nothing spectacular (and just one extra lookup at runtime, but that's ok).

@matthijskooijman
Copy link
Contributor Author

Ok, I pretty much rewrote the entire thing. In a nutshelll:

  • All three hardware configurations (builtin pullups, external fixed pullups and external controllable pullups) are now handled in the same way: Detach, wait, attach.
  • A variant-specified control pin is now always push-pull, the "open-collector" treatment is applied only for the D+ output trick.
  • Variants can now just configure pin numbers instead of pin names.

I tested this on an F3 with internal pullups and on a bluepill. Testing on the MALYAN printers and a maple mini again would be appreciated.

For some more details about what I did and tried, read on.

Nitpicking: those sanity check should be under switch : #ifndef USBD_REENUM_DISABLED

I now moved all of the code under this check, with just an empty USBD_reenumerate() function in the else.

Internal pullups

I found that the internal pullups can be controlled portably using the USB_DevConnect and USB_DevDisconnect LL HAL functions, which the code now does.

Looking through the HAL code I found that the SDIS bit is used only by "USB_OTG_FS" and "USB_OTG_HS" peripherals, apparently some "USB" peripherals have an internal pullup controlled by the "USB_BCDR_DPPU" bit, so I adapted the code to check for both. I used git grep -A 10 USB_DevConnect.*\)$ to check that no other bits are used for pullup control.

Even more, I found that all USB_OTG_HS and USB_OTG_FS peripherals have the SDIS bit, so there is really no need to have code to figure out the D+ trick for them (so I removed that code). I confirmed this observation using a check script I wrote today which compiles a short header file against all known STM32 cpus (see the script for info). I let the script compile this, which worked for all boards:

#include <stm32_def.h>

#if (defined(USB_OTG_FS) || defined(USB_OTG_HS)) && !defined(USB_OTG_DCTL_SDIS)
#error "Foo"
#endif

I have not been able to test the DPPU bit, in particular I'm not sure how it behaves on startup when the USB peripheral is not enabled yet (I think it will just be a no-op, but it might end up enabling the USB pullup earlier than normal, though that should not be a problem either. I see that the STM32F070x6/B has this bit, which includes the MALYAN M200 V2, so that provides a nice opportunity to test this (also, it explains why these V2 boards have no external pullup, since it is included in the CPU now).

I also considered the option of overriding the USB_DevConnect and USB_DevDisconnect functions with our custom code to control the external pullup (or apply the D+ trick), since they are always called from the PCD code (even on chips that do not have internal pullups). However, the dummy definitions of these functions on chips without internal pullups are not defined weak, so we cannot actually override them.

Re-enumerating while connected

The code as it is now, should be usable to re-enumerate while running (instead of just at reset) too. I don't think we should actively advertise this, but it is interesting to know if it would work.

This works well with the F301RE with SDIS support I have, so it seems the USB stack handles external re-attachment fine. There did seem to be a small issue, possibly with buffering while disconnected (I got some duplicate serial output when the port was reconnected), but I did not investigate this.

I also tried on a bluepill, which has the D+ output trick. Switching to OUTPUT and back to INPUT would of course not work, since then the USB peripheral would lose control of the pin. So I tried using the USB pinmap to switch the pin back to USB AF mode.

However, detaching by switching to OUTPUT did not even work on F103C8, since it seems that the USB peripheral (when enabled) forcibly claims the DP pin, regardless of the AF settings. Even when I switch it to output and leave it at that, USB serial keeps working normally. Even more, the pin map in the variant does not even bother to set the pins into AF mode and just leaves them in input (verified using debugger, so there is no bit of code elsewhere that does initialize to AF). I could not find any reference to this in the documentation, and just one mention of it online, but that thread quickly focuses on getting things to work on hardware that does not seem to misbehave in this way. Since I could not test this properly, and the code was a bit fragile anyway (it would put pins in USB mode during startup, way before the USB peripheral was initialized), I reverted this, the pin mode is now reverted back to INPUT and intended to work at reset time only.

I have no board with DPPU support to test with, though. In case anyone wants to test, here is the sketch I used:

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  while (!Serial);
}

unsigned i = 0;

extern "C" void USBD_reenumerate(void);

void loop() {
  // put your main code here, to run repeatedly:
  Serial.println(i++);
  delay(1000);
  if (i % 10 == 0) {
      USBD_reenumerate();
  }
}

Copy link
Contributor

@ABOSTM ABOSTM left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM great job !!
Minor typo in comment

@ABOSTM
Copy link
Contributor

ABOSTM commented Mar 24, 2020

I tested your sketch with PNUCLEO_WB55RG which supports DPPU , it is working !!

@fpistm
Copy link
Member

fpistm commented Mar 25, 2020

Testing on the MALYAN printers and a maple mini again would be appreciated.

@matthijskooijman I've tested the maple and it works as expected with your sketch.

@xC0000005 did you made the test on Mylyan ?

@xC0000005
Copy link
Contributor

xC0000005 commented Mar 26, 2020 via email

Copy link
Member

@fpistm fpistm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.
Wait typo fix raised by @ABOSTM before merge.

Previously, variants could define USB_DISC_PIN when they had an USB
attach pullup that was disabled by default and needed a pin to
be written LOW to enable it. Other hardware configurations could only
overwrite the USBD_reenumerate function, like the M200 board did.

This commit makes the pullup configuration more flexible. By defining
the appropriate macros, enabled-by-default and disabled-by-default
pullups are both supported. The output level of the pin can also be
configured.

The code that controls an external pullup is now merged with the code
that applies the D+ output trick for fixed pullups, since the behaviour
is almost identical (except for reverting to INPUT mode for the D+ pin
instead of inverting the output value).

Finally, for CPUs that have internal USB pullup (as indicated by the
presence of a SDIS or DPPU config bit), the write-to-DP-trick is now not
performed, but the internal pullup is toggled instead.

This fixes stm32duino#885, also see that issue for discussion leading up to this
change.
@matthijskooijman matthijskooijman force-pushed the usb-pullup-configuration branch from 4ad48fa to 7802310 Compare March 28, 2020 16:42
@matthijskooijman
Copy link
Contributor Author

W00ps, forgot about the typo. Force pushed a version just fixing the typo, should be good to merge now.

@fpistm fpistm requested a review from ABOSTM March 28, 2020 16:48
Copy link
Contributor

@ABOSTM ABOSTM left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @matthijskooijman
LGTM

@fpistm fpistm added this to the 1.9.0 milestone Mar 30, 2020
@fpistm fpistm merged commit e1d409f into stm32duino:master Mar 30, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Generalize USB pullup attach/detach support
4 participants