// ************************************************************************************************
///
/// \file lcd_4bit.c
/// \brief Interfacing HD44780 compatible LCD
///
/// \date $LastChangedDate: 2007-01-31 17:23:42 +0100 (Mi, 31 Jan 2007) $
/// \version $Rev: 533 $
/// \todo Implement timeout, when checking if display is ready
/// \todo rename module to lcd.c
/// \author Udo Jakobza, Carsten Kögler
Copyright (c) FTZ Leipzig
/// D-04107 Leipzig
Wächterstr. 13 info@easytoweb.de
/// \par License
/// This library is free software; you can redistribute it and/or modify it
/// under the terms of the GNU Lesser General Public License as published by
/// the Free Software Foundation; either version 2.1 of the License, or
/// (at your option) any later version. This library is distributed in the hope
/// that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
/// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
/// See the GNU Lesser General Public License for more details.
/// see: http://www.gnu.org/copyleft/lesser.html
/// You should have received a copy of the GNU Lesser General Public License
/// along with this library; if not, write to the Free Software Foundation,
/// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/// Die Bibliothek ist freie Software; Sie dürfen sie unter den Bedingungen der
/// GNU Lesser General Public License, wie von der Free Software Foundation
/// veröffentlicht, weiterverteilen und/oder modifizieren; entweder gemäß
/// Version 2.1 der Lizenz oder (nach Ihrer Option) jeder späteren Version.
/// Diese Bibliothek wird in der Hoffnung weiterverbreitet, daß sie nützlich
/// sein wird, jedoch OHNE IRGENDEINE GARANTIE, auch ohne die implizierte
/// Garantie der MARKTREIFE oder der VERWENDBARKEIT FÜR EINEN BESTIMMTEN ZWECK.
/// Mehr Details finden Sie in der GNU Lesser General Public License.
/// see: http://www.gnu.org/copyleft/lesser.de.html
/// Sie sollten eine Kopie der GNU Lesser General Public License zusammen mit
/// dieser Bibliothek erhalten haben; falls nicht, schreiben Sie an die FSF,
/// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
///
// ************************************************************************************************
#include "project.h"
#ifdef _DOXYGEN_PARSER_
#define LCD_4BIT
#define LCD_4BIT_DEBUG
#endif
#ifdef LCD_4BIT
#include
#include
#include "lcd_4bit.h"
#include "delay.h"
#include
// Constants for special characters
#define PC_SIGN_LC_AE 228 // PC-Code for "ä"
#define PC_SIGN_LC_OE 246 // PC-Code for "ö"
#define PC_SIGN_LC_UE 252 // PC-Code for "ü"
#define PC_SIGN_UC_AE 196 // PC-Code for "Ä"
#define PC_SIGN_UC_OE 214 // PC-Code for "Ö"
#define PC_SIGN_UC_UE 220 // PC-Code for "Ü"
#define PC_SIGN_SZ 223 // PC-Code for "ß"
#define PC_SIGN_AT 64 // PC-Code for "@"
#define PC_SIGN_DEGREE 176 // PC-Code for "°"
#define PC_SIGN_MU 'µ' // PC-Code for "µ"
#ifdef LCD_CONTROLLER_KS0073 // Zeichensatz für AE-DIP-Display mit KS0073-Controller
#define LCD_SIGN_LC_AE 123 // LCD-character-code for "ä"
#define LCD_SIGN_LC_OE 124 // LCD-character-code for "ö"
#define LCD_SIGN_LC_UE 126 // LCD-character-code for "ü"
#define LCD_SIGN_MU 228 // LCD-character-Code for "µ"
#define LCD_SIGN_SZ 190 // LCD-character-Code for "ß"
#define LCD_SIGN_UC_UE 94 // LCD-character-code for "Ü"
#define LCD_SIGN_UC_AE 91 // LCD-character-code for "Ä"
#define LCD_SIGN_UC_OE 92 // LCD-character-code for "Ö"
#define LCD_SIGN_AT 160 // LCD-character-Code for "@"
#define LCD_SIGN_DEGREE 128 // LCD-character-Code for "°"
#endif
#ifdef LCD_CONTROLLER_HD44780 // Zeichensatz laut HD4478-datenblatt: ROM Code A00
#define LCD_SIGN_LC_AE 225 // LCD-character-code for "ä"
#define LCD_SIGN_UC_AE 225 //
#define LCD_SIGN_LC_OE 239 // LCD-character-code for "ö"
#define LCD_SIGN_UC_OE 239 //
#define LCD_SIGN_LC_UE 245 // LCD-character-code for "ü"
#define LCD_SIGN_UC_UE 245 //
#define LCD_SIGN_MU 228 // LCD-character-Code for "µ"
#define LCD_SIGN_SZ 226 // LCD-character-Code for "ß"
#define LCD_SIGN_AT 64 // LCD-character-Code for "@"
#define LCD_SIGN_DEGREE 223 // LCD-character-Code for "°"
#endif
#define LCD_CURSOR_ON 0x02
#define LCD_CURSOR_OFF 0x00
#define LCD_DISPLAY_ON 0x04
#define LCD_DISPLAY_OFF 0x00
#ifdef LCD_CONTROLLER_HD44780
#define LCD_LINE1_ADDR_OFFSET 0x80 // 1. line 0x00..0x0f + Bit7==0x80
#define LCD_LINE2_ADDR_OFFSET 0xc0 // 2. line 0x40..0x4f + Bit7==0xc0
#define LCD_LINE3_ADDR_OFFSET LCD_X_SIZE+0x80 // 3. line 0x10..0x1f + Bit7==0x90
#define LCD_LINE4_ADDR_OFFSET LCD_X_SIZE+0xc0 // 4. line 0x50..0x5f + Bit7==0xd0
// BIT7 nessesary for command "Set DD RAM address"
#endif
#ifdef LCD_CONTROLLER_KS0073
#define LCD_LINE1_ADDR_OFFSET 0x80
#define LCD_LINE2_ADDR_OFFSET 0xA0
#define LCD_LINE3_ADDR_OFFSET 0xC0
#define LCD_LINE4_ADDR_OFFSET 0xE0
#endif
const unsigned char _base_y[4]={LCD_LINE1_ADDR_OFFSET,
LCD_LINE2_ADDR_OFFSET,
LCD_LINE3_ADDR_OFFSET,
LCD_LINE4_ADDR_OFFSET};
unsigned char _lcd_x,_lcd_y;
unsigned char _lcd_data_mirror[LCD_Y_SIZE][LCD_X_SIZE];
#ifdef LCD_BUFFER_ENABLE
unsigned char _lcd_data_buffer[LCD_Y_SIZE][LCD_X_SIZE];
unsigned char _lcd_buffer_x,_lcd_buffer_y;
#endif
// local prototypes
void _lcd_ready(void);
void _lcd_write_command(unsigned char data);
void _lcd_delay(void);
void _lcd_write_nibble(unsigned char data);
#ifdef LCD_4BIT_DEBUG
void lcd_debug_init(void);
#endif
// *********************************************************************************
/// \brief Initializes the LCD controller
///
// *********************************************************************************
void lcd_init(void)
{
#ifdef LCD_4BIT_DEBUG
lcd_debug_init();
#endif
lcd_controller_init();
}
// *********************************************************************************
/// \brief Initializes the LCD controller module
///
// *********************************************************************************
void lcd_controller_init(void)
{
LCD_EN_LOW;
LCD_RS_LOW;
LCD_RD_WRITE;
LCD_RS_ACTIV;
LCD_RD_ACTIV;
LCD_EN_ACTIV;
LCD_DATA_ACTIV;
#ifdef LCD_CONTROLLER_HD44780
delay_ms(100); // wait until the display ready
_lcd_write_nibble(0x30);
delay_ms(5);
_lcd_write_nibble(0x30);
delay_ms(1);
_lcd_write_nibble(0x30);
delay_ms(1);
_lcd_write_nibble(0x20); // init 4-bit mode
delay_ms(1);
_lcd_write_command(0x28); // 4-bit + 2lines (1/16 duty)
_lcd_ready();
_lcd_write_command(0x06); // Entry-mode - DD RAM Address increment
lcd_clear(); // Display ON; Cursor Off and Clear
#endif
#ifdef LCD_CONTROLLER_KS0073
delay_ms(20); // wait until the display ready
_lcd_write_command(0x20); // function set: 4 bit, RE=0
_lcd_ready();
_lcd_write_command(0x20); // function set: 4 bit, RE=0
_lcd_ready();
_lcd_write_command(0x20); // function set: 4 bit, RE=0
_lcd_ready();
_lcd_write_command(0x24); // function set: 4 bit, RE=1
_lcd_ready();
_lcd_write_command(0x09); // ext. function set: 5 dot, 4 lines
_lcd_ready();
_lcd_write_command(0x20); // function set: 4 bit, RE=0
_lcd_ready();
_lcd_write_command(0x06); // cursor increasing
_lcd_ready();
lcd_clear();
#endif
}
// *********************************************************************************
/// \brief Checks, if the LCD is ok.
///
/// @retval 0 - ok
/// @retval 1 - error
// *********************************************************************************
unsigned char lcd_check(void)
{
unsigned char temp_u8;
lcd_write_dd_cg_ram(0x40 | 0x00, 0x0A);
temp_u8 = lcd_read_dd_cg_ram(0x40 | 0x00);
if (temp_u8 == 0x0A)
return 0;
else
return 1;
}
// *********************************************************************************
/// \brief Checks the busy flag, see lcd_read_busy_ac()
///
// *********************************************************************************
void _lcd_ready(void)
{
#ifdef LCD_FIX_DELAY
delay_ms(1);
#else
while (lcd_read_busy_ac() & 0x80);
#endif // #ifdef LCD_FIX_DELAY
}
// *********************************************************************************
/// \brief Writes a nibble to LCD dataport
///
// *********************************************************************************
void _lcd_write_nibble(unsigned char data)
{
LCD_DATA_WRITE(data); // write high nibble of data to port high nibble
LCD_EN_HIGH;
_lcd_delay();
LCD_EN_LOW;
_lcd_delay();
}
// *********************************************************************************
/// \brief Delay
///
/// \note 1ms
// *********************************************************************************
void _lcd_delay()
{
delay_ms(1);
}
// *********************************************************************************
/// \brief Writes a command to LCD
///
/// @param[in] data - data to LCD
// *********************************************************************************
void _lcd_write_byte(unsigned char data)
{
LCD_RD_WRITE;
LCD_DATA_ACTIV;
_lcd_write_nibble(data&0xF0); // write MSN
_lcd_write_nibble(data<<4); // write LSN
}
// *********************************************************************************
/// \brief writes a command to LCD
///
/// @param[in] data - data to LCD
// *********************************************************************************
void _lcd_write_command(unsigned char data)
{
LCD_RS_LOW;
_lcd_write_byte(data);
}
// *********************************************************************************
/// \brief writes data to LCD
///
/// @param[in] data - data to LCD
// *********************************************************************************
void _lcd_write_data(unsigned char data)
{
LCD_RS_HIGH;
_lcd_write_byte(data);
}
// *********************************************************************************
/// \brief Reads a nibble from LCD-Dataport
///
/// @param[in] data - data to LCD
// *********************************************************************************
unsigned char _lcd_read_nibble(void)
{
unsigned char data;
LCD_EN_HIGH;
_lcd_delay();
data = LCD_DATA_READ; //read nibble of data (Bit4..Bit7 of Port)
LCD_EN_LOW;
_lcd_delay();
return data;
}
// *********************************************************************************
/// \brief Read the Busy-Flag + actual DD- or CG-Address
///
/// @return data of busy-bit register
// *********************************************************************************
unsigned char _lcd_read_busy_data(void)
{
unsigned char data;
// D7 BusyFlag;
// D6-D0 CG- or DD-Address
// LCD_RD_READ;
LCD_DATA_PASSIV;
_lcd_delay();
data = _lcd_read_nibble(); // read MSN
data |= _lcd_read_nibble()>>4; // read LSN
return data;
}
// *********************************************************************************
/// \brief Read the Busy-Flag + actual DD- or CG-Address
///
/// @return data of busy-bit register
// *********************************************************************************
unsigned char lcd_read_busy_ac(void)
{
// D7 - BusyFlag
// D6-D0 CG- or DD-Address
LCD_RS_LOW;
return _lcd_read_busy_data();
}
// *********************************************************************************
/// \brief read the Busy-Flag + actual DD- or CG-Address
///
/// @return data of busy-bit register
// *********************************************************************************
unsigned char _lcd_read_data(void)
{
// D7 - BusyFlag
// D6-D0 CG- or DD-Address
LCD_RS_HIGH;
return _lcd_read_busy_data();
}
// *********************************************************************************
/// \brief Write a byte to the LCD character generator or display RAM
///
/// @param[in] addr - position in CG or DD-RAM
/// @param[in] data - CG or DD-RAM data
// *********************************************************************************
void lcd_write_dd_cg_ram(unsigned char addr, unsigned char data)
{
// CG-RAM: BIT7=0 BIT6=1 BIT5..0=address
// DD-RAM: BIT7=1 BIT6..0=address
_lcd_ready();
_lcd_write_command(addr); // RS=0
_lcd_ready();
_lcd_write_data(data); // RS=1
}
// *********************************************************************************
/// \brief Writes a pattern to the character generator RAM (CGRAM)
///
/// @param[in] c - number of user-defined character to write
/// @param[in] row - row of the character
/// @param[in] data - pattern
// *********************************************************************************
void lcd_write_cgram(unsigned char c, unsigned char row, unsigned char data)
{
lcd_write_dd_cg_ram(0x40|c<<3|row, data);
}
// *********************************************************************************
/// \brief Read a byte from the LCD character generator or display RAM
///
/// @param[in] addr - position in CG or DD-RAM
/// @return data
// *********************************************************************************
unsigned char lcd_read_dd_cg_ram(unsigned char addr)
{
_lcd_ready();
_lcd_write_command(addr); // RS=0
_lcd_ready();
return _lcd_read_data(); // RS=1
}
// *********************************************************************************
/// \brief Set the LCD display position
///
/// @param[in] x - position on line of LCD; x=0..LCD_X_SIZE
/// @param[in] y - LCD-line; y=0..LCD_Y_SIZE
// *********************************************************************************
void lcd_gotoxy(unsigned char x, unsigned char y)
{
if (x>=LCD_X_SIZE)
x-=LCD_X_SIZE; // starts with first valid sign
if (y>=LCD_Y_SIZE)
y-=LCD_Y_SIZE; // starts with first valid line
_lcd_ready();
_lcd_write_command(_base_y[y]+x);
_lcd_x=x;
_lcd_y=y;
}
// *********************************************************************************
/// \brief Clear the LCD
///
// *********************************************************************************
void lcd_clear(void)
{
memset(_lcd_data_mirror, ' ', sizeof(_lcd_data_mirror));
_lcd_ready();
_lcd_write_command(0x0c); // Display On and Cursor off
_lcd_ready();
_lcd_write_command(0x01); // clear
delay_ms(2);
_lcd_x=_lcd_y=0;
}
// *********************************************************************************
/// \brief Clear the line "y" behind position "x"
///
/// \note Sets cursor on position x/y afterwards
/// @param[in] x - x-position
/// @param[in] y - y-position
// *********************************************************************************
void lcd_clear_line(unsigned char x, unsigned char y)
{
unsigned char Lva;
lcd_gotoxy(x, y);
for (Lva=x; Lva=LCD_X_SIZE)
{
_lcd_y++;
lcd_gotoxy(0,_lcd_y);
}
}
// *********************************************************************************
/// \brief Write a zero-terminated string located in SRAM to the LCD.
///
/// Uses the actual position of cursor
/// @param[in] str - SRAM-pointer to data
// *********************************************************************************
void lcd_puts(char *str)
{
while (*str)
{
lcd_putchar(*str);
str++;
}
}
// *********************************************************************************
/// \brief Write a zero-terminated string located in FLASH to the LCD.
///
/// Uses the actual position of cursor
/// @param[in] str - FLASH-pointer to data
// *********************************************************************************
void lcd_putsf(unsigned char const* str)
{
while (*str)
{
lcd_putchar(*str);
str++;
}
}
// *********************************************************************************
/// \brief Set to position 0 of "line"; write "*str" and delete the rest of "line"
/// uses the actual position of cursor
/// @param[in] str - SRAM-pointer to data
/// @param[in] line - LCD-line position
// *********************************************************************************
void lcd_puts_line(char *str, unsigned char line)
{
lcd_gotoxy(0, line);
lcd_puts(str);
if(_lcd_y==line) // line is not full
lcd_clear_line(_lcd_x, line);
}
/*
// *********************************************************************************
/// \brief Set to position 0 of "line"; write "*str" and delete the rest of "line"
/// uses the actual position of cursor
/// @param[in] str - FLASH-pointer to data
/// @param[in] line - LCD-line position
// *********************************************************************************
void lcd_putsf_line(char flash *str, unsigned char line)
{
lcd_gotoxy(0, line);
lcd_putsf(str);
if(_lcd_y==line) // line is not full
lcd_clear_line(_lcd_x, line);
}
*/
#ifdef LCD_BUFFER_ENABLE
// *********************************************************************************
/// \brief Initializes the local LCD buffer with actual display content
///
// *********************************************************************************
void lcd_buffer_copy(void)
{
memcpy(_lcd_data_buffer, _lcd_data_mirror, sizeof(_lcd_data_buffer));
_lcd_buffer_x = 0;
_lcd_buffer_y = 0;
}
// *********************************************************************************
/// \brief Clears LCD local buffer
///
// *********************************************************************************
void lcd_buffer_clear(void)
{
memset(_lcd_data_buffer, ' ', sizeof(_lcd_data_buffer));
_lcd_buffer_x = 0;
_lcd_buffer_y = 0;
}
// *********************************************************************************
/// \brief Set cursor in local buffer
///
/// @param[in] x - position on line of LCD. x = 0..(LCD_X_SIZE-1)
/// @param[in] y - LCD-line. y = 0..(LCD_Y_SIZE-1)
// *********************************************************************************
void lcd_buffer_gotoxy(unsigned char x, unsigned char y)
{
_lcd_buffer_x = x;
_lcd_buffer_y = y;
}
// *********************************************************************************
/// \brief Write a char "c" to the local LCD buffer.
/// Uses the actual position of cursor, but checks the end of line and
/// switchs over to next line.
/// @param[in] c - character
// *********************************************************************************
void lcd_buffer_putchar(char c)
{
switch (c)
{
case PC_SIGN_LC_AE: c=LCD_SIGN_LC_AE; break; // PC-Code for "ä"
case PC_SIGN_LC_OE: c=LCD_SIGN_LC_OE; break; // PC-Code for "ö"
case PC_SIGN_LC_UE: c=LCD_SIGN_LC_UE; break; // PC-Code for "ü"
case PC_SIGN_UC_AE: c=LCD_SIGN_UC_AE; break; // PC-Code for "Ä"
case PC_SIGN_UC_OE: c=LCD_SIGN_UC_OE; break; // PC-Code for "Ö"
case PC_SIGN_UC_UE: c=LCD_SIGN_UC_UE; break; // PC-Code for "Ü"
case PC_SIGN_SZ: c=LCD_SIGN_SZ; break; // PC-Code for "ß"
case PC_SIGN_AT: c=LCD_SIGN_AT; break; // PC-Code for "@"
case PC_SIGN_DEGREE: c=LCD_SIGN_DEGREE; break; // PC-Code for "°"
case PC_SIGN_MU: c=LCD_SIGN_MU; break; // PC-Code for "µ"
}
_lcd_data_buffer[_lcd_buffer_y][_lcd_buffer_x] = c;
_lcd_buffer_x++;
if (_lcd_buffer_x >= LCD_X_SIZE)
_lcd_buffer_y++;
}
// *********************************************************************************
/// \brief Write a 0-terminated string located in SRAM to the local LCD buffer
/// Uses the actual position of cursor
/// @param[in] str - SRAM-pointer to data
// *********************************************************************************
void lcd_buffer_puts(char *str)
{
while (*str)
{
lcd_buffer_putchar(*str);
str++;
}
}
/*
// *********************************************************************************
/// \brief Write a 0-terminated string located in FLASH to the local LCD buffer
/// Uses the actual position of cursor
/// @param[in] str - FLASH-pointer to data
// *********************************************************************************
void lcd_buffer_putsf(char flash *str)
{
while (*str)
{
lcd_buffer_putchar(*str);
str++;
}
}*/
// *********************************************************************************
/// \brief Writes the local LCD buffer to the display
///
// *********************************************************************************
void lcd_buffer_write(void)
{
unsigned char x,y;
for (y=0; y(LCD_X_SIZE * LCD_Y_SIZE-1))
Lva=(LCD_X_SIZE * LCD_Y_SIZE-1);
TCNT1=0;
lcd_gotoxy(Lva % LCD_X_SIZE, Lva / LCD_X_SIZE);
lcd_print_time();
}
else if (strncmpf(command, "-clear", 3)==0)
{
TCNT1=0;
lcd_clear();
lcd_print_time();
}
else if (strncmpf(command, "-blink", 6)==0)
{
TCNT1=0;
_lcd_write_command(0x0f); // Display On, Cursor on and Blink
lcd_print_time();
}
else if (strncmpf(command, "-address", 8)==0)
{
Lva = debug_get_int_parameter(command);
if (Lva==-1)
Lva = (LCD_X_SIZE-1);
TCNT1=0;
_lcd_write_command(0x80 | Lva); // Set new DD RAM address
lcd_print_time();
}
else if (strncmpf(command, "-puts", 5)==0)
{
p_param = debug_search_parameter(command);
TCNT1=0;
if (p_param)
lcd_puts(p_param);
else
lcd_putsf("0123456789");
lcd_print_time();
}
else if (strncmpf(command, "-putchar-hex", 12)==0)
{
Lva = debug_get_int_parameter(command);
if (Lva==-1)
Lva = 'a';
TCNT1 = 0;
lcd_putchar(Lva);
lcd_print_time();
return;
}
else if (strncmpf(command, "-check", 6)==0)
{
TCNT1 = 0;
result = lcd_check();
printf("Result: %1u\r\n", result);
lcd_print_time();
}
}
}
#endif // #ifdef LCD_4BIT_DEBUG
#endif // #ifdef LCD_4BIT