I am writing my own UART driver for various reasons, and have learned a lot of things (the hard way, of course!)
First off, there is an optional RX FIFO you can turn on or not. If you don’t use the RX FIFO, then your interrupt handlers need to right on top of things because you will have exactly one byte transfer time to process the received byte before you will get an RX data overrun. To avoid overruns, the answer would appear to be simple: turn the RX FIFO on. But using the RX FIFO comes with peculiarities. First off is the RX FIFO level interrupt selection. The data sheet mentions it as as 3-bit field, but gives no indication as to what the 3-bit field means. Since the RX FIFO is 32 bytes long, it would need 5 bits to directly specify a length, so that’s not it. Reading through the SDK HAL helped me figure out that the silicon settings are defined as fractions of the entire FIFO length. For example, the setting AM_HAL_UART_RX_FIFO_1_8 means that the RX FIFO will interrupt when it is 1/8 full, or 1/8 of 32 bytes or 4 bytes. OK, whatever. I wish they could have just documented that in the data sheet.
But it begs the question, what happens if the FIFO interrupt is set to 1/8 full (4 bytes) and you receive a message that is 3 bytes long? Looking at real transfers, I can tell you that the answer: 3 bytes arrive, get stored in the FIFO and that’s the end of it. No RX interrupt. If a 4th byte arrives an hour later, you finally get an RX interrupt and find out about those last three bytes of the first message. IMHO, that kind of RX notification strikes me as almost useless. Unless your sender always sends messages that just happen to match your FIFO interrupt level setting, your messages will always appear to be truncated until more bytes arrive later.
Now this may have occurred to the engineers at Ambiq because of the existence of a mysterious UART interrupt called RTIM. I would love to tell you what it does, except that it is completely undocumented (at least in the first 14 revisions of the data sheet) except to say that RTIM is an interrupt bit that has to do with RX timeouts. What an RX timeout represents, who knows?
So I enabled the RTIM interrupt to see what was going on. And the answer is: it appears to give you an indication that you have received something, followed by nothing for a while, which might be enough to alert you to the fact that your RX FIFO contains something, although less than the number of bytes required to trigger a FIFO level interrupt. So I wrote a program to measure the time between interrupts, being especially interested in the timing of an RTIM interrupt after a regular RX FIFO level interrupt. It seems to work like this: every time a byte gets put into the FIFO, a timer is reset to start counting down 3 byte transfer times. If the timer expires (meaning that it has been 3 bytes times since the last char was received), the RTIM interrupt fires. The receive interrupt handling needs to be triggered on both a normal RX interrupt (meaning that the desired FIFO level of fullness has been reached) and an RTIM interrupt. If either one fires, there is data in the FIFO that should be drained.
Here is my RX ISR psuedocode:
void Uart::isr()
{
// Get all unmasked interrupt that are currently asserted
am_hal_uart_interrupt_status_get(phUART, &unmaskedIntStatus, true);
am_hal_uart_interrupt_clear(unmaskedIntStatus);
if (unmaskedIntStatus & (AM_HAL_UART_INT_RX | AM_HAL_UART_INT_RX_TMOUT)) {
// We have chars in the RX FIFO. Pull them all out now:
while (!UARTn(moduleId)->FR_b.RXFE) {
uint32_t ui32ReadData = UARTn(moduleId)->DR;
// Insert each char into a FreeRTOS Queue struct here...
}
}
}
The bottom line: If you use the RX FIFO, you really need to use RTIM. If someone sends you a message whose length matches up with the RX FIFO interrupt length setting, you will get an interrupt immediately after the last byte. If the message length is not equal to the RX FIFO interrupt length setting, the RTIM interrupt will occur 3 byte transfer times after the silicon received the last byte of the messaget. That’s certainly better than never receiving it until more bytes come in later, but is definitely something to be aware of. If you want to know about a char immediately after it is received, the only way I can see to do that is to not use the FIFO and live with the increased chance of overrun errors.
I wish that Ambiq had just documented the RTIM functionality. I could have this all wrong, but experiments seem to bear out what I have come up with.
There you go, another exciting day making my Artemis listen to a GPS module.