Serial I/O - The Basics - Make: AVR Programming (2014)

Make: AVR Programming (2014)

Part I. The Basics

Chapter 5. Serial I/O

Square-Wave Organ

This chapter gives you a lot of bang for the buck. Before it’s all over, you’re going to have learned how to communicate between the AVR and your desktop or laptop computer and constructed a cheesy computer-controlled musical instrument.

Along the way, you’re going to learn a little about serial communications protocols, and how you can generate quick-and-dirty audio with the AVRs. Serial communication is the simplest possible way to interface your microcontroller with your desktop or laptop computer, your first step toward bridging the world of the physical and the virtual.

WHAT YOU NEED

In addition to the basic kit, you will need:

§ A speaker with a DC-blocking capacitor around 10 uF.

§ A USB-Serial adapter.

§ (Optionally) an amplifier if you want to play it loud.

Serial Communication

How do computers or integrated circuit components actually talk to each other? The (too-short) answer is that it’s almost exactly like what we were doing in Chapter 3. One side sends a signal by outputting high and low voltage pulses on a wire that connects it to the other device. The other side, in input mode, listens to the voltages on the wire.

The rules for encoding data into voltage pulses and decoding the voltage pulses back into data is called a serial protocol. We’ll get into a lot more of the nitty-gritty of other serial protocols in Chapter 16 and Chapter 17. For now, we’ll limit ourselves to the most common serial mode—universal asynchronous receive and transmit (UART) serial.

To understand what’s going on with UART serial, start by thinking of two people who want to talk to each other by sending voltage signals over a few wires. Let’s say Alice wants to send the number 10 to her friend Bob over a wire. For concreteness, let’s say that the wire’s got a pull-up resistor on it so that it’s constantly at five volts. On Alice’s side of the wire, there’s a switch connected to ground, and on Bob’s side, there’s also an LED so that he can see what the voltage on the wire is by whether or not the LED lights up.

Alice is going to send the number 10 to Bob by pressing her button, grounding the wire, and turning off Bob’s LED on the other side. Now, she could send the number by just blinking the LED off 10 times, but that system’s going to break down when she wants to send the number 253, or worse, 64,123. So instead she writes out 10 in binary, 0b00001010, and sends a corresponding pattern of flashes.

Bob and Alice have to agree on a bunch of things beforehand for this to work—the serial protocol. First they need to decide on an encoding: they agree beforehand that a button press (a zero-volt signal on the wire) indicates a zero, and no-press (five volts) indicates a one, and that they’ll send the numbers least-significant-bit first.

Next, they need to agree how often Alice presses or doesn’t press the button. Let’s say they choose to signal once per second. This is the baud rate—how often the voltage is allowed to change on the line, and conversely how often the receiver needs to read in a new voltage.

How does Bob tell when the transmission begins and ends? They’ve agreed to wrap the eight bits with two extra bits: a start bit, which will always be a zero so that you can tell when the transmission starts, and a stop bit, which is a one.

Bob is sitting at his end, staring at the LED, when he sees the LED blink. It blinks off for a second—the start bit! Now once every second after the start bit, he notes down whether the LED is on or off. After the first blink, he sees off, on, off, on, off, off, off, off, and then the LED stays on for a while. He writes down his eight bits, 01010000. He then flips the bit-ordering around, and sees that Alice has sent the number 10!

The oscilloscope trace in Figure 5-1 is a real example of an AVR transmitting the digit 10 to my computer. Instead of one bit per second I used 9,600 bits per second (baud), so each bit takes about 104 microseconds. But you can make out the pattern: 1111100101000011111. Remember that the first low bit is the start bit, then count out eight bits, check that the ninth is high, flip the order, and read it out in binary.

Sending 10 in Serial

Figure 5-1. Sending 10 in Serial

Now if Bob wants to reply, the simplest solution is just to repeat the same physical setup and protocol, but in reverse—that is, give Bob a button and Alice an LED. That way, Bob and Alice can signal each other at any time, sending numbers back and forth. It takes two signal wires to make this work, but the advantages of dedicated lines are simplicity and data throughput.

Figure 5-2 is the trace when two bytes in a row are sent: 9 and then 10. Notice in the center that the stop bit that lies between the two digits is slightly longer—the AVR was doing work and not sending data for an extra few microseconds. This doesn’t bother the receiving computer, which starts timing again only when it receives the next start bit.

Sending 9 and 10 in Serial

Figure 5-2. Sending 9 and 10 in Serial

See Figure 5-3 for the logic interpretation of the signals, and try to read out the bits from the scope trace if you’re so inclined.

Sending 9 and 10

Figure 5-3. Sending 9 and 10

Encoding and decoding this data seems like a lot of work, and getting the timing exactly right to send and receive data at baud rates in the tens of thousands of bits per second is no picnic either. That’s why all of the AVR Mega microcontrollers have at least one dedicated hardware peripheral, called a Universal Synchronous and Asyncronous Receiver and Transmitter (USART) device built in. In the rest of this chapter, we’ll look into how to configure and use the USART hardware, as well as how to set up your desktop computer to talk to the AVR.

UART AND USART

In the introduction, I said we’d be using UART (universal asynchronous receive and transmit) serial, but the AVR’s peripheral is called a USART. What’s the difference? Why the extra “S”?

The AVR’s serial hardware is capable of running both in a synchronous mode—using a clock that helps with data timing—and in asynchronous mode without a clock, hence “USART” (universal synchronous and asynchronous receiver and transmitter). But until Chapter 16, we’ll be exclusively concerned with asynchronous serial communications, so you can feel free to ignore the “S” in the AVR’s USART for now.

Implementing Serial Communication on the AVR: Loopback Project

The first thing you’re going to want to do is make sure that your serial link between computer and microcontroller is up and running, and get used to using it. Time to get your chips talking to your desktop computer.

