UART (BufferedSerial) is not reliable at higher baudrates (>76800)

Hi,

unfortunately I can’t get UART communication working at higher baud rates (>115200 for the NUCLEO-WL55JC1 and >76800 for the DISCO-L072CZ-LRWAN1).

When I use higher baud rates, some bytes are missing.

I use this program to verify the integrity of the communication using a single board, i.e. the UART TX pin is connected to the UART RX pin.

#include "mbed.h"

// See this table for common baud rates:
// https://lucidar.me/en/serialib/most-used-baud-rates-table/
// For the NUCLEO board, I can go up to 115200 bauds without errors
// For the DISCO board, I can go up to 76800 bauds without errors
// const int BAUD_RATE = 76800;
const int BAUD_RATE = 115200;


// For NUCLEO board
// Note: You need to use STM32CubeProgrammer to flash the program to the board.
// const PinName TX_PIN = PB_6;
// const PinName RX_PIN = PB_7;

// For DISCO board
const PinName TX_PIN = PA_9;
const PinName RX_PIN = PA_10;

const int BUFFER_SIZE = 32;

// Connect TX_PIN with RX_PIN (loopback) and also GND if using two boards
BufferedSerial serial_port(TX_PIN, RX_PIN, BAUD_RATE);

uint8_t tx_buffer[BUFFER_SIZE];
uint8_t rx_buffer[BUFFER_SIZE] = {0};

void init_tx_buffer() {
  for (int i = 0; i < BUFFER_SIZE; i++) {
    tx_buffer[i] = i;
  }
}

bool are_buffer_equal(const uint8_t *buffer_1, const uint8_t *buffer_2,
                      size_t buffer_size) {
  return memcmp(buffer_1, buffer_2, buffer_size) == 0;
}

void test_uart() {
  while (!serial_port.writable()) {
    // Wait for serial port to be ready to write
  }

  int sent_bytes_count = serial_port.write(tx_buffer, BUFFER_SIZE);
  printf("Sent %d bytes\n", sent_bytes_count);

  ThisThread::sleep_for(2s);

  while (!serial_port.readable()) {
    // Wait for serial port to be ready to read
  }

  int read_bytes_count = serial_port.read(rx_buffer, BUFFER_SIZE);
  printf("Read %d bytes\n", read_bytes_count);

  if (read_bytes_count != BUFFER_SIZE) {
    printf("Error: Expected to read %d bytes\n", BUFFER_SIZE);
    return;
  }

  if (are_buffer_equal(rx_buffer, tx_buffer, BUFFER_SIZE)) {
    printf("SUCCESS: RX buffer is equal to TX buffer\n");
  } else {
    printf("ERROR: RX buffer is different than TX buffer\n");
  }
}

int main() {
  printf("Starting UART test...\n");

  init_tx_buffer();

  ThisThread::sleep_for(1s);

  while (true) {
    test_uart();
  }
}

So far, I’ve tried:

  • Using different UARTs (UART1 and UART2) → no effect
  • Using a deep sleep lock → no effect
  • Using a different compile profile → Release and Develop seem to be a bit more reliable than Debug
  • Using UnbufferedSerial instead of BufferedSerial ->no effect
  • Increasing the TX and RX buffer sizes for BufferedSerial → no effect
  • Tried different compilers (GCC_ARM vs ARMC6) → no effect
  • Using two DISCO boards for UART communication instead of a single one → no effect

Open questions:

  • Could this be related to Low-Power UART? For the DISCO board I use pins PA_9 and PA_10.
    As far as I can tell from PeriphalPins.c, LPUART it is disabled by default and the proper UART is used, right?
//*** SERIAL ***

MBED_WEAK const PinMap PinMap_UART_TX[] = {
    {PA_0,       UART_4,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF6_USART4)},
    {PA_2,       UART_2,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF4_USART2)}, // Connected to STDIO_UART_TX
//  {PA_2,       LPUART_1,STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF6_LPUART1)}, // Connected to STDIO_UART_TX
    {PA_9,       UART_1,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF4_USART1)},
    {PA_14,      UART_2,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF4_USART2)},
    {PA_14_ALT0, LPUART_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF6_LPUART1)},
    {PB_3,       UART_5,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF6_USART5)}, // Connected to RADIO_SCLK
    {PB_6,       UART_1,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF0_USART1)},
    {PB_10,      LPUART_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF4_LPUART1)},
    {PB_11,      LPUART_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_LPUART1)},
    {PC_1,       LPUART_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF6_LPUART1)}, // Connected to RADIO_ANT_SWITCH_TX_BOOST
    {NC, NC, 0}
};

MBED_WEAK const PinMap PinMap_UART_RX[] = {
    {PA_1,       UART_4,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF6_USART4)}, // Connected to RADIO_ANT_SWITCH_RX
    {PA_3,       UART_2,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF4_USART2)}, // Connected to STDIO_UART_TX
//  {PA_3,       LPUART_1,STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF6_LPUART1)}, // Connected to STDIO_UART_TX
    {PA_10,      UART_1,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF4_USART1)},
    {PA_13,      LPUART_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF6_LPUART1)},
    {PA_15,      UART_2,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF4_USART2)}, // Connected to RADIO_NSS
    {PB_4,       UART_5,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF6_USART5)}, // Connected to RADIO_DIO_0
    {PB_7,       UART_1,  STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF0_USART1)},
    {PB_10,      LPUART_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF7_LPUART1)},
    {PB_11,      LPUART_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF4_LPUART1)},
    {PC_0,       LPUART_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF6_LPUART1)}, // Connected to RADIO_RESET
    {NC, NC, 0}
};

Any help is very much appreciated!

No ideas?

Hello,

It seems like you try to send just 32 bytes as a test string. It would be more transparent to use a TTL convertor (UART to USB bridge), than this code.

I think, I have same board and can take a look on this when I will have a time.

BR, Jan

Hi,

thanks for your reply!

Yes, I’m sending the bytes 0 to 31 and then I try to receive them on the same BufferedSerial instance, where the associated UART has TX connected to its RX pin using a jumper wire.

I don’t understand, what difference a USB to UART adapter would make, but I can try it tomorrow.

I would really appreciate it, if you could try to reproduce my issue :slightly_smiling_face:

Btw, I used mbed OS version 6.16.0

Thank you and kind regards,

Nick

The difference is that it doesn’t depend on your personal code and thank to this is more transparent for others. Your code brings another overhead because of printf functions and static delay with could have an impact in this case.

Tested with:

I made several test and I can agree with your statement.
The best result was achieved with Bare metal profile at 115200 bauds.

As a next step would be cool to make comparison with pure HALs via STM32Cube. This brings some light where the issue really is.

BR, Jan

Hi Jan,

I really appreciate that you tried to reproduce the issue!
And I’m glad that you have similar results!

I think accessing the serial port directly using the HAL is a great idea!
Also, I thought that using BufferedSerial with blocking disabled might also be worth a try.

Unfortunately, I’m close to finishing the university research project involving these STM boards, after which I won’t have access to the hardware anymore.

But in the future, someone else might continue debugging based on this thread :slight_smile:

Kind regards,

Nick