Logo Search packages:      
Sourcecode: lcdproc version File versions  Download package

hd44780.c

/*
 * Base driver module for Hitachi HD44780 based LCD displays. This is
 * a modular driver that readily allows support for alternative HD44780
 * designs to be added in a flexible and maintainable manner.
 *
 * This driver also supports the aggregation of multiple displays to form
 * a single virtual display. e.g., Four 16x2 displays can be combined
 * to form a 16x8 display.
 *
 * To add support for additional HD44780 connections:
 *  1. Add a connection type and mapping to hd44780-drivers.h
 *  2. Call your initialisation routine
 *  3. Create the low-level driver (use hd44780-ext8bit.c as a starting point)
 *  4. Modify the makefile
 *
 * Modular driver created and generic support for multiple displays added
 * Dec 1999, Benjamin Tse <blt@Comports.com>
 *
 * Modified July 2000 by Charles Steinkuehler to use one of 3 methods for delay
 * timing.  I/O reads, gettimeofday, and nanosleep.  Of the three, nanosleep
 * seems to work best, so that's what is set by default.
 *
 * Modified May 2001 by Joris Robijn to add Keypad support.
 *
 * Character mapping for correct display of some special ASCII chars added
 * Sep 2001, Mark Haemmerling <mail@markh.de>.
 *
 * Modified October 2001 to read the configfile.
 *
 * Moved the delay timing code by Charles Steinkuehler to timing.h.
 * Guillaume Filion <gfk@logidac.com>, December 2001
 *
 * This file is released under the GNU General Public License. Refer to the
 * COPYING file distributed with this package.
 *
 * Copyright (c)  2000, 1999, 1995 Benjamin Tse <blt@Comports.com>
 *            2001 Joris Robijn <joris@robijn.net>
 *            2001 Mark Haemmerling <mail@markh.de>
 *            2000 Charles Steinkuehler <cstein@newtek.com>
 *            1999 Andrew McMeikan <andrewm@engineer.com>
 *            1998 Richard Rognlie <rrognlie@gamerz.net>
 *            1997 Matthias Prinke <m.prinke@trashcan.mcnet.de>
 */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "timing.h"

#include "shared/str.h"
#include "render.h"
#include "shared/report.h"
#include "lcd.h"
#include "hd44780.h"
// #include "drv_base.h"

#include "hd44780-low.h"
#include "hd44780-drivers.h"
#include "hd44780-charmap.h"

// default parallel port address
#ifndef LPTPORT
#define LPTPORT    0x378
#endif

unsigned int port = LPTPORT;

lcd_logical_driver *HD44780;
HD44780_functions *hd44780_functions;

// default value
static enum connectionType connection = HD_4bit;
static int connectiontype_index = 0;

// spanList[line number] = display line number is in
static int *spanList = NULL;
static int numLines = 0;

// dispVOffset is a cumulative sized array of line numbers for each display.
// use this to determine the vertical positioning on a given display
static int *dispVOffset = NULL;
static int numDisplays = 0;

// dispSizes is the vertical size of each display. This is the same as the
// input span list but is kept to save some cpu cycles.
static int *dispSizes = NULL;

// Keypad, backlight extended interface and delay options
char have_keypad = 0;    // off by default
char have_backlight = 0; // off by default
char extIF = 0;          // off by default
char delayBus = 0;       // Delay if the computer can send data too fast over
int delayMult = 0;      // Delay multiplier for slow displays
                   // its bus to LPT port

                   // These vars is not static, so it's accessable from
                   // all sub-drivers
                   // Indeed, this should become cleaner...  later...

// Autorepeat values
#define KEYPAD_AUTOREPEAT_DELAY 500
#define KEYPAD_AUTOREPEAT_FREQ 15

// keyMapDirect contains an array of the ascii-codes that should be generated
// when a directly connected key is pressed (not in matrix).
static char keyMapDirect[KEYPAD_MAXX] = { 'A', 'B', 'C', 'D', 'E' };

