stm32/tinyusb: Add High-Speed USB support and fix CDC/VBUS issues.#26
Merged
Conversation
|
Code size report: |
bb25aee to
4c3e95f
Compare
4c3e95f to
1e59002
Compare
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #26 +/- ##
==========================================
+ Coverage 98.46% 98.47% +0.01%
==========================================
Files 176 176
Lines 22811 22831 +20
==========================================
+ Hits 22460 22483 +23
+ Misses 351 348 -3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
105a90a to
2fae783
Compare
ce2c0c9 to
9f396bb
Compare
2e5b8dc to
f6345b7
Compare
Used for allocation of DMA channels. It will be needed for planned modules and methods like adc_timed(), dac_timed(), I2S. It includes management code for DMA IRQ handlers, similar to what was made for Sercom. Signed-off-by: robert-hh <robert@hammelrath.com>
These functions are use to allocate, free and configure a set of TC counter instances. The SAMxx MCU have between 3 to 5 (SAMD21) and 4 to 8 (SAMD51) TC instances. Two of them are used for the µs counter, the remaining 1 - 6 instances are administered here for use by various functions, like timed DMA transfers. Signed-off-by: robert-hh <robert@hammelrath.com>
Used as:
dac.write_timed(data, freq [, count])
dac.deinit()
Working range for dac_timed():
SAMD21: 1 Hz - 100 kHz (1 MHz clock, 10 bit)
SAMD51: 1 Hz - ~500 kHz (8 MHz clock, 12 bit)
The buffer has to be a byte array or a halfword array,
and the data is sent once.
The default for count is 1. If set to a value > 0, the data will be
transmitted count times. If set to 0 or < 0, the date will be
transmitted until deliberately stopped. The playback
can be stopped with dac.deinit().
dac.deinit() just releases the timer and DMA channel needed by
dac_timed(). The DAC object itself does not have to be released.
Signed-off-by: robert-hh <robert@hammelrath.com>
Used as:
adc.read_timed(buffer, freq)
Buffer must be preallocated. The size determines the number of 16 bit
words to be read. The numeric range of the results is that of the raw
ADC. The call returns immediately, and the data transfer is done by DMA.
The caller must wait sufficiently long until the data is sampled
and can be noticed by a callback. No internal checks are made for
a too-high freq value.
Read speeds depends on Average and bit length setting:
SAMD21: Max. 350kS/s (8 bit, Average 1)
SAMD51: Max. 1 MS/s (8 bit, Average 1)
Signed-off-by: robert-hh <robert@hammelrath.com>
The callback is called when a dac_timed() sequence finishes. It will be reset with callback=None or omitting the callback option in the constructor. Side change: Set the clock freq. to 48Mhz. Signed-off-by: robert-hh <robert@hammelrath.com>
Enabling a callback that will be called when a adc.read_timed_into() run is finished. That's especially useful with slow sampling rates and/or many samples, avoiding to guess the sampling time. Raise an error is adc.read_u16() is called while a read_timed_into() is active. Other ADC changes: - SAMD51: use ADC1 if both ADC1 and ADC0 are available at a Pin. Signed-off-by: robert-hh <robert@hammelrath.com>
These return True, while a timed action is ongoing. Side change: Reorder some code in machine_dac.c and do not reset DAC twice. Signed-off-by: robert-hh <robert@hammelrath.com>
Signed-off-by: robert-hh <robert@hammelrath.com>
Since the two channels of a SAMD51 are not completely independent, dac.deinit() now clears both channels, and both channels have to be re-instantiated after a deinit(). Side change: - rearrange some code lines. Signed-off-by: robert-hh <robert@hammelrath.com>
Add fallback define for NRF_FPU_S which was removed from the nRF9160 device headers in nrfx v3. Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Replace the nrf port's custom Pin.irq implementation with the shared mp_irq_obj_t pattern used by rp2 and alif ports. Adds support for the hard kwarg (soft IRQs via mp_sched_schedule when hard=False), makes handler optional, and returns a proper IRQ object with flags() and trigger() methods. Boards without MICROPY_ENABLE_SCHEDULER retain the direct ISR-only path. Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Use -1 sentinels for SPI init parameters so that calling spi.init(baudrate=X) no longer resets polarity, phase, and other settings to defaults. Matches the rp2 port behavior. Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Accept the timeout kwarg (default 50ms) for compatibility with rp2 and alif ports. Also fix TWI peripheral not being disabled on the transfer error path. Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
The old `nrfutil` runs only under CPython 2.x and has been replaced by a new stand-alone `nrfutil` executable, available directly from Nordic. Update the Makefile to work with this new version, and also update the README to describe how to install it. Signed-off-by: Damien George <damien@micropython.org>
The `sys.platform` variable on bare-metal ports is uniformly the port name (except for stm32 which uses "pyboard" for `sys.platform`), regardless of the board. Change the nrf port to follow this convention. Among other things, this gets the test auto-detection working properly on nrf boards, which relies on the value of `sys.platform` (eg for target wiring selection). Signed-off-by: Damien George <damien@micropython.org>
Apply the same workaround as added in cc7eb1a for other boards to the NRF51-Dongle, since it also suffers from the same limitation. This change makes it possible to run unit tests against it. Signed-off-by: Daniël van de Giessen <daniel@dvdgiessen.nl>
Change tcsetattr from TCSAFLUSH to TCSANOW in mp_hal_stdio_mode_raw and mp_hal_stdio_mode_orig. The TCSAFLUSH flag calls tcdrain() before applying terminal settings, which blocks indefinitely on macOS when used with PTY devices if the master side has not read all pending output. This is a known macOS kernel behavior (XNU ttywait loops until the output queue is consumed by the master). The drain serves no practical purpose on the unix port since output is written via write() syscalls that complete before tcsetattr is called. The input flush is also unnecessary since MicroPython's readline handles stale input gracefully. Two repl_ test .exp files are updated to account for the newline after Ctrl-D (end paste mode) no longer being discarded by the input flush; it produces an extra empty prompt line. Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Adds a "# sigint:" directive for repl_ tests that need Ctrl-C to generate SIGINT via the PTY terminal driver. When present, the child process is set up with the PTY as its controlling terminal (via setsid/TIOCSCTTY/tcsetpgrp) so that \x03 written to the PTY master generates SIGINT for the child's process group. This works because MicroPython's REPL restores original terminal settings (with ISIG enabled) before executing user code, allowing the terminal driver to convert \x03 into SIGINT during blocking operations. Test added: - repl_ctrl_c_interrupt_execution.py: Verifies Ctrl-C interrupts a blocking time.sleep() call and the REPL remains functional afterward. Also wraps PTY fd handling in try/finally for all repl_ tests. Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Some socket implementations (eg `extmod/modsocket.c`) don't (and can't easily) raise an exception for an invalid socket type. So just test that passing such a value doesn't crash the device. Signed-off-by: Damien George <damien@micropython.org>
For example, the ESP8266_GENERIC FLASH_512K variant doesn't have the `vfs` module. Signed-off-by: Damien George <damien@micropython.org>
Eg running this on `ADAFRUIT_ITSYBITSY_M0_EXPRESS` would previously crash with "NameError: name not defined" due to the lookup of `complex` as a global. With the change here, that test is skipped automatically. Also, provide better skip messages with the reason for the skip. Signed-off-by: Damien George <damien@micropython.org>
This test is unreliable with 5 UDP groups because on bare-metal lwIP
targets there's only enough buffer space for 4 groups, see
`extmod/modlwip.c`:
// Total queue length for buffered UDP/raw incoming packets.
#define LWIP_INCOMING_PACKET_QUEUE_LEN (4)
Signed-off-by: Damien George <damien@micropython.org>
The nrf port doesn't implement the `freq` argument. Signed-off-by: Damien George <damien@micropython.org>
Two changes here to improve running this test on targets with low memory: - Print the results at end of test, so if a MemoryError occurs part-way- through then the SKIP works correctly. - Reorganise the order of the sub-tests and preallocate the large buffer so it can be reused. This gets the test running and passing on ESP32_GENERIC with the native emitter (and also still works with the bytecode emitter). Signed-off-by: Damien George <damien@micropython.org>
This decreases the variation in the time taken to send, making the test more reliable. Without this change the nrf port is flaky at 2400 baud, eg the `ARDUINO_NANO_33_BLE_SENSE` board fails this test more often than not. Signed-off-by: Damien George <damien@micropython.org>
Prior to this change this test fails on `ADAFRUIT_ITSYBITSY_M0_EXPRESS` with the native emitter, because it doesn't have enough heap to allocate the backing buffer. Signed-off-by: Damien George <damien@micropython.org>
Prior to this change this test fails on `ADAFRUIT_ITSYBITSY_M0_EXPRESS` with the native emitter, because it doesn't have enough heap to allocate the read/write buffers. Signed-off-by: Damien George <damien@micropython.org>
Detect the target's error reporting capabilities, and skip tests as appropriate. For example, all samd boards are configured with `MICROPY_ERROR_REPORTING_TERSE` so need to skip these four tests. Signed-off-by: Damien George <damien@micropython.org>
Allows running with terse error reporting (and normal and detailed). Signed-off-by: Damien George <damien@micropython.org>
For example, `ADAFRUIT_ITSYBITSY_M0_EXPRESS` only has about 20k heap and runs out of memory part way through the integration loop. Signed-off-by: Damien George <damien@micropython.org>
Clang -m32 rounds some floating point reprs differently, likely due to x87 temporary excess precision. Accept this value in addition to the other values that are accepted. Closes: micropython#19120 Signed-off-by: Jeff Epler <jepler@unpythonic.net>
CPython's dir() converts the result of __dir__ to a list and sorts it; MicroPython returns the value as-is. Document the difference with a class whose __dir__ returns a non-list iterable. Signed-off-by: Jeongseop Lim <jseoplim@gmail.com>
Implements mapping from MICROPY_HW_USB_MAIN_DEV to TinyUSB RHPORT configuration, enabling board-specific USB PHY selection for TinyUSB stack. Adds support for HS-in-FS mode (High Speed controller running at Full Speed) which is the default for STM32 boards without external ULPI PHY. Thi STM32F4/F7/H7 high-speed RHPORT mode selection is placed in ports/stm32/tinyusb_port/tusb_config.h, following the alif/nrf pattern. Includes py/mpconfig.h to ensure board config macros are available when TinyUSB processes the header. Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
When a host closes and reopens the CDC serial port, the IN endpoint may remain stalled from a prior runtime USB disconnect (e.g. mpremote connect/disconnect cycles). Clear the stall on DTR high so the connection recovers without requiring a device reset. On DTR low (host close), flush the TX FIFO so stale data does not accumulate and block writes on the next connection. Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
The TinyUSB serial descriptor used a raw hex dump of all 12 UID bytes in sequential order (24-char lowercase), while the legacy USB stack and ST's onboard DFU bootloader use a condensed algorithm that selects 6 bytes with two additions (12-char uppercase). This mismatch caused the device to report a different serial number depending on which USB stack was active, breaking tools that identify devices by serial (e.g. udev rules, mpremote, dfu-util). Use the ST DFU bootloader algorithm for consistency. Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Removes the need for -I$(TOP)/shared/tinyusb/ in the stm32 Makefile by using an explicit path in the two files that include mp_usbd.h outside of the shared/tinyusb/ directory itself. Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
f6345b7 to
d2e0d20
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The STM32 port's TinyUSB integration only supports the FS controller. Boards with a HS controller (PYBD_SF6, STM32F429DISC, OLIMEX_H407, or ULPI-based designs) cannot use TinyUSB — the RHPORT configuration is hardcoded to RHPORT 0 / full-speed only.
This PR adds proper RHPORT mode selection in
tusb_config.hbased onMICROPY_HW_USB_HSandMICROPY_HW_USB_HS_IN_FSboard config, routes HS IRQ handlers to the correct RHPORT (including STM32N6 which maps HS to RHPORT 0), and adds VBUS sensing configuration for the HS-in-FS path on STM32F4/F7 (mirroring the existing FS path, with support for both VBDEN and legacy NOVBUSSENS register variants).Additional issues found and fixed during testing (each is a separate commit and can be split into its own PR if preferred):
CDC reconnect stall: When a host closes and reopens the CDC serial port (e.g. repeated mpremote connect/disconnect cycles), the IN endpoint can remain stalled from a prior runtime USB disconnect. The device becomes unresponsive until reset. Fixed by clearing endpoint stall on DTR high. Additionally, on DTR low (host close) the TX FIFO is now flushed so stale data from a previous session does not accumulate and block writes on the next connection.
DFU bootloader serial number mismatch: The TinyUSB serial descriptor uses a raw hex dump of all 12 UID bytes (24-char lowercase), while the legacy USB stack and ST's onboard DFU bootloader use a condensed algorithm that selects 6 bytes with two additions (12-char uppercase). This causes the device to report a different serial number depending on which USB stack is active, breaking tools that identify devices by serial (udev rules, mpremote, dfu-util). Fixed by using the ST DFU bootloader algorithm.
TinyUSB-specific boot.py template: The factory reset boot.py template references
pyb.usb_mode()which does not exist on the TinyUSB stack. A TinyUSB-specific template is added that usesmachine.USBDevicewithBUILTIN_DEFAULT.Testing
Built with
CFLAGS_EXTRA=-DMICROPY_HW_TINYUSB_STACK=1for:machine.USBDeviceAPI, filesystem accessmachine.USBDeviceAPI, filesystem accessULPI boards (STM32H747I_DISCO) are tested on hardware as part of the original development in micropython#18303 — this branch is split out from that work.
CDC serial throughput (OPENMV_N6, HS link at 480 Mbit/s)
Compared TinyUSB vs legacy USB stack using
tests/serial_test.pymethodology.Read (device → host):
Write (host → device):
TinyUSB read is 6-12x faster. Write is 2.5-4.5x slower due to a pre-existing limitation in the TinyUSB CDC stdin path —
mp_hal_stdin_rx_chr()reads byte-by-byte through a 512-byte ringbuffer, causing backpressure on the USB OUT endpoint. This affects all TinyUSB ports, not just HS, and is being tracked separately.Trade-offs and Alternatives
The RHPORT configuration uses
#ifndefguards so boards like STM32N6 that pre-defineCFG_TUSB_RHPORT0_MODEinmpconfigboard_common.hpass through unchanged. This avoids needing per-family#ifchains but means any future HS-capable family (e.g. STM32U5) needs its own board-level override.