Skip to content

Commit 4ad48fa

Browse files
Refactor USB pullup handling
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.
1 parent eeabfc1 commit 4ad48fa

File tree

6 files changed

+155
-42
lines changed

6 files changed

+155
-42
lines changed

cores/arduino/stm32/usb/usbd_if.c

+132-20
Original file line numberDiff line numberDiff line change
@@ -10,36 +10,148 @@
1010
#include "usbd_if.h"
1111
#include "usbd_cdc_if.h"
1212

13+
#if !defined(USBD_REENUM_DISABLED)
14+
15+
/*
16+
* Below, support for re-attaching to USB is handled. USB-attachment is
17+
* detected by the presence of a pullup on one of the USB datalines.
18+
*
19+
* To force the USB host to re-enumerate, we must detach (disable
20+
* pullup), wait a bit, and re-attach (enable pullup). This can be done
21+
* while running, but must also happen at startup (including the detach
22+
* and delay, since USB might have been active just before reset, so we
23+
* must force a re-enumeration). On a cold boot (e.g. USB just plugged
24+
* in), the forced detach might not be needed, but will not be harmful
25+
* either.
26+
*
27+
* For pullup control, a number of cases are supported:
28+
* 1. An external pullup controllable through a GPIO (e.g. using an
29+
* external transistor). The default output state (e.g. when the
30+
* GPIO is floating) can be enabled or disabled, and the output
31+
* polarity can be configured.
32+
* 2. A pullup built into the chip's USB core. This is automatically
33+
* enabled by hardware or USB HAL code, and can be manually
34+
* controlled using HAL functions.
35+
* 3. A fixed (always enabled) external pullup. To still allow detaching,
36+
* a bit of a hack is used: By setting the D+ to OUTPUT LOW, it
37+
* *looks* like the pullup is removed (the host has own, bigger,
38+
* pulldown, to detact detaching, so writing LOW looks the same).
39+
* This probably violates USB specifications, but does seem to work
40+
* well in practice. It probably also should only be used briefly,
41+
* keeping the pin OUTPUT LOW for longer might be problematic.
42+
*
43+
* These scenarios are considered in order: If the variant defines an
44+
* external pullup control pin, it is used, else if an internal pullup
45+
* is present, that is used, else the D+ trick is used.
46+
*
47+
* To configure #1, the variant should either define USBD_ATTACH_PIN and
48+
* USBD_ATTACH_LEVEL when the pullup is disabled by default and the pin
49+
* can be used to *attach*, or USBD_DETACH_PIN and USBD_DETACH_LEVEL
50+
* when the pullup is enabled by default and the pin can be used to
51+
* *detach*.
52+
*/
53+
54+
/* Compatibility with the old way to specify this */
55+
#if defined(USB_DISC_PIN) && !defined(USBD_ATTACH_PIN)
56+
#define USBD_ATTACH_PIN USB_DISC_PIN
57+
#define USBD_ATTACH_LEVEL LOW
58+
#warning "USB_DISC_PIN is deprecated, use USBD_ATTACH_PIN instead"
59+
#endif /* defined(USB_DISC_PIN) && !defined(USBD_ATTACH_PIN) */
60+
61+
/* Some sanity checks */
62+
#if defined(USBD_ATTACH_PIN) && defined(USBD_DETACH_PIN)
63+
#error "Cannot define both USBD_ATTACH_PIN and USBD_DETACH_PIN"
64+
#endif /* defined(USBD_ATTACH_PIN) && defined(USBD_DETACH_PIN) */
65+
#if defined(USBD_ATTACH_PIN) && !defined(USBD_ATTACH_LEVEL)
66+
#error "USBD_ATTACH_PIN also needs USBD_ATTACH_LEVEL defined"
67+
#endif /* defined(USBD_ATTACH_PIN) && !defined(USBD_ATTACH_LEVEL) */
68+
#if defined(USBD_DETACH_PIN) && !defined(USBD_DETACH_LEVEL)
69+
#error "USBD_DETACH_PIN also needs USBD_DETACH_LEVEL defined"
70+
#endif /* defined(USBD_DETACH_PIN) && !defined(USBD_DETACH_LEVEL) */
71+
72+
/* Either of these bits indicate that there are internal pullups */
73+
#if defined(USB_BCDR_DPPU) || defined(USB_OTG_DCTL_SDIS)
74+
#define USBD_HAVE_INTERNAL_PULLUPS
75+
#endif /* defined(USB_BCDR_DPPU) || defined(USB_OTG_DCTL_SDIS) */
76+
77+
/* Figure out which USB instance is used. This mirrors the decision made
78+
* in USBD_LL_Init in usbd_conf.c. */
79+
#if defined(USE_USB_HS)
80+
#define USBD_USB_INSTANCE USB_OTG_HS
81+
#elif defined(USB_OTG_FS)
82+
#define USBD_USB_INSTANCE USB_OTG_FS
83+
#elif defined(USB)
84+
#define USBD_USB_INSTANCE USB
85+
#endif
86+
87+
/*
88+
* Translate pin number to a pin name (using the same define for both
89+
* attach and detach pins) and also define the complementary level. This
90+
* allows using the same code for the default-enabled and
91+
* default-disabled case as well as for the D+ trick (which does not
92+
* know the pin number, only the pin name).
93+
*/
94+
#if defined(USBD_ATTACH_PIN)
95+
#define USBD_PULLUP_CONTROL_PINNAME digitalPinToPinName(USBD_ATTACH_PIN)
96+
#define USBD_DETACH_LEVEL !(USBD_ATTACH_LEVEL)
97+
#elif defined(USBD_DETACH_PIN)
98+
#define USBD_PULLUP_CONTROL_PINNAME digitalPinToPinName(USBD_DETACH_PIN)
99+
#define USBD_ATTACH_LEVEL !(USBD_DETACH_LEVEL)
100+
#elif !defined(USBD_HAVE_INTERNAL_PULLUPS)
101+
/* When no USB attach and detach pins were defined, and there are also
102+
* no internal pullups, assume there is a fixed external pullup and apply
103+
* the D+ trick. This should happen only for the USB peripheral, since
104+
* USB_OTG_HS and USB_OTG_FS always have internal pullups. */
105+
#if !defined(USB)
106+
#error "Unexpected USB configuration"
107+
#endif
108+
#define USBD_PULLUP_CONTROL_PINNAME USB_DP
109+
#define USBD_DETACH_LEVEL LOW
110+
// USBD_ATTACH_LEVEL not needed.
111+
#define USBD_DP_TRICK
112+
#endif
113+
13114
/**
14-
* @brief Force to re-enumerate USB
115+
* @brief Force to re-enumerate USB.
116+
*
117+
* This is intended to be called at startup by core code. It could be
118+
* used at runtime, while USB is connected, to force re-enumeration
119+
* too, but that does not work in all cases (when USB is enabled on an
120+
* F103C8, setting output mode on the DP pin no longer has any effect).
121+
*
15122
* @param None
16123
* @retval None
17124
*/
18-
19125
WEAK void USBD_reenumerate(void)
20126
{
21-
#ifndef USBD_REENUM_DISABLED
22-
/* Re-enumerate the USB */
23-
#ifdef USB_DISC_PIN
24-
pinMode(USB_DISC_PIN, OUTPUT);
25-
digitalWrite(USB_DISC_PIN, LOW);
127+
#if defined(USBD_PULLUP_CONTROL_PINNAME)
128+
/* Detach */
129+
pin_function(USBD_PULLUP_CONTROL_PINNAME, STM_PIN_DATA(STM_MODE_OUTPUT_PP, GPIO_NOPULL, 0));
130+
digitalWriteFast(USBD_PULLUP_CONTROL_PINNAME, USBD_DETACH_LEVEL);
131+
132+
/* Wait */
133+
delay(USBD_ENUM_DELAY);
134+
135+
/* Attach */
136+
#if defined(USBD_DP_TRICK)
137+
/* Revert back to input (floating), needed for the D+ trick */
138+
pin_function(USBD_PULLUP_CONTROL_PINNAME, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0));
26139
#else
27-
#ifdef USE_USB_HS_IN_FS
28-
PinName pinDP = USB_OTG_HS_DP;
29-
#elif defined(USB_OTG_FS)
30-
PinName pinDP = USB_OTG_FS_DP;
31-
#else /* USB */
32-
PinName pinDP = USB_DP;
33-
#endif
34-
pin_function(pinDP, STM_PIN_DATA(STM_MODE_OUTPUT_PP, GPIO_NOPULL, 0));
35-
digitalWriteFast(pinDP, LOW);
140+
digitalWriteFast(USBD_PULLUP_CONTROL_PINNAME, USBD_ATTACH_LEVEL);
141+
#endif /* defined(USBD_PULLUP_CONTROL_FLOATING) */
142+
#elif defined(USBD_HAVE_INTERNAL_PULLUPS)
143+
USB_DevDisconnect(USBD_USB_INSTANCE);
36144
delay(USBD_ENUM_DELAY);
37-
pin_function(pinDP, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0));
38-
/*delay(USBD_ENUM_DELAY);*/
39-
#endif /* USB_DISC_PIN */
40-
#endif /* USBD_REENUM_DISABLED */
145+
USB_DevConnect(USBD_USB_INSTANCE);
146+
#else
147+
#warning "No USB attach/detach method, USB might not be reliable through system resets"
148+
#endif
41149
}
42150