// keyMapMatrix contrains an array with arrays of the ascii-codes that should be generated
// when a key in the matrix is pressed.
static char keyMapMatrix[KEYPAD_MAXY][KEYPAD_MAXX] = {
      { '1', '2', '3', 'A', 'E' },
      { '4', '5', '6', 'B', 'F' },
      { '7', '8', '9', 'C', 'G' },
      { '*', '0', '#', 'D', 'H' },
      { 'I', 'J', 'K', 'L', 'M' },
      { 'N', 'O', 'P', 'Q', 'R' },
      { 'S', 'T', 'U', 'V', 'W' },
      { 'X', 'Y', 'Z', 'a', 'b' },
      { 'c', 'd', 'e', 'f', 'g' },
      { 'h', 'i', 'j', 'k', 'l' },
      { 'm', 'n', 'o', 'p', 'q' }};

static char pressed_key = 0;
static int pressed_key_repetitions = 0;
static struct timeval pressed_key_time;

// for incremental updates store last lcd contents
static char * lcd_contents = (char *) 0;

// function declarations
void HD44780_close ();
void HD44780_flush ();
void HD44780_clear ();
void HD44780_chr (int x, int y, char ch);
void HD44780_string (int x, int y, char *s);
void HD44780_flush_box (int lft, int top, int rgt, int bot);
void HD44780_backlight (int on);
void HD44780_init_vbar ();
void HD44780_init_hbar ();
void HD44780_vbar (int x, int len);
void HD44780_hbar (int x, int y, int len);
void HD44780_init_num ();
void HD44780_num (int x, int num);
void HD44780_heartbeat (int type);
void HD44780_set_char (int n, char *dat);
void HD44780_icon (int which, char dest);
void HD44780_draw_frame (char *dat);
char HD44780_getkey ();

void HD44780_position (int x, int y);
unsigned char HD44780_scankeypad();
static int parse_span_list (int *spanListArray[], int *spLsize, int *dispOffsets[], int *dOffsize, int *dispSizeArray[], char *spanlist);

