Serial Demo in C for the Arduino Uno

Now we are going to see where the rubber meets the road. The Uno (using the atmega328P chip) has a hardware serial port. The question is whether we need to reinvent the wheel and write a serial driver, or if there is existing code that we can leverage.

What about grabbing something from Arduino 1.0.1 ?

The thing we might want to grab is /usr/share/arduino/hardware/arduino/cores/arduino/HardwareSerial.cpp (along with HardwareSerial.h most likely). This involves us in calling C++ objects from C code, or with writing our own code in C++. I am not sure how difficult the former is (or if it is possible at all). Coding in C++ is one of the very things I am striving to avoid by not using the Arduino GUI. So it is on to other options.

What about wiring_serial.c ?

Back in the old days of arduino, there was wiring_serial.c instead of HardwareSerial.cpp. This was present in arduino-0015 (March of 2009), being replaced by HardwareSerial.cpp in arduino-0016 (May of 2009). Happily, old versions of the arduino distribution can still be obtained. The question is how suitable this will be for a newer board like the Uno. And it turns out to be just fine (see below), which is not all that surprising given that the Uno is based on the venerable atmega328 chip).

What about the Procyon avrlib ?

The Procyon avrlib was last updated in September of 2005. It has a file called uart.c (along with uart.h) that could serve our purposes. I am finding though that it will take some work to get it to compile with the current avr-gcc (in October of 2012 using avr-gcc version 4.6.3). I am also concerned about multitudes of dependencies with other parts of avrlib.

Dive in with wiring_serial.c

Wiring_serial.c turns out to be not too entangled with other things. I only need 3 header files wiring_private.h wiring.h and binary.h. I actually don't need binary.h but it gets pulled in by the other header files. I use the files from arduino-0015, and ignoring binary.h this is only about 300 lines of code. The best part of all is that it works right away at 9600 baud.

Can we go faster? I am downloading to the Uno at 115200 baud. It turns out that I can use 115200 baud just fine with absolutely no hassles. Linux accepts B115200 as a defined speed for /dev/ttyACM0 (somewhat to my surprise). "ACM" by the way is the USB "Abstract Control Module" in linux and uses the cdc_acm driver, which may be of importance someday. The letters "CDC" stand for Communications Device Class".

A peek at the driver source cdc_acm.c shows the following list of speeds, which bodes well for the future with things like the ATmega32u4:

static const __u32 acm_tty_speed[] = {
	0, 50, 75, 110, 134, 150, 200, 300, 600,
	1200, 1800, 2400, 4800, 9600, 19200, 38400,
	57600, 115200, 230400, 460800, 500000, 576000,
	921600, 1000000, 1152000, 1500000, 2000000,
	2500000, 3000000, 3500000, 4000000 };

The interface to wiring_serial is very straightforward:

And wiring_serial is interrupt driven (for receive), who could ask for more?

What about printf() ?

Or in general, what about the whole gamut of C library routines that we know and love? Given serialWrite() (which is the moral equivalent of putchar() or putc(), it is trivial to write a puts() routine, which is immensely handy. Given that and sprintf() -- which is available in avr-libc, it is straightforward to cobble together a printf routine. But it turns out it is even easier than that. This is hardly an intended consequence, I am sure, but it turns out that if you provide a puts() routine, you can just use the printf() routine in avr-libc (no doubt it is calling puts() once it has the string formatted, and it just works ... for now.

There are more official ways to do things using avr-libc. In fact there is a uart module in there, and ways to hook up the uart routines to stdout so that the whole gang of C library routines will be reading and writing from the serial port. There is a swell demo that gives all the details of this in the avr-libc online documentation. My only concern is how much code bloat will result. Perhaps the best approach would be just to use the routines in avr-libc and not worry about things until code size becomes an issue.

The clever folks who have put together avr-libc are not heartless with respect to the issues of code size. You should look at the documentation for vfprintf, which is at the heart of much of this. It can be obtained in 3 flavors by using link time switches. By default you get a version that leaves out floating point formatting (if you want that, use the link switches: "-Wl,-u,vfprintf -lprintf_flt -lm"). The third option is a minimal version that handles only integer and string formatting, and you obtain it by using the link switches: "-Wl,-u,vfprintf -lprintf_min".

This is hardly the place for it, but once I got printf() going, I was curious to find out the size of various integers under avr-gcc:

Testing with picocom

This turns out to be the best thing since sliced bread. I have wrestled with minicom under linux before, and this is far better.
yum install picocom
picocom -b 115200 /dev/ttyACM0
And voila! I have a terminal window. No insane nonsense as with minicom which wants to try to send "AT" commands to a supposed modem by default (which might have made sense back around 1975 or so, ... maybe).

To get out of picocom type the two character salute ^A^X. And this is a fantastic feature: you can go up and down the set of known baud rates using ^A^U to go up and ^A^D to go down. Pure genius! Look at the man page via "man picocom" to get the rest of the story.


Feedback? Questions? Drop me a line!

Tom's Computer Info / tom@mmto.org