/**************************************************************************** Title : HD44780U LCD library / stripped down version for lcd@usb Author: Peter Fleury http://jump.to/fleury lcd@usb modifications by Till Harbaum File: $Id: lcd.c,v 1.2 2007/01/14 12:12:27 harbaum Exp $ Software: AVR-GCC 3.3 Target: any AVR device, memory mapped mode only for AT90S4414/8515/Mega DESCRIPTION Basic routines for interfacing a HD44780U-based text lcd display Originally based on Volker Oth's lcd library, changed lcd_init(), added additional constants for lcd_command(), added 4-bit I/O mode, improved and optimized code. Memory mapped mode compatible with Kanda STK200, but supports also generation of R/W signal through A8 address line. Major changes for LCD2USB: Removed many functions not needed in LCD2USB. Added support for a second controller. USAGE See the C include lcd.h file for a description of each function *****************************************************************************/ #include #include #include #include #include #include "lcd.h" /* ** constants/macros */ #define DDR(x) (*(&x - 1)) /* address of data direction register of port x */ #define PIN(x) (*(&x - 2)) /* address of input register of port x */ #define lcd_e_delay() __asm__ __volatile__( "rjmp 1f\n 1:" ); #define lcd_e0_high() LCD_E_PORT |= _BV(LCD_E0_PIN); #define lcd_e0_low() LCD_E_PORT &= ~_BV(LCD_E0_PIN); #define lcd_e1_high() LCD_E_PORT |= _BV(LCD_E1_PIN); #define lcd_e1_low() LCD_E_PORT &= ~_BV(LCD_E1_PIN); #define lcd_rw_high() LCD_RW_PORT |= _BV(LCD_RW_PIN) #define lcd_rw_low() LCD_RW_PORT &= ~_BV(LCD_RW_PIN) #define lcd_rs_high() LCD_RS_PORT |= _BV(LCD_RS_PIN) #define lcd_rs_low() LCD_RS_PORT &= ~_BV(LCD_RS_PIN) /* we don't really know anything about the display attached, */ /* so assume that it's a display with two lines */ #define LCD_FUNCTION_DEFAULT LCD_FUNCTION_4BIT_2LINES /* ** function prototypes */ static void lcd_e_toggle(uint8_t ctrl); /* ** local functions */ /************************************************************************* delay loop for small accurate delays: 16-bit counter, 4 cycles/loop *************************************************************************/ static inline void _delayFourCycles(unsigned int __count) { if ( __count == 0 ) __asm__ __volatile__( "rjmp 1f\n 1:" ); // 2 cycles else __asm__ __volatile__ ( "1: sbiw %0,1" "\n\t" "brne 1b" // 4 cycles/loop : "=w" (__count) : "0" (__count) ); } /************************************************************************* delay for a minimum of microseconds the number of loops is calculated at compile-time from MCU clock frequency *************************************************************************/ #define delay(us) _delay_us(us) /* toggle Enable Pin to initiate write */ static void lcd_e_toggle(uint8_t ctrl) { if(ctrl & LCD_CTRL_0) lcd_e0_high(); if(ctrl & LCD_CTRL_1) lcd_e1_high(); lcd_e_delay(); if(ctrl & LCD_CTRL_0) lcd_e0_low(); if(ctrl & LCD_CTRL_1) lcd_e1_low(); } /************************************************************************* Low-level function to write byte to LCD controller Input: data byte to write to LCD rs 1: write data 0: write instruction Returns: none *************************************************************************/ static void lcd_write(uint8_t ctrl, uint8_t data, uint8_t rs) { unsigned char dataBits ; if (rs) lcd_rs_high(); /* write data (RS=1, RW=0) */ else lcd_rs_low(); /* write instruction (RS=0, RW=0) */ lcd_rw_low(); /* configure data pins as output */ DDR(LCD_DATA_PORT) |= 0x0F; /* output high nibble first */ dataBits = LCD_DATA_PORT & 0xF0; LCD_DATA_PORT = dataBits |((data>>4)&0x0F); lcd_e_toggle(ctrl); /* output low nibble */ LCD_DATA_PORT = dataBits | (data&0x0F); lcd_e_toggle(ctrl); /* all data pins high (inactive) */ LCD_DATA_PORT = dataBits | 0x0F; } /************************************************************************* Low-level function to read byte from LCD controller Input: rs 1: read data 0: read busy flag / address counter Returns: byte read from LCD controller *************************************************************************/ static uint8_t lcd_read(uint8_t ctrl, uint8_t rs) { uint8_t data; if (rs) lcd_rs_high(); /* RS=1: read data */ else lcd_rs_low(); /* RS=0: read busy flag */ lcd_rw_high(); /* RW=1 read mode */ DDR(LCD_DATA_PORT) &= 0xF0; /* configure data pins as input */ LCD_DATA_PORT |= 0x0F; /* enable pullups to get a busy */ /* on unconnected display */ if(ctrl & LCD_CTRL_0) lcd_e0_high(); if(ctrl & LCD_CTRL_1) lcd_e1_high(); lcd_e_delay(); data = (PIN(LCD_DATA_PORT) & 0x0F)<<4; /* read high nibble first */ if(ctrl & LCD_CTRL_0) lcd_e0_low(); if(ctrl & LCD_CTRL_1) lcd_e1_low(); lcd_e_delay(); /* Enable 500ns low */ if(ctrl & LCD_CTRL_0) lcd_e0_high(); if(ctrl & LCD_CTRL_1) lcd_e1_high(); lcd_e_delay(); data |= (PIN(LCD_DATA_PORT)&0x0f); /* read low nibble */ if(ctrl & LCD_CTRL_0) lcd_e0_low(); if(ctrl & LCD_CTRL_1) lcd_e1_low(); return data; } /************************************************************************* loops while lcd is busy, returns address counter *************************************************************************/ static void lcd_waitbusy(uint8_t ctrl) { #if 1 uint8_t busy; do { busy = 0; /* check all controllers separately */ if(ctrl & LCD_CTRL_0) if((lcd_read(LCD_CTRL_0, 0)) & (1<>4); lcd_e_toggle(ctrl); delay(4992); /* delay, busy flag can't be checked here */ /* repeat last command */ lcd_e_toggle(ctrl); delay(64); /* delay, busy flag can't be checked here */ /* repeat last command a third time */ lcd_e_toggle(ctrl); delay(64); /* delay, busy flag can't be checked here */ /* now configure for 4bit mode */ LCD_DATA_PORT &= ~(_BV(LCD_FUNCTION_8BIT) >> 4); lcd_e_toggle(ctrl); delay(64); /* some displays need this additional delay */ /* from now the LCD only accepts 4 bit I/O, we can use lcd_command() */ /* try to find out of there's a controller and return 0 if not */ /* display must not be busy anymore */ if((lcd_read(ctrl, 0)) & (1<