Our setup here is going to involve three different stages:

1. Configuring the AVR

2. Installing serial terminal software on your computer

3. Connecting them together

Setup: Configuring the AVR

Because the AVR microprocessors have dedicated USART circuitry inside, all we have to do to communicate is configure the interface, and then dump our bytes into the special hardware registers designated for sending and receiving. The hardware peripheral will take care of the rest. That’s fantastically handy.

First, just to make sure everything is working, we’ll flash in a quick demo program. If you’ve got the LEDs still plugged in from the last few chapters, that’s great! If not, you’ll miss on out seeing the ASCII numeric representation of each character’s value as you type it out. Before we dive into the nitty-gritty of configuring the USART hardware, let’s see how we can use it by working through Example 5-1.

Example 5-1. serialLoopback.c listing

/*

A simple test of serial-port functionality.

Takes in a character at a time and sends it right back out,

displaying the ASCII value on the LEDs.

*/

// ------- Preamble -------- //

#include <avr/io.h>

#include <util/delay.h>

#include "pinDefines.h"

#include "USART.h"

int main(void) {

char serialCharacter;

// -------- Inits --------- //

LED_DDR = 0xff; /* set up LEDs for output */

initUSART();

printString("Hello World!\r\n"); /* to test */

// ------ Event loop ------ //

while (1) {

serialCharacter = receiveByte();

transmitByte(serialCharacter);

LED_PORT = serialCharacter;

/* display ascii/numeric value of character */

} /* End event loop */

return (0);

}

Skip straight down to the event loop, and you can see that there’s only three things our program is doing. It receives a byte from the serial line using the function receiveByte(). Then it transmits that same byte back out using transmitByte(). Finally, the LED displays the byte that it just sent across in ASCII.

In the initialization section, you shouldn’t be surprised to find that we’re initializing the LEDs for output just as we did in the previous chapter. What’s new are the functions initUSART() and printString(). They must be defined somewhere, but where? Scrolling up to the preamble, you’ll see that I’ve included a new file, USART.h. If you open that file up, you’ll find their prototypes, and we’ll go into detail about the configuration routine in Configuring USART: The Nitty-Gritty Details and including modules in Modules in C. For now, we’ll focus on making sure everything is up and running.

FUNCTIONS AS VERBS

It’s a style question, but I find that if I name all of my functions with verbs that describe what they do, and all the variables as descriptive nouns, C code can start to read like a sentence.

Sure, you may have to type a little more the first time, but consider yourself coming back to look at this code six months from now. Will you be able to browse through the code and find the section that you’d like to modify? You will, if your functions and variables have descriptive names.

That’s the overview. If you haven’t already, now’s a good time to flash the serialLoopback project code into your AVR.

Setup: Your Computer

On your computer, it’s just a matter of installing the right software. In particular, you need to connect to a terminal application that’ll let you type to the AVR and read what it types back. If you’ve got a favorite terminal application, by all means feel free to use it. Otherwise, I’ve got a few suggestions.

TERMINAL EMULATOR SOFTWARE

Linux

gtkterm is nice and easy to configure and should work on most Linuxes. Another option is screen, which is available on both Mac OS and Linux, but which you may not realize is a serial terminal in disguise. Invoking screen with screen [portName] 9600 should get you talking to your AVR directly. Ctrl-A followed by Ctrl-? will get you help. Ctrl-A then Ctrl-K will kill your current session. Linux users can find out which USB serial port is connected by typing ls /dev/tty*.

Mac OS

