I run a thread with an UART shell (commands to control the system).
There is a GPIO interrupt, which comes with 1 KHz repetition rate.
The INT Handler for this GPIO uses EXTMEM, e.g. to store data in EXTMEM (which can be 1 KByte per interrupt).
So, the load due to this GPIO INT plus EXTMEM is pretty large (but not too large: all INTs are still processed properly, nothing lost on user INT handling).
What I see:
The USB UART is not responsive anymore.
I can still type characters on UART, manually, as a human normal typing speed, but I have lost characters.
The command line is not responsive anymore. It is tough to send a command string, often characters lost (not received: I have a local echo on MCU).
I have to type twice to get one character, a regular manual human typing of commands is not possible anymore.
It looks like, the frequent data access to EXTMEM (caused by the periodic GPIO interrupts) blocks the reception of USB UART.
Why?
A bit obvious: EXTMEM write is much slower as internal RAM. But why does it block the reception of USB UART?
I would hope, an access to EXTMEM does not block the reception of USB UART characters (but it seems to do).
BTW:
I saw in Teensy 4 LIB code this:
"HardwareSerial.cpp", when Serial.available() is called, even Serial.Read() - it disables the MCU IRQ almost for the entire time of the function calls
(using __disable_irq()
Similar in USB "usb_serial.c", function usb_serial_read() uses NVIC_DISABLE_IRQ(IRQ_USB1);
So, it tells me this:
If you call Serial.available() "too often and fast" - it will disable (ALL !) interrupts: and this for more as 50% of time as MCU executes code - the INTs are disabled.
Assume this code:
which means: you poll full speed if Serial.available() has something, and even you do a Serial.read() - MOST OF THE TIME the interrupts (USB1 or ALL) are disabled!.
So, there is just a tiny "timing window" where an INT can happen and would be processed (as fast as possible). But most of the time, the INT is "blocked" (and would come later, and randomly).
My impression:
The UART code, for USB UART, HardwareSerial, is not written in a way that other real-time stuff (INTs) can be processed.
I do not see a need to disable all INTs (IRQs), even not the USB INT - even I understand the concern with "race conditions". A proper FIFO implementation for received data
can work even without to block "all the time' other INTs. I do not see a need for disabling INT(s) if the implementation would be more tolerant for "race conditions".
So, using UART in combination with other real-time stuff, like other INTs, having the UART still responsive during a high INT load (frequency of other INTs, e.g. GPIO),
does not seem to be possible.
The Serial.available() and Serial.read() block and make the real-time response "bad and unpredictable".
BTW:
If you think to solve by using using a sleep(1), for 1 milli-second, when Serial.available() "fails", it does a create a new problem: now you lower the maximum possible
UART baudrate (e.g. just one character every 1 milli-second possible). Not possible anymore to send UART from a script (e.g. Python) in max. speed potentially possible.
Why the USB UART (and HardwareSerial) does not have an "Non-INT-Blocking" implementation, no guarantee that all characters are received, even with a higher INT load in background?
I am pretty unhappy with the UART implementation and performance (not designed for real-time systems).
UART becomes unresponsive with background INTs and access to EXTMEM.
There is a GPIO interrupt, which comes with 1 KHz repetition rate.
The INT Handler for this GPIO uses EXTMEM, e.g. to store data in EXTMEM (which can be 1 KByte per interrupt).
So, the load due to this GPIO INT plus EXTMEM is pretty large (but not too large: all INTs are still processed properly, nothing lost on user INT handling).
What I see:
The USB UART is not responsive anymore.
I can still type characters on UART, manually, as a human normal typing speed, but I have lost characters.
The command line is not responsive anymore. It is tough to send a command string, often characters lost (not received: I have a local echo on MCU).
I have to type twice to get one character, a regular manual human typing of commands is not possible anymore.
It looks like, the frequent data access to EXTMEM (caused by the periodic GPIO interrupts) blocks the reception of USB UART.
Why?
A bit obvious: EXTMEM write is much slower as internal RAM. But why does it block the reception of USB UART?
I would hope, an access to EXTMEM does not block the reception of USB UART characters (but it seems to do).
BTW:
I saw in Teensy 4 LIB code this:
"HardwareSerial.cpp", when Serial.available() is called, even Serial.Read() - it disables the MCU IRQ almost for the entire time of the function calls
(using __disable_irq()
Similar in USB "usb_serial.c", function usb_serial_read() uses NVIC_DISABLE_IRQ(IRQ_USB1);
So, it tells me this:
If you call Serial.available() "too often and fast" - it will disable (ALL !) interrupts: and this for more as 50% of time as MCU executes code - the INTs are disabled.
Assume this code:
Code:
char * UART_gets(char *b, int len) {
int c;
do {
if (Serial.available()) {
*b++ = c = Serial.read();
}
} while (c != '\r');
//...complete the string received
which means: you poll full speed if Serial.available() has something, and even you do a Serial.read() - MOST OF THE TIME the interrupts (USB1 or ALL) are disabled!.
So, there is just a tiny "timing window" where an INT can happen and would be processed (as fast as possible). But most of the time, the INT is "blocked" (and would come later, and randomly).
My impression:
The UART code, for USB UART, HardwareSerial, is not written in a way that other real-time stuff (INTs) can be processed.
I do not see a need to disable all INTs (IRQs), even not the USB INT - even I understand the concern with "race conditions". A proper FIFO implementation for received data
can work even without to block "all the time' other INTs. I do not see a need for disabling INT(s) if the implementation would be more tolerant for "race conditions".
So, using UART in combination with other real-time stuff, like other INTs, having the UART still responsive during a high INT load (frequency of other INTs, e.g. GPIO),
does not seem to be possible.
The Serial.available() and Serial.read() block and make the real-time response "bad and unpredictable".
BTW:
If you think to solve by using using a sleep(1), for 1 milli-second, when Serial.available() "fails", it does a create a new problem: now you lower the maximum possible
UART baudrate (e.g. just one character every 1 milli-second possible). Not possible anymore to send UART from a script (e.g. Python) in max. speed potentially possible.
Why the USB UART (and HardwareSerial) does not have an "Non-INT-Blocking" implementation, no guarantee that all characters are received, even with a higher INT load in background?
I am pretty unhappy with the UART implementation and performance (not designed for real-time systems).
UART becomes unresponsive with background INTs and access to EXTMEM.