Full Version : USARTs & LCD Displays by Climber (AVR GCC)
avr >>LCD BASED PROJECTS >>USARTs & LCD Displays by Climber (AVR GCC)


AVR_Admin- 05-08-2006
Climber's info on AVR programming - USART to control LCD displays

Many AVRs come with USARTS: Universal Synchronous and Asynchronous serial Receiver and Transmitter. It does many things but, mainly, it does the same job as your PC's serial port. In fact, you can use it to communicate with the PC's serial port. I use it to communicate with an LCD display.

Adding an alphanumeric LCD display to my projects was a HUGE leap forward for me in the world of microcontrollers. Besides making for a pretty user interface it does wonders for DEBUGGING. There's an old saying: writing the software takes the first ninety percent of the project's time and debugging takes the other ninety percent. Truer words could hardly have ever been spoken. Consequently, I always design my projects to make them EASY TO DEBUG. Being able to display arbitrary text even with small displays helps a lot. Now, why would I use the USART to talk to an LCD?

The answer: to save I/O pins. It takes only one to talk to an LCD through the USART but at least six to talk to it directly. Now, we're talking about HD44780 controlled LCD displays here. The vast majority of alphanumeric displays made in the last few years are based on this controller. Of course, knocking off these pins comes at a price: the circuit that goes between the LCD and the TX pin on your USART. There are two choices that I have used: the HVWtech.com econ LCD serial interface kit and the All Electronics LCD controller IC. My example code belows has code for both.

Electrically, all you have to do is wire up the LCD to the controller as specified in it's documentation and hook the serial in pin on the controller to your TX pin on the AVR and program away.

As with most programs I have been doing on the AVR lately it is interrupt based. This example uses our old friend, the ATMega8.

First, let's talk a bit about the setup. Both the HVWtech and AllCorp controllers like the data at 9600 bps with 8 bit data, one stop bit and no parity. This is pretty much the most common way to do serial on old devices.

Before we go any further, let's talk a bit about the clock. Normally the accuracy isn't a big deal but I have found that it becomes so when I start trying to talk to another time sensitive device. All of my early attempts at serial communication with an LCD controller we very unreliable until I started using a crystal for the clock. When I was designing and building my WCRG Mine Sweeper Robot I temporarily found myself without a crystal and had to fall back on the internal RC clock. It was inaccurate and I had to fiddle with the internal calibration in order to get the it accurate enough to talk to the LCD driver. This was very annoying and has made me less keen to depend on the internal RC oscillator. I'll stick with crystals. A resonator is probably good enough too.

So, let's start with some example code. The UCSRB, UCSRC and UBRR registers are what we need to twiddle to set it all up. Let's do that now (as usual I am using an 8 MHz clock):

CODE
// enable the internal USART
UCSRB = _BV(TXCIE)  // transmit complete interrupt enable
     | _BV(TXEN);  // enable USART in general
UCSRC = _BV(UCSZ1)  // 8 data bits
     | _BV(UCSZ0)  // ..
     | _BV(URSEL); // must be 1 when writing UCSRC
UBRRL = 25;         // 9600 baud with 8mHz clock and double speed off
UBRRH = 0;

// set data direction register for PORT D for usart
DDRD = _BV(DDD1);   // USART to LCD


UBRR is a 16 bit register that determines the baud rate. I plucked these numbers from the manual near the end of the USART section.

With the USART set up now we need the code to do interesting things. I use a global buffer to store the stuff I want to send to the LCD and have a couple of 8 bit variables to store indexes and the number of bytes in the message:

CODE
uint8_t numbytes,
       bufindex;
unsigned char buffer[35],      // buffer for the LCD
             prebuffer[3];

When I want to send something I set up a temporary string with the stuff that I want to display and when I call transmit (or transmit2ede if using the Allcorp driver IC) and my code copies it into buffer and starts sending it. You will note that buffer has only 35 characters in it. This pretty much assumes that the LCD has no more than 32 displayable characters such as seen in a 16x2 LCD. If I am using a larger LCD this needs to get bigger.

For the most part, I stick with the smaller ones as they fit better on the little bots. Most of these are based on the HD44780 controller chip. The big one is a 640x480 BW display with touch panel. I haven't figured out how it works yet. The one on the very bottom right is a 4 line x 40 character display. The buffer for this one is 170 bytes. Not for use on the smaller controllers.

The two on the bottom left are a pair of 2 line by 8 character displays that I got from Digikey for about 12 bucks each. These are going to go into some new small bots. The two little tiny displays on the upper left are some IS01NCF displays from NKK Smartswitch. They are 36x24 pixel displays requiring special software to make go. Haven't gotten around to that one yet. Just to the right of them is a 2x16 character display that I built into a little breadboard plugin module. The pins plug into the breadboard power rails and a single wire runs to my ATMega8 experimenter's board.

The handling of the transmission of the data is all automatic. Here are the two routines that I use. I just include the appropriate function depending on whether I use the HVWTech or AllCorp driver.

CODE
//----------------------------------------------------
void transmit2ede(uint8_t clear, unsigned char *string, unsigned char *string2)
//
// this here function sets up the transmission to the LCD by setting up the
// buffer.  For use on the AllCorp driver IC
//
{
 if (bufindex) return;    // still sending last message
 if (clear)
   (void)strcpy(prebuffer, "\xFE\x01 ");
 else
   (void)strcpy(prebuffer, "\xFE\x80");
 if (string2[0] != '\0')           // there's something in string2
   numbytes = sprintf(buffer, "%s%s%s%s", prebuffer, string, "\xFE\xC0",
              string2);
 else
   numbytes = sprintf(buffer, "%s%s", prebuffer, string);
 UDR = buffer[0];
 bufindex = 1;
}

//----------------------------------------------------
void transmit(uint8_t clear, unsigned char *string)
//
// this here function sets up the transmission to the LCD by setting up the
// buffer and enabling the interrupts.
//
{
 if (bufindex)
   return;
 if (clear)
   numbytes = sprintf(buffer, "%c%c%s", 0xFE, 'X', string);
 else
   numbytes = sprintf(buffer, "%s", string);
 UDR = buffer[0];
 bufindex = 1;
}



You will note that the parameters are slightly different between the two routines. The HVWtech driver has a jumper on the board to set the size of the display. If it the string were to wrap the driver takes care of it for us. The AllCorp driver isn't as smart so I pass two strings, one for the top line and the other for the bottom. I suppose I could just modify the code to use one function but, eh, I'm lazy and it works.

Timing is important. You don't want to try and send a message if the last one is still in the buffer. That's why I check bufindex at the start of both routines. Although it doesn't show up here I often light an LED if an attempt to write new data to display is tried when the old one hasn't been flushed yet. Very handy for debugging again.

There's only one thing remaining: the interrupt routine to handle the transmission. Every time one byte is finished getting sent an interrupt is generated. All the handler does is take a byte from the buffer and send it along by putting it into the UDR register. The USART is pretty smart. The TX pin will be idle until something appears in the UDR register. From then on the transmission is automatic. Tres cool.

CODE
//---------------------------------------------------
SIGNAL(SIG_UART_TRANS)
//
// This here signal is called when a byte has been sent through the
// USART.  If there are more bytes to be sent then do the next one
//
{
 if (bufindex == numbytes)
   bufindex = 0;
 else
   UDR = buffer[bufindex++];
}



Link to Site: http://members.shaw.ca/climber/avrserial.html


Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.