CoolTerm and ZTerm are nice serial terminal applications for Macs. If they don’t detect your serial port automatically, you can find it by opening up a regular terminal and typing ls /dev/*usbserial*. You’re looking for something like /dev/cu.usbserial-XXXX.

Windows

There are a bazillion terminal programs for Windows systems. In the olden days, Hyperterminal was the default and came under Accessories. In recent times, I’ve used Bray’s Terminal, which is minimalistic and gets the job done. If you’re not sure which port the USB serial is connected to, check in Device Manager under Ports → USB Serial Port.

Cross-platform, Python

If you’ve already got Python and the serial library installed, you can run the included serial terminal emulator. Typing python -m serial.tools.miniterm --cr [portName] 9600 should get you talking. Note that you type Ctrl-] to quit and Ctrl-T then Ctrl-H for help. (And make a shortcut for this if you know how.)

Cross-platform, Arduino

If you’ve got the Arduino IDE set up on your computer, you’ll find its serial terminal program under Tools → Serial Monitor. This “terminal” program works well enough to listen to the AVR, but it requires you to hit Enter after every character you type in order to send it. For text, you can send a line at a time this way, but for anything interactive, it’s inconvenient. In short, I can’t recommend the Arduino Serial Monitor as a general-purpose terminal program, even though it works fine if you only need to receive data from the AVR.

Setup: USB-Serial Adapter

So your AVR is flashed with the serialLoopback code, and your computer’s got brand-new serial terminal software loaded and running. Time to hook them up together.

If you’ve got one of the ubiquitous FTDI cables, it’s got a pinout, as summarized in Table 5-1.

Table 5-1. FTDI cable pinout

!

Color

Signal

Black

GND

Brown

CTS#

Red

VCC

Orange

TXD

Yellow

RXD

Green

RTS#

If you don’t have an FTDI cable, the lines you’re looking for are going to be labeled something like RX, TX, GND, and optionally VCC. You can choose between powering the project from an external source of 5V, which is the right way to do it, or powering the whole circuit from the USB cable, which is convenient but will probably be drawing more current through the FTDI cable than its specifications allow.

HOOKING UP SERIAL PINS

Connecting your serial adapter to the AVR is as simple as connecting three wires, and the first one’s simple: it’s GND. The other two are “tricky.” You need to connect the RX of one device to the TX of the other, and the TX of one to the RX of the other.

“Crossing” the wires this way makes sense electronically, but note that the naming convention is totally different from the way that the SPI pins that we used in the programmer were named! There, MOSI on the programmer connects to MOSI on the AVR, SCK to SCK, etc. Almost all of the modern serial protocols are specified this way: pins with the same names are connected together.

The newer convention makes labelling the cable that runs between them particularly easy as well. The wire that connects MOSI to MOSI should be labelled “MOSI.” But what do you label the wire that connects RX to TX? Or the one that connects TX to RX? Usually, the answer is something like “TXA - RXB” and “RXA - TXB,” but besides being a mouthful, you then have to remember which device is “A” and which is “B.” It’s a mess.

Engineers learned their lesson after the crappy naming convention of old-school serial ports, but that doesn’t help us here, in USART-land. So just remember: when using (old-school) USART serial, you connect RX to TX, and vice versa. Schematically, your setup should look like Figure 5-5.

Double-check that you’ve hooked up the computer/FTDI cable’s RX pin to the AVR’s TX pin, and vice versa. On the breadboard, with an FTDI cable represented by the rainbow-colored, six-pin header strip, it’ll look like Figure 5-4.

Serial I/O breadboard hookup

Figure 5-4. Serial I/O breadboard hookup

RX-TX hookup

Figure 5-5. RX-TX hookup

Again, notice that we’ve hooked up the RX and TX wires, and connected the ground from the FTDI cable to the ground that’s shared between our AVR chip and the programmer. (And if you’ve still got the LEDs hooked up from last chapter, that’s a bonus.)

Putting It All Together: Test Out Your Loopback

So if you’ve flashed the serialLoopback code to your chip, hooked up the RX/TX pins (and GND) to the USB-Serial converter, and plugged it into your computer, it’s time to test this baby out.

Open up the terminal program, make sure it’s configured for the right port (the USB converter) and with the right baud rate (9600). If it’s got an option for “local echo,” set it to no—we’re going to be echoing back from the AVR chip. If you’ve got local echo on, you’ll see everything you type twice, lliikkee tthhiiss.

Now type something. The LED’s should display the binary values that correspond to the letter you typed in ASCII. Success! And if you unplug and replug power to the AVR (or momentarily ground the RESET pin), you should be greeted with a friendly “Hello World!” If you’re at this point, you’ve got it all working.

ASCII

When sending 8-bit bytes across the serial line, it’s natural to think of them as numbers. But if you want to send letters, we’ll have to work out a mapping from the numbers into letters. Enter the American Standard Code for Information Interchange (ASCII).

Have a look at an ASCII table on the Web, or type man ascii in Linux or OS X. Otherwise, you’ll be well-oriented just by knowing that 'A' is at 0x41, 'B' is at 0x42, 'a' is at 0x61, and '0' is at 0x30. What comes in between should be self-explanatory.

C and ASCII work very well together. You can transition seamlessly between letters and numbers, and because the characters are defined in corresponding alphabetical and numerical order, there’s all sorts of tricks you can play with ASCII. 'A' + 2 = 'C' for instance, and '0' + 3 = '3'. This means that if you have the variable a=3; and you’d like to send single digits represented as their ASCII characters, then you can send '0' + a. The printByte() function from my USART library exploits this fact to send a byte as its text representation:

void printByte(uint8_t number){

/* Hundreds */

transmitByte('0'+ (number/100));

/* Tens */

transmitByte('0'+ ((number/10) % 10));

/* Ones */

transmitByte('0'+ (number % 10));

}

With all this in mind, load up the serialLoopback.c demo, type some letters on your keyboard, and watch the blinking (ASCII binary numeric) lights. You’re watching the ghost in the machine!

PRINTBINARYBYTE IN LIEU OF LEDS

If you don’t have the LEDs connected but would still like to see how individual characters look as binary ASCII bits, you can replace the line:

transmitByte(serialCharacter);

in serialLoopback.c with:

printBinaryByte(serialCharacter);

printString("\r\n");

and then, instead of a simple echo, you’ll get the ASCII value of the character you just typed sent back to you. A character-to-ASCII converter!

Troubleshooting Serial Connections

Something’s not working? Let’s step through it:

1. You’ve installed terminal software, right?

2. When you plug in the USB-Serial cable, does your computer recognize it? On Linux and Mac, type lsusb and look for “FT232 USB-Serial.” On Windows, open up the Device Manager and look around.

3. On Windows, did you install the correct driver? If not, try unplugging and re-plugging the FTDI cable. If the driver is not installed, it should walk you through it.

4. Do you have the right port name? In Linux and Mac, it’ll be something like /dev/ttyUSB0 or /dev/XXX.usbserial-XXXXXXXX. In Windows, click on the FTDI device in the Device Manager and select the Hardware tab. It should show you which port it’s connected to.

5. Do you have permissions? On Linux and Mac, if you’re getting hassled, try running your terminal with sudo. (sudo is a command that lets you temporarily run one command at a time as super user—the user with maximum permissions. When something won’t run because of permissions issues, you can sidestep it quickly by using sudo.) In the long term, you may need to add yourself to a group with read/write permissions to the USB-Serial adapter. Search “serial port permissions” plus your operating system name if you don’t already know how to do this.

6. Do you have the TX and RX lines mixed up? It’s a quick experiment to switch them and see if that fixes things.

7. Finally, you can remove the AVR from the picture and make sure that your computer isn’t to blame by using a hardware loopback. Take a wire and connect it between the TX and RX lines of your USB-Serial adapter. Now open up your terminal program and type something. The signal should go out the TX, back into the RX, and be typed back out on your screen. If this works, it’s possible that the wires connecting to your AVR aren’t appropriately crossed, or you’ve set the baud rates wrong somewhere. Double-check.

8. Once you’ve got it all working, take a picture of the circuit. You can refer back to this later when you’re hooking things up again.

