ATTiny2313 Controlling a HD44780 LCD via AVR-GCC

After a day of tinkering I finally figured out how to control a HD44780-style LCD display from an ATTiny2313 class ATMEL AVR microcontroller. There are a lot of websites out there claiming to show you how to do this on similar AVRs. I tried about 10 of them and, intriguingly, only one of them worked! I don’t know if it’s user error, or an incompatibility of the ATTiny2313 running code written for an ATMega8, but since it took me so long to get this right I decided to share it on the internet for anyone else having a similar struggle. First, the results:
attiny_2313_lcd_hd44780
You might recognize this LCD panel from some PC parallel port / LCD interface projects I worked on about 5 years ago. It’s a 20-column, 2-row, 8-bit parallel character LCD. This means that ranter than telling each little square to light up to form individual letters, you can just output text to the microcontroller embedded in the display and it can draw the letters, move the cursor, or clear the screen.
attiny_2313_lcd_hd44780_2
As you can see this thing is pretty easy to wire-up to the ATTiny2313. These are the connections I made:

  • LCD1 -> GND
  • LCD2 -> +5V
  • LCD3 (contrast) -> GND
  • LCD4 (RS) -> AVR D0 (pin2)
  • LCD5 (R/W) -> AVR D1 (pin3)
  • LCD6 (ES) -> AVR D2 (pin6)
  • LCD 11-14 (data) -> AVR B0-B3 (pins 12-15)

The code I used to FINALLY allow me to control this LCD from the ATTiny2313 was found on Martin Thomas’ page. [HIS CODE] I included the .h and .c files and successfully ran the following program (with great results) on my AVR. The internal RC clock works, and supposedly any external clock (<8MHz) should work too.

// THIS THE TEST PROGRAM "main.c"
// ATTiny2313 / HD44780 LCD INTERFACE
#include <stdlib.h>
#include <avr/io.h>
#include <util/delay.h>
#include "lcd.h"
#include "lcd.c"

int main(void)
{
    int i=0;
    lcd_init(LCD_DISP_ON);
    lcd_clrscr();
    lcd_puts("ATTiny 2313 LCD Demo");
    lcd_puts("  www.SWHarden.com  ");
    _delay_ms(1000);
    lcd_clrscr();
    for (;;) {
        lcd_putc(i);
        i++;
        _delay_ms(50);
    }
}
// THIS IS PART OF MY INCLUDED "lcd.h"
// THE WIRING CHART DESCRIBED IN THIS BLOG ENTRY
// IS MATCHED TO THE VALUES DISPLAYED BELOW
#define LCD_PORT         PORTB        /**< port for the LCD lines   */
#define LCD_DATA0_PORT   LCD_PORT     /**< port for 4bit data bit 0 */
#define LCD_DATA1_PORT   LCD_PORT     /**< port for 4bit data bit 1 */
#define LCD_DATA2_PORT   LCD_PORT     /**< port for 4bit data bit 2 */
#define LCD_DATA3_PORT   LCD_PORT     /**< port for 4bit data bit 3 */
#define LCD_DATA0_PIN    0            /**< pin for 4bit data bit 0  */
#define LCD_DATA1_PIN    1            /**< pin for 4bit data bit 1  */
#define LCD_DATA2_PIN    2            /**< pin for 4bit data bit 2  */
#define LCD_DATA3_PIN    3            /**< pin for 4bit data bit 3  */
#define LCD_RS_PORT      PORTD     /**< port for RS line         */
#define LCD_RS_PIN       0            /**< pin  for RS line         */
#define LCD_RW_PORT      PORTD     /**< port for RW line         */
#define LCD_RW_PIN       1            /**< pin  for RW line         */
#define LCD_E_PORT       PORTD     /**< port for Enable line     */
#define LCD_E_PIN        2            /**< pin  for Enable line     */

// AND A LITTLE LOWER, I CHANGED THIS LINE TO 4-BIT MODE
#define LCD_FUNCTION_8BIT     0      /*   DB4: set 8BIT mode (0->4BIT mode) */

And some video of the output… Notice how this display can show English (lowercase/uppercase/numbers) as well as the Japanese character set! Sweet!