I'm trying to manually* configure a USART on a Nucleo F401RE to transmit over the USB virtual COM port, which means using USART2 on PA2 (TX) and PA3 (RX). Not only am I not receiving any characters, but the TX pin is never high, which I believe it should be when idle.

Here's what I've done:

PLL is running at 84MHz (confirmed using MCO), and the APB1 prescaler is set to /2:

    (2 << RCC_CFGR_SW_BIT) // Use PLL as clock source
    | (0b100 << RCC_CFGR_PPRE1_BIT) // Prescale APB1 clock /2

GPIOA peripheral clock is enabled:

// Enable GPIOA peripheral clock

Then the USART configuration:

GPIOA_MODER |= 0b1010 << 4; // PA2, PA3 to AF
GPIOA_OSPEEDR |= 0b1111 << 4; // PA2, PA3 to high speed
GPIOA_AFRL |= (7 << 8) | (7 << 12); // Set PA2, PA3 to AF7 (USART2)
RCC_APB1ENR |= RCC_APB1ENR_USART2EN; // Enable peripheral clock for USART2

// Baud rate 115k2 with SYSCLK 84MHz (BRR = 45.5625)
USART2_BRR = (45 << 4) | 9;


I believe at this point, TX should be high, but it isn't. I send with:

while ((USART2_SR & USART_SR_TXE) == 0);
USART2_DR = c;

Turning on the LED within the while shows that the transmit buffer is initially filled. Turning it on after the while shows that it successfully empties. Using TC instead of TXE shows it is considered successfully transmitted.

What have I missed?

All the code is here: https://github.com/danellis/zeptos/tree/usart-fail

* Please don't try to persuade me to use CMSIS or mbed; it's a learning exercise.

  • \$\begingroup\$ no one will read the magic numbers. \$\endgroup\$ May 22, 2018 at 16:36
  • \$\begingroup\$ So assume they're correct. \$\endgroup\$
    – Isvara
    May 22, 2018 at 16:45
  • \$\begingroup\$ BTW you are setting the registers in a potentially dangerous way. You should clear the existing bit values before applying the OR operation. For example, add GPIOA_MODER &= ~(0xF << 4); for that register. This will save your colleagues (and future self) from many hours of debugging. \$\endgroup\$
    – Jon
    May 24, 2018 at 8:59
  • \$\begingroup\$ @Jon Eventually, yes. I have a YAML file of register descriptions to generate the appropriate macros for any fields wider than one bit. For now, they take the reset values into consideration. \$\endgroup\$
    – Isvara
    May 24, 2018 at 9:40

1 Answer 1


It turns out that the code was fine, and it was all hardware issues. Firstly, the USART2 TX/RX pins on that board's connector are by design not connected to anything. The MCU pins are only connected to the ST-Link. (There are solder bridges to change this configuration.) So that's why I didn't get anything on the scope.

As for why I didn't get any data on the USB port, I think the ST-Link was not compatible with USB 3.0. When I upgraded its firmware, suddenly I saw my data.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.