If you have the serialLoopback program up and running, then all is well with your computer, the AVR chip, and the connection between them. Now you are ready to start using the serial port for something. If you want to move straight on down to the AVR Organ project in AVR Square-Wave Organ, feel free. If you’d like a little more detail on just exactly how the USART library that we’re including works, keep on reading.

Configuring USART: The Nitty-Gritty Details

In this section, I’m going to go into a bit of detail on how the initUSART() code does its work. You’ll see similar examples of hardware configuration and initialization routines in almost every chapter of this book. It can be a little bit intimidating on the first pass, but after working through it a couple of times, either in this chapter or any of the later chapters in this book, you’ll get it.

The essential outline of the hardware configuration is that there are a handful of registers for each of the AVR’s built-in peripherals. Each bit in a register byte is a switch that enables or disables some functions of the peripheral. Setting up the hardware to do its job, then, is just a matter of figuring out which switches you need to set and bit twiddling them into the right states.

To dig in, let’s look at the USART.h and USART.c files. The .h file is pretty standard—it defines a default BAUD rate if you haven’t already specified one, and then introduces the functions with descriptive comments. Any macro defines you may need in USART.c are also found here.

So on to the source file, USART.c where the functions are actually defined. I’ve partially excerpted the file in Example 5-2.

To read along with the USART.c file, you’ll want to get the datasheet for the mega168 chip, and open up to the chapter labeled "USART0.” Feel free to read the overview section of the datasheet, and then jump on down to the section on “USART Initialization.”

Example 5-2. USART.c partial listing

/*

Quick and dirty functions that make serial communications work.

*/

#include <avr/io.h>

#include "USART.h"

#include <util/setbaud.h>

void initUSART(void) { /* requires BAUD */

UBRR0H = UBRRH_VALUE; /* defined in setbaud.h */

UBRR0L = UBRRL_VALUE;

#if USE_2X

UCSR0A |= (1 << U2X0);

#else

UCSR0A &= ~(1 << U2X0);

#endif

/* Enable USART transmitter/receiver */

UCSR0B = (1 << TXEN0) | (1 << RXEN0);

UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); /* 8 data bits, 1 stop bit */

}

void transmitByte(uint8_t data) {

/* Wait for empty transmit buffer */

loop_until_bit_is_set(UCSR0A, UDRE0);

UDR0 = data; /* send data */

}

uint8_t receiveByte(void) {

loop_until_bit_is_set(UCSR0A, RXC0); /* Wait for incoming data */

return UDR0; /* return register value */

}

// Example of a useful printing command

void printString(const char myString[]) {

uint8_t i = 0;

while (myString[i]) {

transmitByte(myString[i]);

i++;

}

}

Because this is your first time configuring an AVR hardware peripheral, I’ll take you through the configuration steps in some detail. Have a look at the general structure of the initUSART() function. What’s going on? Well, we’re writing some numbers into registersUBRR0H and UBRR0L at the top of the code. At the bottom, we’re setting a couple of bits in the UCSR0B and UCSR0C registers using a bit-shift roll. In particular, for instance, I’ll claim that UCSR0B = (1 << TXEN0) | (1 << RXEN0) sets the “transmit enable” bit and the “receive enable” bit for USART0. But how do I know what any of these uppercase macro definitions stand for? Where are they defined?

The meanings of all these cryptic uppercase macro definitions are found in the AVR datasheet, and they’re all #defined in the header file avr/io.h that we include at the top of our code. Similarly to how the DDRB register definition that we used in Chapter 3 pointed to the register that switched pin-modes between input and output, UCSR0B points to a configuration register for the serial hardware that contains eight bits that act as switches to control up to eight different hardware functions.

To figure out the specifics of what these serial hardware configuration registers do, we’ll consult the datasheet for the AVR. In particular, find the “Register Description” section of the “USART0” chapter, and scroll down until you find UCSR0B. (OK, I’ll excerpt a diagram from the register description in Figure 5-6, but you should really get used to looking through the datasheet.)

The UCSR0B register: bits and their names

Figure 5-6. The UCSR0B register: bits and their names

In a typical configuration register like UCSR0B, each bit is essentially a switch, and each switch has its own function. These functions are laid out in the datasheet, along with a “mnemonic” name associated with it, and these are the same names that get assigned to these pin numbers in the AVR-specific avr/io.h file that we included at the top of our code.

What this means is that you don’t have to remember if the bit to enable transmission is bit two or bit three in the control register, but you can instead refer to it as TXEN0 and the preprocessor substitutes a three for you because a define statement in io.h maps this to bit three. Unfortunately, you do have to remember which of the many control registers you need to be accessing, and sometimes the mnemonics aren’t really all that clear, but it’s better than nothing. If your code reads UCSR0B |= (1<<4), you’d have absolutely no idea what was going on, whereas if it reads UCSR0B |= (1 << RXEN0), you at least stand a chance of figuring out that the line enables the serial receiver hardware bit.

AVR/IO.H

Actually, the avr/io.h file in turn includes a different subfile depending on which chip you are writing for so that you don’t have to change much of your code when you change chips. For instance, using an ATmega168P, the file that actually gets included is called iom168p.h. Looking through this file, you can see all the special register definitions for each register byte and its bits.

Scrolling down to the USART section, we find that:

#define UCSR0B _SFR_MEM8(0xC1)

#define TXB80 0

#define RXB80 1

#define UCSZ02 2

#define TXEN0 3

#define RXEN0 4

#define UDRIE0 5

#define TXCIE0 6

#define RXCIE0 7

But bear in mind that the macro definition doesn’t do everything for you—all it does is substitute a four everywhere it finds RXEN0, for instance. The real meaning of that bit number is only realized when you flip that bit in the right register, UCSR0B. Which is to say, when you have to configure some AVR peripheral by hand, there’s no substitute for the “Register Description” sections of the AVR datasheet.

