1236 lines
33 KiB
C
1236 lines
33 KiB
C
/** \file server/drivers/sed1330.c
|
|
* LCDd \c sed1330 driver for SED1330/1335 graphical displays.
|
|
*/
|
|
|
|
/*
|
|
* Driver for SED1330/1335 graphical displays
|
|
*
|
|
* EPSON has changed the the chip-labeling.
|
|
* SED1330 now is S1D13300 and SED1335 is S1D13305
|
|
*
|
|
* This driver drives the LCD in text mode.
|
|
* Probably the driver can easily be adapted to work for 1336 too.
|
|
*
|
|
* 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) 2001-2003, Joris Robijn <joris@robijn.net>
|
|
* 2003, Michael Rohde <Micha.R@online.de>
|
|
* 2006, Benjamin Wiedmann <benjamin.wiedmann@gmx.net>
|
|
*
|
|
*
|
|
* Changelog:
|
|
*
|
|
* November 2001, Joris Robijn:
|
|
* - Created the driver
|
|
* - Parts copied from HD44780 driver
|
|
* December 2001, Joris Robijn:
|
|
* - Adapted to v0.5 API
|
|
* June 2002, Joris Robijn:
|
|
* - Modified init things to support multiple font sizes
|
|
* - More calculations v.s. static init data
|
|
* - Finished keypad stuff
|
|
* May 2003, Micha_R
|
|
* - removing all the cursor visible/invisible stuff, because it produce much flicker
|
|
* (software rendering is now used (Joris))
|
|
* - added support for displays with 192x192 resulution (type 4 = Seiko G191D, but beware,
|
|
* this display has no controller. You need an SED1330/1335 based interface circuit).
|
|
* - fixed problem with clear display function. With displays for which the formula
|
|
* "display-hight / character hight" don't give a integer result, unusable lines at the
|
|
* bottom of the display wasn't cleared.
|
|
* - verified with SED1335. The only difference between the two chips is the minimum
|
|
* reset-raise-time (SED1330 = 1 ms, SED1335 = 0.2 ms)
|
|
* - added function sed1330_icon to get correct display of full blocks
|
|
* May 2003, Joris Robijn
|
|
* - Made bars have spaces between them (I hope people like this)
|
|
* December 2006, Benjamin Wiedmann
|
|
* - added support for Hitachi SP14Q002 gLCD (320x240) with ccfl inverter
|
|
* - built for parport version of this interface: Wallbraun Electronics lcdinterface
|
|
* (specifications here: http://wallbraun-electronics.de/produkte/lcdinterface/index.html)
|
|
* - wiring scheme used: "bitshaker" (called "yasedw" in serdisplib)
|
|
* --> classic wiring: wr=16; a0=17; rd=01; cs=14
|
|
* --> bitshaker wiring: wr=01; a0=14; rd=16; cs=17
|
|
* December 2006, Benjamin Wiedmann (additional changes, fixup)
|
|
* - wiring scheme can now be changed at run time using "ConnectionType" config parameter
|
|
* in sed1330 driver section
|
|
* Usage of ConnectionType in LCDd.conf:
|
|
* ConnectionType=<classic|bitshaker>
|
|
* - if no ConnectionType is set it defaults to "classic" wiring
|
|
*
|
|
* IMPORTANT: MODULES OTHER THAN G321D
|
|
* ===================================
|
|
* If you are using a module other than the G321D, beware that the software
|
|
* has not been tested. You may need to adapt the initialization parameters
|
|
* to get it working properly.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
* Connections
|
|
* ===========
|
|
*
|
|
* Connections below are for the G242C, G121C and G321D displays.
|
|
* Always consult documentation about the specific display before asuming
|
|
* the connections given here are also correct for your display !
|
|
*
|
|
* Ordered by LCD pins
|
|
* LCD pin? <---> 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
|
|
* 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
|
|
* 10 ^ACK
|
|
* GND 11 BUSY
|
|
* 12 PAPEREND
|
|
* 13 ^SELECT
|
|
* 14 ^LF
|
|
* 15 ^ERROR
|
|
* ^WR 3 16 ^INIT
|
|
* A0 7 17 ^SELECT_IN
|
|
* Vss 17 GND 18..25 GND
|
|
* ^RD 2 +5V
|
|
* SEL1 4 GND
|
|
* SEL2 5 GND
|
|
* ^CS 6 GND
|
|
* Vdd 16 +5V
|
|
* V0 18 potmeter
|
|
* Vlc 19 -24V (not required for G242C)
|
|
* Frame 20 GND
|
|
*
|
|
* 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:
|
|
* type=G121C
|
|
* type=G242C
|
|
* type=G321D
|
|
* type=G191D
|
|
* type=G2446
|
|
* type=SP14Q002
|
|
*
|
|
* You can also change the wiring scheme by using the ConnectionType= option:
|
|
* ConnectionType=<classic|bitshaker>
|
|
* If not set, classic wiring is used.
|
|
*
|
|
* The port= value should be set to the LPT port address that the LCD is
|
|
* connected to. Examples:
|
|
* port=0x378
|
|
* port=0x278
|
|
* port=0x3BC
|
|
*
|
|
* The cellsize= value indicates the size of a character. Default:
|
|
* cellsize=6x10
|
|
*
|
|
*/
|
|
|
|
#include "lcd.h"
|
|
#include "sed1330.h"
|
|
|
|
#include "port.h"
|
|
#include "lpt-port.h"
|
|
#include "report.h"
|
|
#include "timing.h"
|
|
#define uPause timing_uPause
|
|
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
|
|
// Autorepeat values
|
|
#define KEYPAD_AUTOREPEAT_DELAY 500
|
|
#define KEYPAD_AUTOREPEAT_FREQ 15
|
|
|
|
// 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 KEYPAD_MAXX 5
|
|
#define KEYPAD_MAXY 8
|
|
|
|
#define TYPE_G321D 1
|
|
#define TYPE_G121C 2
|
|
#define TYPE_G242C 3
|
|
#define TYPE_G191D 4
|
|
#define TYPE_G2446 5
|
|
#define TYPE_SP14Q002 6
|
|
|
|
#define SCR1_L 0x00 // Memory locations
|
|
#define SCR1_H 0x00
|
|
#define SCR2_L 0x00
|
|
#define SCR2_H 0x06
|
|
|
|
|
|
/** private data for the \c sed1330 driver */
|
|
typedef struct sed1330_private_data {
|
|
int type; /**< display type */
|
|
|
|
// wiring scheme variables to be set by sed1330_init()
|
|
int A0;
|
|
int nRESET;
|
|
int nWR;
|
|
|
|
int port; /**< LPT port to use */
|
|
|
|
unsigned char *framebuf_text;
|
|
unsigned char *lcd_contents_text;
|
|
unsigned char *framebuf_graph;
|
|
unsigned char *lcd_contents_graph;
|
|
|
|
int width, height;
|
|
int cellwidth, cellheight;
|
|
int graph_width, graph_height;
|
|
int bytesperline, textlines_in_memory;
|
|
|
|
char have_keypad;
|
|
|
|
// keyMapDirect contains an array of the ascii-codes that should be generated
|
|
// when a directly connected key is pressed (not in matrix).
|
|
char *keyMapDirect[KEYPAD_MAXX];
|
|
|
|
// keyMapMatrix contrains an array with arrays of the ascii-codes that should be generated
|
|
// when a key in the matrix is pressed.
|
|
char *keyMapMatrix[KEYPAD_MAXY][KEYPAD_MAXX];
|
|
|
|
char *pressed_key;
|
|
int pressed_key_repetitions;
|
|
struct timeval pressed_key_time;
|
|
|
|
int stuckinputs;
|
|
} PrivateData;
|
|
|
|
static char *defaultKeyMapDirect[KEYPAD_MAXX] = { "Enter", "Up", "Down", "Escape", "F1" };
|
|
|
|
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 }};
|
|
|
|
|
|
// Vars for the server core
|
|
MODULE_EXPORT char * api_version = API_VERSION;
|
|
MODULE_EXPORT int stay_in_foreground = 0;
|
|
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, unsigned char * data );
|
|
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 );
|
|
unsigned char sed1330_scankeypad(PrivateData *p);
|
|
unsigned char sed1330_readkeypad (PrivateData *p, unsigned int YData);
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// Init the driver and display
|
|
//
|
|
MODULE_EXPORT int
|
|
sed1330_init( Driver * drvthis )
|
|
{
|
|
const char *s;
|
|
PrivateData * p;
|
|
unsigned char data[8];
|
|
|
|
debug(RPT_DEBUG, "%s( %p )", __FUNCTION__, drvthis);
|
|
|
|
// Alocate and store private p
|
|
p = (PrivateData *) calloc(1, sizeof(PrivateData));
|
|
if (p == NULL)
|
|
return -1;
|
|
if (drvthis->store_private_ptr(drvthis, p))
|
|
return -1;
|
|
|
|
// initialize PrivateData
|
|
p->framebuf_text = NULL;
|
|
p->lcd_contents_text = NULL;
|
|
p->framebuf_graph = NULL;
|
|
p->lcd_contents_graph = NULL;
|
|
|
|
// READ THE CONFIG FILE
|
|
|
|
// Port
|
|
p->port = drvthis->config_get_int(drvthis->name, "Port", 0, 0x278);
|
|
|
|
// Char size
|
|
s = drvthis->config_get_string(drvthis->name, "CellSize", 0, "6x10");
|
|
if (sscanf(s, "%dx%d", &(p->cellwidth), &(p->cellheight)) != 2) {
|
|
report(RPT_ERR, "%s: cannot interpret CellSize %s",
|
|
drvthis->name, s);
|
|
return -1;
|
|
}
|
|
if ((p->cellwidth < 6) || (p->cellwidth > 8)
|
|
|| (p->cellheight < 7) || (p->cellheight > 16)) {
|
|
report(RPT_ERR, "%s: CellSize exceeds allowed range of 6x7 to 8x16",
|
|
drvthis->name);
|
|
return -1;
|
|
}
|
|
|
|
// Type
|
|
s = drvthis->config_get_string(drvthis->name, "Type", 0, NULL);
|
|
if (s == NULL) {
|
|
report(RPT_ERR, "%s: you need to specify the display type",
|
|
drvthis->name);
|
|
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 if (strcmp(s, "G191D") == 0) {
|
|
p->type = TYPE_G191D;
|
|
p->graph_width = 192;
|
|
p->graph_height = 192;
|
|
} else if (strcmp(s, "G2446") == 0) {
|
|
p->type = TYPE_G2446;
|
|
p->graph_width = 240;
|
|
p->graph_height = 64;
|
|
} else if(strcmp(s, "SP14Q002") == 0) {
|
|
p->type = TYPE_SP14Q002;
|
|
p->graph_width = 320;
|
|
p->graph_height = 240;
|
|
} else {
|
|
report(RPT_ERR, "%s: Unknown display type %s", drvthis->name, s);
|
|
return -1;
|
|
}
|
|
report(RPT_INFO, "%s: Using LCD type %s", drvthis->name, s);
|
|
|
|
// Set wiring scheme to be used
|
|
//
|
|
// Valid ConnectionTypes:
|
|
// - classic (default)
|
|
// - bitshaker
|
|
//
|
|
// Get ConnectionType, if no type is set, default to "classic" wiring so it even
|
|
// works with config files missing that ConnectionType entry in sed1330 driver section
|
|
s = drvthis->config_get_string(drvthis->name, "ConnectionType", 0, "classic");
|
|
|
|
// Set wiring initialization parameters based on ConnectionType
|
|
if (strcmp(s, "classic") == 0) {
|
|
// Use classic wiring
|
|
p->A0 = SEL; // port 17
|
|
p->nRESET = STRB; // port 1
|
|
p->nWR = INIT; // port 16
|
|
} else if(strcmp(s, "bitshaker") == 0) {
|
|
// Use bitshaker wiring
|
|
p->A0 = nLF; // port 14
|
|
p->nRESET = INIT; // port 16
|
|
p->nWR = STRB; // port 1
|
|
} else {
|
|
report(RPT_ERR, "%s: Unknown ConnectionType %s", drvthis->name, s);
|
|
return -1;
|
|
}
|
|
report(RPT_INFO, "%s: Using ConnectionType %s", drvthis->name, s);
|
|
|
|
// Keypad ?
|
|
p->have_keypad = drvthis->config_get_bool(drvthis->name, "keypad", 0, 0);
|
|
|
|
// Keymap
|
|
if (p->have_keypad) {
|
|
int x, y;
|
|
|
|
// Read keymap
|
|
for (x = 0; x < KEYPAD_MAXX; x++) {
|
|
char buf[40];
|
|
|
|
// First fill with default value
|
|
p->keyMapDirect[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 != NULL) {
|
|
p->keyMapDirect[x] = strdup(s);
|
|
report(RPT_INFO, "%s: Direct key %d: \"%s\"",
|
|
drvthis->name, x, s );
|
|
}
|
|
}
|
|
|
|
for (x = 0; x < KEYPAD_MAXX; x++) {
|
|
for (y = 0; y < KEYPAD_MAXY; y++) {
|
|
char buf[40];
|
|
|
|
// First fill with default value
|
|
p->keyMapMatrix[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 != NULL) {
|
|
p->keyMapMatrix[y][x] = strdup(s);
|
|
report(RPT_INFO, "%s: Matrix key %d,%d: \"%s\"",
|
|
drvthis->name, x, y, s);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calculate some sizes
|
|
p->width = p->graph_width / p->cellwidth;
|
|
p->height = p->graph_height / p->cellheight;
|
|
p->bytesperline = (p->graph_width - 1) / p->cellwidth + 1;
|
|
p->textlines_in_memory = (p->graph_height - 1) / p->cellheight + 1;
|
|
|
|
report(RPT_INFO, "%s: Text size: %dx%d", drvthis->name, p->width, p->height);
|
|
report(RPT_INFO, "%s: Cell size: %dx%d", drvthis->name, p->cellwidth, p->cellheight);
|
|
report(RPT_INFO, "%s: Graphical size: %dx%d", drvthis->name, p->graph_width, p->graph_height);
|
|
|
|
// Allocate framebuffer
|
|
p->framebuf_text = (unsigned char *) malloc(p->bytesperline * p->textlines_in_memory);
|
|
if (p->framebuf_text == NULL) {
|
|
report(RPT_ERR, "%s: error allocating text framebuffer", drvthis->name);
|
|
return -1;
|
|
}
|
|
memset(p->framebuf_text, ' ', p->bytesperline * p->textlines_in_memory);
|
|
|
|
p->lcd_contents_text = (unsigned char *) malloc(p->bytesperline * p->textlines_in_memory);
|
|
if (p->lcd_contents_text == NULL) {
|
|
report(RPT_ERR, "%s: error allocating lcd_contents_text", drvthis->name);
|
|
return -1;
|
|
}
|
|
memset(p->lcd_contents_text, 0, p->bytesperline * p->textlines_in_memory);
|
|
|
|
p->framebuf_graph = (unsigned char *) malloc(p->bytesperline * p->graph_height);
|
|
if (p->framebuf_graph == NULL) {
|
|
report(RPT_ERR, "%s: error allocating graphical framebuffer", drvthis->name);
|
|
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 == NULL) {
|
|
report(RPT_ERR, "%s: error allocating lcd_contents_graph", drvthis->name);
|
|
return -1;
|
|
}
|
|
memset(p->lcd_contents_graph, 0xFF, p->bytesperline * p->graph_height);
|
|
|
|
// Arrange for access to port
|
|
debug(RPT_DEBUG, "%s: getting port access", __FUNCTION__);
|
|
if (port_access_multiple(p->port,3)) {
|
|
report(RPT_ERR, "%s: cannot get IO-permission for 0x%03X: %s",
|
|
drvthis->name, p->port, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if (timing_init() == -1) {
|
|
report(RPT_ERR, "%s: timing_init() failed (%s)",
|
|
drvthis->name, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
// INITIALIZE THE LCD
|
|
// End reset-state
|
|
debug(RPT_DEBUG, "%s: initializing LCD", __FUNCTION__);
|
|
port_out(p->port+2, (p->nWR) ^ OUTMASK); // raise ^RD and ^WR
|
|
port_out(p->port+2, (p->nRESET|p->nWR) ^ OUTMASK); // lower RESET
|
|
uPause(200);
|
|
port_out(p->port+2, (p->nWR) ^ OUTMASK); // raise RESET
|
|
uPause(200);
|
|
port_out(p->port+2, (p->nRESET|p->nWR) ^ OUTMASK); // lower RESET
|
|
uPause(4000);
|
|
|
|
switch (p->type) {
|
|
case TYPE_G321D:
|
|
data[4] = 0x38;
|
|
break;
|
|
case TYPE_SP14Q002:
|
|
data[4] = 0x38;
|
|
break;
|
|
case TYPE_G121C:
|
|
data[4] = 0x7F; // ? please confirm these numbers
|
|
break;
|
|
case TYPE_G242C:
|
|
data[4] = 0x7F; // ?
|
|
break;
|
|
case TYPE_G2446:
|
|
data[4] = 0x7F; // ?
|
|
break;
|
|
case TYPE_G191D:
|
|
data[4] = 0x5c; // ?
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
data[0] = 0x30;
|
|
data[1] = 0x80 + p->cellwidth - 1;
|
|
data[2] = p->cellheight - 1;
|
|
data[3] = p->width - 1;
|
|
// data[4] should be filled already
|
|
// TC/R = ((clock / (refresh * (L/F - 1))) - 1) / 9
|
|
data[5] = p->graph_height - 1;
|
|
data[6] = p->bytesperline;
|
|
data[7] = 0;
|
|
sed1330_command(p, CMD_SYSTEM_SET, 8, data);
|
|
|
|
// TODO: The memory locations need to be calculated !
|
|
sed1330_command(p, CMD_SCROLL, 6, ((unsigned char[6]) {SCR1_L,SCR1_H,0xC7,SCR2_L,SCR2_H,0xC7})); // screen1 and screen2 memory locations
|
|
|
|
data[0] = p->cellwidth-1;
|
|
data[1] = 7;
|
|
sed1330_command(p, CMD_CSR_FORM, 2, data); // set cursor size
|
|
|
|
sed1330_command(p, CMD_HDOT_SCR, 1, ((unsigned char[1]) {0x00})); // horizontal pixel shift=0
|
|
sed1330_command(p, CMD_OVLAY, 1, ((unsigned char[1]) {0x01})); // XOR mode, screen1 text, screen3 text (screen2 and screen4 are always graph)
|
|
sed1330_command(p, CMD_DISP_DIS, 1, ((unsigned char[1]) {0x14})); // display off,set cursor off, 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
|
|
|
|
report(RPT_DEBUG, "%s: init() done", drvthis->name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// Send a command and accompanying p
|
|
// INTERNAL
|
|
//
|
|
void
|
|
sed1330_command( PrivateData * p, char command, int datacount, unsigned char * data )
|
|
{
|
|
int i;
|
|
int port = p->port;
|
|
|
|
port_out(port+2, (p->nRESET|p->nWR|p->A0) ^ OUTMASK); // set A0 to indicate command
|
|
port_out(port, command); // set up p
|
|
port_out(port+2, (p->nRESET|p->A0) ^ OUTMASK); // activate ^WR
|
|
port_out(port+2, (p->nRESET|p->nWR|p->A0) ^ OUTMASK); // deactivate ^WR again
|
|
port_out(port+2, (p->nRESET|p->nWR) ^ OUTMASK); // clear A0 to indicate p
|
|
|
|
for (i = 0; i < datacount; i++) {
|
|
port_out(port, data[i]); // set up data
|
|
port_out(port+2, (p->nRESET) ^ OUTMASK); // activate ^WR
|
|
port_out(port+2, (p->nRESET|p->nWR) ^ OUTMASK); // deactivate ^WR again
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// Close the display
|
|
//
|
|
MODULE_EXPORT void
|
|
sed1330_close( Driver * drvthis )
|
|
{
|
|
PrivateData * p = drvthis->private_data;
|
|
|
|
debug(RPT_DEBUG, "%s()", __FUNCTION__);
|
|
|
|
if (p != NULL) {
|
|
int i, j;
|
|
|
|
for (i = 0; i < KEYPAD_MAXX; i++) {
|
|
if (p->keyMapDirect[i] != NULL)
|
|
free(p->keyMapDirect[i]);
|
|
for (j = 0; j < KEYPAD_MAXY; j++) {
|
|
if (p->keyMapMatrix[i][j] != NULL)
|
|
free(p->keyMapMatrix[i][j]);
|
|
}
|
|
}
|
|
|
|
if (p->framebuf_text != NULL)
|
|
free(p->framebuf_text);
|
|
if (p->lcd_contents_text != NULL)
|
|
free(p->lcd_contents_text);
|
|
if (p->framebuf_graph != NULL)
|
|
free(p->framebuf_graph);
|
|
if (p->lcd_contents_graph != NULL)
|
|
free(p->lcd_contents_graph);
|
|
|
|
free(p);
|
|
}
|
|
drvthis->store_private_ptr(drvthis, NULL);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// Returns the display width
|
|
//
|
|
MODULE_EXPORT int
|
|
sed1330_width( Driver * drvthis )
|
|
{
|
|
PrivateData * p = drvthis->private_data;
|
|
|
|
debug(RPT_INFO, "%s()", __FUNCTION__);
|
|
|
|
return p->width;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// Returns the display height
|
|
//
|
|
MODULE_EXPORT int
|
|
sed1330_height( Driver * drvthis )
|
|
{
|
|
PrivateData * p = drvthis->private_data;
|
|
|
|
debug(RPT_INFO, "%s()", __FUNCTION__);
|
|
|
|
return p->height;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the width of a character in pixels.
|
|
* \param drvthis Pointer to driver structure.
|
|
* \return Number of pixel columns a character cell is wide.
|
|
*/
|
|
MODULE_EXPORT int
|
|
sed1330_cellwidth(Driver *drvthis)
|
|
{
|
|
PrivateData *p = drvthis->private_data;
|
|
|
|
debug(RPT_DEBUG, "%s: returning cellwidth", drvthis->name);
|
|
|
|
return p->cellwidth;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the height of a character in pixels.
|
|
* \param drvthis Pointer to driver structure.
|
|
* \return Number of pixel lines a character cell is high.
|
|
*/
|
|
MODULE_EXPORT int
|
|
sed1330_cellheight(Driver *drvthis)
|
|
{
|
|
PrivateData *p = drvthis->private_data;
|
|
|
|
debug(RPT_DEBUG, "%s: returning cellheight", drvthis->name);
|
|
|
|
return p->cellheight;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// Clear the framebuffer
|
|
//
|
|
MODULE_EXPORT void
|
|
sed1330_clear( Driver * drvthis )
|
|
{
|
|
PrivateData * p = drvthis->private_data;
|
|
|
|
debug(RPT_DEBUG, "%s()", __FUNCTION__);
|
|
|
|
memset(p->framebuf_text, ' ', p->bytesperline * p->textlines_in_memory);
|
|
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;
|
|
unsigned char * dest;
|
|
int offset, len;
|
|
|
|
debug(RPT_DEBUG, "%s( x=%d, y=%d, str=\"%s\" )", __FUNCTION__, x, y, str);
|
|
|
|
if ((y < 1) || (y > p->height)) {
|
|
return; // outside framebuf
|
|
}
|
|
// Calculate offset and length to write
|
|
if (x < 1) {
|
|
offset = (1 - x);
|
|
x = 1;
|
|
} else {
|
|
offset = 0;
|
|
}
|
|
len = strlen(str) - offset;
|
|
if (len > p->width - x + 1) {
|
|
len = p->width - x + 1;
|
|
}
|
|
// Calculate destination address
|
|
dest = p->framebuf_text + (y-1)*p->bytesperline + (x-1);
|
|
|
|
// And write
|
|
memcpy(dest, 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_DEBUG, "%s( x=%d, y=%d, c='%c' )", __FUNCTION__, x, y, c);
|
|
|
|
if ((y < 1) || (y > p->height) || (x < 1) || (x > p->width)) {
|
|
return; // outside framebuf
|
|
}
|
|
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;
|
|
unsigned char csrloc[2];
|
|
|
|
debug(RPT_DEBUG, "%s()", __FUNCTION__);
|
|
|
|
/* sed1330_command(p, CMD_DISP_EN, 1, ((char[1]) {0x14})); // cursor off */
|
|
// TODO: Flickering here needs to be prevented
|
|
|
|
fblen = p->bytesperline * p->textlines_in_memory;
|
|
for (pos = 0; pos < fblen; ) {
|
|
start_pos = pos;
|
|
for (nr_equal = 0; pos<fblen && nr_equal<4; pos++) {
|
|
if (p->lcd_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; pos < fblen; ) {
|
|
start_pos = pos;
|
|
for (nr_equal = 0; pos < fblen && nr_equal < 4; pos++) {
|
|
if (p->lcd_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);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// Sets the backlight on or off
|
|
//
|
|
MODULE_EXPORT void
|
|
sed1330_backlight( Driver * drvthis, int on )
|
|
{
|
|
//PrivateData * p = drvthis->private_data;
|
|
|
|
debug(RPT_DEBUG, "%s( on=%d )", __FUNCTION__, 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/p->cellwidth;
|
|
bitmask = 0x80 >> (x % p->cellwidth);
|
|
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_DEBUG, "%s( x=%d, y=%d, len=%d, promille=%d, pattern=%d )", __FUNCTION__, x, y, len, promille, pattern);
|
|
|
|
sed1330_rect(p, (x-1) * p->cellwidth, y * p->cellheight,
|
|
x * p->cellwidth - 2, y * p->cellheight - (long) len * p->cellheight * 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_DEBUG, "%s( x=%d, y=%d, len=%d, promille=%d, pattern=%d )", __FUNCTION__, x, y, len, promille, pattern);
|
|
|
|
sed1330_rect(p, (x-1) * p->cellwidth, (y-1) * p->cellheight,
|
|
(x-1) * p->cellwidth + (long) len * p->cellwidth * promille / 1000 - 1, y * p->cellheight - 3, 1);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// Writes a big number.
|
|
//
|
|
MODULE_EXPORT void
|
|
sed1330_num( Driver * drvthis, int x, int y, int num )
|
|
{
|
|
//PrivateData * p = drvthis->private_data;
|
|
|
|
debug(RPT_DEBUG, "%s( x=%d, y=%d, num=%d )", __FUNCTION__, x, y, num);
|
|
|
|
// TODO: add code here :)
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////
|
|
// 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][8] = {
|
|
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0x87, 0x87, 0xCF },
|
|
{ 0xFF, 0xFF, 0xCF, 0x87, 0x87, 0xCF, 0xFF, 0xFF },
|
|
{ 0xFF, 0xCF, 0x87, 0x87, 0xCF, 0xFF, 0xFF, 0xFF },
|
|
{ 0xFF, 0x87, 0x87, 0x87, 0xFF, 0xFF, 0xFF, 0xFF },
|
|
{ 0xCF, 0x87, 0x87, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF },
|
|
{ 0xFF, 0x87, 0x87, 0x87, 0xFF, 0xFF, 0xFF, 0xFF },
|
|
{ 0xFF, 0xCF, 0x87, 0x87, 0xCF, 0xFF, 0xFF, 0xFF },
|
|
{ 0xFF, 0xFF, 0xCF, 0x87, 0x87, 0xCF, 0xFF, 0xFF },
|
|
};
|
|
|
|
debug(RPT_DEBUG, "%s( type=%d )", __FUNCTION__, type);
|
|
|
|
if (type == HEARTBEAT_OFF)
|
|
return;
|
|
|
|
p->framebuf_text[p->width-1] = ' ';
|
|
//whichIcon = (! ((timer + 4) & 5));
|
|
|
|
pos = p->width - 1;
|
|
for (n = 0; n < p->cellheight; n++) {
|
|
//p->framebuf_graph[pos] = heartdata[whichIcon][n];
|
|
if (n < 8) {
|
|
p->framebuf_graph[pos] = bouncing_ball[timer][n];
|
|
} else {
|
|
p->framebuf_graph[pos] = 0;
|
|
}
|
|
pos += p->bytesperline;
|
|
}
|
|
|
|
timer++;
|
|
timer %= 8;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////
|
|
// Place an icon
|
|
//
|
|
MODULE_EXPORT int
|
|
sed1330_icon( Driver * drvthis, int x, int y, int icon )
|
|
{
|
|
switch (icon) {
|
|
case ICON_BLOCK_FILLED:
|
|
sed1330_chr(drvthis, x, y, 255);
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////
|
|
// Get a key from the keypad (if there is one)
|
|
//
|
|
MODULE_EXPORT const char *
|
|
sed1330_get_key(Driver *drvthis)
|
|
{
|
|
PrivateData *p = (PrivateData *) drvthis->private_data;
|
|
unsigned char scancode;
|
|
char * keystr = NULL;
|
|
struct timeval curr_time, time_diff;
|
|
|
|
if (!p->have_keypad)
|
|
return NULL;
|
|
|
|
gettimeofday(&curr_time,NULL);
|
|
|
|
scancode = sed1330_scankeypad(p);
|
|
if (scancode) {
|
|
if (scancode & 0xF0) {
|
|
keystr = p->keyMapMatrix[((scancode&0xF0)>>4)-1][(scancode&0x0F)-1];
|
|
}
|
|
else {
|
|
keystr = p->keyMapDirect[scancode - 1];
|
|
}
|
|
}
|
|
|
|
if (keystr != NULL) {
|
|
if (keystr == 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 NULL;
|
|
}
|
|
// 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;
|
|
report(RPT_INFO, "%s: Key pressed: %s (%d,%d)",
|
|
drvthis->name, keystr, scancode&0x0F, (scancode&0xF0)>>4);
|
|
}
|
|
}
|
|
|
|
// Store the key for the next round
|
|
p->pressed_key = keystr;
|
|
|
|
return keystr;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////
|
|
// Scan the keypad
|
|
//
|
|
// Called by get_key
|
|
//
|
|
unsigned char sed1330_scankeypad(PrivateData *p)
|
|
{
|
|
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 = sed1330_readkeypad (p, 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 (sed1330_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 = sed1330_readkeypad (p, Ypattern);
|
|
if (!keybits) {
|
|
Yval += (1 << exp);
|
|
}
|
|
}
|
|
|
|
// Which key is pressed in that row ?
|
|
keybits = sed1330_readkeypad(p, 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;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////
|
|
// Read a keypad byte
|
|
//
|
|
// Called by scankeypad
|
|
//
|
|
unsigned char sed1330_readkeypad (PrivateData *p, unsigned int YData)
|
|
{
|
|
unsigned char readval;
|
|
|
|
// 8 bits output
|
|
// Convert the positive logic to the negative logic on the LPT port
|
|
port_out(p->port, ~YData & 0x00FF);
|
|
|
|
// Read inputs
|
|
readval = ~ port_in (p->port + 1) ^ INMASK;
|
|
|
|
// And convert value back (MSB first).
|
|
return (((readval & FAULT) / FAULT <<4) | /* pin 15 */
|
|
((readval & SELIN) / SELIN <<3) | /* pin 13 */
|
|
((readval & PAPEREND) / PAPEREND <<2) | /* pin 12 */
|
|
((readval & BUSY) / BUSY <<1) | /* pin 11 */
|
|
((readval & ACK) / ACK)) & ~p->stuckinputs; /* pin 10 */
|
|
}
|
|
|