/* * Driver for SED1330 graphical displays * * This driver drives the LCD in text mode. * Probably the driver can easily be adapted to work for 1335 and 1336 too. * * 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) 2001, Joris Robijn pin LPT port * ^RESET 1 1 ^STROBE * ^RD 2 +5V * ^WR 3 16 ^INIT * SEL1 4 GND * SEL2 5 GND * ^CS 6 GND * A0 7 17 ^SELECT_IN * D0 8 2 D0 * D1 9 3 D1 * D2 10 4 D2 * D3 11 5 D3 * D4 12 6 D4 * D5 13 7 D5 * D6 14 8 D6 * D7 15 9 D7 * Vdd 16 +5V * Vss 17 GND 18..25 GND * V0 18 potmeter * Vlc 19 -24V * Frame 20 GND * 10 ^ACK * GND 11 BUSY * 12 PAPEREND * 13 ^SELECT * 14 ^LF * 15 ^ERROR * * Or ordered by the LPT port pins: * LCD pin? <---> pin LPT port * ^RESET 1 1 ^STROBE * 14 ^LF * D0 8 2 D0 * 15 ^ERROR * D1 9 3 D1 * ^WR 3 16 ^INIT * D2 10 4 D2 * A0 7 17 ^SELECT_IN * D3 11 5 D3 * Vss 17 GND 18..25 GND * D4 12 6 D4 * D5 13 7 D5 * D6 14 8 D6 * D7 15 9 D7 * 10 ^ACK * GND 11 BUSY * 12 PAPEREND * 13 ^SELECT * Vdd 16 +5V * ^RD 2 +5V * Frame 20 GND * SEL1 4 GND * SEL2 5 GND * ^CS 6 GND * Vlc 19 -24V (not required for G242C) * V0 18 potmeter * * The potmeter should be connected like this on these display modules: * * === GND * | * .-. * | | * | |5k * '-' * | * | * .-.10k potmeter * | | * | |<----------------o V0 * | | * '-' * | * O Vlc (= -24V ) * * The G242C generates -24V internally. It is available on Vlc. * * To generate -24 from the +5V without an external power source, you can * use the following circuit. * * 5V O------+----------+ pinout: * | | _____ * | --- 100uF | _ | * | --- 10V | (_) | <-3 * | | |_____| * | +--------+--------+--------+ | max | * |5 | | | | | 724 | * --------- === GND C - | |_____| * | | C coil | | | ||||| * | | C 47uH | |10k | |||||. * | |4 | - | | | | * | MAX724 |--------------+ | | 12345 * | or | | | | * | MAX726 |1 | | |+ * | |-----------------------+ --- 47uF * | | | | --- 50V * | | | | | * --------- | - | * |2 |3 | | | | * | | '---, | |1k | * --- | SB160 / \ - | * 100nF--- | ^T^ | | * | | | | | * +-----+----------------+--------+--------+----O -24V out * * * * * Config options * ============== * * With the display= option you should specify what display module you have. * Accepted values are: * display=G121C * display=G242C * display=G321D * * The port= value should be set to the LPT port address that the LCD is * connected to. Examples: * port=0x378 * port=0x278 * port=0x3BC * */ #include "lcd.h" #include "sed1330.h" #include "port.h" #include "lpt-port.h" #include "report.h" //#include "configfile.h" #include "timing.h" #define uPause timing_uPause #include #include #include // LPT lines #define A0 SEL #define nRESET STRB #define nWR INIT // Command definitions #define CMD_SYSTEM_SET 0x40 #define CMD_SLEEP_IN 0x53 #define CMD_DISP_DIS 0x58 #define CMD_DISP_EN 0x59 #define CMD_SCROLL 0x44 #define CMD_CSR_FORM 0x5D #define CMD_CGRAM_ADR 0x5C #define CMD_CSR_DIR_R 0x4C #define CMD_CSR_DIR_L 0x4D #define CMD_CSR_DIR_U 0x4E #define CMD_CSR_DIR_D 0x4F #define CMD_HDOT_SCR 0x5A #define CMD_OVLAY 0x5B #define CMD_CSRW 0x46 #define CMD_CSRR 0x47 #define CMD_MWRITE 0x42 #define CMD_MREAD 0x43 // Data definitions #define MAXKEYS 8 #define TYPE_G321D 1 #define TYPE_G121C 2 #define TYPE_G242C 3 #define SCR1_L 0x00 // Memory locations #define SCR1_H 0x00 #define SCR2_L 0x00 #define SCR2_H 0x06 #define CHARWIDTH 6 #define CHARHEIGHT 10 #define PIXELSPERBYTE CHARWIDTH // The above should be (CHARWIDTH/2) if CHARWIDTH > 8 typedef struct p { int type; int port; char * keymap[MAXKEYS]; char * framebuf_text; char * lcd_contents_text; char * framebuf_graph; char * lcd_contents_graph; int width, height; //int cellwidth, cellheight; int graph_width, graph_height; int cursor_x, cursor_y; char cursor_state; int bytesperline; } PrivateData; // Vars for the server core MODULE_EXPORT char * api_version = API_VERSION; MODULE_EXPORT int stay_in_foreground = 1; MODULE_EXPORT int supports_multiple = 1; // yes, we have no global variables (except for constants) MODULE_EXPORT char *symbol_prefix = "sed1330_"; // Local functions void uPause (int usecs); void sed1330_command( PrivateData * p, char command, int datacount, char * data ); void sed1330_update_cursor( PrivateData * p ); void sed1330_rect( PrivateData * p, int x1, int y1, int x2, int y2, char pattern ); void sed1330_line ( PrivateData * p, int x1, int y1, int x2, int y2, char pattern ); inline void sed1330_set_pixel( PrivateData * p, int x, int y, int value ); ///////////////////////////////////////////////////////////////// // Init the driver and display // MODULE_EXPORT int sed1330_init( Driver * drvthis, char *args ) { char * s; int port, i; PrivateData * p; debug( RPT_INFO, "SED1330: init(%p,%s)", drvthis, args ); // Alocate and store private p p = (PrivateData *) malloc( sizeof(PrivateData) ); if( ! p ) return -1; if( drvthis->store_private_ptr( drvthis, p ) ) return -1; // Clear keymap memset( p->keymap, 0, sizeof(p->keymap) ); // READ THE CONFIG FILE // Port port = drvthis->config_get_int( drvthis->name, "port", 0, 0x278 ); p->port = port; // Type s = drvthis->config_get_string( drvthis->name, "type", 0, NULL ); if( !s ) { report( RPT_ERR, "SED1330: you need to specify the display type" ); return -1; } else if( strcmp( s, "G321D" ) == 0 ) { p->type = TYPE_G321D; p->graph_width = 320; p->graph_height = 200; } else if( strcmp( s, "G121C" ) == 0 ) { p->type = TYPE_G121C; p->graph_width = 128; p->graph_height = 128; } else if( strcmp( s, "G242C" ) == 0 ) { p->type = TYPE_G242C; p->graph_width = 240; p->graph_height = 128; } else { report( RPT_ERR, "SED1330: Unknown display type: %s", s ); return -1; } p->width = p->graph_width / CHARWIDTH; p->height = p->graph_height / CHARHEIGHT; p->bytesperline = (p->graph_width - 1 ) / CHARWIDTH + 1; report( RPT_INFO, "SED1330: Using LCD type: %s", s ); report( RPT_INFO, "SED1330: Text size: %dx%d", p->width, p->height ); // Keymap for( i=0; iconfig_get_string( drvthis->name, buf, 0, NULL ); if( s ) { p->keymap[i] = (char *) malloc( strlen(s)+1 ); strcpy( p->keymap[i], s ); report( RPT_INFO, "SED1330: Key %d: \"%s\"", i, s ); } else { p->keymap[i] = ""; // Pointing to an constant empty string } } // Init cursor p p->cursor_x = 1; p->cursor_y = 1; p->cursor_state = CURSOR_OFF; // Allocate framebuffer p->framebuf_text = (unsigned char *) malloc( p->bytesperline * p->height ); if( ! p->framebuf_text ) return -1; memset( p->framebuf_text, ' ', p->bytesperline * p->height); p->lcd_contents_text = (unsigned char *) malloc( p->bytesperline * p->height ); if( ! p->lcd_contents_text ) return -1; memset( p->lcd_contents_text, 0, p->bytesperline * p->height); p->framebuf_graph = (unsigned char *) malloc( p->bytesperline * p->graph_height ); if( ! p->framebuf_graph ) return -1; memset( p->framebuf_graph, 0, p->bytesperline * p->graph_height ); p->lcd_contents_graph = (unsigned char *) malloc( p->bytesperline * p->graph_height ); if( ! p->lcd_contents_graph ) return -1; memset( p->lcd_contents_graph, 0xFF, p->bytesperline * p->graph_height ); // Arrange for access to port debug( RPT_DEBUG, "SED1330: getting port access" ); port_access(p->port); port_access(p->port+1); port_access(p->port+2); if (timing_init() == -1) return -1; // INITIALIZE THE LCD // End reset-state debug( RPT_DEBUG, "SED1330: initializing LCD" ); port_out( port+2, (nWR) ^ OUTMASK ); // raise ^RD and ^WR port_out( port+2, (nRESET|nWR) ^ OUTMASK ); // lower RESET uPause( 200 ); port_out( port+2, (nWR) ^ OUTMASK ); // raise RESET uPause( 200 ); port_out( port+2, (nRESET|nWR) ^ OUTMASK ); // lower RESET uPause( 3000 ); switch( p->type ) { case TYPE_G321D: sed1330_command( p, CMD_SYSTEM_SET, 8, ((char[8]) {0x30,0x80+CHARWIDTH-1,CHARHEIGHT-1,0x34,0x38,0xC7,0x36,0x00}) ); // Set textmode 53x20 sed1330_command( p, CMD_SCROLL, 10, ((char[6]) {SCR1_L,SCR1_H,0xC7,SCR2_L,SCR2_H,0xC7}) ); // screen1 and screen2 memory locations break; case TYPE_G121C: sed1330_command( p, CMD_SYSTEM_SET, 8, ((char[8]) {0x30,0x80+CHARWIDTH-1,CHARHEIGHT-1,0x14,0x58,0x7F,0x16,0x00}) ); // Set textmode 21x12 sed1330_command( p, CMD_SCROLL, 10, ((char[6]) {SCR1_L,SCR1_H,0xC7,SCR2_L,SCR2_H,0xC7}) ); // screen1 and screen2 memory locations break; case TYPE_G242C: sed1330_command( p, CMD_SYSTEM_SET, 8, ((char[8]) {0x30,0x80+CHARWIDTH-1,CHARHEIGHT-1,0x27,0x58,0x7F,0x29,0x00}) ); // Set textmode 40x12 sed1330_command( p, CMD_SCROLL, 10, ((char[6]) {SCR1_L,SCR1_H,0xC7,SCR2_L,SCR2_H,0xC7}) ); // screen1 and screen2 memory locations break; default: return -1; } sed1330_command( p, CMD_CSR_FORM, 2, ((char[2]) {0x04,0x07}) ); // 5x8 cursor sed1330_command( p, CMD_HDOT_SCR, 1, ((char[1]) {0x00}) ); // horizontal pixel shift=0 sed1330_command( p, CMD_OVLAY, 1, ((char[1]) {0x01}) ); // XOR mode, screen1 text, screen3 text (screen2 and screen4 are always graph) sed1330_command( p, CMD_DISP_DIS, 1, ((char[1]) {0x17}) ); // display off, set cursor slow, screen1 on, screen2 on, screen3 off sed1330_command( p, CMD_CSR_DIR_R, 0, NULL ); // cursor move right sed1330_flush( drvthis ); // Clear the contents of the LCD sed1330_command( p, CMD_DISP_EN, 0, NULL ); // And display on return 0; } ///////////////////////////////////////////////////////////////// // Send a command and accompanying p // INTERNAL // void sed1330_command( PrivateData * p, char command, int datacount, char * data ) { int i; int port = p->port; port_out( port+2, (nRESET|nWR|A0) ^ OUTMASK ); // set A0 to indicate command port_out( port, command ); // set up p //uPause( 1 ); port_out( port+2, (nRESET|A0) ^ OUTMASK ); // activate ^WR uPause( 1 ); port_out( port+2, (nRESET|nWR|A0) ^ OUTMASK ); // deactivate ^WR again port_out( port+2, (nRESET|nWR) ^ OUTMASK ); // clear A0 to indicate p for( i=0; icursor_y-1) * p->bytesperline + (p->cursor_x-1) + 256 * SCR1_H + SCR1_L; csrloc[0] = cursor_pos % 256; csrloc[1] = cursor_pos / 256; switch( p->cursor_state ) { case CURSOR_OFF: // Off fc = 0; csrform[0] = 0x04; csrform[1] = 0x07; break; case CURSOR_DEFAULT_ON: case CURSOR_UNDER: // Underlined fc = 2; csrform[0] = 0x04; csrform[1] = 0x07; break; case CURSOR_BLOCK: // Block fc = 2; csrform[0] = 0x84; csrform[1] = 0x07; break; } disp_en = 0x14 + fc; sed1330_command( p, CMD_CSRW, 2, csrloc ); sed1330_command( p, CMD_DISP_EN, 1, &disp_en ); // set cursor on/off sed1330_command( p, CMD_CSR_FORM, 2, csrform ); // set cursor shape } ///////////////////////////////////////////////////////////////// // Close the display // MODULE_EXPORT void sed1330_close( Driver * drvthis ) { PrivateData * p = drvthis->private_data; debug( RPT_INFO, "sed1330_close" ); free( p ); } ///////////////////////////////////////////////////////////////// // Returns the display width // MODULE_EXPORT int sed1330_width( Driver * drvthis ) { PrivateData * p = drvthis->private_data; return p->width; } ///////////////////////////////////////////////////////////////// // Returns the display height // MODULE_EXPORT int sed1330_height( Driver * drvthis ) { PrivateData * p = drvthis->private_data; return p->height; } ///////////////////////////////////////////////////////////////// // Clear the framebuffer // MODULE_EXPORT void sed1330_clear( Driver * drvthis ) { PrivateData * p = drvthis->private_data; debug( RPT_INFO, "sed1330_clear" ); memset( p->framebuf_text, ' ', p->bytesperline * p->height); memset( p->framebuf_graph, 0, p->bytesperline * p->graph_height ); } ///////////////////////////////////////////////////////////////// // Place a string in the framebuffer // MODULE_EXPORT void sed1330_string( Driver * drvthis, int x, int y, char *str ) { PrivateData * p = drvthis->private_data; char * start; int len; debug( RPT_INFO, "sed1330_string x=%d y=%d s=\"%s\"", x, y, str ); if( y > p->height ) { return; // outside framebuf_textfer } // Calculate where to start and length to write start = p->framebuf_text + (y-1)*p->bytesperline + (x-1); len = strlen(str); if( p->width <= len ) { len = p->width - x + 1; } memcpy( start, str, len ); } ///////////////////////////////////////////////////////////////// // Place a character in the framebuffer // MODULE_EXPORT void sed1330_chr( Driver * drvthis, int x, int y, char c ) { PrivateData * p = drvthis->private_data; debug( RPT_INFO, "sed1330_chr x=%d y=%d c='%c'", x, y, c ); if( y > p->height || x > p->width ) { return; // outside framebuf_textfer } p->framebuf_text[(y-1)*p->bytesperline + (x-1)] = c; } ///////////////////////////////////////////////////////////////// // Flush the framebuffer to the display // MODULE_EXPORT void sed1330_flush( Driver * drvthis ) { PrivateData * p = drvthis->private_data; unsigned int pos, start_pos, nr_equal, fblen, len, cursor_pos; char csrloc[2]; debug( RPT_INFO, "sed1330_flush" ); sed1330_command( p, CMD_DISP_EN, 1, ((char[1]) {0x16}) ); // cursor off fblen = p->bytesperline * p->height; for( pos=0; poslcd_contents_text[pos] == p->framebuf_text[pos] ) { nr_equal ++; } else { nr_equal = 0; } } len = pos - start_pos - nr_equal; if( len > 0 ) { cursor_pos = start_pos + 256 * SCR1_H + SCR1_L; csrloc[0] = cursor_pos % 256; csrloc[1] = cursor_pos / 256; sed1330_command( p, CMD_CSRW, 2, csrloc ); sed1330_command( p, CMD_MWRITE, len, p->framebuf_text + start_pos ); memcpy( p->lcd_contents_text + start_pos, p->framebuf_text + start_pos, len ); } } fblen = p->bytesperline * p->graph_height; for( pos=0; poslcd_contents_graph[pos] == p->framebuf_graph[pos] ) { nr_equal ++; } else { nr_equal = 0; } } len = pos - start_pos - nr_equal; if( len > 0 ) { cursor_pos = start_pos + 256 * SCR2_H + SCR2_L; csrloc[0] = cursor_pos % 256; csrloc[1] = cursor_pos / 256; sed1330_command( p, CMD_CSRW, 2, csrloc ); sed1330_command( p, CMD_MWRITE, len, p->framebuf_graph + start_pos ); memcpy( p->lcd_contents_graph + start_pos, p->framebuf_graph + start_pos, len ); } } sed1330_update_cursor( p ); } ///////////////////////////////////////////////////////////////// // Let the cursor go to a certain location // MODULE_EXPORT void sed1330_cursor( Driver * drvthis, int x, int y, char state ) { PrivateData * p = drvthis->private_data; debug( RPT_INFO, "sed1330_cursor x=%d y=%d state='%c'", x, y, state ); p->cursor_x = x; p->cursor_y = y; p->cursor_state = state; sed1330_update_cursor( p ); } ///////////////////////////////////////////////////////////////// // Sets the backlight on or off // MODULE_EXPORT void sed1330_backlight( Driver * drvthis, int on ) { //PrivateData * p = drvthis->private_data; debug( RPT_INFO, "sed1330_backlight on='%c'", on ); // unimplemented } ///////////////////////////////////////////////////////////////// // Draws a rectangle // INTERNAL // void sed1330_rect ( PrivateData * p, int x1, int y1, int x2, int y2, char pattern ) /* pattern: 0=clear 1=set later more patterns ? */ { int x, y; // Swap coordinates if needed if( x1>x2 ) { int swap; swap=x1; x1=x2; x2=swap; } if( y1>y2 ) { int swap; swap=y1; y1=y2; y2=swap; } for( x=x1; x<=x2; x++ ) { for( y=y1; y<=y2; y++ ) { sed1330_set_pixel( p, x, y, pattern ); } } } ///////////////////////////////////////////////////////////////// // Draws a line // INTERNAL // void sed1330_line ( PrivateData * p, int x1, int y1, int x2, int y2, char pattern ) /* pattern: 0=clear 1=set later more patterns ? */ { int x, y; int more_x; /* Swap coordinates if needed. We want to draw the line from left * to right. */ if( x1>x2 ) { int swap; swap=x1; x1=x2; x2=swap; swap=y1; y1=y2; y2=swap; } /* Draw from left to right... */ more_x = 1; for( x=x1, y=y1; x<=x2; x++ ) { int more_y = 1; /* always draw one pixel */ while( more_y ) { /* set the pixel */ sed1330_set_pixel( p, x, y, pattern ); /* Check what we need to do next */ if( y1 < y2 ) { more_y = (y<=y2); if( x1 != x2 ) { more_y &= ((float)y+0.5-y1) < ((float) x+0.5-x1) * (y2-y1) / ((float) x2-x1) ; } } else { more_y = (y>=y2); if( x1 != x2 ) { more_y &= ((float)y+0.5-y1) > ((float) x+0.5-x1) * (y2-y1) / ((float) x2-x1) ; } } /* Increment y if we should draw a other pixel for this x value */ if( more_y ) { if( y1 < y2 ) { y ++; } else { y --; } } } } } ///////////////////////////////////////////////////////////////// // Sets a specified pixel // INTERNAL // inline void sed1330_set_pixel( PrivateData * p, int x, int y, int value ) /* x, y are graph LCD coordinates, 0-based */ /* value: 0=clear 1=set */ { unsigned int bytepos; char bitmask; bytepos = y*p->bytesperline + x/PIXELSPERBYTE; bitmask = 0x80 >> (x % PIXELSPERBYTE); if( value ) { p->framebuf_graph[bytepos] |= bitmask; /* set it */ } else { p->framebuf_graph[bytepos] &= ~bitmask; /* clear it */ } } ///////////////////////////////////////////////////////////////// // Draws a vertical bar at the bottom // MODULE_EXPORT void sed1330_vbar( Driver * drvthis, int x, int y, int len, int promille, int pattern ) { PrivateData * p = drvthis->private_data; debug( RPT_INFO, "sed1330_vbar x=%d len=%d", x, len ); sed1330_rect ( p, (x-1) * CHARWIDTH, y * CHARHEIGHT, x * CHARWIDTH - 1, y * CHARHEIGHT - (long) len * CHARHEIGHT * promille / 1000 - 1, 1 ); } ///////////////////////////////////////////////////////////////// // Draws a horizontal bar to the right (len=pos) // or to the left (len=neg) // MODULE_EXPORT void sed1330_hbar( Driver * drvthis, int x, int y, int len, int promille, int pattern ) { PrivateData * p = drvthis->private_data; debug( RPT_INFO, "sed1330_hbar x=%d y=%d len=%d", x, y, len ); sed1330_rect ( p, x * CHARWIDTH, (y-1) * CHARHEIGHT, x * CHARWIDTH + (long) len * CHARWIDTH * promille / 1000 - 1, y * CHARHEIGHT - 1, 1 ); } ///////////////////////////////////////////////////////////////// // Writes a big number. // MODULE_EXPORT void sed1330_num( Driver * drvthis, int x, int num ) { //PrivateData * p = drvthis->private_data; debug( RPT_INFO, "sed1330_bignum x=%d num=%d", x, num ); } ///////////////////////////////////////////////////////////// // Does the heartbeat... // Or in fact a bouncing ball :) // MODULE_EXPORT void sed1330_heartbeat( Driver * drvthis, int type ) { PrivateData * p = drvthis->private_data; static int timer = 0; int pos; //int whichIcon; int n; //char heartdata[2][CHARHEIGHT] = { // { 0xFF, 0xFF, 0xAF, 0x07, 0x8F, 0xDF, 0xFF, 0xFF, 0x00, 0x00 }, // { 0xFF, 0xAF, 0x07, 0x07, 0x07, 0x8F, 0xDF, 0xFF, 0x00, 0x00 } //}; char bouncing_ball[8][CHARHEIGHT] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0x87, 0x87, 0xCF, 0x00, 0x00 }, { 0xFF, 0xFF, 0xCF, 0x87, 0x87, 0xCF, 0xFF, 0xFF, 0x00, 0x00 }, { 0xFF, 0xCF, 0x87, 0x87, 0xCF, 0xFF, 0xFF, 0xFF, 0x00, 0x00 }, { 0xFF, 0x87, 0x87, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00 }, { 0xCF, 0x87, 0x87, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00 }, { 0xFF, 0x87, 0x87, 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00 }, { 0xFF, 0xCF, 0x87, 0x87, 0xCF, 0xFF, 0xFF, 0xFF, 0x00, 0x00 }, { 0xFF, 0xFF, 0xCF, 0x87, 0x87, 0xCF, 0xFF, 0xFF, 0x00, 0x00 }, }; report( RPT_INFO, "sed1330_heartbeat type=%d", type ); p->framebuf_text[p->width-1] = ' '; //whichIcon = (! ((timer + 4) & 5)); pos = p->width - 1; for( n=0; nframebuf_graph[pos] = heartdata[whichIcon][n]; p->framebuf_graph[pos] = bouncing_ball[timer][n]; pos += p->bytesperline; } timer++; timer %= 8; }