/////////////////////////////////////////////////////////////////
// Opens com port and sets baud correctly...
//
int
HD44780_init (lcd_logical_driver * driver, char *args)
{
      // TODO: remove the two magic numbers below
      // TODO: single point of return
      //char *argv[64];
      //int i;
      char buf[40];
      char *s;

      HD44780 = driver;

      // TODO: replace DriverName with driver->name when that field exists.
      #define DriverName "HD44780"


      //// READ THE CONFIG FILE

      port        = driver->config_get_int( DriverName, "port", 0, 0x278 );
      extIF       = driver->config_get_bool( DriverName, "extended", 0, 0 );
      have_keypad = driver->config_get_bool( DriverName, "keypad", 0, 0 );
      have_backlight    = driver->config_get_bool( DriverName, "backlight", 0, 0 );
      delayMult   = driver->config_get_int( DriverName, "delaymult", 0, 1 );
      delayBus    = driver->config_get_bool( DriverName, "delaybus", 0, 1 );

      // Get and search for the connection type
      s = driver->config_get_string( DriverName, "connectiontype", 0, "4bit" );
      for (connectiontype_index = 0; strcmp (s, connectionMapping[connectiontype_index].connectionTypeStr) != 0 && connectionMapping[connectiontype_index].type != HD_unknown; ++connectiontype_index);

      if (connectionMapping[connectiontype_index].type == HD_unknown) {
            report (RPT_ERR, "HD44780_init: Unknown connection type: %s", s);
            return -1; // fatal error
      } else
            connection = connectionMapping[connectiontype_index].type;

      // Get and parse vspan only when specified
      s = driver->config_get_string( DriverName, "vspan", 0, "" );
      if( s[0] != 0 ) {
            if (parse_span_list (&spanList, &numLines, &dispVOffset, &numDisplays, &dispSizes, s) == -1) {
                  report (RPT_ERR, "HD44780_init: invalid vspan value: %s", s );
                  return -1;
            }
      }

      // Get and parse size
      s = driver->config_get_string( DriverName, "size", 0, "20x4" );
      if( sscanf( s, "%dx%d", &(driver->wid), &(driver->hgt) ) != 2 ) {
            report (RPT_ERR, "HD44780_init: Cannot read size: %s", s );
            return -1;
      }

      // default case for when spans aren't indicated
      // - add a sanity check against HD44780->hgt ??
      if (numLines == 0) {
            if ((spanList = (int *) malloc (sizeof (int) * HD44780->hgt))) {
                  int i;
                  for (i = 0; i < HD44780->hgt; ++i) {
                        spanList[i] = 1;
                        numLines = HD44780->hgt;
                  }
            } else {
                  report (RPT_ERR, "HD44780_init: Error mallocing");
                  return -1;
            }
      }
      if (numDisplays == 0) {
            if ((dispVOffset = (int *) malloc (sizeof (int))) && (dispSizes = (int *) malloc (sizeof (int)))) {
                  dispVOffset[0] = 0;
                  dispSizes[0] = HD44780->hgt;
                  numDisplays = 1;
            } else {
                  report (RPT_ERR, "HD44780_init: Error mallocing");
                  return -1;
            }
      }

      // no timing functions needed for picanlcd, anslcd and usblcd
      if ( (connectionMapping[connectiontype_index].type != HD_picanlcd) && (connectionMapping[connectiontype_index].type != HD_anslcd) && (connectionMapping[connectiontype_index].type != HD_usblcd)) {
            if (timing_init() == -1)
                  return -1;
      }
      
      // Make sure the frame buffer is there...
      if (!HD44780->framebuf)
            HD44780->framebuf = (unsigned char *) malloc (HD44780->wid * HD44780->hgt);

      if (!HD44780->framebuf) {
            report (RPT_ERR, "HD44780_init: Error mallocing");
            return -1;
      }

      // Allocate and clear the buffer for incremental updates
      lcd_contents = (unsigned char *) malloc (HD44780->wid * HD44780->hgt);
      if (!lcd_contents) {
            report (RPT_ERR, "HD44780_init: Error mallocing");
            return -1;
      }
      memset(lcd_contents, ' ', HD44780->wid * HD44780->hgt);


      // Set the functions the driver supports...
      // These are the default HD44780 functions
      driver->init = HD44780_init;
      driver->close = HD44780_close;
      driver->flush = HD44780_flush;
      driver->clear = HD44780_clear;
      driver->chr = HD44780_chr;
      driver->string = HD44780_string;
      driver->flush_box = HD44780_flush_box; // TO BE REMOVED
      //driver->contrast = HD44780_contrast; // contrast is set by potmeter we assume

      //driver->output = HD44780_output; // not implemented
      driver->init_vbar = HD44780_init_vbar;
      driver->init_hbar = HD44780_init_hbar;
      driver->vbar = HD44780_vbar;
      driver->hbar = HD44780_hbar;
      driver->init_num = HD44780_init_num;
      driver->num = HD44780_num;
      driver->heartbeat = HD44780_heartbeat;
      driver->set_char = HD44780_set_char;
      driver->icon = HD44780_icon;
      driver->draw_frame = HD44780_draw_frame; // TO BE REMOVED

      if ( have_backlight ) {
            driver->backlight = HD44780_backlight;
      }
      if ( have_keypad ) {
            driver->getkey = HD44780_getkey;
      }

      if ((hd44780_functions = (HD44780_functions *) malloc (sizeof (HD44780_functions))) == NULL) {
            report (RPT_ERR, "HD44780_init: Error mallocing");
            return -1;
      }

      if ((connectionMapping[connectiontype_index].type == HD_picanlcd) || (connectionMapping[connectiontype_index].type == HD_anslcd) || (connectionMapping[connectiontype_index].type == HD_usblcd)) {
            hd44780_functions->uPause = HD44780_uPause_dummy;
      } else {
            hd44780_functions->uPause = HD44780_uPause;
      }

      hd44780_functions->scankeypad = HD44780_scankeypad;
      
      
      if (connectionMapping[connectiontype_index].init_fn (hd44780_functions, driver, args, port) < 0)
            return -1;
      
      HD44780_clear ();
      sprintf (buf, "HD44780 %dx%d", HD44780->wid, HD44780->hgt );
      HD44780_string (1, 1, buf);
      sprintf (buf, "LPT 0x%x %s %s", port, (have_backlight?"bl":""), (have_keypad?"key":"") );
      HD44780_string (1, 2, buf);
      HD44780_flush ();
      sleep (1);

      return port;
}

