/**************************************************************************** * * Copyright (c) 2006 Dave Hylands * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. * ****************************************************************************/ /** * * @file lcd-hal-avr.c * * @brief Provides an implementation for using the HD44780 LCD on an * AVR. * * This particular implementation needs 6 I/O pins to connect to * the LCD. 4 data lines, a register select line, and an Enable line. * * The R/W line on the LCD can be tied to ground. * * The 4 data lines should be contiguous and wired to D4, D5, D6, and * D7 on the LCD, with the least significant bit on the AVR side * going to D4 on the LCD. * * See: http://homepage.hispeed.ch/peterfleury/avr-lcd44780.html * for further details. * * This implementation allows for the following configuration parameters, * with the default shown. * * CFG_LCD_DATA_PORT PORTC - Specifies which port the data pins are on * CFG_LCD_DATA_SHIFT 0 - Identifies the LSB of the 4 data bits * CFG_LCD_E_PORT PORTC - Identifies the port used for the enable line * CFG_LCD_E_PIN 6 - Identifies the pin used for the enable line * CFG_LCD_RS_PORT PORTC - Identifies the port used for the register select * CFG_LCD_RS_PIN 4 - Identifies the pin used for the register select * CFG_LCD_RW_PORT - Identifies the port used for the R/W line * CFG_LCD_RW_PIN - Identifies the pin used for the R/W line * * Note: The RW PORT and PIN definitions are optional. If present, that pin * will be set to zero and not changed. * ****************************************************************************/ /* ---- Include Files ----------------------------------------------------- */ #include #include "Config.h" #include "lcd.h" #include "lcd-hal.h" #include "Delay.h" #if !defined( CFG_LCD_DATA_PORT ) # define CFG_LCD_DATA_PORT PORTC #endif #if !defined( CFG_LCD_DATA_SHIFT ) # define CFG_LCD_DATA_SHIFT 0 #endif #if !defined( CFG_LCD_E_PORT ) # define CFG_LCD_E_PORT PORTC #endif #if !defined( CFG_LCD_E_PIN ) # define CFG_LCD_E_PIN 6 #endif #if !defined( CFG_LCD_RS_PORT ) # define CFG_LCD_RS_PORT PORTC #endif #if !defined( CFG_LCD_RS_PIN ) # define CFG_LCD_RS_PIN 4 #endif #define LCD_DATA_MASK ( 0x0F << CFG_LCD_DATA_SHIFT ) #define LCD_E_MASK ( 1 << CFG_LCD_E_PIN ) #define LCD_RS_MASK ( 1 << CFG_LCD_RS_PIN ) #define LCD_RW_MASK ( 1 << CFG_LCD_RW_PIN ) /* ---- Public Variables -------------------------------------------------- */ /* ---- Private Constants and Types --------------------------------------- */ #define DDR( port ) (*(&(port) - 1)) /* ---- Private Variables ------------------------------------------------- */ /* ---- Private Function Prototypes --------------------------------------- */ static inline void LCD_HAL_E_High( void ) { CFG_LCD_E_PORT |= LCD_E_MASK; } static inline void LCD_HAL_E_Low( void ) { CFG_LCD_E_PORT &= ~LCD_E_MASK; } static inline void LCD_HAL_E_Pulse( void ) { // E needs to be high for 230 nsec. If we assume a 16 MHz clock, then // that would be 4 NOP's (1 NOP = 62.5 msec) LCD_HAL_E_High(); asm( " nop" ); asm( " nop" ); asm( " nop" ); asm( " nop" ); LCD_HAL_E_Low(); } static inline void LCD_HAL_RS_High( void ) { CFG_LCD_RS_PORT |= LCD_RS_MASK; } static inline void LCD_HAL_RS_Low( void ) { CFG_LCD_RS_PORT &= ~LCD_RS_MASK; } static inline void LCD_HAL_RW_Low( void ) { #if defined( CFG_LCD_RW_PORT ) CFG_LCD_RW_PORT &= ~LCD_RW_MASK; #endif } static inline void LCD_HAL_Data( uint8_t data ) { CFG_LCD_DATA_PORT &= ~LCD_DATA_MASK; CFG_LCD_DATA_PORT |= (( data & 0x0F ) << CFG_LCD_DATA_SHIFT ); } //*************************************************************************** /** * LCD_HAL_Delay * * Delays for the specified number of milliseconds */ void LCD_HAL_Delay( uint8_t msec ) { ms_spin( msec ); } // LCD_HAL_Delay //*************************************************************************** /** * LCD_HAL_Init * * Initializes the AVR for connecting to an LCD. * * We currently assume 4-bit mode. */ void LCD_HAL_Init( void ) { // Configure the pins as outputs DDR( CFG_LCD_DATA_PORT ) |= LCD_DATA_MASK; DDR( CFG_LCD_RS_PORT ) |= LCD_RS_MASK; DDR( CFG_LCD_E_PORT ) |= LCD_E_MASK; #if defined( CFG_LCD_RW_PORT ) DDR( CFG_LCD_RW_PORT ) |= LCD_RW_MASK; #endif LCD_HAL_RW_Low(); LCD_HAL_E_Low(); LCD_HAL_RS_Low(); // Do a soft reset LCD_HAL_Delay( 20 ); // Allow LCD to powerup LCD_HAL_Data( LCD_FUNCTION_RESET >> 4 ); LCD_HAL_E_Pulse(); LCD_HAL_Delay( 5 ); // Need to delay for at least 4.1 msec LCD_HAL_E_Pulse(); // Sends Reset a 2nd time LCD_HAL_Delay( 1 ); LCD_HAL_E_Pulse(); // and a 3rd time. LCD_HAL_Delay( 1 ); // Put the LCD into 4-bit mode LCD_HAL_Data( LCD_FUNCTION >> 4 ); LCD_HAL_E_Pulse(); LCD_HAL_Delay( 1 ); // Now that the LCD is in 4 bit mode, we can call LCD_HAL_Write if ( LCD_NumLines() == 1 ) { LCD_HAL_Write( LCD_RS_CMD, LCD_FUNCTION ); } else { LCD_HAL_Write( LCD_RS_CMD, LCD_FUNCTION | LCD_FUNCTION_2LINES ); } } // LCD_HAL_Init //*************************************************************************** /** * LCD_HAL_Write * * Writes a byte (command or data) to the LCD. */ void LCD_HAL_Write( uint8_t rs, uint8_t cmd ) { if ( rs ) { LCD_HAL_RS_High(); } else { LCD_HAL_RS_Low(); } // Output high nibble LCD_HAL_Data( cmd >> 4 ); LCD_HAL_E_Pulse(); // Output low nibble LCD_HAL_Data( cmd & 0x0F ); LCD_HAL_E_Pulse(); } // LCD_HAL_Write