diff options
Diffstat (limited to 'glcddrivers/gu140x32f.c')
-rw-r--r-- | glcddrivers/gu140x32f.c | 443 |
1 files changed, 443 insertions, 0 deletions
diff --git a/glcddrivers/gu140x32f.c b/glcddrivers/gu140x32f.c new file mode 100644 index 0000000..212b381 --- /dev/null +++ b/glcddrivers/gu140x32f.c @@ -0,0 +1,443 @@ +/* + * GraphLCD driver library + * + * gu140x32f.c - 8-bit driver module for Noritake GU140x32-F7806 VFD + * displays. The VFD is operating in its 8 bit-mode + * connected to a single PC parallel port. + * + * based on: + * HD61830 device + * (c) 2001-2003 by Carsten Siebholz <c.siebholz AT t-online.de> + * lcdproc 0.4 driver hd44780-ext8bit + * (c) 1999, 1995 Benjamin Tse <blt AT Comports com> + * + * This file is released under the GNU General Public License. Refer + * to the COPYING file distributed with this package. + * + * (c) 2003 Andreas Brachold <vdr04 AT deltab.de> + */ + +#include <errno.h> +#include <syslog.h> +#include <unistd.h> +#include <sys/time.h> + +#include "common.h" +#include "config.h" +#include "gu140x32f.h" +#include "port.h" + + +namespace GLCD +{ + +// Defines for hd44780 Displays +#define RS_DAT 0x00 +#define RS_CMD 0x01 + +#define CLEAR 0x01 + +#define HOMECURSOR 0x02 + +#define ENTRYMODE 0x04 +#define E_MOVERIGHT 0x02 +#define E_MOVELEFT 0x00 +#define EDGESCROLL 0x01 +#define NOSCROLL 0x00 + +#define ONOFFCTRL 0x08 +#define DISPON 0x04 +#define DISPOFF 0x00 +#define CURSORON 0x02 +#define CURSOROFF 0x00 +#define CURSORBLINK 0x01 +#define CURSORNOBLINK 0x00 + +#define CURSORSHIFT 0x10 +#define SCROLLDISP 0x08 +#define MOVECURSOR 0x00 +#define MOVERIGHT 0x04 +#define MOVELEFT 0x00 + +#define FUNCSET 0x20 +#define IF_8BIT 0x10 +#define IF_4BIT 0x00 + +// Control output lines +// Write to baseaddress+2 +#define nSTRB 0x01 // pin 1; negative logic +#define nLF 0x02 // pin 14 +#define nINIT 0x04 // pin 16; the only positive logic output line +#define nSEL 0x08 // pin 17 +#define ENIRQ 0x10 // Enable IRQ via ACK line (don't enable this withouT + // setting up interrupt stuff too) +#define ENBI 0x20 // Enable bi-directional port (is nice to play with! + // I first didn't know a SPP could do this) + +#define OUTMASK 0x0B // SEL, LF and STRB are hardware inverted + // Use this mask only for the control output lines + // XOR with this mask ( ^ OUTMASK ) + +static const std::string kWiringStandard = "Standard"; +static const std::string kWiringWindows = "Windows"; + +// standard wiring +// #define RS nSTRB +// #define RW nLF +// #define EN1 nINIT +// #define BL nSEL + +// windows wiring +// #define RS nINIT +// #define RW nLF +// #define EN1 nSTRB +// #define BL nSEL + + +cDriverGU140X32F::cDriverGU140X32F(cDriverConfig * config) +: config(config), + m_pDrawMem(0), + m_pVFDMem(0) +{ + oldConfig = new cDriverConfig(*config); + + port = new cParallelPort(); + + m_nRefreshCounter = 0; +} + +cDriverGU140X32F::~cDriverGU140X32F() +{ + delete port; + delete oldConfig; +} + +int cDriverGU140X32F::Init() +{ + int x; + struct timeval tv1, tv2; + + // default values + width = config->width; + if (width <= 0) + width = 140; + height = config->height; + if (height <= 0) + height = 32; + m_iSizeYb = ((height + 7) / 8); // 4 + m_WiringRS = nSTRB; + m_WiringEN1 = nINIT; + + for (unsigned int i = 0; i < config->options.size(); i++) + { + if (config->options[i].name == "Wiring") + { + if (config->options[i].value == kWiringStandard) + { + m_WiringRS = nSTRB; + m_WiringEN1 = nINIT; + } + else if (config->options[i].value == kWiringWindows) + { + m_WiringRS = nINIT; + m_WiringEN1 = nSTRB; + } + else + syslog(LOG_ERR, "%s error: wiring %s not supported, using default (Standard)!\n", + config->name.c_str(), config->options[i].value.c_str()); + } + } + + // setup the memory array for the drawing array gu140x32f + m_pDrawMem = new unsigned char[width * m_iSizeYb]; + Clear(); + + // setup the memory array for the display array gu140x32f + m_pVFDMem = new unsigned char[width * m_iSizeYb]; + ClearVFDMem(); + + if (config->device == "") + { + // use DirectIO + if (port->Open(config->port) != 0) + return -1; + uSleep(10); + } + else + { + // use ppdev + if (port->Open(config->device.c_str()) != 0) + return -1; + } + + if (nSleepInit() != 0) + { + syslog(LOG_ERR, "%s: INFO: cannot change wait parameters Err: %s (cDriver::Init)\n", config->name.c_str(), strerror(errno)); + m_bSleepIsInit = false; + } + else + { + m_bSleepIsInit = true; + } + + syslog(LOG_DEBUG, "%s: benchmark started.\n", config->name.c_str()); + gettimeofday(&tv1, 0); + for (x = 0; x < 10000; x++) + { + port->WriteData(x % 0x100); + } + gettimeofday(&tv2, 0); + nSleepDeInit(); + m_nTimingAdjustCmd = ((tv2.tv_sec - tv1.tv_sec) * 10000 + (tv2.tv_usec - tv1.tv_usec)) / 1000; + syslog(LOG_DEBUG, "%s: benchmark stopped. Time for Port Command: %ldns\n", config->name.c_str(), m_nTimingAdjustCmd); + + + // setup the lcd in 8 bit mode + Write(RS_CMD, FUNCSET | IF_8BIT, 4100); + Write(RS_CMD, FUNCSET | IF_8BIT, 100); + Write(RS_CMD, FUNCSET | IF_8BIT, 40); + + Write(RS_CMD, ONOFFCTRL | DISPON | CURSOROFF | CURSORNOBLINK, 40); + Write(RS_CMD, CLEAR, 1600); + Write(RS_CMD, HOMECURSOR, 1600); + + port->Release(); + + *oldConfig = *config; + + // Set Display SetBrightness + SetBrightness(config->brightness); + // clear display + ClearVFDMem(); + Clear(); + + syslog(LOG_INFO, "%s: gu140x32f initialized.\n", config->name.c_str()); + return 0; +} + +int cDriverGU140X32F::DeInit() +{ + if (m_pVFDMem) + delete[] m_pVFDMem; + if (m_pDrawMem) + delete[] m_pDrawMem; + + if (port->Close() != 0) + return -1; + return 0; +} + +int cDriverGU140X32F::CheckSetup() +{ + if (config->device != oldConfig->device || + config->port != oldConfig->port || + config->width != oldConfig->width || + config->height != oldConfig->height) + { + DeInit(); + Init(); + return 0; + } + + if (config->brightness != oldConfig->brightness) + { + oldConfig->brightness = config->brightness; + SetBrightness(config->brightness); + } + + if (config->upsideDown != oldConfig->upsideDown || + config->invert != oldConfig->invert) + { + oldConfig->upsideDown = config->upsideDown; + oldConfig->invert = config->invert; + return 1; + } + return 0; +} + +void cDriverGU140X32F::ClearVFDMem() +{ + for (int n = 0; m_pVFDMem && n < (width * m_iSizeYb); n++) + m_pVFDMem[n] = 0x00; +} + +void cDriverGU140X32F::Clear() +{ + for (int n = 0; m_pDrawMem && n < (width * m_iSizeYb); n++) + m_pDrawMem[n] = 0x00; +} + +void cDriverGU140X32F::SetBrightness(unsigned int percent) +{ + port->Claim(); + + unsigned char level; + if (percent > 100) + percent = 100; + level = percent / 25; + if (level < 1) + level = 1; + level = (4 - level) & 0x03; + // Set Brightness + // 00 - 100% + // 01 - 75% + // 02 - 50% + // 03 - 25% + Write(RS_CMD, FUNCSET | IF_8BIT, 40); + Write(RS_DAT, level, 40); + + port->Release(); +} + +void cDriverGU140X32F::Write(unsigned char nFlags, unsigned char bData, unsigned int nMicroSecBusyTime) +{ + if (m_bSleepIsInit) + nSleepInit(); + + unsigned char enableLines = 0, portControl; + + // Only one controller is supported + enableLines = m_WiringEN1; + + if (nFlags == RS_CMD) + portControl = 0; + else // if (nFlags == RS_DAT) + portControl = m_WiringRS; + + // portControl |= m_WiringBL; + + port->WriteControl(portControl ^ OUTMASK); //Reset controlbits + port->WriteData(bData); //Set data + port->WriteControl((enableLines | portControl) ^ OUTMASK); //Set controlbits + + // How long hold the data active + if (m_bSleepIsInit && (25 + (100 * config->adjustTiming) - m_nTimingAdjustCmd > 0)) + { + // Wait 50ns + nSleep(std::max(25L, 50 + (100 * config->adjustTiming) - m_nTimingAdjustCmd)); + } + + port->WriteControl(portControl ^ OUTMASK); //Reset controlbits + + nSleep((nMicroSecBusyTime * 1000) + (100 * config->adjustTiming) - m_nTimingAdjustCmd); + + if (m_bSleepIsInit) + nSleepDeInit(); +} + +void cDriverGU140X32F::SetPixel(int x, int y) +{ + unsigned char c; + int n; + + if (!m_pDrawMem) + return; + + if (x >= width || x < 0) + return; + if (y >= height || y < 0) + return; + + if (config->upsideDown) + { + x = width - 1 - x; + y = height - 1 - y; + } + + n = x + ((y / 8) * width); + c = 0x80 >> (y % 8); + + m_pDrawMem[n] |= c; +} + +void cDriverGU140X32F::Set8Pixels(int x, int y, unsigned char data) +{ + int n; + + // x - pos is'nt mayby align to 8 + x &= 0xFFF8; + + for (n = 0; n < 8; ++n) + { + if (data & (0x80 >> n)) // if bit is set + SetPixel(x + n, y); + } +} + +void cDriverGU140X32F::Refresh(bool refreshAll) +{ + int n, x, yb; + + if (!m_pVFDMem || !m_pDrawMem) + return; + + bool doRefresh = false; + int minX = width; + int maxX = 0; + int minYb = m_iSizeYb; + int maxYb = 0; + + if (CheckSetup() > 0) + refreshAll = true; + + for (yb = 0; yb < m_iSizeYb; ++yb) + for (x = 0; x < width; ++x) + { + n = x + (yb * width); + if (m_pVFDMem[n] != m_pDrawMem[n]) + { + m_pVFDMem[n] = m_pDrawMem[n]; + minX = std::min(minX, x); + maxX = std::max(maxX, x); + minYb = std::min(minYb, yb); + maxYb = std::max(maxYb, yb + 1); + doRefresh = true; + } + } + + m_nRefreshCounter = (m_nRefreshCounter + 1) % config->refreshDisplay; + + if (!refreshAll && !m_nRefreshCounter) + refreshAll = true; + + if (refreshAll || doRefresh) + { + if (refreshAll) + { + minX = 0; + maxX = width; + minYb = 0; + maxYb = m_iSizeYb; + // and reset RefreshCounter + m_nRefreshCounter = 0; + } + + minX = std::max(minX, 0); + maxX = std::min(maxX, width - 1); + minYb = std::max(minYb, 0); + maxYb = std::min(maxYb, m_iSizeYb); + + port->Claim(); + // send lcd data to display, controller + Write(RS_CMD, 0xF1, 40); + Write(RS_DAT, minX, 40); + Write(RS_DAT, (minYb * 8) & 0xFFF8, 40); + Write(RS_DAT, maxX, 40); + Write(RS_DAT, (maxYb * 8), 40); + + Write(RS_DAT, 'v', 500); + + for (yb = minYb; yb <= maxYb; ++yb) + for (x = minX; x <= maxX; ++x) + { + n = x + (yb * width); + + if (n >= (width * m_iSizeYb)) + break; + Write(RS_DAT, (m_pVFDMem[n]) ^ (config->invert ? 0xff : 0x00), 40); + } + port->Release(); + } +} + +} // end of namespace |