/////////////////////////////////////////////////////////////////
// Common initialisation sequence - sets cursor off and not blinking,
// clear display and homecursor
// Does not set twoline mode nor small characters (5x8). The init function of
// the connectiontype should do this.
//
void
common_init ()
{
      hd44780_functions->senddata (0, RS_INSTR, ONOFFCTRL | DISPON | CURSOROFF | CURSORNOBLINK);
      hd44780_functions->uPause (40);
      hd44780_functions->senddata (0, RS_INSTR, CLEAR);
      hd44780_functions->uPause (1600);
      hd44780_functions->senddata (0, RS_INSTR, HOMECURSOR);
      hd44780_functions->uPause (1600);
}

/////////////////////////////////////////////////////////////////
// Delay a number of microseconds
//
void
HD44780_uPause (int usecs)
{
      timing_uPause( usecs * delayMult );
}

/////////////////////////////////////////////////////////////////
// Do Nothing 
//
void
HD44780_uPause_dummy (int usecs)
{
}

/////////////////////////////////////////////////////////////////
// Clean-up
//
void HD44780_close()
{
      free( HD44780->framebuf );
      free( lcd_contents );
}

/////////////////////////////////////////////////////////////////
// Set position (not part of API)
//
// x and y here are for the virtual HD44780->hgt x HD44780->wid display
void
HD44780_position (int x, int y)
{
      int dispID = spanList[y];
      int relY = y - dispVOffset[dispID - 1];
      int DDaddr;

      // 16x1 is a special case
      if (dispSizes[dispID - 1] == 1 && HD44780->wid == 16) {
            if (x >= 8) {
                  x -= 8;
                  relY = 1;
            }
      }
      DDaddr = x + (relY % 2) * 0x40;
      if ((relY % 4) >= 2)
            DDaddr += HD44780->wid;

      hd44780_functions->senddata (dispID, RS_INSTR, POSITION | DDaddr);
      hd44780_functions->uPause (40);  // Minimum exec time for all commands
}

/////////////////////////////////////////////////////////////////
// Flush the framebuffer to the display
//
void
HD44780_flush ()
{
      HD44780_draw_frame (HD44780->framebuf);
}

/////////////////////////////////////////////////////////////////
// Clear the framebuffer
//
void HD44780_clear ()
{
      memset(HD44780->framebuf, ' ', HD44780->wid * HD44780->hgt);
}

/////////////////////////////////////////////////////////////////
// Place a character in the framebuffer
//
void
HD44780_chr (int x, int y, char ch)
{
      y--;
      x--;

      HD44780->framebuf[ (y * HD44780->wid) + x] = ch;
}

/////////////////////////////////////////////////////////////////
// Place a string in the framebuffer
//
void
HD44780_string (int x, int y, char *s)
{
      int i;

      x --;  // Convert 1-based coords to 0-based
      y --;

      for (i = 0; s[i]; i++) {
            // Check for buffer overflows...
            if ((y * HD44780->wid) + x + i > (HD44780->wid * HD44780->hgt))
                  break;
            HD44780->framebuf[(y*HD44780->wid) + x + i] = s[i];
      }
}

/////////////////////////////////////////////////////////////////
// Flush box
//
// TO BE REMOVED
//
void
HD44780_flush_box (int lft, int top, int rgt, int bot)
{
      int x, y;

      for (y = top; y <= bot; y++) {
            HD44780_position (lft, y);
            for (x = lft; x <= rgt; x++) {

                  hd44780_functions->senddata (spanList[y], RS_DATA, HD44780->framebuf[(y * HD44780->wid) + x]);
                  hd44780_functions->uPause (40);  // Minimum exec time for all commands
            }
      }

}

