Thursday, May 8, 2014

high-speed UART: pumping out the data at 6mbps

TTL UART communication is the most common way to send data from a micro-controller to a PC.  It is simple and requires very little code in the MCU.  It's fine for configuration and debugging, but usually limited to low-speed (<=115.2kbps) communication.  With a 16Mhz AVR, USART timing is off by > 3% at 230.4kbps.  My bit-bang half-duplex UART can go up to 460.8kbps while keeping timing within 2.1%, and even 921.6kbps with a small tweak (a nop instruction) to the code.  This is still far short of the maximum 3mbps supported by FTDI FT232 chips and the 6mbps supported by Prolific's PL2303 chips.

There is a way to transmit at 6mbps with the AVR USART - synchronous master mode with a 12MHz system clock.
Rather than building a circuit with an AVR and a 12MHz crystal, I used a cheap USBasp.  The version made by Shenzhen LC Tech has the UART Rx and Tx pins connected to the 10-pin header, and has a 12Mhz crystal.  For power, it just plugs into a USB port.

The code isn't much different than regular asynchronous UART mode.  UMSEL needs to be set in UCSRC in order to turn on synchronous mode, and the XCK pin needs to be set to output for master mode.  The XCK pin is not connected on the USBasp, but it is not needed as the UART outputs a valid frame with start and stop bits.  Here's the initialization code:
#define XCKPIN 4
#define XCKDDR DDRD

static void uartInit()
{
    XCKDDR |= (1<<XCKPIN);       // output mode for master 
    UCSRC = (1<<URSEL)|(1<<UMSEL)|(1<<UCSZ1)|(1<<UCSZ0);
    UCSRB |= (1<<TXEN);
#ifdef HALF_SPEED
    UBRRL = 1;
#endif
}
Defining HALF_SPEED will output at 3mbps, for use with FTDI chips.  I set the self-program jumper (JP2) on the USBasp, connected the 10-pin ribbon cable to another USBasp, and flashed the code.  Then I plugged the USBasp into a free USB port, and connected the Tx pin to the Rx pin of a PL2303HX module.
To test the data reception, I could tell data was being received by looking at the Rx activity LED, so I tried a terminal program.  There were no errors in the data stream, however there were lots of dropped characters.  Suspecting the problem was receive buffer overruns while updating the display, I wrote a perl program to receive the data:
#!/usr/bin/perl

# 1st argument must be port name
$port = $ARGV[0];

# open port in read/write mode
open(UART, "+<$port") or die "$port ", $!;
binmode UART;
my ($buf, $count);
$count = read (UART, $buf, 240);
print "read:\n";
print "$buf";
close(UART) or die $!;

The baud rate for the port needs to be set in advance - use mode or stty for Windows or Linux respectively.  Without the delays for displaying the characters as they are received, there were no errors:

You might ask when would you need to transmit data at such high speeds.  Using an AVR as a logic analyzer is the first thing I intend to try.  Watch for it in my next post.

2 comments:

  1. Hi, nice thing; so you are using bitbang/sw for this ok? 10 years ago I was trying to design some network of AVR "slave sensor servers" on RS-485 link (so 9bit hw mode) controlled by single master (infinite loop: broadcast command "sample" to ALL of 16 modules for 8 ADCs at exactly the same time and then one by one "fetch" command to get sampled data from them - the datasheet was about 1 or 2Mbps max on 16Mhz AVR ... and today, I would probably use some 32bit Cortex for this rather... and saw some 5Mbit CAN TI chips too; I am stil thinking about it, back then was finally no time to continue with it, but today things are cheaper and faster - its something you can recommend for this? I was considering RS485 chip for differential link on few meters only, having untwisted flat cables though; kindly please, if you are able to help with this, I would be happy to ask you more, if possible :)

    ReplyDelete
    Replies
    1. No, as I explained in the post, I'm using the hardware USART.

      Delete