Files
lcdproc/server/drivers/hd44780-serialLpt.c
T

312 lines
8.7 KiB
C
Raw Normal View History

2008-12-21 17:04:59 +00:00
/** \file server/drivers/hd44780-serialLpt.c
2007-10-04 16:30:19 +00:00
* \c serialLPT connection type of \c hd44780 driver for Hitachi HD44780 based LCD displays.
*
2007-10-04 16:30:19 +00:00
* The LCD is operated in it's 4 bit-mode through a 4094 shift register and supports a keypad.
*/
/* Copyright (c) 1999 Andrew McMeikan <andrewm@engineer.com>
2001-10-05 21:01:24 +00:00
* modular driver 1999 Benjamin Tse <blt@Comports.com>
*
* 2001 Joris Robijn <joris@robijn.net>
* - Keypad support
* - Changed for 2 line wire control
*
2001-10-05 21:01:24 +00:00
* printer port 4094/LCD
* D2 (4) EN (6 - LCD)
* D3 (5) D (2 - 4094)
* D4 (6) CLK (3 - 4094)
2001-10-05 21:01:24 +00:00
* +Vcc OE, STR (15, 1 - 4094)
* D7 (9) EN2 (6 - LCD2) (optional)
*
2001-10-05 21:01:24 +00:00
* 4094 LCD
* Q1 (4) D4 (11)
* Q2 (5) D5 (12)
* Q3 (6) D6 (13)
* Q4 (7) D7 (14)
* Q6 (13) RS (4)
2001-10-05 21:01:24 +00:00
* Gnd nRW (5)
*
* Keypad connection (optional):
* This is connected on the 4094 parallel to the LCD.
* Some diodes and resistors are needed, see further documentation.
* Q1 (4) Y1
* Q2 (5) Y2
* Q3 (6) Y3
* Q4 (7) Y4
* Q5 (14) Y5
* Q6 (13) Y6
* Q7 (12) Y7
2001-10-05 21:01:24 +00:00
* The 'output' of the keys should be connected to the following LPT pins:
* nACK (10) X1
* BUSY (11) X2
* PAPEREND (12) X3
* SELIN (13) X4
* nFAULT (15) X5
2001-10-05 21:01:24 +00:00
* If you want to use as few LPT lines as possible, only use X0.
*
* This file is released under the GNU General Public License. Refer to the
* COPYING file distributed with this package.
*/
2001-10-05 21:01:24 +00:00
#include "hd44780-serialLpt.h"
2001-12-30 00:15:25 +00:00
#include "hd44780-low.h"
2001-11-29 15:51:51 +00:00
#include "lpt-port.h"
2001-10-05 21:01:24 +00:00
#include "port.h"
#include "report.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
// Hardware specific functions
2007-04-04 13:52:38 +00:00
void lcdserLpt_HD44780_senddata(PrivateData *p, unsigned char displayID, unsigned char flags, unsigned char ch);
void lcdserLpt_HD44780_backlight(PrivateData *p, unsigned char state);
2007-04-04 13:52:38 +00:00
unsigned char lcdserLpt_HD44780_scankeypad(PrivateData *p);
2008-12-21 14:58:51 +00:00
static void rawshift(PrivateData *p, unsigned char r);
static void shiftreg(PrivateData *p, unsigned char displayID, unsigned char r);
2001-10-05 21:01:24 +00:00
#define RS 32
#define LCDDATA 8
#define LCDCLOCK 16
2001-10-05 21:01:24 +00:00
#define EN1 4
#define EN2 32
2008-12-21 14:58:51 +00:00
/**
* Initialize the driver.
* \param drvthis Pointer to driver structure.
* \retval 0 Success.
* \retval -1 Error.
*/
int
2007-04-04 13:52:38 +00:00
hd_init_serialLpt(Driver *drvthis)
{
2001-12-30 00:15:25 +00:00
PrivateData *p = (PrivateData*) drvthis->private_data;
HD44780_functions *hd44780_functions = p->hd44780_functions;
2001-10-05 21:01:24 +00:00
unsigned char enableLines = EN1 | EN2;
2001-10-05 21:01:24 +00:00
// Reserve the port registers
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;
}
2000-04-03 22:13:57 +00:00
hd44780_functions->senddata = lcdserLpt_HD44780_senddata;
2001-11-01 00:37:12 +00:00
hd44780_functions->backlight = lcdserLpt_HD44780_backlight;
hd44780_functions->scankeypad = lcdserLpt_HD44780_scankeypad;
2000-04-03 22:13:57 +00:00
// setup the lcd in 4 bit mode
2007-04-04 13:52:38 +00:00
shiftreg(p, enableLines, 3);
hd44780_functions->uPause(p, 15000);
2001-12-09 14:33:17 +00:00
2007-04-04 13:52:38 +00:00
shiftreg(p, enableLines, 3);
hd44780_functions->uPause(p, 5000);
2007-04-04 13:52:38 +00:00
shiftreg(p, enableLines, 3);
hd44780_functions->uPause(p, 100);
2002-02-22 01:13:24 +00:00
2007-04-04 13:52:38 +00:00
shiftreg(p, enableLines, 3);
hd44780_functions->uPause(p, 100);
2007-04-04 13:52:38 +00:00
shiftreg(p, enableLines, 2);
hd44780_functions->uPause(p, 100);
2007-04-04 13:52:38 +00:00
hd44780_functions->senddata(p, 0, RS_INSTR, FUNCSET | IF_4BIT | TWOLINE | SMALLCHAR);
hd44780_functions->uPause(p, 40);
2009-02-27 22:28:58 +00:00
common_init(p, IF_4BIT);
2001-10-05 21:01:24 +00:00
2000-04-03 22:13:57 +00:00
return 0;
}
2008-12-21 14:58:51 +00:00
/**
* Send data or commands to the display.
* \param p Pointer to driver's private data structure.
* \param displayID ID of the display (or 0 for all) to send data to.
* \param flags Defines whether to end a command or data.
* \param ch The value to send.
*/
void
2007-04-04 13:52:38 +00:00
lcdserLpt_HD44780_senddata(PrivateData *p, unsigned char displayID, unsigned char flags, unsigned char ch)
{
2001-10-05 21:01:24 +00:00
unsigned char enableLines;
unsigned char portControl = 0;
unsigned char h = ch >> 4, l = ch & 15;
2000-04-03 22:13:57 +00:00
if (displayID == 1)
2001-10-05 21:01:24 +00:00
enableLines = EN1;
2000-04-03 22:13:57 +00:00
else if (displayID == 2)
2001-10-05 21:01:24 +00:00
enableLines = EN2;
2000-04-03 22:13:57 +00:00
else
2001-10-05 21:01:24 +00:00
enableLines = EN1 | EN2;
2000-04-03 22:13:57 +00:00
if (flags == RS_DATA)
portControl = RS;
else
portControl = 0;
2007-04-04 13:52:38 +00:00
shiftreg(p, enableLines, portControl | h);
shiftreg(p, enableLines, portControl | l);
2001-12-09 14:33:17 +00:00
// Restore line status for backlight
2007-04-04 13:52:38 +00:00
port_out(p->port, p->backlight_bit);
}
2008-12-21 14:58:51 +00:00
/**
* Turn display backlight on or off.
* \param p Pointer to driver's private data structure.
* \param state New backlight status.
*/
2001-11-01 00:37:12 +00:00
void
2007-04-04 13:52:38 +00:00
lcdserLpt_HD44780_backlight(PrivateData *p, unsigned char state)
{
2001-12-09 14:33:17 +00:00
// Store new state
2001-12-30 00:15:25 +00:00
p->backlight_bit = (state?LCDDATA:0);
2001-12-09 14:33:17 +00:00
// Set line status for backlight
2007-04-04 13:52:38 +00:00
port_out(p->port, p->backlight_bit);
2001-11-01 00:37:12 +00:00
}
2008-12-21 14:58:51 +00:00
/**
* Read keypress.
* \param p Pointer to driver's private data structure.
* \return Bitmap of the pressed keys.
*/
2007-04-04 13:52:38 +00:00
unsigned char lcdserLpt_HD44780_scankeypad(PrivateData *p)
2001-11-01 00:37:12 +00:00
{
2001-12-09 14:33:17 +00:00
// Unfortunately just bit shifting does not work with the 2-wire version...
2001-10-05 21:01:24 +00:00
2001-11-01 00:37:12 +00:00
unsigned char keybits;
unsigned int shiftcount;
unsigned int shiftingbit;
2001-12-09 14:33:17 +00:00
unsigned char readval, inputs_zero;
2001-11-01 00:37:12 +00:00
int i;
unsigned int scancode = 0;
2001-12-30 00:15:25 +00:00
// While scanning the keypad, the 2-wire version will place the
// character 0xFF on the current cursor position. Therefor we first
// set the cursor position to home, do the keypad reading and
// afterwards restore the first character (on all connected displays).
//
2001-12-09 14:33:17 +00:00
// I could not prevent this, while staying compatible with both
// wiring versions. Joris.
//
// (Positioning the cursor out of screen does not work either :( )
// Set cursor position
2007-04-04 13:52:38 +00:00
p->hd44780_functions->senddata(p, 0, RS_INSTR, POSITION | 0);
p->hd44780_functions->uPause(p, 40);
2001-11-01 00:37:12 +00:00
2001-12-09 14:33:17 +00:00
// Clear the shiftregister, needed for 3-wire version
2001-12-30 00:15:25 +00:00
rawshift(p, 0);
2007-04-04 13:52:38 +00:00
p->hd44780_functions->uPause(p, 1);
2001-11-01 00:37:12 +00:00
2007-04-04 13:52:38 +00:00
readval = ~ port_in(p->port + 1) ^ INMASK;
2002-02-22 01:13:24 +00:00
// And convert value back (MSB first).
inputs_zero = (((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 )); /* pin 10 */
2002-02-22 01:13:24 +00:00
2001-11-01 00:37:12 +00:00
2007-04-04 13:52:38 +00:00
if (inputs_zero == 0) {
2001-12-09 14:33:17 +00:00
// No keys were pressed
// Restore line status for backlight.
2007-04-04 13:52:38 +00:00
port_out(p->port, p->backlight_bit);
2001-11-01 00:37:12 +00:00
return 0;
}
// Scan the keypad while sending the first half of the command (high nibble)
2001-12-09 14:33:17 +00:00
for (i = 7; i >= 0; i--) { /* MSB first */
2007-04-04 13:52:38 +00:00
port_out(p->port, LCDDATA); /*set up data */
port_out(p->port, LCDDATA | LCDCLOCK); /*rising edge of clock */
2001-11-01 00:37:12 +00:00
2007-04-04 13:52:38 +00:00
p->hd44780_functions->uPause(p, 1);
2001-11-01 00:37:12 +00:00
2007-04-04 13:52:38 +00:00
if (!scancode) {
2001-11-01 00:37:12 +00:00
// Read input line(s)
2007-04-04 13:52:38 +00:00
readval = ~ port_in(p->port + 1) ^ INMASK;
2002-02-22 01:13:24 +00:00
// And convert value back (MSB first).
keybits = (((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 )); /* pin 10 */
2007-04-04 13:52:38 +00:00
if (keybits != inputs_zero) {
2001-11-01 00:37:12 +00:00
shiftingbit = 1;
for (shiftcount=0; shiftcount<KEYPAD_MAXX && !scancode; shiftcount++) {
2007-04-04 13:52:38 +00:00
if ((keybits ^ inputs_zero) & shiftingbit) {
2001-11-01 00:37:12 +00:00
// Found !
scancode = ((8-i)<<4) | (shiftcount+1);
}
shiftingbit <<= 1;
}
2000-04-03 22:13:57 +00:00
}
}
2001-11-01 00:37:12 +00:00
}
2001-12-09 14:33:17 +00:00
// Wait for 2-wire version to clear the latch...
2007-04-04 13:52:38 +00:00
p->hd44780_functions->uPause(p, 6);
2001-11-01 00:37:12 +00:00
2001-12-09 14:33:17 +00:00
// And again for the second half of the command (low nibble).
// Needed for 2-wire version.
2007-04-04 13:52:38 +00:00
rawshift(p, 0xFF);
2001-11-01 00:37:12 +00:00
// Wait 6us for 2-wire version to clear the latch and wait for
// the data to be processed
2007-04-04 13:52:38 +00:00
p->hd44780_functions->uPause(p, 40);
2001-12-09 14:33:17 +00:00
// Restore the screen state
// Move back to home cursor position
2007-04-04 13:52:38 +00:00
p->hd44780_functions->senddata(p, 0, RS_INSTR, POSITION | 0);
p->hd44780_functions->uPause(p, 40);
// Output the corect byte
2007-04-04 13:52:38 +00:00
p->hd44780_functions->senddata(p, 1, RS_DATA,
p->framebuf[0]);
// ... and second display if connected ...
if (p->numDisplays>1) {
2007-04-04 13:52:38 +00:00
p->hd44780_functions->senddata(p, 2, RS_DATA,
p->framebuf[ p->width * p->dispVOffset[2-1] ]);
}
2007-04-04 13:52:38 +00:00
p->hd44780_functions->uPause(p, 40);
// No need to restore the line for backlight, already done by senddata.
2001-11-01 00:37:12 +00:00
return scancode;
2001-10-05 21:01:24 +00:00
}
/* this function sends r out onto the shift register */
2008-12-21 14:58:51 +00:00
static void
2007-04-04 13:52:38 +00:00
rawshift(PrivateData *p, unsigned char r)
{
2000-04-03 22:13:57 +00:00
int i;
2001-11-01 00:37:12 +00:00
2001-10-05 21:01:24 +00:00
for (i = 7; i >= 0; i--) { /* MSB first */
2007-04-04 13:52:38 +00:00
port_out(p->port, ((r >> i) & 1) * LCDDATA); /*set up data */
port_out(p->port, (((r >> i) & 1) * LCDDATA) | LCDCLOCK); /*rising edge of clock */
2000-04-03 22:13:57 +00:00
}
}
2001-10-05 21:01:24 +00:00
// enableLines = value on parallel port to toggle the correct display
2008-12-21 14:58:51 +00:00
static void
2007-04-04 13:52:38 +00:00
shiftreg(PrivateData *p, unsigned char enableLines, unsigned char r)
{
2007-04-04 13:52:38 +00:00
rawshift(p, r | 0x80); // highest bit always set to 1 for Clear for 2-wire version
port_out(p->port, enableLines); // latch it, to correct display
p->hd44780_functions->uPause(p, 1);
port_out(p->port, 0); // for 3-wire version
p->hd44780_functions->uPause(p, 5); // wait for 2-wire version to clear the latch...
}