/////////////////////////////////////////////////////////////////
// Sets the backlight on or off
//
void
HD44780_backlight (int on)
{
      hd44780_functions->backlight (on);
}

/////////////////////////////////////////////////////////////////
// Sets up for vertical bars.  Call before HD44780->vbar()
//
void
HD44780_init_vbar ()
{
      char a[] = {
            0, 0, 0, 0, 0,
            0, 0, 0, 0, 0,
            0, 0, 0, 0, 0,
            0, 0, 0, 0, 0,
            0, 0, 0, 0, 0,
            0, 0, 0, 0, 0,
            0, 0, 0, 0, 0,
            1, 1, 1, 1, 1,
      };
      char b[] = {
            0, 0, 0, 0, 0,
            0, 0, 0, 0, 0,
            0, 0, 0, 0, 0,
            0, 0, 0, 0, 0,
            0, 0, 0, 0, 0,
            0, 0, 0, 0, 0,
            1, 1, 1, 1, 1,
            1, 1, 1, 1, 1,
      };
      char c[] = {
            0, 0, 0, 0, 0,
            0, 0, 0, 0, 0,
            0, 0, 0, 0, 0,
            0, 0, 0, 0, 0,
            0, 0, 0, 0, 0,
            1, 1, 1, 1, 1,
            1, 1, 1, 1, 1,
            1, 1, 1, 1, 1,
      };
      char d[] = {
            0, 0, 0, 0, 0,
            0, 0, 0, 0, 0,
            0, 0, 0, 0, 0,
            0, 0, 0, 0, 0,
            1, 1, 1, 1, 1,
            1, 1, 1, 1, 1,
            1, 1, 1, 1, 1,
            1, 1, 1, 1, 1,
      };
      char e[] = {
            0, 0, 0, 0, 0,
            0, 0, 0, 0, 0,
            0, 0, 0, 0, 0,
            1, 1, 1, 1, 1,
            1, 1, 1, 1, 1,
            1, 1, 1, 1, 1,
            1, 1, 1, 1, 1,
            1, 1, 1, 1, 1,
      };
      char f[] = {
            0, 0, 0, 0, 0,
            0, 0, 0, 0, 0,
            1, 1, 1, 1, 1,
            1, 1, 1, 1, 1,
            1, 1, 1, 1, 1,
            1, 1, 1, 1, 1,
            1, 1, 1, 1, 1,
            1, 1, 1, 1, 1,
      };
      char g[] = {
            0, 0, 0, 0, 0,
            1, 1, 1, 1, 1,
            1, 1, 1, 1, 1,
            1, 1, 1, 1, 1,
            1, 1, 1, 1, 1,
            1, 1, 1, 1, 1,
            1, 1, 1, 1, 1,
            1, 1, 1, 1, 1,
      };

      HD44780_set_char (1, a);
      HD44780_set_char (2, b);
      HD44780_set_char (3, c);
      HD44780_set_char (4, d);
      HD44780_set_char (5, e);
      HD44780_set_char (6, f);
      HD44780_set_char (7, g);
}

/////////////////////////////////////////////////////////////////
// Inits horizontal bars...
//
void
HD44780_init_hbar ()
{

      char a[] = {
            1, 0, 0, 0, 0,
            1, 0, 0, 0, 0,
            1, 0, 0, 0, 0,
            1, 0, 0, 0, 0,
            1, 0, 0, 0, 0,
            1, 0, 0, 0, 0,
            1, 0, 0, 0, 0,
            1, 0, 0, 0, 0,
      };
      char b[] = {
            1, 1, 0, 0, 0,
            1, 1, 0, 0, 0,
            1, 1, 0, 0, 0,
            1, 1, 0, 0, 0,
            1, 1, 0, 0, 0,
            1, 1, 0, 0, 0,
            1, 1, 0, 0, 0,
            1, 1, 0, 0, 0,
      };
      char c[] = {
            1, 1, 1, 0, 0,
            1, 1, 1, 0, 0,
            1, 1, 1, 0, 0,
            1, 1, 1, 0, 0,
            1, 1, 1, 0, 0,
            1, 1, 1, 0, 0,
            1, 1, 1, 0, 0,
            1, 1, 1, 0, 0,
      };
      char d[] = {
            1, 1, 1, 1, 0,
            1, 1, 1, 1, 0,
            1, 1, 1, 1, 0,
            1, 1, 1, 1, 0,
            1, 1, 1, 1, 0,
            1, 1, 1, 1, 0,
            1, 1, 1, 1, 0,
            1, 1, 1, 1, 0,
      };

      HD44780_set_char (1, a);
      HD44780_set_char (2, b);
      HD44780_set_char (3, c);
      HD44780_set_char (4, d);

}

