The design decisions for the edge cases differ for the application and interrupt code. The most important requirement for interrupt code is that it may never block. For example, if the receive data register is full and the RX queue is also full, the only way to remove the interrupt condition is to read the receive data register and discard the data. Thus, an interrupt driven USART cannot completely eliminate the lost data problem of the polling based solution – that will come with the addition of flow control. In contrast, the application code may block. For example, if the application executes putchar and the TX queue is full, then it may “poll” to wait for the full condition to be removed (by the interrupt handler). In this case, the application code
is again slowed to the transmit rate, but only after the TX queue is filled. An important implementation decision is how large the queues should be to prevent application stalling