Talk:Serial Programming/Complete Wikibook

Look at the table of the LCR.. the parity select columns say "Bit 3 | Bit 4 | Bit 5" and the word length bits go "Bit 1 | Bit 0". The one ascending and the other descending. Look at the parity select table from here http://www.beyondlogic.org/serial/serial.htm

Am I right to think that the parity select table there is correct? (Yes it is, I checked. Main document is wrong)

I've written a serial communication program in Turbo C (DOS) recently and I've encountered quite a few "gotcha's" that don't seem to be described in the main document. You mostly run into these problems when doing high volume/high speed data transfers. I am crapy with formatting wiki text, so I'll leave this here for others to implement into the main document.


 * Flowcontrol. When implementing flow control in your program keep in mind that some data will be 'on the wire' or in the UARTS fifo. Only the more advanced 16750 uarts can have hardware support for flowcontrol, (and it usually doesn't). In the most common 16550 uart, you will have to implement flowcontrol in software yourself. So, in case of RTS/CTS flowcontrol, make sure you raise your RTS before you reach your absolute limit in your receive buffer or you will have to drop data bytes. Since most uarts use a 16 byte fifo, raising the RTS some 20 bytes before your receive buffer is full should be sufficient.  In the same spirit, keep your transmit buffer on short leash. When the remote side raises a flowcontrol flag, try stop sending as soon as possible. Keep in mind that anything in the transmit fifo will go out regardless.
 * Shared IRQs. Data transfer over multiple serial ports on the same IRQ should be possible since the uarts will use a flag (bit 0 of IIR will be 0) to indicate if it generated any interrupts.
 * Broken fifos. Not all uart types have working fifo's. Be sure to check which one you are running on and disable them if needed.
 * BREAK signal. If you have 'Break signal enabled' (bit 6 in the LCR) then receiving a BREAK from the remote side will set your parity to space (aka sticky) to send a BREAK signal back. If you are not doing anything with BREAKs, I reccoment you turn this off.
 * Interrupt priorities. For EACH event the uart will generate an interrupt; receiving a byte, having send a byte or changes on status lines. However, some events will be of a higher priority then others. This creates a problem for the 'Transmit holding register empty' interrupt which is on the lowest priority. Sometimes this event will be lost if there's a lot of other events coming in. If you are relying on 'THR empty' interrupts to drive your transmit-buffer sending code, then you will end up in a deadlock where you are waiting for the interrupt but it won't come since it has been lost. To prevent this, you must twiddle the 'TX interrupt enable' bit (bit 1 of IER) after servicing an interrupt event from the uart. This will ensure that if the THR is empty, an interrupt will be guaranteed.
 * Many events in one interrupt. If you are doing high-speed, full duplex, flowcontrolled transfers then many events will occur. To speed up handling these events you can keep processing events in a single interrupt. Once you clear the current event by reading the correct register for example, then you can jump back to checking the uart for more events.
 * Shadow registers. Reading some registers will reset counters and some, like you speed divisor, are just hard to reach. I reccomend you keep shadow registers for most of these registers and read these out instead of the registers themselves. An exception can be made for line signals like CTS, DCD and such.

Here's a correct and complete template for a C ISR function. Note that this template does not include XON/XOFF flowcontrol.

void interrupt serial_isr(void) { unsigned char iir, t, b;

disable;                             /* Disable all interrupts */

for(t=0; t<MAX_UARTS; t++)             /* Loop through the UARTS we're servicing */ {

if(UART[t].in_use==0)                /* This uart in use? */   continue;

UART[t].counter_int++;               /* We received an interrupt for this uart */

for                              /* Keep looping until we serviced ALL events for this uart */ {   iir=inportb(UART[t].base+IIR);      /* Get interrupt status */

if(iir & BIT_0)                    /* Is there (still) an uart event available? (1==No further events) */ break;

switch(iir&(BIT_1+BIT_2+BIT_3))    /* Check the status bits to see what event this is */ {     case  0:  /* 000 - Modem status change (MSR) */

UART[t].counter_status_change++; UART[t].reg.msr=inportb(UART[t].base+MSR); UART[t].status_changed=1;

/* cts, ring, and such indicators are here */ break;

case 2:  /* 001 - Transmitter holding register empty (IIR/THR) */

UART[t].counter_tx_int++;

/* Don't send anything if we're being flowcontrolled */

if(UART[t].flow_config==FLOW_RTS_CTS && get_CTS(t)==0) break;

/* If the THR is _NOT_ empty, break */

if((inportb(UART[t].base+LSR) & BIT_5) == 0) break;

/* Output 1 byte from the ring buffer to the uart */

if(read_ring(UART[t].out, &b, 1)) outportb(UART[t].base+THR, b);

/* If we have nothing more to transmit, turn off ENABLE TRANSMIT INTERRUPT */

if(used_ring(UART[t].out)==0) set_write_irq(t, 0);

break;

case 12: /* 110 - Character timeout (16550) (RBR) */

UART[t].counter_timeout++;

/* FALL THROUGH */

case 4:  /* 010 - Received data available (RBR) */

UART[t].counter_rx_int++;

/* Read in the byte from the uart and store it in the ring buffer */

b=inportb(UART[t].base+RBR); if(write_ring(UART[t].in, &b, 1)==0) UART[t].counter_rx_dropped++;

/* Raise flowcontrol flag if we're starting to run low on space in the receive buffer */

if(UART[t].flow_config==FLOW_RTS_CTS && free_ring(UART[t].in)<20) set_RTS(t, 0); /* Tell other side to stop sending data */

break;

case 6:  /* 011 - Line status change (LSR) */

UART[t].counter_lsr++;

b=inportb(UART[t].base+LSR);

if(b & BIT_1) UART[t].counter_overrun_err++;

if(b & BIT_2) UART[t].counter_parity_err++;

if(b & BIT_3) UART[t].counter_framing_err++;

if(b & BIT_4) UART[t].break_active=1;

if(b & BIT_7) UART[t].counter_fifo_err++;

break;

default: UART[t].counter_unknown++; break;

} /* switch */

twiddle_write_enable(t);  /* Make sure we don't miss any write interrupts */

} /* for loop that keeps checking the IRQ_active bit */

clear_irq(UART[t].irq);  /* Dismiss IRQ, PIC1 or PIC2-PIC1 */

} /* for */

enable;      /* Re-enable all interrupts */

} /* serial_isr */

-- Ren (82.197.192.59 11:57, 13 October 2007 (UTC))