/////////////////////////////////////////////////////////////////
// Draws a vertical bar...
//
void
HD44780_vbar (int x, int len)
{
      char map[9] = { 32, 1, 2, 3, 4, 5, 6, 7, 255 };

      int y;
      for (y = HD44780->hgt; y > 0 && len > 0; y--) {
            if (len >= HD44780->cellhgt)
                  HD44780_chr (x, y, 255);
            else
                  HD44780_chr (x, y, map[len]);

            len -= HD44780->cellhgt;
      }

}

/////////////////////////////////////////////////////////////////
// Draws a horizontal bar to the right.
//
void
HD44780_hbar (int x, int y, int len)
{
      char map[6] = { 32, 1, 2, 3, 4, 255 };

      for (; x <= HD44780->wid && len > 0; x++) {
            if (len >= HD44780->cellwid)
                  HD44780_chr (x, y, 255);
            else
                  HD44780_chr (x, y, map[len]);

            len -= HD44780->cellwid;

      }
}

/////////////////////////////////////////////////////////////////
// Sets up for big numbers.
//
void
HD44780_init_num ()
{

}

/////////////////////////////////////////////////////////////////
// Writes a big number.
//
void
HD44780_num (int x, int num)
{
      int offset = HD44780->wid * ( HD44780->hgt / 2 );
      HD44780->framebuf[x+offset] = num + '0';
}

/////////////////////////////////////////////////////////////
// Does the heartbeat...
//
void
HD44780_heartbeat (int type)
{
      static int timer = 0;
      int whichIcon;
      static int saved_type = HEARTBEAT_ON;

      if (type)
            saved_type = type;

      if (type == HEARTBEAT_ON) {
            // Set this to pulsate like a real heart beat...
            whichIcon = (! ((timer + 4) & 5));

            // This defines a custom character EVERY time...
            // not efficient... is this necessary?
            HD44780_icon (whichIcon, 0);

            // Put character on screen...
            HD44780_chr (HD44780->wid, 1, 0);

            // change display...
            HD44780_flush ();
      }

      timer++;
      timer &= 0x0f;
}

/////////////////////////////////////////////////////////////////
// Sets a custom character from 0-7...
//
// For input, values > 0 mean "on" and values <= 0 are "off".
//
// The input is just an array of characters...
//
void
HD44780_set_char (int n, char *dat)
{
      int row, col;
      int letter;

      if (n < 0 || n > 7)
            return;
      if (!dat)
            return;

      hd44780_functions->senddata (0, RS_INSTR, SETCHAR | n * 8);
      hd44780_functions->uPause (40);  // Minimum exec time for all commands

      for (row = 0; row < HD44780->cellhgt; row++) {
            letter = 0;
            for (col = 0; col < HD44780->cellwid; col++) {
                  letter <<= 1;
                  letter |= (dat[(row * HD44780->cellwid) + col] > 0);
            }
            hd44780_functions->senddata (0, RS_DATA, letter);
            hd44780_functions->uPause (40);  // Minimum exec time for all commands
      }
}

