From 26d8fe43835557b5bf56a97ed830660aed1b92c6 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Sun, 14 Sep 2025 22:51:22 +0200 Subject: [PATCH] cpu/stm32/usbdev_fs: allow inverted disconnect GPIO The STM32F3 requires a dedicated digital signal to emulate a disconnect event by pulling D+ down via a 1.5 kOhm resistor. Some boards, such as the OLIMEXINO-STM32F3, do not directly connect a GPIO but place a transistor in between. Depending on the exact implementation, the logic level may end up being inverted compared to directly connecting a GPIO. This adds a flag member to the `stm32_usbdev_fs_config_t` and a new flag to indicate inverted logic. In addition the members in the struct are sorted by alignment, as this is a foolproof algorithm to prevent wasting memory on unneeded padding. Finally, the USB driver is adapted to honor the flag. Co-authored-by: crasbe --- boards/common/blxxxpill/include/periph_conf.h | 4 ++-- boards/nucleo-f303ze/include/periph_conf.h | 4 ++-- boards/p-nucleo-wb55/include/periph_conf.h | 4 ++-- cpu/stm32/include/periph/cpu_usbdev.h | 18 +++++++++++++++--- cpu/stm32/periph/usbdev_fs.c | 6 +++--- 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/boards/common/blxxxpill/include/periph_conf.h b/boards/common/blxxxpill/include/periph_conf.h index 99b448975d78..ac8dbb29af54 100644 --- a/boards/common/blxxxpill/include/periph_conf.h +++ b/boards/common/blxxxpill/include/periph_conf.h @@ -343,12 +343,12 @@ static const stm32_usbdev_fs_config_t stm32_usbdev_fs_config[] = { { .base_addr = (uintptr_t)USB, .rcc_mask = RCC_APB1ENR_USBEN, - .irqn = USB_LP_CAN1_RX0_IRQn, - .apb = APB1, .dm = GPIO_PIN(PORT_A, 11), .dp = GPIO_PIN(PORT_A, 12), .af = GPIO_AF_UNDEF, .disconn = GPIO_UNDEF, + .irqn = USB_LP_CAN1_RX0_IRQn, + .apb = APB1, }, }; diff --git a/boards/nucleo-f303ze/include/periph_conf.h b/boards/nucleo-f303ze/include/periph_conf.h index 4710f4146065..3005f98dafcf 100644 --- a/boards/nucleo-f303ze/include/periph_conf.h +++ b/boards/nucleo-f303ze/include/periph_conf.h @@ -176,12 +176,12 @@ static const stm32_usbdev_fs_config_t stm32_usbdev_fs_config[] = { { .base_addr = (uintptr_t)USB, .rcc_mask = RCC_APB1ENR_USBEN, - .irqn = USB_LP_CAN_RX0_IRQn, - .apb = APB1, .dm = GPIO_PIN(PORT_A, 11), .dp = GPIO_PIN(PORT_A, 12), .af = GPIO_AF14, .disconn = GPIO_PIN(PORT_G, 6), + .irqn = USB_LP_CAN_RX0_IRQn, + .apb = APB1, }, }; diff --git a/boards/p-nucleo-wb55/include/periph_conf.h b/boards/p-nucleo-wb55/include/periph_conf.h index 77b425365779..031a37dc4f83 100644 --- a/boards/p-nucleo-wb55/include/periph_conf.h +++ b/boards/p-nucleo-wb55/include/periph_conf.h @@ -141,12 +141,12 @@ static const stm32_usbdev_fs_config_t stm32_usbdev_fs_config[] = { { .base_addr = (uintptr_t)USB, .rcc_mask = RCC_APB1ENR1_USBEN | RCC_APB1ENR1_CRSEN, - .irqn = USB_LP_IRQn, - .apb = APB1, .dm = GPIO_PIN(PORT_A, 11), .dp = GPIO_PIN(PORT_A, 12), .af = GPIO_AF10, .disconn = GPIO_UNDEF, + .irqn = USB_LP_IRQn, + .apb = APB1, }, }; diff --git a/cpu/stm32/include/periph/cpu_usbdev.h b/cpu/stm32/include/periph/cpu_usbdev.h index 8e97b0b440bd..400615e97884 100644 --- a/cpu/stm32/include/periph/cpu_usbdev.h +++ b/cpu/stm32/include/periph/cpu_usbdev.h @@ -10,7 +10,7 @@ #pragma once /** - * @ingroup cpu_stm32 + * @ingroup cpu_stm32_usbdev * @{ * * @file @@ -38,18 +38,30 @@ extern "C" { */ #define USBDEV_CPU_DMA_REQUIREMENTS __attribute__((aligned(USBDEV_CPU_DMA_ALIGNMENT))) +/** + * @name Flags used in stm32_usbdev_fs_config_t + * @{ + */ +/** + * @brief Indicates that logic levels are inverted compared to a GPIO connected + * directly via a 1.5 kOhm resistor to the USB D+ signal. + */ +#define STM32_USBDEV_FS_CONFIG_FLAG_DISCONN_INVERTED 0x01 +/** @} */ + /** * @brief stm32 USB device FS configuration */ typedef struct { uintptr_t base_addr; /**< USB peripheral base address */ uint32_t rcc_mask; /**< bit in clock enable register */ - uint8_t irqn; /**< IRQ channel */ - uint8_t apb; /**< APB bus */ gpio_t dm; /**< Data- gpio */ gpio_t dp; /**< Data+ gpio */ gpio_af_t af; /**< Alternative function */ gpio_t disconn; /**< GPIO if used for USB disconnect */ + uint8_t irqn; /**< IRQ channel */ + uint8_t apb; /**< APB bus */ + uint8_t flags; /**< Configuration flags */ } stm32_usbdev_fs_config_t; #ifdef __cplusplus diff --git a/cpu/stm32/periph/usbdev_fs.c b/cpu/stm32/periph/usbdev_fs.c index 5ffd3d837741..88cd6743d49f 100644 --- a/cpu/stm32/periph/usbdev_fs.c +++ b/cpu/stm32/periph/usbdev_fs.c @@ -240,7 +240,7 @@ static void _enable_gpio(const stm32_usbdev_fs_config_t *conf) /* In case the MCU has no internal D+ pullup, a GPIO is used to * connect/disconnect from USB bus */ gpio_init(conf->disconn, GPIO_OUT); - gpio_clear(conf->disconn); + gpio_write(conf->disconn, (conf->flags & STM32_USBDEV_FS_CONFIG_FLAG_DISCONN_INVERTED)); } } @@ -283,7 +283,7 @@ static inline void _usb_attach(stm32_usbdev_fs_t *usbdev) #else /* If configuration uses a GPIO for USB connect/disconnect */ if (conf->disconn != GPIO_UNDEF) { - gpio_set(conf->disconn); + gpio_write(conf->disconn, !(conf->flags & STM32_USBDEV_FS_CONFIG_FLAG_DISCONN_INVERTED)); } #endif /* USB_BCDR_DPPU */ } @@ -301,7 +301,7 @@ static inline void _usb_detach(stm32_usbdev_fs_t *usbdev) #else /* If configuration uses a GPIO for USB connect/disconnect */ if (conf->disconn != GPIO_UNDEF) { - gpio_clear(conf->disconn); + gpio_write(conf->disconn, (conf->flags & STM32_USBDEV_FS_CONFIG_FLAG_DISCONN_INVERTED)); } #endif }