Skip to main content
Associate II
August 3, 2026
Question

USART issue in loopback mode

  • August 3, 2026
  • 29 replies
  • 177 views

Hi 👋

I have implemented a simple program to send and receive characters over the USART device which works as expected when I connect my MCU up to minicom via usb as in I can see the characters sent by the program getting printed on the console and I can see the characters I type on minicom in the relevant variable in gdb.

Next, I tried connecting the TX and RX pins together to have transmission and reception in loopback mode but that doesn’t seem to work. My program (which does both transmission and reception in simple polling mode) simply gets stuck on the RXNE != 1 loop and never exits that indicating that the RX pin either never receives any data or it never detects a start bit so it just remains at the idle state waiting for that. So, I’m wondering what the reason for this might be..could it be that minicom simply sends the right start bit sequence which the Receiver picks-up? Or something else..?

The program in both cases is exactly the same so the only difference is that in loopback mode the two pins are naturally jumpered.

Also, within the tight loop the USART_SR register has the following (constant) content:

0x40011000:    11000000    00000000    00000000    00000000

 

Any thoughts? I am on an STM32F4 MCU and using the USART1 (if that makes any difference).

 

Thanks!

29 replies

Pavel A.
August 4, 2026

> My program (which does both transmission and reception in simple polling mode) simply gets stuck on the RXNE != 1 loop

Show the actual code.

Do you detect and handle RX overflow (ORE) ?

 

Associate II
August 4, 2026

Thanks for your reply.

Yes, I do - See code below (it’s in Ada but should be fairly straightforward to follow, let me know if not):

 

procedure Receive (C         : out Character ;
                                USART_Reg : USART_Registers.USART_Reg_Acc) is
   begin
      loop
         exit when USART_Reg.USART_SR.RXNE; --   Line 1
         if USART_Reg.USART_SR.ORE then -- Line 2
            --   ORE reset sequence
            declare
               Unused1 : Boolean;
               Unused2 : HAL.UInt9;
            begin
               Unused1 := USART_Reg.USART_SR.PE;
               Unused2 := USART_Reg.USART_DR.DR;
            end;
         end if;
      end loop;
      C := Character'Val (USART_Reg.USART_DR.DR);
   end Receive;

Stepping through in gdb the flow effectively alternates between Line 1 and Line 2 without getting into the if so no overflow seems to be happening.

Pavel A.
August 4, 2026

Wow! Thrilled to see someone actually using Ada :)

Try to move line 1 after the whole block line 2 (check ORE first).

Also, make sure that USART_Reg.USART_SR is read only once. Copy it to a variable before Line 1 and check that variable.

Maybe, a better idea: some USARTs in STM2F4 can be configured to ignore RX overflow so ORE is silently ignored. You should then use a protocol that detects block errors and corrects/retries.

 

David Littell
Senior II
August 4, 2026

I hesitate to mention this because of its obviousness but in this loopback scenario maybe something called Transmit() should be called before Receive()?

Associate II
August 4, 2026

hello 👋 thanks for your reply.

Yeah, the driver logic is actually doing that (please, see screenshot in previous reply).

I am also starting the receiver first followed by the transmitter prior to running the transmit/receive flow:

 

Andrew Neil
Super User
August 5, 2026

I am also starting the receiver first followed by the transmitter prior to running the transmit/receive flow:

 

For that to work, your Start_Receiver would need to be non-blocking …

 

 

A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.
David Littell
Senior II
August 4, 2026

Alrighty, let’s look closer at the hardware.  How exactly are you creating your loopback?  Schematic and photos (for ​@Andrew Neil  ;-) will possibly be helpful.

David Littell
Senior II
August 5, 2026

I just re-read an earlier reply (thanks, forum, for still collapsing replies - NOT!) and noticed that the OP said he’s jumpering PA9 and PA10 to create the loopback.  If the MCU’s RX signal is still connected to anything it’ll be forced to the idle state by that device and the MCU’s TX won’t be able to overcome it.  

Associate II
August 5, 2026