/////////////////////////////////////////////////////////////
// Set default icon into a userdef char
//
void
HD44780_icon (int which, char dest)
{
      char icons[3][5 * 8] = {
            {
             1, 1, 1, 1, 1,            // Empty Heart
             1, 0, 1, 0, 1,
             0, 0, 0, 0, 0,
             0, 0, 0, 0, 0,
             0, 0, 0, 0, 0,
             1, 0, 0, 0, 1,
             1, 1, 0, 1, 1,
             1, 1, 1, 1, 1,
             },

            {
             1, 1, 1, 1, 1,            // Filled Heart
             1, 0, 1, 0, 1,
             0, 1, 0, 1, 0,
             0, 1, 1, 1, 0,
             0, 1, 1, 1, 0,
             1, 0, 1, 0, 1,
             1, 1, 0, 1, 1,
             1, 1, 1, 1, 1,
             },

            {
             0, 0, 0, 0, 0,            // Ellipsis
             0, 0, 0, 0, 0,
             0, 0, 0, 0, 0,
             0, 0, 0, 0, 0,
             0, 0, 0, 0, 0,
             0, 0, 0, 0, 0,
             0, 0, 0, 0, 0,
             1, 0, 1, 0, 1,
             },

      };

      HD44780_set_char (dest, &icons[which][0]);
}

/////////////////////////////////////////////////////////////
// Blasts a single frame onscreen, to the lcd...
//
// Input is a character array, sized HD44780->wid*hgt
//
// TO BE REMOVED (move it into flush())
void
HD44780_draw_frame (char *dat)
{
      int x, y;
      int wid = HD44780->wid;
      char drawing;

      if (!dat)
            return;

      // Update LCD incrementally by comparing with last contents
      for (y=0; y<HD44780->hgt; y++) {
            drawing = 0;
            for (x=0 ; x<wid; x++) {
                  if( dat[(y*wid)+x] != lcd_contents[(y*wid)+x] ) {
                        if( !drawing || x % 8 == 0 ) { // x%8 for 16x1 displays
                              drawing = 1;
                              HD44780_position(x,y);
                        }
                        hd44780_functions->senddata( spanList[y], RS_DATA, HD44780_charmap[(unsigned char)dat[(y * wid) + x]]);
                        hd44780_functions->uPause (40);  // Minimum exec time for all commands
                        lcd_contents[(y*wid)+x] = dat[(y*wid)+x];
                  }
                  else {
                        drawing = 0;
                  }
            }
      }
}

/////////////////////////////////////////////////////////////
// Get a key from the keypad (if there is one)
//
char HD44780_getkey()
{
      unsigned char scancode;
      char ch;
      struct timeval curr_time, time_diff;

      gettimeofday(&curr_time,NULL);

      scancode = hd44780_functions->scankeypad();
      if( scancode ) {
            if( scancode & 0xF0 ) {
                  ch = keyMapMatrix[((scancode&0xF0)>>4)-1][(scancode&0x0F)-1];
            }
            else {
                  ch = keyMapDirect[scancode - 1];
            }
      }
      else {
            ch = 0;
      }

      if (ch) {
            if (ch == pressed_key) {
                  timersub (&curr_time, &pressed_key_time, &time_diff);
                  if (((time_diff.tv_usec / 1000 + time_diff.tv_sec * 1000) - KEYPAD_AUTOREPEAT_DELAY) < 1000 * pressed_key_repetitions / KEYPAD_AUTOREPEAT_FREQ ) {
                        // The key is already pressed quite some time
                        // but it's not yet time to return a repeated keypress
                        return 0;
                  }
                  // Otherwise a keypress will be returned
                  pressed_key_repetitions ++;
            }
            else {
                  // It's a new keypress
                  pressed_key_time = curr_time;
                  pressed_key_repetitions = 0;
                  report( RPT_INFO, "Key: %c  (%d,%d)", ch, scancode&0x0F, (scancode&0xF0)>>4 );
            }
      }

      // Store the key for the next round
      pressed_key = ch;

      return ch;
}

