arc_lupus
arc_lupus

Reputation: 4114

Using usart in full-duplex-functionality

I want to use the USART-functionality of my Atxmega-µC. Therefore I looked into the usart-example for the first steps. It is designed to get a char, and afterwards send it back.

while (true) {
        received_byte = usart_getchar(USART_SERIAL_EXAMPLE);
        if (received_byte == '\r') {
            for (i = 0; i < tx_length; i++) {
                usart_putchar(USART_SERIAL_EXAMPLE, tx_buf[i]);
            }
        } else
            usart_putchar(USART_SERIAL_EXAMPLE, received_byte);
    }

I was wondering if that is a parallel process (appearently not), and how I should refactor this code if I want to send and receive data truly in parallel, not as shown in the example code above in sequence.
In theory (not tested till now) splitting this up into several threads won't improve the situation, after the µC is only "single-core". Is there another way to send and receive data in parallel via USART?

Upvotes: 1

Views: 589

Answers (1)

milenko121175
milenko121175

Reputation: 84

You can not achieve true parallelism. I did a similar design using Atmel UC3, but I pulled from a random XMega datasheet to confirm they at least looked similar without going into great detail. There is no separate buffers for read and write. Look at register description:

USART Register Description

The data register is shared for both transmit and receive. Having said that, it is still possible to design for full-duplex to be "near" parallel using interrupts and a FIFO. See code below:

static void usart_int_handler(void)
{
    int result;
    while (PS_USART->csr & RX_READY)
    {
        result = fifo_push_byte(&PS_FIFO, PS_USART->rhr);
        if (result == FIFO_ERROR_OVERFLOW)
        {
            fifo_flush(&PS_FIFO);
            break;
        }
    }
    int parity_error = usart_parity_error(PS_USART);
    int framing_error = usart_framing_error(PS_USART);
    int overrun_error = usart_overrun_error(PS_USART);
    if ( parity_error || framing_error || overrun_error )
    {
        // Clear out FIFO and reset errors.
        // Then exit out and let calling object
        // timeout.
        while (PS_USART->csr & RX_READY)
        {
            result = PS_USART->rhr;
        }
        fifo_flush(&PS_FIFO);
        usart_reset_status(PS_USART);
        return;
    }
}

The FIFO can be polled or you can set an interrupt that it has reached a threshold of data. I handled transmission in this fashion:

void write_ps_serial (volatile avr32_usart_t *usart, const unsigned char *string, int size)
{
    // When using this - we needed a software workaround
    // to set and reset RTS pin.  Operation of RTS is not
    // correct.
    Disable_global_interrupt();
    gpio_set_pin_high(PS_RTS_PIN);
    for (int index = 0 ; index < size ; index++)
    {
        usart_putchar(usart, string[index]);
    }
    while ( (PS_USART->csr & TX_EMPTY) == 0 )
    {
        // Sit here waiting for the channel
        // status register to indicate TX_EMPTY
        // has gone high which means we can shut
        // off RTS.
    }
    gpio_clr_gpio_pin(PS_RTS_PIN);  
    Enable_global_interrupt();
}

I still think that there is something in the hardware that prevents collision. Even though I check for errors, I had the emulator on for 3 days while pounding the bus with constant communication and never once received an error. But you would definitely need to encode your own protocol to handle dropped packets or use stricter RS232 controls (this code was for full duplex 485). I have more code if you would like. I hope this helps.

Upvotes: 1

Related Questions