/* * 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 * * 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 . * * Modified October 2001 to read the configfile. * * Moved the delay timing code by Charles Steinkuehler to timing.h. * Guillaume Filion , 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 * 2001 Joris Robijn * 2001 Mark Haemmerling * 2000 Charles Steinkuehler * 1999 Andrew McMeikan * 1998 Richard Rognlie * 1997 Matthias Prinke */ // Uncomment one of the lines below to select your desired delay generation // mechanism. If both defines are commented, the original I/O read timing // loop is used. Using DELAY_NANOSLEEP seems to provide the best performance. //#define DELAY_GETTIMEOFDAY #define DELAY_NANOSLEEP //#define DELAY_IOCALLS // Default parallel port address #define LPTPORT 0x378 // Autorepeat values #define KEYPAD_AUTOREPEAT_DELAY 500 #define KEYPAD_AUTOREPEAT_FREQ 15 #include #include #include #include #include #include #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "shared/str.h" #include "lcd.h" #include "hd44780.h" #include "report.h" #include "port.h" #include "timing.h" #include "hd44780-low.h" #include "hd44780-drivers.h" #include "hd44780-charmap.h" // Only one alternate delay method at a time, please ;-) #if defined DELAY_GETTIMEOFDAY # undef DELAY_NANOSLEEP #elif defined DELAY_NANOSLEEP # include # include #endif static char *defaultKeyMapDirect[KEYPAD_MAXX] = { "A", "B", "C", "D", "E" }; static char *defaultKeyMapMatrix[KEYPAD_MAXY][KEYPAD_MAXX] = { { "1", "2", "3", "A", "E" }, { "4", "5", "6", "B", "F" }, { "7", "8", "9", "C", "G" }, { "*", "0", "#", "D", "H" }, { NULL, NULL, NULL, NULL, NULL }, { NULL, NULL, NULL, NULL, NULL }, { NULL, NULL, NULL, NULL, NULL }, { NULL, NULL, NULL, NULL, NULL }, { NULL, NULL, NULL, NULL, NULL }, { NULL, NULL, NULL, NULL, NULL }, { NULL, NULL, NULL, NULL, NULL }}; // function declarations void HD44780_position (Driver *drvthis, int x, int y); static void uPause (PrivateData *p, int usecs); unsigned char HD44780_scankeypad(PrivateData *p); static int parse_span_list (int *spanListArray[], int *spLsize, int *dispOffsets[], int *dOffsize, int *dispSizeArray[], char *spanlist); // Vars for the server core MODULE_EXPORT char * api_version = API_VERSION; MODULE_EXPORT int stay_in_foreground = 1; // because of port access problems MODULE_EXPORT int supports_multiple = 1; // yes, we have no global variables (except for constants) MODULE_EXPORT char *symbol_prefix = "HD44780_"; ///////////////////////////////////////////////////////////////// // Opens com port and sets baud correctly... // int HD44780_init (Driver * drvthis, char *args) { // TODO: remove the two magic numbers below // TODO: single point of return //char *argv[64]; //int i; char buf[40]; char *s; int i; PrivateData *p; // Alocate and store private data p = (PrivateData *) malloc( sizeof( PrivateData) ); if( ! p ) return -1; if( drvthis->store_private_ptr( drvthis, p ) ) return -1; // Clear data struct memset( p, 0, sizeof(*p) ); p->cellheight = 8; /* Do not change this !!! This is a controller property, not a display property !!! */ p->cellwidth = 5; p->udcmode = UDCMODE_STANDARD; //// READ THE CONFIG FILE p->port = drvthis->config_get_int( drvthis->name, "port", 0, LPTPORT ); p->extIF = drvthis->config_get_bool( drvthis->name, "extended", 0, 0 ); p->have_keypad = drvthis->config_get_bool( drvthis->name, "keypad", 0, 0 ); p->have_backlight = drvthis->config_get_bool( drvthis->name, "backlight", 0, 0 ); p->delayMult = drvthis->config_get_int( drvthis->name, "delaymult", 0, 1 ); p->delayBus = drvthis->config_get_bool( drvthis->name, "delaybus", 0, 1 ); // Get and search for the connection type s = drvthis->config_get_string( drvthis->name, "connectiontype", 0, "4bit" ); for (i = 0; connectionMapping[i].name != NULL && strcmp (s, connectionMapping[i].name) != 0; i++); if (connectionMapping[i].name == NULL) { fprintf (stderr, "HD44780_init: Unknown connection type: %s\n", s); return -1; // fatal error } else { p->connectiontype_index = i; } // Get and parse vspan only when specified s = drvthis->config_get_string( drvthis->name, "vspan", 0, "" ); if( s[0] != 0 ) { if (parse_span_list (&(p->spanList), &(p->numLines), &(p->dispVOffset), &(p->numDisplays), &(p->dispSizes), s) == -1) { fprintf (stderr, "HD44780_init: invalid vspan value: %s\n", s ); return -1; } } // Get and parse size s = drvthis->config_get_string( drvthis->name, "size", 0, "20x4" ); if( sscanf( s, "%dx%d", &(p->width), &(p->height) ) != 2 || (p->width <= 0) || (p->width > LCD_MAX_WIDTH) || (p->height <= 0) || (p->height > LCD_MAX_HEIGHT)) { fprintf( stderr, "HD44780_init: Cannot read size: %s\n", s ); } // default case for when spans aren't indicated // - add a sanity check against p->height ?? if (p->numLines == 0) { if ((p->spanList = (int *) malloc (sizeof (int) * p->height))) { int i; for (i = 0; i < p->height; ++i) { p->spanList[i] = 1; p->numLines = p->height; } } else fprintf (stderr, "Error mallocing for span list\n"); } if (p->numDisplays == 0) { if ((p->dispVOffset = (int *) malloc (sizeof (int))) && (p->dispSizes = (int *) malloc (sizeof (int)))) { p->dispVOffset[0] = 0; p->dispSizes[0] = p->height; p->numDisplays = 1; } else fprintf (stderr, "Error mallocing for display sizes list\n"); } if (timing_init() == -1) return -1; #if defined DELAY_NANOSLEEP // Change to Round-Robin scheduling for nanosleep { // Set priority to 1 struct sched_param param; param.sched_priority=1; if (( sched_setscheduler(0, SCHED_RR, ¶m)) == -1) { fprintf (stderr, "HD44780_init: failed (%s)\n", strerror (errno)); return -1; } } #endif // Allocate framebuffer p->framebuf = (unsigned char *) malloc (p->width * p->height); if (!p->framebuf) { //HD44780_close(); return -1; } // Allocate and clear the buffer for incremental updates p->lcd_contents = (unsigned char *) malloc (p->width * p->height); if (!p->lcd_contents) { return -1; } memset(p->lcd_contents, 0, p->width * p->height); // Allocate and clear the buffer for defineable characters p->udc_buf = (unsigned char *) malloc (NUM_UDCs * p->cellheight); p->udc_dirty = (unsigned char *) malloc (NUM_UDCs); if (!p->udc_buf || !p->udc_dirty) { return -1; } memset(p->udc_buf, 0, NUM_UDCs * p->cellheight); memset(p->udc_dirty, 1, NUM_UDCs); /* all udcs dirty */ // Backlight ? if ( p->have_backlight ) { drvthis->backlight = HD44780_backlight; } // Keypad ? if ( p->have_keypad ) { int x, y; drvthis->getkey = HD44780_getkey; // Read keymap for( x=0; xkeyMapDirect[x] = defaultKeyMapDirect[x]; // Read config value sprintf( buf, "keydirect_%1d", x+1 ); s = drvthis->config_get_string( drvthis->name, buf, 0, NULL ); // Was a key specified in the config file ? if( s ) { p->keyMapDirect[x] = (char *) malloc( strlen(s)+1 ); strcpy( p->keyMapDirect[x], s ); //report( RPT_INFO, "HD44780: Direct key %d: \"%s\"", x, s ); } } for( x=0; xkeyMapMatrix[y][x] = defaultKeyMapMatrix[y][x]; // Read config value sprintf( buf, "keymatrix_%1d_%d", x+1, y+1 ); s = drvthis->config_get_string( drvthis->name, buf, 0, NULL ); // Was a key specified in the config file ? if( s ) { p->keyMapMatrix[y][x] = (char *) malloc( strlen(s)+1 ); strcpy( p->keyMapMatrix[y][x], s ); //report( RPT_INFO, "HD44780: Matrix key %d %d: \"%s\"", x, y, s ); } } } } // Set variables for server drvthis->api_version = api_version; drvthis->stay_in_foreground = &stay_in_foreground; drvthis->supports_multiple = &supports_multiple; // Set the functions the driver supports... // These are the default HD44780 functions drvthis->init = HD44780_init; drvthis->close = HD44780_close; drvthis->width = HD44780_width; drvthis->height = HD44780_height; drvthis->flush = HD44780_flush; drvthis->clear = HD44780_clear; drvthis->chr = HD44780_chr; drvthis->string = HD44780_string; //drvthis->flush_box = HD44780_flush_box; // TO BE REMOVED //drvthis->contrast = HD44780_contrast; // contrast is set by potmeter we assume //drvthis->output = HD44780_output; // not implemented drvthis->init_vbar = HD44780_init_vbar; drvthis->init_hbar = HD44780_init_hbar; drvthis->vbar = HD44780_vbar; drvthis->hbar = HD44780_hbar; drvthis->init_num = HD44780_init_num; drvthis->num = HD44780_num; drvthis->heartbeat = HD44780_heartbeat; drvthis->set_char = HD44780_set_char; drvthis->icon = HD44780_icon; //drvthis->draw_frame = HD44780_draw_frame; // TO BE REMOVED if ((p->hd44780_functions = (HD44780_functions *) malloc (sizeof (HD44780_functions))) == NULL) { fprintf (stderr, "Error mallocing for function struct"); return -1; } p->hd44780_functions->uPause = uPause; p->hd44780_functions->scankeypad = HD44780_scankeypad; // Do connection type specific display init connectionMapping[p->connectiontype_index].init_fn (drvthis); // Display startup parameters on the LCD HD44780_clear (drvthis); sprintf (buf, "HD44780 %dx%d", p->width, p->height ); HD44780_string (drvthis, 1, 1, buf); sprintf (buf, "LPT 0x%x %s %s", p->port, (p->have_backlight?"bl":""), (p->have_keypad?"key":"") ); HD44780_string (drvthis, 1, 2, buf); HD44780_flush (drvthis); sleep (2); return 1; } ///////////////////////////////////////////////////////////////// // 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 (PrivateData *p) { p->hd44780_functions->senddata (p, 0, RS_INSTR, ONOFFCTRL | DISPON | CURSOROFF | CURSORNOBLINK); p->hd44780_functions->uPause (p, 40); p->hd44780_functions->senddata (p, 0, RS_INSTR, CLEAR); p->hd44780_functions->uPause (p, 1600); p->hd44780_functions->senddata (p, 0, RS_INSTR, HOMECURSOR); p->hd44780_functions->uPause (p, 1600); } ///////////////////////////////////////////////////////////////// // Delay a number of microseconds // void uPause (PrivateData *p, int usecs) { timing_uPause( usecs * p->delayMult ); } ///////////////////////////////////////////////////////////////// // Clean-up // MODULE_EXPORT void HD44780_close(Driver *drvthis) { PrivateData *p = (PrivateData *) drvthis->private_data; if(p->framebuf) free( p->framebuf ); if(p->lcd_contents) free( p->lcd_contents ); free( p ); drvthis->store_private_ptr( drvthis, NULL ); } ///////////////////////////////////////////////////////////////// // Returns the display width // MODULE_EXPORT int HD44780_width (Driver *drvthis) { PrivateData * p = (PrivateData*) drvthis->private_data; return p->width; } ///////////////////////////////////////////////////////////////// // Returns the display height // MODULE_EXPORT int HD44780_height (Driver *drvthis) { PrivateData * p = (PrivateData*) drvthis->private_data; return p->height; } ///////////////////////////////////////////////////////////////// // Set position (not part of API) // // x and y here are for the virtual p->height x p->width display void HD44780_position (Driver *drvthis, int x, int y) { PrivateData *p = (PrivateData *) drvthis->private_data; int dispID = p->spanList[y]; int relY = y - p->dispVOffset[dispID - 1]; int DDaddr; // 16x1 is a special case if (p->dispSizes[dispID - 1] == 1 && p->width == 16) { if (x >= 8) { x -= 8; relY = 1; } } DDaddr = x + (relY % 2) * 0x40; if ((relY % 4) >= 2) DDaddr += p->width; p->hd44780_functions->senddata (p, dispID, RS_INSTR, POSITION | DDaddr); p->hd44780_functions->uPause (p, 40); // Minimum exec time for all commands } ///////////////////////////////////////////////////////////////// // Flush the framebuffer to the display // MODULE_EXPORT void HD44780_flush (Driver *drvthis) { PrivateData *p = (PrivateData *) drvthis->private_data; int x, y; int wid = p->width; char ch; char drawing; int row; int i; int count; // Update LCD incrementally by comparing with last contents count = 0; for (y=0; yheight; y++) { drawing = 0; for (x=0 ; xframebuf[(y * wid) + x]; if( ch != p->lcd_contents[(y*wid)+x] ) { if( !drawing || x % 8 == 0 ) { // x%8 is for 16x1 displays ! drawing = 1; HD44780_position(drvthis,x,y); } p->hd44780_functions->senddata (p, p->spanList[y], RS_DATA, HD44780_charmap[(unsigned char)ch]); p->hd44780_functions->uPause (p, 40); // Minimum exec time for all commands p->lcd_contents[(y*wid)+x] = ch; count ++; } else { drawing = 0; } } } debug( RPT_DEBUG, "HD44780: flushed %d chars", count ); /* Check which defineable chars we need to update */ count = 0; for( i = 0; i < NUM_UDCs; i ++ ) { if( p->udc_dirty[i] ) { /* Tell the HD44780 we will redefine char number i */ p->hd44780_functions->senddata (p, 0, RS_INSTR, SETCHAR | i * 8); p->hd44780_functions->uPause (p, 40); // Minimum exec time for all commands /* Send the subsequent rows */ for (row = 0; row < p->cellheight; row++) { p->hd44780_functions->senddata (p, 0, RS_DATA, p->udc_buf[i*p->cellheight+row]); p->hd44780_functions->uPause (p, 40); /* Minimum exec time for all commands */ } /* Mark as not dirty anymore */ p->udc_dirty[i] = 0; count ++; } } debug( RPT_DEBUG, "HD44780: flushed %d udc's", count ); } ///////////////////////////////////////////////////////////////// // Clear the framebuffer // MODULE_EXPORT void HD44780_clear (Driver *drvthis) { PrivateData *p = (PrivateData *) drvthis->private_data; memset(p->framebuf, ' ', p->width * p->height); p->udcmode = UDCMODE_STANDARD; } ///////////////////////////////////////////////////////////////// // Place a character in the framebuffer // MODULE_EXPORT void HD44780_chr (Driver *drvthis, int x, int y, char ch) { PrivateData *p = (PrivateData *) drvthis->private_data; y--; x--; p->framebuf[ (y * p->width) + x] = ch; } ///////////////////////////////////////////////////////////////// // Place a string in the framebuffer // MODULE_EXPORT void HD44780_string (Driver *drvthis, int x, int y, char *s) { PrivateData *p = (PrivateData *) drvthis->private_data; int i; x --; // Convert 1-based coords to 0-based y --; for (i = 0; s[i]; i++) { // Check for buffer overflows... if ((y * p->width) + x + i > (p->width * p->height)) break; p->framebuf[(y*p->width) + x + i] = s[i]; } } ///////////////////////////////////////////////////////////////// // Sets the backlight on or off // MODULE_EXPORT void HD44780_backlight (Driver *drvthis, int on) { PrivateData *p = (PrivateData *) drvthis->private_data; p->hd44780_functions->backlight (p, on); } ///////////////////////////////////////////////////////////////// // Sets up for vertical bars. Call before HD44780->vbar() // MODULE_EXPORT void HD44780_init_vbar (Driver *drvthis) { PrivateData *p = (PrivateData *) drvthis->private_data; 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, }; if( p->udcmode == UDCMODE_VBAR ) { /* Work already done */ return; } if( p->udcmode != UDCMODE_STANDARD ) { /* Not supported (yet) */ report( RPT_WARNING, "HD44780_init_vbar: Cannot combine two modes using user defined characters" ); return; } p->udcmode = UDCMODE_VBAR; HD44780_set_char (drvthis, 1, a); HD44780_set_char (drvthis, 2, b); HD44780_set_char (drvthis, 3, c); HD44780_set_char (drvthis, 4, d); HD44780_set_char (drvthis, 5, e); HD44780_set_char (drvthis, 6, f); HD44780_set_char (drvthis, 7, g); } ///////////////////////////////////////////////////////////////// // Inits horizontal bars... // MODULE_EXPORT void HD44780_init_hbar (Driver *drvthis) { PrivateData *p = (PrivateData *) drvthis->private_data; 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, }; if( p->udcmode == UDCMODE_HBAR ) { /* Work already done */ return; } if( p->udcmode != UDCMODE_STANDARD ) { /* Not supported (yet) */ report( RPT_WARNING, "HD44780_init_hbar: Cannot combine two modes using user defined characters" ); return; } p->udcmode = UDCMODE_HBAR; HD44780_set_char (drvthis, 1, a); HD44780_set_char (drvthis, 2, b); HD44780_set_char (drvthis, 3, c); HD44780_set_char (drvthis, 4, d); } ///////////////////////////////////////////////////////////////// // Draws a vertical bar... // MODULE_EXPORT void HD44780_vbar (Driver *drvthis, int x, int y, int len, int promille, int options) { //PrivateData *p = (PrivateData *) drvthis->private_data; char map[8] = { 32, 1, 2, 3, 4, 5, 6, 7 }; int total_pixels = (long) len * LCD_DEFAULT_CELLHEIGHT * promille / 1000; int pos; /* x and y are the start position of the bar. * The bar by default grows in the 'up' direction * (other direction not yet implemented). * len is the number of characters that the bar is long at 100% * promille is the number of promilles (0..1000) that the bar should be filled. */ HD44780_init_vbar(drvthis); for (pos = 0; pos < len; pos ++ ) { int pixels = total_pixels - LCD_DEFAULT_CELLHEIGHT * pos; if( pixels >= LCD_DEFAULT_CELLHEIGHT ) { /* write a "full" block to the screen... */ HD44780_icon (drvthis, x, y-pos, ICON_BLOCK_FILLED); } else if( pixels > 0 ) { /* write a partial block... */ HD44780_chr (drvthis, x, y-pos, map[pixels]); break; } else { ; // write nothing (not even a space) } } } ///////////////////////////////////////////////////////////////// // Draws a horizontal bar to the right. // MODULE_EXPORT void HD44780_hbar (Driver *drvthis, int x, int y, int len, int promille, int options) { //PrivateData *p = (PrivateData *) drvthis->private_data; char map[5] = { 32, 1, 2, 3, 4 }; int total_pixels = (long) len * LCD_DEFAULT_CELLWIDTH * promille / 1000; int pos; /* x and y are the start position of the bar. * The bar by default grows in the 'up' direction * (other direction not yet implemented). * len is the number of characters that the bar is long at 100% * promille is the number of promilles (0..1000) that the bar should be filled. */ HD44780_init_hbar(drvthis); for (pos = 0; pos < len; pos ++ ) { int pixels = total_pixels - LCD_DEFAULT_CELLWIDTH * pos; if( pixels >= LCD_DEFAULT_CELLWIDTH ) { /* write a "full" block to the screen. */ HD44780_icon (drvthis, x+pos, y, ICON_BLOCK_FILLED); } else if( pixels > 0 ) { /* write a partial block... */ HD44780_chr (drvthis, x+pos, y, map[pixels]); break; } else { ; // write nothing (not even a space) } } } ///////////////////////////////////////////////////////////////// // Sets up for big numbers. // MODULE_EXPORT void HD44780_init_num (Driver *drvthis) { PrivateData *p = (PrivateData *) drvthis->private_data; int i; char bignum_udcs[8][5*8] = {{ 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 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, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 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, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0 }, { 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 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 }}; if( p->udcmode == UDCMODE_BIGNUM ) { /* Work already done */ return; } if( p->udcmode != UDCMODE_STANDARD ) { /* Not supported (yet) */ report( RPT_WARNING, "HD44780_init_num: Cannot combine two modes using user defined characters" ); return; } p->udcmode = UDCMODE_BIGNUM; for( i=0; i<8; i++ ) { HD44780_set_char (drvthis, i, bignum_udcs[i]); } } ///////////////////////////////////////////////////////////////// // Writes a big number. // MODULE_EXPORT void HD44780_num (Driver *drvthis, int x, int num) { PrivateData *p = (PrivateData *) drvthis->private_data; char bignum_map[11][4][3] = { { /* 0: */ {1,2,3}, {6,32,6}, {6,32,6}, {7,2,32} }, { /* 1: */ {7,6,32}, {32,6,32}, {32,6,32}, {7,2,32}, }, { /* 2: */ {1,2,3}, {32,5,0}, {1,32,32}, {2,2,0}, }, { /* 3: */ {1,2,3}, {32,5,0}, {3,32,6}, {7,2,32} }, { /* 4: */ {32,3,6}, {1,32,6}, {2,2,6}, {32,32,0} }, { /* 5: */ {1,2,0}, {2,2,3}, {3,32,6}, {7,2,32} }, { /* 6: */ {1,2,32}, {6,5,32}, {6,32,6}, {7,2,32} }, { /* 7: */ {2,2,6}, {32,1,32}, {32,6,32}, {32,0,32} }, { /* 8: */ {1,2,3}, {4,5,0}, {6,32,6}, {7,2,32} }, { /* 9: */ {1,2,3}, {4,3,6}, {32,1,32}, {7,32,32} }, { /* colon: */ {32}, {7}, {7}, {32} }}; if( num < 0 || num > 10 ) return; HD44780_init_num(drvthis); if( p->width >= 20 && p->height >= 4 ) { int y = ( p->height - 2 ) / 2; int x2, y2; for( x2 = 0; x2 <= 2; x2 ++ ) { for( y2 = 0; y2 <= 3; y2 ++ ) { HD44780_chr( drvthis, x+x2, y+y2, bignum_map[num][y2][x2] ); } if( num == 10 ) { x2 = 2; /* =break, for colon only */ } } } else { int y = ( p->height - 1 ) / 2; if( num == 11 ) { HD44780_chr( drvthis, x, y, ':' ); } else { HD44780_chr( drvthis, x, y, num + '0' ); } } } ///////////////////////////////////////////////////////////// // Does the heartbeat... // MODULE_EXPORT void HD44780_heartbeat (Driver *drvthis, int type) { PrivateData *p = (PrivateData *) drvthis->private_data; 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... */ if( ((timer + 4) & 5)) whichIcon = ICON_HEART_OPEN; else whichIcon = ICON_HEART_FILLED; /* place the icon */ HD44780_icon (drvthis, p->width, 1, whichIcon); } 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... // MODULE_EXPORT void HD44780_set_char (Driver *drvthis, int n, char *dat) { PrivateData *p = (PrivateData *) drvthis->private_data; int row, col; int letter; if (n < 0 || n > 7) return; if (!dat) return; for (row = 0; row < p->cellheight; row++) { letter = 0; for (col = 0; col < p->cellwidth; col++) { letter <<= 1; letter |= (dat[(row * p->cellwidth) + col] > 0); } if( p->udc_buf[n*p->cellheight+row] != letter ) { p->udc_dirty[n] = 1; /* only mark as dirty if really different */ } p->udc_buf[n*p->cellheight+row] = letter; } } ///////////////////////////////////////////////////////////// // Set default icon into a userdef char // MODULE_EXPORT void HD44780_icon (Driver *drvthis, int x, int y, int icon) { char heart_open[] = { 1, 1, 1, 1, 1, 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 }; char heart_filled[] = { 1, 1, 1, 1, 1, 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 }; switch( icon ) { case ICON_BLOCK_FILLED: HD44780_chr( drvthis, x, y, 255 ); break; case ICON_HEART_FILLED: HD44780_set_char( drvthis, 0, heart_filled ); HD44780_chr( drvthis, x, y, 0 ); break; case ICON_HEART_OPEN: HD44780_set_char( drvthis, 0, heart_open ); HD44780_chr( drvthis, x, y, 0 ); break; default: report( RPT_WARNING, "HD44780_icon: unknown or unsupported icon: %d", icon ); } } ///////////////////////////////////////////////////////////// // Get a key from the keypad (if there is one) // MODULE_EXPORT char HD44780_getkey(Driver *drvthis) { PrivateData *p = (PrivateData *) drvthis->private_data; unsigned char scancode; char ch; struct timeval curr_time, time_diff; gettimeofday(&curr_time,NULL); scancode = p->hd44780_functions->scankeypad(p); if( scancode ) { if( scancode & 0xF0 ) { ch = ( p->keyMapMatrix[((scancode&0xF0)>>4)-1][(scancode&0x0F)-1] )[0]; } else { ch = ( p->keyMapDirect[scancode - 1] )[0]; } } else { ch = 0; } if (ch) { if (ch == p->pressed_key) { timersub (&curr_time, &(p->pressed_key_time), &time_diff); if (((time_diff.tv_usec / 1000 + time_diff.tv_sec * 1000) - KEYPAD_AUTOREPEAT_DELAY) < 1000 * p->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 p->pressed_key_repetitions ++; } else { // It's a new keypress p->pressed_key_time = curr_time; p->pressed_key_repetitions = 0; printf( "Key: %c (%d,%d)\n", ch, scancode&0x0F, (scancode&0xF0)>>4 ); } } // Store the key for the next round p->pressed_key = ch; return ch; } ///////////////////////////////////////////////////////////// // Scan the keypad // unsigned char HD44780_scankeypad(PrivateData *p) { unsigned int keybits; unsigned int shiftcount; unsigned int shiftingbit; unsigned int Ypattern; unsigned int Yval; char exp; unsigned char scancode = 0; // First check if a directly connected key is pressed // Put all zeros on Y of keypad keybits = p->hd44780_functions->readkeypad (p, 0); if (keybits) { // A directly connected key was pressed // Which key was it ? shiftingbit = 1; for (shiftcount=0; shiftcounthd44780_functions->readkeypad (p, 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 = p->hd44780_functions->readkeypad (p, Ypattern); if (!keybits) { Yval += (1 << exp); } } // Which key is pressed in that row ? keybits = p->hd44780_functions->readkeypad (p, 1<= '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 { fprintf (stderr, "Error reallocing for span list\n"); retVal = -1; } } else { fprintf (stderr, "Error reading spansize\n"); retVal = -1; } } return retVal; }