/////////////////////////////////////////////////////////////
// Scan the keypad
//
unsigned char HD44780_scankeypad()
{
      unsigned int keybits;
      unsigned int shiftcount;
      unsigned int shiftingbit;
      unsigned int Ypattern;
      unsigned int Yval;
      signed char exp;

      unsigned char scancode = 0;

      // First check if a directly connected key is pressed
      // Put all zeros on Y of keypad
      keybits = hd44780_functions->readkeypad (0);

      if (keybits) {
            // A directly connected key was pressed
            // Which key was it ?
            shiftingbit = 1;
            for (shiftcount=0; shiftcount<KEYPAD_MAXX && !scancode; shiftcount++) {
                  if ( keybits & shiftingbit) {
                        // Found !   Return from function.
                        scancode = shiftcount+1;
                  }
                  shiftingbit <<= 1;
            }
      }
      else {
            // Now check the matrix
            // First check with all 1's
            Ypattern = (1 << KEYPAD_MAXY) - 1;
            if( hd44780_functions->readkeypad (Ypattern)) {
                  // Yes, a key on the matrix is pressed

                  // OK, now we know a key is pressed.
                  // Determine which one it is

                  // First determine the row
                  // Do a 'binary search' to minimize I/O
                  Ypattern = 0;
                  Yval = 0;
                  for (exp=3; exp>=0; exp--) {
                        Ypattern = ((1 << (1 << exp)) - 1) << Yval;
                        keybits = hd44780_functions->readkeypad (Ypattern);
                        if (!keybits) {
                              Yval += (1 << exp);
                        }
                  }

                  // Which key is pressed in that row ?
                  keybits = hd44780_functions->readkeypad (1<<Yval);
                  shiftingbit=1;
                  for (shiftcount=0; shiftcount<KEYPAD_MAXX && !scancode; shiftcount++) {
                        if ( keybits & shiftingbit) {
                              // Found !
                              scancode = (Yval+1) << 4 | (shiftcount+1);
                        }
                        shiftingbit <<= 1;
                  }
            }
      }
      return scancode;
}


/////////////////////////////////////////////////////////////
// Parse the span list
//      spanListArray   - array to store vertical spans
//      spLsize    - size of spanListArray
//      dispOffsets     - array to store display offsets
//      dOffsize  - size of dispOffsets
//      dispSizeArray   - array of display vertical sizes (= spanlist)
//      spanlist  - null terminated input span list in comma delimited
//                format. All span elements [1-9] e.g. "1,4,2"
//      returns number of span elements, -1 on parse error

int
parse_span_list (int *spanListArray[], int *spLsize, int *dispOffsets[], int *dOffsize, int *dispSizeArray[], char *spanlist)
{
      int j = 0, retVal = 0;

      *spLsize = 0;
      *dOffsize = 0;

      while (j < strlen (spanlist)) {
            if (spanlist[j] >= '1' && spanlist[j] <= '9') {
                  int spansize = spanlist[j] - '0';

                  // add spansize lines to the span list, note the offset to
                  // the previous display and the size of the display
                  if ((*spanListArray = (int *) realloc (*spanListArray, sizeof (int) * (*spLsize + spansize)))
                         && (*dispOffsets = (int *) realloc (*dispOffsets, sizeof (int) * (*dOffsize + 1)))
                         && (*dispSizeArray = (int *) realloc (*dispSizeArray, sizeof (int) * (*dOffsize + 1)))) {
                        int k;
                        for (k = 0; k < spansize; ++k)
                              (*spanListArray)[*spLsize + k] = *dOffsize + 1;

                        (*dispOffsets)[*dOffsize] = *spLsize;
                        (*dispSizeArray)[*dOffsize] = spansize;
                        *spLsize += spansize;
                        ++(*dOffsize);
                        retVal = *dOffsize;

                        // find the next number (\0 is also outside this range)
                        for (++j; spanlist[j] < '1' || spanlist[j] > '9'; ++j);
                  } else {
                        report (RPT_ERR, "Error reallocing for span list");
                        retVal = -1;
                  }
            } else {
                  report (RPT_ERR, "Error reading spansize");
                  retVal = -1;
            }
      }
      return retVal;
}


Generated by  Doxygen 1.6.0   Back to index