THE “REGISTER DESCRIPTION” SECTION

When I’m approaching an AVR peripheral configuration problem, I almost always start off by rereading the “Features” and “Overview” sections, and then jump straight across to the “Register Description.” After the first two sections, I have a good idea of what the hardware is supposed to do. The “Register Description” section then describes how to do it.

The first hunk of code in initUSART() makes use of some macro definitions from the util/setbaud.h include file to set up the USART configuration registers. UBRR0L and UBRR0H contain, respectively, the low byte and high byte of a 16-bit number that determines what baud rate the AVR will use for serial communications. To sample the serial line at just the right times, the AVR divides the system CPU clock, which usually runs between 1 MHz to 20 MHz, down to a baud-rate clock running between 9,600 Hz and 115,200 Hz. The divisor for this clock is stored in UBRR0.

Because there’s such a wide range of possible system clocks and desired baud rates, the AVR also has a double-speed mode to handle the higher speeds. A USE_2X macro is defined in setbaud.h that figures out whether we should use the regular or double-speed modes. The #if, #else, and #endif commands are like #define statements; they’re intended for the preprocessor. This is the only time I’ll use preprocessor #if statements, so don’t sweat this if you don’t want to.

Next, the code enables the USART hardware by writing the enable bits for both transmit and receive. When we do this, the AVR takes over the normal operation of these ports for the USART, setting the data direction registers and pull-up resistors and everything else appropriately for serial communication. Finally, we set a few extra configuration bits that set the USART for 8-bit bytes and one stop bit.

Feel free to skim through the “Register Description” section in the datasheet to see what all the special register configuration bits can do. We’ll only use a very small subset of the options here, because we’re doing the default—speaking “normal” hardware serial to the computer. In particular, if you’d like to change the number of bits per frame, or include a parity bit or extra stop bits, you’ll see which bits you need to set here in the register description.

Once the USART is configured, using it is a piece of cake. Look at the functions transmitByte() and receiveByte(). They both check bits in the UCSR0A (USART Control and Status Register 0 A) register to make sure that the hardware is ready, then read or write their target byte to a special hardware register, UDR0 (USART Data Register), that you use for reading and writing data to and from the USART hardware.

To transmit, you simply wait until the UDRE0 (USART Data Register Empty) bit in the status register is set and then load your byte to transmit into UDR0. The USART hardware logic then shuttles that data along to another memory where it writes it out bit by bit over the serial line as described in the Bob and Alice example.

Similarly, there is a receive complete bit (RXC0) in the status register UCSR0A that lets you know when a byte has received. When that bit is set, you can read out the data that just came in. Indeed, if you didn’t want to loop around until data came in over the USART—say you wanted the AVR to process other events in the meantime—instead of using a loop_until_bit_is_set(UCSR0A, RXC0); construct, you could simply test if the RXC0 bit is set from within your main event loop and act on the new data when it comes in. You’ll see examples of this style of code later on.

THE REGISTERS: USING USART

Sending

Wait until the USART data register is clear, indicated by the UDRE0 bit in UCSR0A being set, then write your data to UDR0.

Receiving

When data comes in, the RXC0 bit in UCSR0A will be set, and you can read the incoming data out of UDR0.

The function printString() just loops through all the characters in a string until the end, transmitting them one at a time. How does it know when it’s come to the end? C strings end in the NULL character, a character with the special value of zero, which you can test easily enough for in an if() or while() statement.

Two more functions, printByte() and printBinaryByte(), are mostly for your convenience and to help make pretty output for your computer. printByte() takes the numeric value of an 8-bit byte and turns it into the three ASCII digits it represents and sends them out. So if a=56, printByte(a) sends “0” and then “5” and then “6” across the serial line:

void printByte(uint8_t byte){

/* Converts a byte to a string of decimal text, sends it */

transmitByte('0'+ (byte/100)); /* Hundreds */

transmitByte('0'+ ((byte/10) % 10)); /* Tens */

transmitByte('0'+ (byte % 10)); /* Ones */

}

printBinaryByte() does a similar thing, but in binary; printBinaryByte(56) prints out “00111000” to your terminal:

void printBinaryByte(uint8_t byte){

/* Prints out a byte as a series of 1's and 0's */

uint8_t bit;

for (bit=7; bit < 255; bit--){

if ( bit_is_set(byte, bit) )

transmitByte('1');

else

transmitByte('0');

}

}

Because we’re used to reading binary in most-significant-bit order, the for() loop in printBinaryByte() runs the variable bit from seven to zero. The if() statement tests each bit and sends a 1 or 0 character accordingly.

If you look into the full version of the USART.h and USART.c files, you’ll find a few more useful functions that we’ll employ at various points in the rest of the book. But feel free to hold off on that for later. It’s time to start using this stuff.

MODULES IN C

All of this USART code, wrapped up in the USART.h and USART.c files, is written in a way so that it’s maximally easy to import it into your own code. When your projects get complex enough, you’re going to want to do this, too. So let’s take a minute to look at the ways people bundle up their code into reusable modules in C.

Generally speaking, the .c file is where your code goes, and the .h, or header file is reserved for #defines that the user is likely to want to change and for descriptions of the functions that are included in the .c file. This makes the header file the first place to look when looking at someone else’s code—the header file should provide an overview.

Additionally, in strict C, every function should be prototyped before it is defined or used, and the traditional way to make sure this happens is to put function prototypes in the header files. A function prototype is very similar to a function definition, just without the code. The important part is that you must declare what type of variables each function takes as arguments, and what value it will return. But while you’re at it, you might as well write some nice explanations of what everything does. This makes the header files very valuable.

