This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Swapped Characters with Older Serial.C putbuf()

The solutions I wrote about in
"Problems with putchar and putbuf in "Getting Started" and Traffic example"
at:
<http://www.keil.com/forum/docs/thread11351.asp>
seem to have solved all our serial and printf issues in C51. I'm now convinced we're seeing some of the same issues in our C16x application. The symptoms there included not only swapped characters, but random watchdog resets that seemed to be caused by a heavy load of serial _input_ characters.

The post from Mike Kleshov,
"A problem with serial IO in the 'Traffic' example"
at:
<http://www.keil.com/forum/docs/thread2936.asp>
describes what happens, and solves most of the problem by disabling interrupts, but that code still depends on the state of "sendactive" to determine whether to send the next output character directly to the hardware or put it into the software buffer. It is still possible for "sendactive" to be true for the test at the top of putbuf, causing the character to be destined for the software buffer, but then have a transmit interrupt clear the buffer and defeat the automatic next character interrupt before the new character actually gets moved into the buffer.

I thought moving the disable interrupts call above the first test of "sendactive" would work, and I haven't caught a "swapped character" error with that code, but under heavy loading many more characters are lost - apparently having the transmit disabled for more of putbuf wastes too much time.

In C51, I solved the problem this way:

if((t_in - t_out) <= (TBUF_SIZE - 2))  // if space in buffer
    {
    ES = 0;  // disable serial port interrupt

    tbuf [t_in++] = putme;
    if (t_disabled)  // if transmitter is disabled
        {
        t_disabled = 0;
        TI = 1;  // enable it
        }

    ES = 1;  // enable serial port interrupt
    return(putme);  // return character: ANSI requirement
    }

All characters go into the software buffer, and the initial send is triggered by raising "TI" in software.

It turns out the example of Serial.C shown in the Keil C16x Getting Started User's Guide 5.99 is similar:

void serial_TX_irq (void) interrupt S0TINT = 42
        {
        if (tx_in != tx_out) /* buffer not empty? */
        S0TBUF = tx_buf [tx_out++]; /* transmit the next character */
        else tx_restart = 1; /* transmit must be re-started */
        }

char putchar (char c)
        { /* substitute function for putchar */
        while (((unsigned char)(tx_in + 1)) == tx_out); /* buffer full? */

        _atomic_ (0); /* start un-interruptable code */
        tx_buf [tx_in++] = c; /* put the character in the buffer */
        _endatomic_ (); /* end un-interruptable code */

        if (_testbit_ (tx_restart))
                { /* if transmits must be restarted... */
                S0TIR = 1; /* enable transmit request */
                }
        return (c);
        }

Putting all transmit characters into the software buffer, and restarting the transmit interrupt cycle with S0TIR = 1 seems to work reliably and rapidly. There is still an opportunity for "tx_restart" to change between the two functions that use it, but if that happens in the new code it just means one character is delayed until the next one arrives. They still get sent in the proper order.

Loren (Hoping he doesn't need to revisit these issues!)

0