cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F7 UART5 issue

SBurc
Associate II

Before I explain the problem, I have to give some background. Our project went through a long stage of Proof-of-design, in which a maintenance program was written which tests all the interfaces. Part of this incuded BIT routines for those interfaces.

The project has moved to a new stage, including a bootloader and application. The bootloader incorporates much of the BIT testing from the maintenance program.

The issue is that for UART5, the BIT test works correctly in the maintenance program, but fails as part of the loader program. I have put in a couple of days debugging, and see things I cannot explain.

There are 6 UARTs in our system. 5 of them are RS422 with an external driver chip, 1 is a HW selectable RS232/422 with and external driver chip. The BIT works on all of these, except UART 5 in the loader (UART 5 is one of the standard RS422s).

The BIT works by putting the UART into loopback made, then sending and receiving (by polling) the characters ‘A’..’Z’. Here is the routine which has been working:

static bool SerialBITInternal(UART_HandleTypeDef *p)

{

 uint8_t i, data = 0;

 bool result = true;

 UART_EndRxTransfer(p); // cancel the wait for an interrupt if there is one, we work polling here in the BIT. 

 for(i = 'A';i

 {

  HAL_UART_Transmit(p, &i, 1, 10);

  HAL_UART_Receive(p, &data, 1, 10);

  if (i != data)

   result = false; 

 }

 return result;

}

Surrounding the calls to the routine are routines which make the DXEN of the chip a regular GPIO and drives it low (so the loopback remains internal to the UART).

The issue with UART 5 under the loader is evident when debugging. When I arrive at the for loop in the routine, I have (in the loader only) for reasons unknown, a value of 0x006000F0. This is an overrun error. I added the code to the routine __HAL_UART_CLEAR_OREFLAG(p) after the call to UART_EndRxTransfer to fix this (in fact, added macros clearing all of the error flags). This doesn’t work when running – it does work if I place a breakpoint after them but before the test.  As soon as I start the test with 0x006000D0 in the ISR, the test passes. But no matter what I do to clear the error, until I hit a breakpoint it does not change.

Note that this is only an issue for UART5 – all the other U(S)ARTs are fine.

The main difference between the maintenance program and the loader program is that the loader program runs from RAM. I tried adding a cache clear after call to __HAL_UART_CLEAR_OREFLAG, but this did nothing. Only a breakpoint. If I break before it, and step through it, like this:

 bool result = true;

 UART_EndRxTransfer(p); // cancel the wait for an interrupt if there is one, we work polling here in the BIT. 

 __HAL_UART_CLEAR_PEFLAG(p); // break here and step through

 __HAL_UART_CLEAR_OREFLAG(p);

 for(i = 'A';i

I can see the ISR register change value, and then if I run to the “return result�? everthing is OK. If I break in the middle of the “for�? loop, then I can see that (a) result is already false, (b) the value is 0x006000D0 and (c) the value in the RXD is the same as the value in the TXD. This, I believe, is caused by the breakpoint, because otherwise it would have happened sooner.

UART5 is, on my board, what we number as 1, so it was the first UART being tested. I changed the order of the testing of the UARTs, only UART 5 suffers from this problem.

To summarize: Only UART5, only when running from RAM, reason is an unknown-cause overrun error, which does not clear until a breakpoint is reached.

9 REPLIES 9
SBurc
Associate II

For some reason, the "for" loop got cut while pasting - it should read

 for(i = 'A';i<='Z';i++)

> Part of this incuded BIT routines for those interfaces.

What is BIT?

> routines which make the DXEN of the chip a regular GPIO

What does this exactly mean?

> When I arrive at the for loop in the routine, I have (in the loader only) for reasons unknown, a value of 0x006000F0. This is an overrun error.

I guess you see this in the status register. But overflow is bit 3, so 0x006000F0 is not an overflow.

If you suspect you have overflow, well, then maybe you have a character reception under way when you enter this routine. You can either check the BUSY flag, or you can wait one character's time and then check RXNE after that.

Note, that reading the data registers - even with the debugger reading out and displaying UART registers - clears the RXNE bit.

JW

SBurc
Associate II

Sorry for the delay - First I'll answer your questions, then put in some of the debug information gathered since then.

  • BIT = Built-in test. In the case of the serial ports, this is placing them in loopback mode and seeing they receive what they send.

  •  routines which make the DXEN of the chip a regular GPIO - Normally we are using the U(S)ARTs with HW drive enabled. That is, if you look at the reference manual in section 34.5.16, there is a pin for RS485 drive enable, and this can be controlled by the U(S)ART hardware. However, in the self-test, we don't want the data to go out on the lines (local loopback), so I shut the driver down by changing the in to a standard GPIO and pulling it low.

You are correct about the ORE bit - my mistake earlier. But here is what happens:

Here is the BIT routine with a lot of extras in attempt to force the the function behave correctly.

static bool SerialBITInternal(UART_HandleTypeDef *p)

{

 uint8_t j, i, data = 0;

 bool result = true;

  UART_EndRxTransfer(p);  // BP1

 __HAL_UART_CLEAR_OREFLAG(p);

  __HAL_UART_CLEAR_NEFLAG(p);

 __HAL_UART_CLEAR_FEFLAG(p);

 j = (uint8_t)(p->Instance->RDR);

 for(i = 'A';i<='Z';i++) // BP2

 {

   HAL_StatusTypeDef st;

   st = HAL_UART_Transmit(p, &i, 1, 10);

   if (st != HAL_OK)

   {

       __HAL_UART_CLEAR_OREFLAG(p);

       __HAL_UART_CLEAR_NEFLAG(p);

       __HAL_UART_CLEAR_FEFLAG(p);

       __HAL_UART_CLEAR_PEFLAG(p);

       j = (uint8_t)(p->Instance->RDR); // and any previous data!!

       st = HAL_UART_Transmit(p, &i, 1, 10);

       if (st != HAL_OK)

         result = false;

   } else

   {

     st = HAL_UART_Receive(p, &data, 1, 10);

     if (st != HAL_OK)

     {

       __HAL_UART_CLEAR_OREFLAG(p);

       __HAL_UART_CLEAR_NEFLAG(p);

       __HAL_UART_CLEAR_FEFLAG(p);

       __HAL_UART_CLEAR_PEFLAG(p);

       j = (uint8_t)(p->Instance->RDR); // and any previous data!!

       st = HAL_UART_Transmit(p, &i, 1, 10);

       if (st != HAL_OK)

         result = false;

       st = HAL_UART_Receive(p, &data, 1, 10);

       if (st != HAL_OK)

         result = false;

     }

     if (i != data)

     {

       __HAL_UART_CLEAR_OREFLAG(p);

       __HAL_UART_CLEAR_NEFLAG(p);

       __HAL_UART_CLEAR_FEFLAG(p);

       __HAL_UART_CLEAR_PEFLAG(p);

       j = (uint8_t)(p->Instance->RDR); // and any previous data!!

       st = HAL_UART_Transmit(p, &data, 1, 10);

       st = HAL_UART_Transmit(p, &i, 1, 10);

       if (st != HAL_OK)

         result = false;

       st = HAL_UART_Receive(p, &data, 1, 10);

       if (st != HAL_OK)

         result = false;

       if (i != data)

         result = false;  

      }

   }

 }

 return result; // BP3

}

It seems posts are limited, so I will continue in another post...

SBurc
Associate II

When I place a breakpoint at BP1, this is how the UART5 registers appear.

Name   Value   Access 

CR1       0x0000100D       ReadWrite         

CR2       0x00000000        ReadWrite         

CR3       0x00004000        ReadWrite         

BRR       0x000001A0       ReadWrite         

GTPR    0x00000000        ReadWrite         

RTOR    0x00000000        ReadWrite         

RQR      WWWWWWWW            WriteOnly          

ISR         0x006000F4        ReadOnly           

    REACK             1             ReadOnly           

    TEACK             1             ReadOnly           

    WUF 0             ReadOnly           

    RWU 0             ReadOnly           

    SBKF 0             ReadOnly           

    CMF 0             ReadOnly           

    BUSY 0             ReadOnly           

    ABRF 0             ReadOnly           

    ABRE 0             ReadOnly           

    EOBF 0             ReadOnly           

    RTOF 0             ReadOnly           

    CTS   0             ReadOnly           

    CTSIF               0             ReadOnly           

    LBDF 0             ReadOnly           

    TXE   1             ReadOnly           

    TC                     1             ReadOnly           

    RXNE               1             ReadOnly           

    IDLE 1             ReadOnly           

    ORE  0             ReadOnly           

    NF                    1             ReadOnly           

    FE                     0             ReadOnly           

    PE                     0             ReadOnly           

    ICR   WWWWWWWW            WriteOnly          

    RDR  0x000001FF        ReadOnly           

    TDR  0x0000015A       ReadWrite         

If, after stopping at BP1, I run or step to BP2, they appear like so:

Name   Value   Access 

CR1       0x0000100D       ReadWrite         

CR2       0x00000000        ReadWrite         

CR3       0x00004000        ReadWrite         

BRR       0x000001A0       ReadWrite         

GTPR    0x00000000        ReadWrite         

RTOR    0x00000000        ReadWrite         

RQR      WWWWWWWW            WriteOnly          

ISR         0x006000D0       ReadOnly           

ICR        WWWWWWWW            WriteOnly          

RDR       0x000001FF        ReadOnly           

TDR       0x0000015A       ReadWrite         

... more below ...

SBurc
Associate II

And now if I run to BP3, the result is 1 (pass). If, however, I do not break until BP3, I arrive at BP3 with

the registers like this and the result being 0 (fail).

Name   Value   Access 

CR1       0x0000100D       ReadWrite         

CR2       0x00000000        ReadWrite         

CR3       0x00004000        ReadWrite         

BRR       0x000001A0       ReadWrite         

GTPR    0x00000000        ReadWrite         

RTOR    0x00000000        ReadWrite         

RQR      WWWWWWWW            WriteOnly          

ISR         0x006010D0       ReadOnly           

ICR        WWWWWWWW            WriteOnly          

RDR       0x0000015A       ReadOnly           

TDR       0x0000015A       ReadWrite         

Now, the EOBF flag shouldn't be set at all - in the datasheet, it reads:

Note: If Smartcard mode is not supported, this bit is reserved and forced by hardware to ‘0’.

But apart from that, the bit which seems to be causing the issue is either NF or RXNE - those are the difference between 0x006000D0 and 0x006000F4. My code with the line  = (uint8_t)(p->Instance->RDR); should be clearing RXNE - I will try to forcibly clear NF and see if it helps.

More prone to thinking it's what you have externally than a failure of UART5 per se. Try on a NUCLEO, or other board, without external conflicts.

Should have your own code dump registers, whole heap of side-effects letting the debugger do it. Instrument rather than breakpoint.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
SBurc
Associate II

Clive - no, the same code is running without any issues when running from flash memory. The bootloader code is running from RAM without cache (to eliminate bootloader issues) - as a bootloader it works fine, but the BIT code which is there is having issues which it did not have when running in the maintenance program. There is also an SPI driven device which is giving another issue, I'm looking into it as well.

As far as code dump registers - am writing that now. The breakpoint "fixing" the issue was a side effect of trying to see what was happening.

SBurc
Associate II

OK - this "solves" the issue - and I use the term very loosely -

static bool SerialBITInternal(UART_HandleTypeDef *p)

{

 uint8_t i, data = 0, cleanupretry = 0;

 bool result = true;

  UART_EndRxTransfer(p); // cancel the wait for an interrupt, we work polling here in the BIT. 

  while ((p->Instance->ISR & 0xFF) != 0xD0)

 {

   __HAL_UART_CLEAR_OREFLAG(p); // clear any previous errors...

   __HAL_UART_CLEAR_NEFLAG(p);

   __HAL_UART_CLEAR_FEFLAG(p);

   __HAL_UART_CLEAR_PEFLAG(p);

   volatile uint8_t j = (uint8_t)(p->Instance->RDR); // and any previous data!!

   if (++cleanupretry == 0)

     break;

 }

 for(i = 'A';i<='Z';i++)

 {

   HAL_StatusTypeDef st;

   st = HAL_UART_Transmit(p, &i, 1, 10);

   if (st != HAL_OK)

   {

       st = HAL_UART_Transmit(p, &i, 1, 10);

       if (st != HAL_OK)

         result = false;

   } else

   {

     st = HAL_UART_Receive(p, &data, 1, 10);

     if (st != HAL_OK)

     {

       st = HAL_UART_Transmit(p, &i, 1, 10);

       if (st != HAL_OK)

         result = false;

       st = HAL_UART_Receive(p, &data, 1, 10);

       if (st != HAL_OK)

         result = false;

     }

     if (i != data)

     {

       st = HAL_UART_Transmit(p, &data, 1, 10);

       st = HAL_UART_Transmit(p, &i, 1, 10);

       if (st != HAL_OK)

         result = false;

       st = HAL_UART_Receive(p, &data, 1, 10);

       if (st != HAL_OK)

         result = false;

       if (i != data)

         result = false;  

      }

   }

 }

 return result;

}

The code at the beginning - the while loop that clears errors - seems to get things back to normal - but I (a) don't have an inkling why this is behaving differently between running with/without cache from flash/ram - it's completely internal to the UART - and specific to a single UART.

  while ((p->Instance->ISR & 0xFF) != 0xD0)

 {

   __HAL_UART_CLEAR_OREFLAG(p); // clear any previous errors...

   __HAL_UART_CLEAR_NEFLAG(p);

   __HAL_UART_CLEAR_FEFLAG(p);

   __HAL_UART_CLEAR_PEFLAG(p);

   volatile uint8_t j = (uint8_t)(p->Instance->RDR); // and any previous data!!

   if (++cleanupretry == 0)

     break;

 }

SBurc
Associate II

Just a note to anyone looking at this: I wrote earlier "There is also an SPI driven device which is giving another issue, I'm looking into it as well."... well, It is not an issue, I had to add a couple of lines of code to dis-inhibit some lines, the maintenance program did this elsewhere. The only issue is the one with UART 5.