151+
#else /* !defined(USBD_REENUM_DISABLED) */
152+
WEAK void USBD_reenumerate(void) { }
153+
#endif
154+
43155
#ifdef USBD_USE_CDC
44156
void USBD_CDC_init(void)
45157
{

variants/Generic_F401Rx/variant.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,9 @@ extern "C" {
126126
#define PIN_SERIAL_TX PA9
127127

128128
#ifdef ARDUINO_CoreBoard_F401RC
129-
#define USB_DISC_PIN PD2
129+
// USB, pull this pin low to enable the USB attach pullup
130+
#define USBD_ATTACH_PIN PD2
131+
#define USBD_ATTACH_LEVEL LOW
130132
#endif
131133

132134
#ifdef __cplusplus

variants/MALYANM200_F103CB/variant.cpp

-19
Original file line numberDiff line numberDiff line change
@@ -86,25 +86,6 @@ const PinName digitalPin[] = {
8686
extern "C" {
8787
#endif
8888

89-
/**
90-
* Initialize the variant. Turn on PB9 for USB.
91-
*/
92-
void initVariant()
93-
{
94-
pinMode(PB9, OUTPUT);
95-
digitalWrite(PB9, 1);
96-
}
97-
98-
void USBD_reenumerate(void)
99-
{
100-
pinMode(PB9, OUTPUT);
101-
digitalWrite(PB9, HIGH);
102-
delay(10);
103-
digitalWrite(PB9, LOW);
104-
delay(10);
105-
digitalWrite(PB9, HIGH);
106-
}
107-
10889
/**
10990
* @brief System Clock Configuration
11091
* The system Clock is configured as follow :

variants/MALYANM200_F103CB/variant.h

+4
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ extern "C" {
105105
#define PIN_SERIAL_RX PA10
106106
#define PIN_SERIAL_TX PA9
107107

108+
// USB, pull this pin low to *disable* the USB attach pullup
109+
#define USBD_DETACH_PIN PB9
110+
#define USBD_DETACH_LEVEL LOW
111+
108112
/* Dedicated definitions */
109113
#ifndef MAX_PRIORITY
110114
#define MAX_PRIORITY 15

variants/MAPLEMINI_F103CB/variant.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,9 @@ extern "C" {
108108
#define PIN_SERIAL_RX PA10
109109
#define PIN_SERIAL_TX PA9
110110

111-
// USB
112-
#define USB_DISC_PIN PB9
111+
// USB, pull this pin low to enable the USB attach pullup
112+
#define USBD_ATTACH_PIN PB9
113+
#define USBD_ATTACH_LEVEL LOW
113114

114115
#ifdef __cplusplus
115116
} // extern "C"

variants/board_template/variant.h

+13
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,19 @@ extern "C" {
162162
// See AN4879 https://www.st.com/content/st_com/en/search.html#q=AN4879-t=resources-page=1
163163
//#define USBD_VBUS_DETECTION_ENABLE
164164

165+
// If the board has external USB pullup (on DP/DM depending on speed)
166+
// that can be controlled using a GPIO pin, define these:
167+
// - If the the pullup is disabled (USB detached) by default, define
168+
// USBD_ATTACH_PIN to the pin that, when written to
169+
// USBD_ATTACH_LEVEL, attaches the pullup.
170+
// - If the the pullup is enabled (attached) by default, define
171+
// USBD_DETACH_PIN to the pin that, when written to
172+
// USBD_DETACH_LEVEL, detaches the pullup.
173+
//#define USBD_ATTACH_PIN x
174+
//#define USBD_ATTACH_LEVEL LOW
175+
//#define USBD_DETACH_PIN x
176+
//#define USBD_DETACH_LEVEL LOW
177+
165178
#ifdef __cplusplus
166179
} // extern "C"
167180
#endif

0 commit comments

Comments
 (0)