2019-08-19 02:15 AM
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.
2019-08-19 02:16 AM
For some reason, the "for" loop got cut while pasting - it should read
for(i = 'A';i<='Z';i++)
2019-08-19 03:17 AM
> 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
2019-08-19 08:10 AM
Sorry for the delay - First I'll answer your questions, then put in some of the debug information gathered since then.
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...
2019-08-19 08:11 AM
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 ...
2019-08-19 08:17 AM
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.
2019-08-19 08:32 AM
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.
2019-08-19 08:58 AM
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.
2019-08-19 09:27 AM
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;
}
2019-08-19 09:53 AM
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.