When I need to measure character timestamps from a UART (having a FIFO), I use the occurrence of RDRF as the time. This has been working well, but I noticed that when I receive only a single character, the RDRF flag gets triggered about one character behind when the character was received. In other words, measuring `micros()` is about one character time too late. (This makes sense because this is, after all, a buffer.)
I accommodate this with the following logic in the ISR (complete program):
Results:
Notice that the characters are received at approximately the same time as they are sent, with the second byte of the 2-byte send being received 44us after the first byte. The point to note here is that for the 1-byte send, I need to subtract 44 before passing it to `handleByte` in order to get the correct character-end timestamp. This means that there is a one character delay before the RDRF bit is triggered.
To try this yourself, connect Serial2 TX to Serial1 RX.
My questions:
I mean, this makes sense when having a FIFO because the point is to buffer, but I'm trying to find where this is mentioned and how this relates to the watermark.
I accommodate this with the following logic in the ISR (complete program):
Code:
#include <kinetis.h>
void setup() {
Serial.begin(500000);
while (!Serial) {}
pinMode(LED_BUILTIN, OUTPUT);
Serial1.begin(250000, SERIAL_8N2);
attachInterruptVector(IRQ_UART0_STATUS, uart0_rx_isr);
while (!Serial1) {}
Serial2.begin(250000, SERIAL_8N2);
while (!Serial2) {}
}
void loop() {
static elapsedMillis p{1000};
static bool mode = false;
static uint8_t b[2]{0x02, 0x03};
// Every second, Serial2 sends data to Serial1, alternating between
// one byte and two bytes
if (p >= 1000) {
if (!mode) {
Serial2.write(0x01);
Serial2.flush();
Serial.printf("tx: %d: %02xh\n", micros(), 0x01);
} else {
Serial2.write(b, 2);
Serial2.flush();
Serial.printf("tx: %d: %02xh %02xh\n", micros(), b[0], b[1]);
}
digitalWrite(LED_BUILTIN, HIGH);
digitalWrite(LED_BUILTIN, LOW);
p = 0;
mode = !mode;
}
}
// Handles a byte. The timestamp marks the end of the character.
void handleByte(uint8_t b, uint32_t t) {
Serial.printf("RX: %d: %02xh\n", t, b);
}
// Handles UART0 receive.
void uart0_rx_isr() {
if ((UART0_S1 & (UART_S1_RDRF | UART_S1_IDLE)) != 0) {
__disable_irq();
uint8_t avail = UART0_RCFIFO;
if (avail == 0) {
// Re-align the FIFO
UART0_D;
UART0_CFIFO = UART_CFIFO_RXFLUSH;
__enable_irq();
} else if (avail == 1 && UART0_RWFIFO > 1) {
__enable_irq();
// It appears that the data-available flag isn't triggered until
// one character time after there is only one character available,
// so accommodate this when the FIFO has only one element
UART0_S1;
handleByte(UART0_D, micros() - 44); // <-- HERE
} else if (avail > 0) {
__enable_irq();
uint32_t timestamp = micros() - 44*avail;
while (--avail > 0) {
handleByte(UART0_D, timestamp += 44);
}
UART0_S1;
handleByte(UART0_D, timestamp += 44);
}
}
}
Results:
Code:
tx: 1934097: 01h
RX: 1934096: 01h
tx: 2934097: 02h 03h
RX: 2934096: 02h
RX: 2934140: 03h
tx: 3934049: 01h
RX: 3934048: 01h
tx: 4934097: 02h 03h
RX: 4934096: 02h
RX: 4934140: 03h
tx: 5934049: 01h
RX: 5934048: 01h
tx: 6934097: 02h 03h
RX: 6934096: 02h
RX: 6934140: 03h
Notice that the characters are received at approximately the same time as they are sent, with the second byte of the 2-byte send being received 44us after the first byte. The point to note here is that for the 1-byte send, I need to subtract 44 before passing it to `handleByte` in order to get the correct character-end timestamp. This means that there is a one character delay before the RDRF bit is triggered.
To try this yourself, connect Serial2 TX to Serial1 RX.
My questions:
- Does this only happen when UARTx_RWFIFO > 1? i.e. do I need that extra logic or is 'avail == 1' sufficient?
- Is "one character behind" the only case, or will this ever be "N characters behind"?
- I can't seem to find a mention of this in any of the KINETISK chip specs (for any of the Teensy 3's); does anyone know where this might be mentioned?
- Does the Teensy 4 processor have this same behaviour, according to its chip docs? Again, I couldn't find a mention anywhere.
I mean, this makes sense when having a FIFO because the point is to buffer, but I'm trying to find where this is mentioned and how this relates to the watermark.
Last edited: