Skip to content

serial: change the baud rate without tearing the port down - #12000

Open
MrScothh wants to merge 1 commit into
iNavFlight:maintenance-10.xfrom
MrScothh:feature/uart-baud-in-place
Open

MrScothh wants to merge 1 commit into
iNavFlight:maintenance-10.xfrom
MrScothh:feature/uart-baud-in-place

Conversation

@MrScothh

Copy link
Copy Markdown
Contributor

What this fixes

uartSetBaudRate() goes through uartReconfigure(), which starts with HAL_UART_DeInit().
That releases the peripheral and its pins, and the line noise it leaves behind is read as
data by whatever is on the other end.

A u-blox receiver stops taking input for about a second after hearing it. On an H743 bench
with a NEO-F10N, a sequence that only stepped the port through other rates before speaking
to the receiver at its own rate reached it 3 times out of 10. With the port reconfigured in
place, the same sequence reached it 10 times out of 10.

Anything else INAV changes the rate of is exposed to the same noise, including the SRXL2 ESC
driver, which raises the bus to 400000 once the handshake is done.

What I changed

Everything but the tear-down now lives in uartConfigure(). HAL_UART_Init() is happy to be
called on a port that is already open: it reconfigures the peripheral and leaves the pins
alone. Opening a port, and changing its mode or options, still go through the full
reconfigure, tear-down included.

Only the HAL families are affected. The F4 and AT32 drivers already disable the peripheral
and reprogram it without releasing anything.

Testing

Bench measurement above, on an H743 with a NEO-F10N. Built for F722 and H743; F405 and AT32
are untouched by this change but were built as well.

@qodo-code-review

Copy link
Copy Markdown
Contributor

ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Reconfigure HAL UART baud rates without port teardown

🐞 Bug fix 🕐 10-20 Minutes

Grey Divider

AI Description

• Reconfigures HAL UART baud rates without deinitializing the peripheral or releasing pins.
• Preserves full teardown for port opening, mode changes, and option changes.
• Prevents line noise that can disrupt connected receivers during baud-rate transitions.
Diagram

graph TD
  BAUD["Baud change"] --> STORE["Update baud"] --> CONFIG["uartConfigure"] --> INIT["HAL init"]
  FULL["Open, mode, options"] --> RECONFIG["uartReconfigure"] --> DEINIT["HAL deinit"] --> CONFIG
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Program baud registers directly
  • ➕ Changes only the baud-rate divisor with minimal peripheral disturbance.
  • ➕ Avoids relying on repeated HAL initialization behavior.
  • ➖ Requires family-specific register and clock calculations.
  • ➖ Bypasses HAL abstractions and increases maintenance across supported STM32 families.
  • ➖ Must safely coordinate UART state, oversampling, and active transfers.

Recommendation: Keep the PR's shared uartConfigure() approach. It prevents GPIO teardown while retaining the existing HAL-supported configuration path; direct register programming would be narrower but substantially less portable and more error-prone.

Files changed (1) +13 / -3

Bug fix (1) +13 / -3
serial_uart_hal.cApply HAL UART baud changes without peripheral teardown +13/-3

Apply HAL UART baud changes without peripheral teardown

• Extracts UART initialization into 'uartConfigure()' and keeps 'uartReconfigure()' as the explicit deinitialize-then-configure path. Baud-rate changes now configure the active peripheral in place, while opening ports and changing modes or options still perform full teardown.

src/main/drivers/serial_uart_hal.c

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Sep 21, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Baud changes can stall the controller ✓ Resolved 🐞 Bug ☼ Reliability
Description
uartSetBaudRate() calls uartConfigure() while UART interrupts remain enabled, and
HAL_UART_Init() sets gState to busy before disabling and rewriting the peripheral without
clearing TXEIE. If transmit-empty becomes asserted during the change, the UART ISR neither
services nor disables it while the state is busy, so the continuously pending interrupt can prevent
initialization and baud-switching callers from completing.
Code

src/main/drivers/serial_uart_hal.c[201]

+    uartConfigure(uartPort);
Evidence
The changed baud path directly invokes uartConfigure, whose HAL initialization is reached without
the register-clearing DeInit used by the full reconfigure path. The F7 HAL sets the state to busy
and disables only the UART itself before performing configuration, whereas DeInit previously cleared
CR1, CR2, and CR3; the driver ISR leaves transmit-empty untouched whenever that busy state is
observed. Runtime callers such as GPS autobaud and SRXL2 invoke this path immediately after the
software transmit ring reports empty, which can occur while the final byte still leaves TXEIE armed.

src/main/drivers/serial_uart_hal.c[103-129]
src/main/drivers/serial_uart_hal.c[133-136]
src/main/drivers/serial_uart_stm32f7xx.c[286-305]
lib/main/STM32F7/Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_uart.c[321-348]
lib/main/STM32F7/Drivers/STM32F7xx_HAL_Driver/Src/stm32f7xx_hal_uart.c[623-629]
src/main/io/gps_ublox.c[1214-1228]
src/main/io/motor_srxl2.c[847-851]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new in-place baud-rate path enters HAL initialization with UART interrupt sources still enabled. A transmit-empty interrupt can remain asserted while the HAL handle is busy, repeatedly invoking an ISR that deliberately performs no work in that state and preventing initialization from completing.
## Fix Focus Areas
- src/main/drivers/serial_uart_hal.c[103-129]
- src/main/drivers/serial_uart_hal.c[196-201]
## Recommended Fix
Disable the UART peripheral interrupt sources before calling `HAL_UART_Init()` or `HAL_HalfDuplex_Init()`, then restore the driver-required RX, error, and TX interrupt state after initialization finishes. Ensure the masking occurs before the HAL changes `gState` and that an already pending interrupt cannot repeatedly preempt the configuration sequence.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can add REVIEW.md to your repo root and Qodo follows it on every PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/main/drivers/serial_uart_hal.c
uartSetBaudRate() went through uartReconfigure(), which starts with
HAL_UART_DeInit(). That releases the peripheral and its pins, and the line
noise it leaves behind is read as data by whatever is on the other end.

A u-blox receiver stops taking input for about a second after hearing it,
which is long enough to swallow the commands that follow. On a bench with a
NEO-F10N, a sequence that only changed the port's rate before speaking to the
receiver reached it 3 times out of 10; with the configuration done in place it
reached it 10 times out of 10.

Everything but the tear-down is now in uartConfigure(), which HAL_UART_Init()
is happy to be called with again: it reconfigures a port that is already open
and leaves the pins alone. Opening a port and changing its mode or options
still go through the full reconfigure.

A port reprogrammed in place still has its interrupts enabled, so they are
masked for the duration: one taken while the HAL has the handle marked busy
would find the peripheral half written. The function enables them again at its
end, as it already did. A port being opened has no handle yet and nothing to
mask.

Only the HAL families are affected. The F4 and AT32 drivers already disable
the peripheral and reprogram it without releasing anything.
@MrScothh
MrScothh force-pushed the feature/uart-baud-in-place branch from 12eac16 to 2a3be52 Compare September 21, 2026 19:48
@MrScothh

Copy link
Copy Markdown
Contributor Author

Good catch, thank you. The interrupt sources are now masked before the peripheral is reprogrammed, and enabled again at the end of the same function, which it already did for the rest. A port being opened has no handle yet, so there is nothing to mask and the masking is skipped.

Measured again on the bench after the change, with a NEO-F10N on an H743: ten baud rate changes out of ten, no timeouts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant