summaryrefslogtreecommitdiff
path: root/glcddrivers/noritake800.c
diff options
context:
space:
mode:
Diffstat (limited to 'glcddrivers/noritake800.c')
-rw-r--r--glcddrivers/noritake800.c537
1 files changed, 537 insertions, 0 deletions
diff --git a/glcddrivers/noritake800.c b/glcddrivers/noritake800.c
new file mode 100644
index 0000000..fe7bc72
--- /dev/null
+++ b/glcddrivers/noritake800.c
@@ -0,0 +1,537 @@
+/*
+ * GraphLCD driver library
+ *
+ * noritake800.c - Noritake 800(A) series VFD graphlcd driver,
+ * different "Medium 0.6 dot" sizes should work,
+ * see http://www.noritake-itron.com:
+ * - GU128X64-800A,
+ * - GU256X32-800A,
+ * - GU128X32-800A,
+ * - GU160X16-800A,
+ * - GU160X32-800A,
+ * - GU192X16-800A.
+ *
+ * based on:
+ * ideas and HW-command related stuff from the open source project
+ * "lcdplugin for Winamp":
+ * (c) 1999 - 2003 Markus Zehnder <lcdplugin AT markuszehnder.ch>
+ * GU256x64-372 driver module for graphlcd
+ * (c) 20040410 Andreas 'Randy' Weinberger <randy AT smue.org>
+ * gu140x32f driver module for graphlcd
+ * (c) 2003 Andreas Brachold <vdr04 AT deltab de>
+ * 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) 2004 Lucian Muresan <lucianm AT users.sourceforge.net>
+ */
+
+#include <errno.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include "common.h"
+#include "config.h"
+#include "noritake800.h"
+#include "port.h"
+
+namespace GLCD
+{
+
+/* LPT Control Port lines */
+#define LPT_CTL_HI_DIR 0x20
+#define LPT_CTL_HI_IRQEN 0x10
+#define LPT_CTL_LO_STROBE 0x01
+#define LPT_CTL_LO_LFEED 0x02
+#define LPT_CTL_LO_INIT 0x04
+#define LPT_CTL_LO_SELECT 0x08
+
+/* Noritake 800(A) VFD control signals bit masks*/
+#define VFDSGN_CD 0x01
+#define VFDSGN_WR 0x02
+#define VFDSGN_RD 0x04
+#define VFDSGN_CSS 0x08
+
+//wirings
+#define WIRING_LIQUIDMP3 0
+static const std::string kWiringLiquidmp3 = "LiquidMp3";
+#define WIRING_MZ 1
+static const std::string kWiringMZ = "MZ";
+// ... other wirings may follow
+
+/* Command set for this display */
+#define ANDCNTL 0x03
+#define ORCNTL 0x01
+#define XORCNTL 0x02
+#define Init800A 0x5F /* initialization code sequence 5f */
+#define Init800B 0x62
+#define Init800C 0x00+n
+#define Init800D 0xFF
+#define CLEARSCREENS 0x5e /* clear all screens (layers) */
+#define LAYER0ON 0x24 /* screen0 both on */
+#define LAYER1ON 0x28 /* screen1 both on */
+#define LAYERSON 0x2c /* both screens both on */
+#define LAYERSOFF 0x20 /* screens both off */
+#define ORON 0x40 /* OR screens */
+#define ANDON 0x48 /* AND screens */
+#define XORON 0x44 /* XOR screens */
+#define SETX 0x64 /* set X position */
+#define SETY 0x60 /* set Y position */
+#define HSHIFT 0x70 /* set horizontal shift */
+#define VSHIFT 0xB0
+#define AUTOINCOFF 0x80 /* address auto increment off */
+#define SETPOSITION 0xff
+
+
+cDriverNoritake800::cDriverNoritake800(cDriverConfig * config)
+{
+ int x = 0;
+ m_bGraphScreen0_On = true;
+ m_bGraphScreen1_On = false;
+ // default initilaization for the wiring
+ m_nWiring = WIRING_LIQUIDMP3;
+
+ m_Config = config;
+ m_oldConfig = new cDriverConfig(* config);
+
+ m_pport = new cParallelPort();
+
+ m_nTimingAdjustCmd = 0;
+ m_nRefreshCounter = 0;
+
+ width = m_Config->width; // 128
+ if (width <= 0)
+ width = 128;
+ height = m_Config->height; // 64
+ if (height <= 0)
+ height = 64;
+ m_iSizeYb = (height + 7)/8; // 8
+
+ //
+ // initialize wiring
+ //
+ for (unsigned int i = 0; i < m_Config->options.size(); i++)
+ {
+ if (m_Config->options[i].name == "Wiring")
+ {
+ if (m_Config->options[i].value == kWiringLiquidmp3)
+ {
+ m_nWiring = WIRING_LIQUIDMP3;
+ }
+ else if (m_Config->options[i].value == kWiringMZ)
+ {
+ m_nWiring = WIRING_MZ;
+ }
+ else
+ syslog(LOG_ERR, "%s error: wiring %s not supported, using default wiring(%s)!\n",
+ config->name.c_str(), config->options[i].value.c_str(), kWiringLiquidmp3.c_str());
+ }
+ }
+ // fill the wiring mask cache for all the 16 possibilities
+ m_pWiringMaskCache = new unsigned char[16];
+ for (unsigned int i = 0; i < 16; i++)
+ {
+ m_pWiringMaskCache[i] = N800LptWiringMask(i);
+ }
+
+ // setup linear lcd array
+ m_pDrawMem = new unsigned char*[width];
+ if (m_pDrawMem)
+ {
+ for (x = 0; x < width; x++)
+ {
+ m_pDrawMem[x] = new unsigned char[m_iSizeYb];
+ memset(m_pDrawMem[x], 0, m_iSizeYb);
+ }
+ }
+ Clear();
+
+ // setup the lcd array for the "vertikal" mem
+ m_pVFDMem = new unsigned char*[width];
+ if (m_pVFDMem)
+ {
+ for (x = 0; x < width; x++)
+ {
+ m_pVFDMem[x] = new unsigned char[m_iSizeYb];
+ memset(m_pVFDMem[x], 0, m_iSizeYb);
+ }
+ }
+ ClearVFDMem();
+}
+
+cDriverNoritake800::~cDriverNoritake800()
+{
+ int x;
+
+ if (m_pVFDMem)
+ for (x = 0; x < (width + 7) / 8; x++)
+ {
+ delete[] m_pVFDMem[x];
+ }
+ delete[] m_pVFDMem;
+ if (m_pDrawMem)
+ for (x = 0; x < (width + 7) / 8; x++)
+ {
+ delete[] m_pDrawMem[x];
+ }
+ delete[] m_pDrawMem;
+ delete[] m_pWiringMaskCache;
+ delete m_oldConfig;
+ delete m_pport;
+}
+
+void cDriverNoritake800::Clear()
+{
+ for (int x = 0; x < width; x++)
+ {
+ memset(m_pDrawMem[x], 0, m_iSizeYb);
+ }
+}
+
+void cDriverNoritake800::ClearVFDMem()
+{
+ for (int x = 0; x < width; x++)
+ {
+ memset(m_pVFDMem[x], 0, m_iSizeYb);
+ }
+}
+
+int cDriverNoritake800::DeInit()
+{
+ if (m_pport->Close() != 0)
+ return -1;
+ return 0;
+}
+
+int cDriverNoritake800::CheckSetup()
+{
+ if (m_Config->device != m_oldConfig->device ||
+ m_Config->port != m_oldConfig->port ||
+ m_Config->width != m_oldConfig->width ||
+ m_Config->height != m_oldConfig->height)
+ {
+ DeInit();
+ Init();
+ return 0;
+ }
+
+ if (m_Config->brightness != m_oldConfig->brightness)
+ {
+ m_oldConfig->brightness = m_Config->brightness;
+ SetBrightness(m_Config->brightness);
+ }
+
+ if (m_Config->upsideDown != m_oldConfig->upsideDown ||
+ m_Config->invert != m_oldConfig->invert)
+ {
+ m_oldConfig->upsideDown = m_Config->upsideDown;
+ m_oldConfig->invert = m_Config->invert;
+ return 1;
+ }
+ return 0;
+}
+
+int cDriverNoritake800::Init()
+{
+ int x;
+ struct timeval tv1, tv2;
+
+ if (m_Config->device == "")
+ {
+ // use DirectIO
+ if (m_pport->Open(m_Config->port) != 0)
+ return -1;
+ uSleep(10);
+ }
+ else
+ {
+ // use ppdev
+ if (m_pport->Open(m_Config->device.c_str()) != 0)
+ return -1;
+ }
+
+ if (nSleepInit() != 0)
+ {
+ syslog(LOG_ERR, "%s: INFO: cannot change wait parameters Err: %s (cDriver::Init)\n", m_Config->name.c_str(), strerror(errno));
+ m_bSleepIsInit = false;
+ }
+ else
+ {
+ m_bSleepIsInit = true;
+ }
+
+ // benchmark port access
+ m_pport->Claim();
+ syslog(LOG_DEBUG, "%s: benchmark started.\n", m_Config->name.c_str());
+ gettimeofday(&tv1, 0);
+ int nBenchFactor = 100000;
+ for (x = 0; x < nBenchFactor; x++)
+ {
+ m_pport->WriteData(x % 0x100);
+ }
+ gettimeofday(&tv2, 0);
+ nSleepDeInit();
+ //m_nTimingAdjustCmd = ((tv2.tv_sec - tv1.tv_sec) * 10000 + (tv2.tv_usec - tv1.tv_usec)) / 1000;
+ m_nTimingAdjustCmd = long(double((tv2.tv_sec - tv1.tv_sec) * 1000 + (tv2.tv_usec - tv1.tv_usec)) / double(nBenchFactor));
+ syslog(LOG_DEBUG, "%s: benchmark stopped. Time for Port Command: %ldns\n", m_Config->name.c_str(), m_nTimingAdjustCmd);
+ m_pport->Release();
+
+
+ // initialize display
+ N800Cmd(Init800A);
+
+ int n;
+ for (n=0; n < 15; n++)
+ {
+ N800Cmd(0x62);
+ nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd);
+ N800Cmd(n);
+ nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd);
+ N800Data(0xff);
+ nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd);
+ }
+ nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd);
+
+
+ N800Cmd(LAYERSOFF | LAYER0ON); // layer 0 of the graphic RAM on
+ N800Cmd(ORON); // OR the layers
+ N800Cmd(HSHIFT); // set horizontal shift
+ N800Cmd(0x00); // no shift
+ N800Cmd(VSHIFT); // Vertical shift =0
+ N800Cmd(AUTOINCOFF); // auto increment off
+ N800Cmd(SETX); // set x coord
+ N800Cmd(0x40); // to 0
+ N800Cmd(SETY); // set y coord
+ N800Cmd(0); // to 0
+
+ m_pport->Release();
+
+ *m_oldConfig = *m_Config;
+
+ // Set Display SetBrightness
+ SetBrightness(m_Config->brightness);
+ // clear display
+ ClearVFDMem();
+ Refresh(true);
+
+ syslog(LOG_INFO, "%s: initialization done.\n", m_Config->name.c_str());
+ return 0;
+}
+
+void cDriverNoritake800::Refresh(bool refreshAll)
+{
+ //
+ // for VFD displays, we can safely ignore refreshAll, as they are "sticky"
+ //
+ int xb, yb;
+
+ if (CheckSetup() > 0)
+ refreshAll = true; // we don't use it
+
+ if (!m_pVFDMem || !m_pDrawMem)
+ return;
+
+// // just refresh if the time needed between refreshes is up
+// m_nRefreshCounter = (m_nRefreshCounter + 1) % m_Config->refreshDisplay;
+// if(!m_nRefreshCounter)
+// {
+ m_pport->Claim();
+ for (xb = 0; xb < width; ++xb)
+ {
+ for (yb = 0; yb < m_iSizeYb; ++yb)
+ {
+ if (m_pVFDMem[xb][yb] != m_pDrawMem[xb][yb])
+ {
+ m_pVFDMem[xb][yb] = m_pDrawMem[xb][yb];
+ // reset RefreshCounter
+ m_nRefreshCounter = 0;
+ // actually write to display
+ N800WriteByte(
+ (m_pVFDMem[xb][yb]) ^ ((m_Config->invert != 0) ? 0xff : 0x00),
+ xb,
+ yb,
+ 0);
+ }
+ }
+ }
+ m_pport->Release();
+// }
+}
+
+void cDriverNoritake800::N800Cmd(unsigned char data)
+{
+ if (m_bSleepIsInit)
+ nSleepInit();
+
+ // set direction to "port_output" & C/D to C
+ m_pport->WriteControl(m_pWiringMaskCache[0x00]);
+ // write to data port
+ m_pport->WriteData(data);
+ //nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd);
+ // set /WR on the control port
+ m_pport->WriteControl(m_pWiringMaskCache[VFDSGN_WR]);
+ //nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd);
+ // reset /WR on the control port
+ m_pport->WriteControl(m_pWiringMaskCache[0x00]);
+ //nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd);
+ // set direction to "port_input"
+ m_pport->WriteControl(LPT_CTL_HI_DIR | m_pWiringMaskCache[0x00]);
+}
+
+void cDriverNoritake800::N800Data(unsigned char data)
+{
+ if (m_bSleepIsInit)
+ nSleepInit();
+
+ // set direction to "port_output" & C/D to C
+ m_pport->WriteControl(m_pWiringMaskCache[VFDSGN_CD]);
+ // write to data port
+ m_pport->WriteData(data);
+ //nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd);
+ // set /WR on the control port
+ m_pport->WriteControl(m_pWiringMaskCache[VFDSGN_CD | VFDSGN_WR]);
+ //nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd);
+ // reset /WR on the control port
+ m_pport->WriteControl(m_pWiringMaskCache[VFDSGN_CD]);
+ //nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd);
+ // set direction to "port_input"
+ m_pport->WriteControl(LPT_CTL_HI_DIR | m_pWiringMaskCache[0x00]);
+}
+
+void cDriverNoritake800::SetPixel(int x, int y)
+{
+ unsigned char c;
+
+ if (!m_pDrawMem)
+ return;
+
+ if (x >= width || x < 0)
+ return;
+ if (y >= height || y < 0)
+ return;
+
+ if (m_Config->upsideDown)
+ {
+ x = width - 1 - x;
+ y = height - 1 - y;
+ }
+
+ c = 0x80 >> (y % 8);
+
+ m_pDrawMem[x][y/8] |= c;
+}
+
+void cDriverNoritake800::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 cDriverNoritake800::SetBrightness(unsigned int percent)
+{
+ // display can do 16 brightness levels,
+ // 0 = light
+ // 15 = dark
+
+ // convert from "light percentage" into darkness values from 0 to 15
+ if (percent > 100)
+ {
+ percent = 100;
+ }
+ unsigned int darkness = 16 - (unsigned int)((double)percent * 16.0 / 100.0);
+
+ m_pport->Claim();
+ N800Cmd(0x40 + (darkness & 0xf));
+ m_pport->Release();
+}
+
+unsigned char cDriverNoritake800::N800LptWiringMask(unsigned char ctrl_bits)
+{
+ unsigned char newstatus = 0x0;
+
+ if (m_nWiring == WIRING_LIQUIDMP3)
+ {
+ if (ctrl_bits & VFDSGN_CSS)
+ newstatus |= LPT_CTL_LO_STROBE;
+ else
+ newstatus &= ~LPT_CTL_LO_STROBE;
+
+ if (ctrl_bits & VFDSGN_RD)
+ newstatus |= LPT_CTL_LO_LFEED;
+ else
+ newstatus &= ~LPT_CTL_LO_LFEED;
+
+ if (ctrl_bits & VFDSGN_WR)
+ newstatus |= LPT_CTL_LO_INIT;
+ else
+ newstatus &= ~LPT_CTL_LO_INIT;
+
+ if (ctrl_bits & VFDSGN_CD)
+ newstatus |= LPT_CTL_LO_SELECT;
+ else
+ newstatus &= ~LPT_CTL_LO_SELECT;
+
+ // control commands are XOR-ed with 0x5
+ // to account for active lows and highs
+ newstatus ^= 0x5;
+ }
+ else if (m_nWiring == WIRING_MZ)
+ {
+ if (ctrl_bits & VFDSGN_CSS)
+ newstatus |= LPT_CTL_LO_INIT;
+ else
+ newstatus &= ~LPT_CTL_LO_INIT;
+
+ if (ctrl_bits & VFDSGN_RD)
+ newstatus |= LPT_CTL_LO_LFEED;
+ else
+ newstatus &= ~LPT_CTL_LO_LFEED;
+
+ if (ctrl_bits & VFDSGN_WR)
+ newstatus |= LPT_CTL_LO_STROBE;
+ else
+ newstatus &= ~LPT_CTL_LO_STROBE;
+
+ if (ctrl_bits & VFDSGN_CD)
+ newstatus |= LPT_CTL_LO_SELECT;
+ else
+ newstatus &= ~LPT_CTL_LO_SELECT;
+ }
+ return newstatus;
+}
+
+void cDriverNoritake800::N800WriteByte(unsigned char data, int nCol, int nRow, int layer)
+{
+ /* set cursor to desired address */
+ N800Cmd(SETX); /* set upper cursor address */
+ N800Cmd(nCol);
+
+ if (layer==0)
+ {
+ N800Cmd(SETY); /* set lower cursor address */
+ N800Cmd(nRow); /*layer0 */
+ }
+ else if (layer==1)
+ {
+ N800Cmd(SETY); /* set lower cursor address */
+ N800Cmd(nRow+8); /* layer 1 */
+ }
+
+ N800Data(ReverseBits(data));
+}
+
+} // end of namespace
+