To use functions from a given module, say module.c, into your code, you’ve got to first include all the function prototypes by including the module.h file at the top of your main file. (See how I do that with #include "USART.h" at the top of the code in this chapter?) Next, you’ve got to tell the compiler where to find the module.c file in which the actual code for the functions are stored. Much of the project-specific detail, like locations of module files and so on, is incorporated into the project’s makefile.

Makefiles automate a bunch of the repetitive and otherwise boring parts of software compilation by defining which parts of the project depend on which other parts. So when you’re adding a new module to your code, you’ll need to tell the makefile where it can find the module.c file, so that it can compile it along with your main program’s .cfile.

If you are only using a given module for this one project, it makes sense to keep its header and code files in the same folder as your main code. If, on the other hand, you are reusing the same code across multiple projects, you may want to store the module in some other “library” directory and include it from there.

In my makefiles, I provide options for both of these possibilities. If you’d like to include .c files that are in the same directory as the rest of your project, you can add the filenames to the LOCAL_SOURCE variable. If you’d like to include library functions that you’ve stored somewhere else, you can pass the directory name to EXTRA_SOURCE_DIR and the filenames to EXTRA_SOURCE_FILES. Other makefiles will have similar definitions.

So, in short, if you want to include a module module into your main code, you need to:

1. Copy the module.h and module.c files into your project directory or include the directory where you’ve stored the files in your makefile.

2. #include module.h at the top of your code before you use any of the functions.

3. Add the .c file to your makefile as extra source to be compiled.

Now you know what makes it all work. Again, because the USART code is all bundled up in the .h and .c files, all you need to do to use serial in your code is include the .h file in your preamble and add the .c file into your makefile. Then you can have your AVR talk freely to your computer, and tremendous power becomes yours.

OTHER USES FOR USART

Besides communicating with your computer, here are some other uses of the USART:

§ Debugging. The ability to send out info about the AVR’s state over the serial line can be priceless. The functions printByte(), printWord(), and printBinaryByte() make it painless.

§ Using LCD and VFD displays that take serial input.

§ Making your own GPS datalogger is fairly easy, because GPS modules usually send out their location data using a USART-style serial protocol.

§ Integrating card-readers, both magnetic-stripe and RFID, into your designs, because they often send their data to you in USART serial.

§ Sending data via radio protocols, because most use USART encoded in various ways.

§ Building other examples in this book: we’ll set up a low-speed “oscilloscope” using the ADC in Chapter 7.

AVR Square-Wave Organ

It’s time to make some noise, and put our serial-port to good use. I love sound projects, and thought an AVR organ would be a nice demonstration. But then, if you’re going to play an organ, you certainly need a keyboard. Rather than build one out of ebony, ivory, and 88 keyswitches, we’ll hijack your computer’s keyboard and use the serial port to connect the computer with the AVR. Let’s get started.

Hearing is frankly amazing. In case you don’t share my sense of wonder, think about this: periodic pressure waves in the air around you press on a thin skin membrane that wiggles some tiny bones that are configured like a lever, amplifying the movements and pressing on a fluid-filled sac that contains hairs that are connected to nerves that sense vibrations. When the longer hairs are vibrated by the fluid, you hear bass tones. When the shorter ones are vibrated, you hear treble.

Right. So the hearing part is already sorted out by Mother Nature. All that’s left for us to do is make the corresponding periodic pressure waves in the air. And for that, we’ll use a paper cone that we’ve strapped an electromagnet onto, and use electricity to wiggle the electromagnet and cone back and forth in the field of a permanent magnet. And we’ll call this device a speaker. Now all we have to do is send time-varying voltages to the speaker coil, and we’ll hear tones. Glorious!

Making Music with Your Micro

Microcontrollers are not meant to drive speakers. Speakers have a very low resistance to direct current (DC), around 8 ohms. For instance, if we run our AVR at 5 volts and we connect it directly to an 8-ohm speaker, a current of 625 milliamps (5 volts divided by 8 ohms) will be drawn, which is significantly more than the 20 to 50 milliamps that an AVR pin is good for. The solution is to add a blocking capacitor to the circuit, as shown in Figure 5-7.

Speaker with a blocking capacitor

Figure 5-7. Speaker with a blocking capacitor

When a capacitor is subject to a DC voltage, it lets a little current through until it is “charged up” to that voltage, then it blocks further current. This means that capacitors pass current only for changes in voltage. This is perfect for our speaker! The changing signal that makes up the audio is passed through, while the speaker doesn’t overload the AVR’s output current when the voltage is held high.

But do note that if you’re using an electrolytic capacitor (one that comes in a metal tube-shaped can), they often have a positive and negative sides to them. We’re not running enough current through the capacitor to damage it anyway, but you might as well hook it up with the stripe (negative terminal) to ground. On the breadboard, it will look like Figure 5-8.

Speaker with a blocking capacitor on the breadboard

Figure 5-8. Speaker with a blocking capacitor on the breadboard

Just how large a capacitor to use depends on your desired bass response, the particulars of the speaker, and how willing you are to overdrive the AVR’s outputs. In my experience, anything between 10 and 100 microfarads works well, and I tend to use 47 microfarads. Experiment with whatever values you can get your hands on; it’s unlikely that you can break anything.

Amplification

It’s equally unlikely that the AVR will be driving your speaker to deafening levels, so to truly annoy your neighbors, you may need to redirect the audio signal to whatever amplification you’ve got handy: amplified computer speakers, your stereo, a mixing board, etc. But first, you’ll have to attenuate the signal. The 5 V signal that the AVR puts out is not enough to drive your speaker very loudly, but it’s too loud for direct input into an amplifier.

Most amplified devices expect a “line level” signal; that is, something from one to two volts peak to peak. Assuming the AVR is running on 5 V, you’ll want to cut the signal at least in half before sending it on. The easiest way to do this is to place a voltage-divider in the circuit before the capacitor to limit the output. Any potentiometer with a resistance in the 10 kΩ to 100 kΩ range makes a good voltage divider, and a volume-adjust knob to boot. See Figure 5-9 for the circuit diagram.

Audio with pot, blocking capacitor

Figure 5-9. Audio with pot, blocking capacitor

This circuit will work very nicely with self-powered “computer montior” type speakers. In fact, you’ll probably want to use something small and cheap rather than your hi-fi equipment. I have an old powered computer speaker; I’ve chopped the plug off and replaced it with three alligator clips (for stereo). This comes in handy quite often, and I’m not really worried about destroying it. Given the very harsh square waves we’ll be producing, I would caution against plugging this straight into your stereo’s line in unless you really know what you’re doing.

Once you’ve hooked up the speakers, powered or otherwise, you’re ready to go. Flash in the serialOrgan.c code, and then fire up your terminal program, and play some notes on the home row of the keyboard.

The Organ Library

I went module crazy in this program. Most of the code for making the actual sound is stored in the organ.h and organ.c libraries so that you can use them again in your own code if you’d like. Let’s look them over quickly.

As usual, have a first look at organ.h. Two functions are defined there: playNote(uint16_t wavelength, uint16_t duration) and rest(uint16_t duration). If you look at any of the player programs, they all include the file scale16.h as well, which provides#defines for a scale in terms of the wavelengths that the playNote function requires. So let’s look at playNote() in Example 5-3 and see what’s going on.

Example 5-3. playNote function listing

void playNote(uint16_t wavelength, uint16_t duration){

uint16_t elapsed;

uint16_t i;

for(elapsed = 0; elapsed < duration; elapsed += wavelength){

/* For loop with variable delay selects the pitch */

for (i = 0; i < wavelength; i++){

_delay_us(1);

}

SPEAKER_PORT ^= (1 << SPEAKER);

}

}

playNote() takes a wavelength (essentially the inverse of a frequency or pitch) and a duration, and plays a note of the corresponding pitch and length. It does this by toggling the SPEAKER bit between zero and one at a given frequency, and because the speaker pin is configured for output, this sends 5 V and then 0 V to the speaker cone, making it move back and forth and creating a tone.

The innermost for loop is doing nothing but waiting for a precise amount of time. By waiting one microsecond per trip through the i loop, the loop creates a delay of approximately wavelength microseconds. Thus, larger values of wavelength correspond to slower back-and-forths of the speaker cone, and thus to lower frequencies. If we choose these times just right, we’ll have musical pitches.

The outermost for loop (looping over elapsed) has one little bit of cleverness that’s worth noting. Musically we want to be able to specify a note that lasts for a given duration, hence the duration argument. But at the same time, the pitch of the note we play is varied by changing how long our innermost loop takes per step—wavelength microseconds. How do we write the outer loop so that it knows exactly how many times to loop around the inner loop while the inner loop takes a variable amount of time?

Although we often increment the counting variable in a for() loop by one each time through, we don’t have to. For each trip through the inner loop, wavelength microseconds have elapsed. The outer loop keeps track of the total elapsed time by addingwavelength microseconds to the elapsed variable each time it moves through the loop. So instead of adding only one to elapsed per loop through, the increment to elapsed is made to depend on the length of time that the inner loop took: wavelengthmicroseconds. Now it’s easy to exit the loop as soon as elapsed time is no longer less than the duration.

The Code

If you’ve been too distracted by all of this coding mumbo-jumbo, please flash in the serialOrgan.c code, open up a serial terminal, and press some keys. If you’ve hooked up the speaker, you should be able to play some simple songs using your computer’s keyboard. Enjoy this for a while, and then let’s get down to figuring out what makes it all tick in Example 5-4.

Example 5-4. serialOrgan.c listing

/*

serialOrgan.c

Reads a character in serial from the keyboard, plays a note.

See organ.h for pin defines and other macros

See organ.c (and include it in the Makefile) for playNote() and rest()

*/

// ------- Preamble -------- //

#include <avr/io.h>

#include <util/delay.h>

#include "organ.h"

#include "scale16.h"

#include "pinDefines.h"

#include "USART.h"

#define NOTE_DURATION 0xF000 /* determines long note length */

int main(void) {

// -------- Inits --------- //

SPEAKER_DDR |= (1 << SPEAKER); /* speaker for output */

initUSART();

printString("----- Serial Organ ------\r\n");

char fromCompy; /* used to store serial input */

uint16_t currentNoteLength = NOTE_DURATION / 2;

const uint8_t keys[] = { 'a', 'w', 's', 'e', 'd', 'f', 't',

'g', 'y', 'h', 'j', 'i', 'k', 'o',

'l', 'p', ';', '\''

};

const uint16_t notes[] = { G4, Gx4, A4, Ax4, B4, C5, Cx5,

D5, Dx5, E5, F5, Fx5, G5, Gx5,

A5, Ax5, B5, C6

};

uint8_t isNote;

uint8_t i;

// ------ Event loop ------ //

while (1) {

/* Get Key */

fromCompy = receiveByte(); /* waits here until there is input */

transmitByte('N'); /* alert computer we're ready for next note */

/* Play Notes */

isNote = 0;

for (i = 0; i < sizeof(keys); i++) {

if (fromCompy == keys[i]) { /* found match in lookup table */

playNote(notes[i], currentNoteLength);

isNote = 1; /* record that we've found a note */

break; /* drop out of for() loop */

}

}

/* Handle non-note keys: tempo changes and rests */

if (!isNote) {

if (fromCompy == '[') { /* code for short note */

currentNoteLength = NOTE_DURATION / 2;

}

else if (fromCompy == ']') { /* code for long note */

currentNoteLength = NOTE_DURATION;

}

else { /* unrecognized, just rest */

rest(currentNoteLength);

}

}

} /* End event loop */

return (0);

}

First and foremost, look at the laundry list of include files. We want to use the playNote() function from the organ.c and organ.h files, so I’ve included that here. I’ll also use the predefined scale data from the scale16.h file. (“16” because the scale makes use of 16-bit numbers for greater pitch accuracy.) And finally, I include the USART.h file that includes the utility serial functions that we’ve already seen.

And speaking of the USART library, look down to the main() function. Notice that before entering the event loop, in the initializations section, I call the initUSART() function and print out something friendly to the serial port. As you saw earlier, initUSART() selects the baud rate and configures the USART hardware appropriately. The print command gives us something to look for in the serial terminal window to verify that all’s well with the serial connection. And before we leave the initialization section, notice that we’ve set up the speaker pin for output by setting the correct bit in the DDR.

Finally, inside the event loop, the program starts off by waiting for a serial byte from your computer. Once it gets a byte, it acknowledges it back to your computer, which will let you play around with scripting the serial organ later, and then the routine plays an appropriate note depending on which key you’ve sent. The way the code maps these keys to notes is a tiny bit advanced, so feel free to skip the following sidebar if you’d like.

CONVERTING KEYPRESSES TO NOTES

The organ code needs to take input from your serial terminal program and map those keys to notes in a way that’s intuitive for you. To code this lookup in a flexible and maintainable manner, I use two constant arrays, one with the list of keypresses and the other with a list of the pitches. This is actually a pretty common case for dealing with user input, so I thought I’d mention it here.

The “trick” to the lookup is the for(i=0; i < sizeof(keys); i++){} loop. The code looks through all of the possible keys that correspond to notes. If it matches one of them, it plays the i‘th note immediately and drops out of the for() loop. Slick, no?

There’s one nuance in the for loop that I’d like to point out. Notice that i is only incremented as long as i < sizeof(keys). Because the code indexes the keys[] array with i, this seems natural. But we should double-check that i never gets bigger than the last element of the array. Because each key[] entry is a byte and the array is 18 bytes long, the sizeof(keys) command will return 18, and the maximum value i will ever take on is 17. That is, we only want to run the for() loop as long as i < sizeof(keys).

The error that new C programmers make here is to write something like i <= sizeof(keys), because we have 18 bytes and we’d like to loop over all of them, right? Right, but wrong! Because C indexes its arrays starting at position 0, the last element is keys[17] and not keys[18]. That is, the less-than-or-equal-to construction ends up indexing one past the end of the array, which is no good.

If you’re still new at C, you’ll probably want to double-check the variables you use to index arrays each time you see one. Just remember that the maximum index for an array is one less than its length because we started counting at zero rather than one.

If the program hasn’t received a valid note command, it checks to see if you’ve changed the note length, and sets the appropriate value if you have. If none of these have happened, it defaults to resting (not playing a note) for the duration.

And that’s it. You can now play your AVR from your laptop’s keyboard, and that’s pretty cool. But this is just the beginning of a long and fruitful friendship; your computer and your AVR are a match made in hacker heaven.

Extra Goodies

I’ve also included two more files that might be interesting in the software bundle for the serialOrgan project. Both of them are written in Python, rather than being written in C. Python is an interpreted language, and includes modules that let you do basically anything, easily. I’ll use it throughout the book for projects that require computer/AVR interaction, or for doing any sort of computations that are too demanding for the AVR.

Creating the scale16.h header file that contains the note pitch data is an example of something that you’ll really want to do with a simple routine on your big computer, rather than reimplementing on the fly in the AVR. I’ve included the generateScale.py file that created the header file in the software distribution for this project.

I’ve also included a simple script (autoPlay.py) that demonstrates one way to interface your desktop computer with the AVR. The code relies on Python and the pyserial library. This script sends a character to the organ to play a note, and then waits for the AVR’s response before sending the next note—you’ll recall the AVR responds with an N when it’s ready to continue.

Though it’s a little bit difficult to get the timing right, and even to remember which keys are which while playing the AVR organ live, it’s a lot easier to type in strings of characters and let a Python routine shuttle them out to the AVR for you. And for pure frivolity, I have the demonstration program fetch text from a web page that contains “song” data, and play it across on the organ.

Neither of these programs are necessary to your understanding of the AVR serial peripheral, so I’m going to leave digging into the code up to you—they’re just here to whet your appetite. If you’ve already got Python installed on your computer, feel free to run them both. If you don’t have Python or pyserial installed yet and you just can’t wait, flip ahead to Installing Python and the Serial Library and install them both. If you’d like to get on to more and better AVR programming, don’t sweat it; I’ll catch you up on the whole Python thing later.

Summary

After digging deep into a complex application like this, you may have forgotten the reason we came here—to learn something about the AVR’s serial peripheral.

Using the USART:

1. Choose a baud rate, here by defining BAUD, and write the appropriate values to the baud rate registers UBRRL and UBRRH. (The setbaud.h library will help you with this.)

2. Enable the serial receive and transmit register bits.

3. If you’re transmitting, wait until the hardware is ready (loop_until_bit_is_set(UCSR0A, UDRE0)) and then load your byte data into UDR0. The AVR’s hardware handles everything else automatically.

4. If you’re waiting for data, check the data-received bit (bit_is_set(UCSR0A, RXC0)) and then read the data out of UDR0. Reading UDR0 automatically clears the data-received bit, and the AVR gets ready for the next received byte.

Additionally, I’ve wrapped up these simple statements into a few functions that I find convenient. I use initUSART(), transmitByte(), and receiveByte() in my code all the time. If you feel adventurous, you can look through the USART.c file and see how I’ve implemented other useful serial functions, like printing out a byte as its binary or hexadecimal equivalent, and printing whole strings at a time.

Connecting your big desktop computer to the little computer that lives inside the AVR provides you with many creative possibilities. Your computer can fetch and parse websites, check up on stock prices, and do incredible amounts of math. The AVR can move motors, blink LEDs, sense light levels, respond to pushbuttons in real time, and generally interface with the world of DIY electronics. Putting these two together is a recipe for winning, and the serial port is your link.