hmm..don’t believe the RX pin connected to anything 🤔 it’s just set-up for alternate function, Pull_Up, Speed_50MHz, Push_Pull and it is jumpered to the TX pin via a rather large jumper wire..wonder if that somehow results in a noisy line..

 

Andrew Neil
Super User
August 5, 2026

 I connect my MCU up to minicom via usb as in I can see the characters sent by the program getting printed on the console and I can see the characters I type on minicom in the relevant variable in gdb

Excellent! Too many people miss this basic first step!

 

 I tried connecting the TX and RX pins together to have transmission and reception in loopback mode but that doesn’t seem to work

Suggests a timing problem:  your code can’t cope with receiving immediately after transmitting - or, depending on how you have this structured, can’t cope with receiving during transmission.

 

A simple test might be to set up something on your minicom host which does a software loopback, with an adjustable delay.

Then adjust the delay to see where it fails, and where it works...

A complex system that works is invariably found to have evolved from a simple system that worked.A complex system designed from scratch never works and cannot be patched up to make it work.
Pavel A.
August 5, 2026

> Suggests a timing problem:  your code can’t cope with receiving immediately after transmitting - or, depending on how you have this structured, can’t cope with receiving during transmission.

Loopback should work even in polling mode: wait for TX complete, write a char to DR, then poll for the char ready in RX. Read DR. Repeat.

As ​@FBL advised, better try plain C and/or ST HAL first. For completeness, check for all possible errors (FE).

Does the USART work in normal full-duplex mode or unidirectional? Is your USB adapter RS-232 or RS-485?

 

Associate II
August 5, 2026

better try plain C and/or ST HAL first. For completeness, check for all possible errors (FE).

Cool, I’ll try and do that 👍

Does the USART work in normal full-duplex mode or unidirectional? Is your USB adapter RS-232 or RS-485?

So, using the asynchronous mode (just the TX/RX pins) which I believe is full-duplex? Also using the RS-232 adapter.

ST Technical Moderator
August 5, 2026

Hi ​@sidisyom_s 

Do you reproduce the issue using our HAL? If not, that would be a good first step to isolate the problem from your custom driver implementation.

To give better visibility on the answered topics, please click on "Best answer" on the reply which solved your issue or answered your question.Best regards,FBL
Associate II
August 5, 2026

Will try and do that, thanks 👍

Karl Yamashita
Principal
August 6, 2026

If you’re using HAL, you can’t use polling for receive because the HAL_UART_Receive routine waits for a size of data to be receive or it times out before returning from the call.

Calling HAL_UART_Transmit after sends data but the HAL_UART_Receive routine is no longer waiting for data. Instead you need to use interrupt base routines, HAL_UART_Receive_IT or HAL_UART_Receive_DMA, then HAL_UART_Transmit will work. 

If a reply has proven helpful, click on Accept as Solution so that it'll show at top of the post.CAN Jammer an open source CAN bus hacking toolCANableV3 Open Source
TDK
August 6, 2026

With polling, you can only send 1 character before you need to receive it.

Loopback mode can work. If you showed the actual code being executed (all of it, not just a line or two here and there) this could be solved in max 5 minutes.

"If you feel a post has answered your question, please click ""Accept as Solution""."
Associate II
August 6, 2026

I hesitate to do that because my code is in Ada and that’s why I’ve been focusing on the mcu aspect and the fact that this works on minicom..but if you fancy reading a bit of Ada then the following is the full code (excluding the hal layer of course for brevity) 🙂:

The above is a run against minicom where, if you see on the debugger console a bit further down, ‘Q’ has been received fine which is what I typed on minicom (I just pressed enter a few times hence you see this printed multiple times).

Transmitting also works as the characters of Message are getting printed fine:

 

But the exact same code breaks when I jumper PA9 & PA10 (as in the USART_SR.RXNE bit never goes high so Receive just spins forever)..so something in loopback mode it doesn’t like.

Pavel A.
August 6, 2026

One last thing - can your debugger or register viewer read the UART DR in background? Then it will steal the received byte.

Please do the C & ST HAL test first, as we agreed. Do you need help with this?