Teensy 3.1 + ChibiOs + non-blocking Serial

Take away: you can use multiple Serial I/O in Greiman’s ChibiOs port in various tasks without giving it much thought, it will work without tasks blocking each other.

I was wondering what would happen in using ChibiOs on the Teensy 3.1 with two threads, say one making the led blink and the other one reading on the serial port (the USB one for example). I’m using Bill Greiman’s ChibiOs port for teensy (https://github.com/greiman/ChibiOS-Arduino). The point is that only the kernel has been ported, not the HAL (Hardware Abstraction Layer) part, thus my guess was that a blocking I/O operation would block other tasks, and in particular that it wouldn’t be feasible to read on multiple serial ports.

It turns out this is not the case (for reading multiple serial ports).

First, I should have been more clever, since Serial API (the Serial.read()) has a non-blocking semantics (it returns -1 if there is nothing to read).

Second, by examining the Teensy Serial code, I got that (in HardwareSerial.h):

int serial_getchar(void) {         
         uint32_t head, tail;
         int c;

         head = rx_buffer_head;
         tail = rx_buffer_tail;
         if (head == tail) return -1;    
         if (++tail >= RX_BUFFER_SIZE) tail = 0;
         c = rx_buffer[tail];
         rx_buffer_tail = tail;
         return c; 
} 

As we can see, there’s nothing blocking there, we’re just making some in-memory operations, no loop, nothing. But where does the character in the buffer come from ?

The answer is in the uart0_status_isr interrupt routine. I won’t copy it there, but in short, the ISR is called (by the hardware) when there is something to read in the UART, read it bit by bit and returns. The point is there is no polling, nowhere, and a short interrupt masking during the reading of the UART data register.

What about writing ? First, after all, writing to the UART doesn’t need to be blocking (if the receiver isn’t ready, that’s its business). Furthermore, there are generous yield spreaded through the code, but since I’m not sure yet how it translates with ChibiOs, I’ll have to investigate further.

2015-05-13: Is delay a busy loop ?

However, using a loop with delay in one task blocks the other one, in spite of a yield in the implementation of delay.

Actually, if two tasks have the same priority, they don’t block each other. It means however (with ChibiOs), that a task executing delay blocks tasks with lower priority (unlike chThreadSleep).

2015-05-14: At least we know what’s in yield

void yield(void) __attribute__ ((weak));
void yield(void)
{
        static uint8_t running=0;

        if (running) return; // TODO: does this need to be atomic?                                  
	running = 1;
	if (Serial.available()) serialEvent();
	if (Serial1.available()) serialEvent1();
	if (Serial2.available()) serialEvent2();
	if (Serial3.available()) serialEvent3();
	running = 0;
};