graphlcd-base/glcddrivers/noritake800.c
changeset 4 df6a40031aa5
equal deleted inserted replaced
3:d0e62fc47285 4:df6a40031aa5
       
     1 /*
       
     2  * GraphLCD driver library
       
     3  *
       
     4  * noritake800.c  -  Noritake 800(A) series VFD graphlcd driver,
       
     5  *                   different "Medium 0.6 dot" sizes should work,
       
     6  *                   see http://www.noritake-itron.com:
       
     7  *                    - GU128X64-800A,
       
     8  *                    - GU256X32-800A,
       
     9  *                    - GU128X32-800A,
       
    10  *                    - GU160X16-800A,
       
    11  *                    - GU160X32-800A,
       
    12  *                    - GU192X16-800A.
       
    13  *
       
    14  * based on:
       
    15  *   ideas and HW-command related stuff from the open source project
       
    16  *   "lcdplugin for Winamp":
       
    17  *     (c) 1999 - 2003 Markus Zehnder <lcdplugin AT markuszehnder.ch>
       
    18  *   GU256x64-372 driver module for graphlcd
       
    19  *     (c) 20040410 Andreas 'Randy' Weinberger <randy AT smue.org>
       
    20  *   gu140x32f driver module for graphlcd
       
    21  *     (c) 2003 Andreas Brachold <vdr04 AT deltab de>
       
    22  *   HD61830 device
       
    23  *     (c) 2001-2003 by Carsten Siebholz <c.siebholz AT t-online.de>
       
    24  *   lcdproc 0.4 driver hd44780-ext8bit
       
    25  *     (c) 1999, 1995 Benjamin Tse <blt AT Comports.com>
       
    26  *
       
    27  * This file is released under the GNU General Public License. Refer
       
    28  * to the COPYING file distributed with this package.
       
    29  *
       
    30  * (c) 2004 Lucian Muresan <lucianm AT users.sourceforge.net>
       
    31  */
       
    32 
       
    33 #include <errno.h>
       
    34 #include <syslog.h>
       
    35 #include <unistd.h>
       
    36 #include <sys/time.h>
       
    37 
       
    38 #include "common.h"
       
    39 #include "config.h"
       
    40 #include "noritake800.h"
       
    41 #include "port.h"
       
    42 
       
    43 namespace GLCD
       
    44 {
       
    45 
       
    46 /* LPT Control Port lines */
       
    47 #define LPT_CTL_HI_DIR      0x20
       
    48 #define LPT_CTL_HI_IRQEN    0x10
       
    49 #define LPT_CTL_LO_STROBE   0x01
       
    50 #define LPT_CTL_LO_LFEED    0x02
       
    51 #define LPT_CTL_LO_INIT     0x04
       
    52 #define LPT_CTL_LO_SELECT   0x08
       
    53 
       
    54 /* Noritake 800(A) VFD control signals bit masks*/
       
    55 #define VFDSGN_CD   0x01
       
    56 #define VFDSGN_WR   0x02
       
    57 #define VFDSGN_RD   0x04
       
    58 #define VFDSGN_CSS  0x08
       
    59 
       
    60 //wirings
       
    61 #define WIRING_LIQUIDMP3 0
       
    62 static const std::string kWiringLiquidmp3 = "LiquidMp3";
       
    63 #define WIRING_MZ  1
       
    64 static const std::string kWiringMZ = "MZ";
       
    65 // ... other wirings may follow
       
    66 
       
    67 /* Command set for this display */
       
    68 #define ANDCNTL         0x03
       
    69 #define ORCNTL          0x01
       
    70 #define XORCNTL         0x02
       
    71 #define Init800A        0x5F /* initialization code sequence 5f */
       
    72 #define Init800B        0x62
       
    73 #define Init800C        0x00+n
       
    74 #define Init800D        0xFF
       
    75 #define CLEARSCREENS    0x5e /* clear all screens (layers) */
       
    76 #define LAYER0ON        0x24 /* screen0 both on */
       
    77 #define LAYER1ON        0x28 /* screen1 both on */
       
    78 #define LAYERSON        0x2c /* both screens both on */
       
    79 #define LAYERSOFF       0x20 /* screens both off */
       
    80 #define ORON            0x40 /* OR screens */
       
    81 #define ANDON           0x48 /* AND screens */
       
    82 #define XORON           0x44 /* XOR screens */
       
    83 #define SETX            0x64 /* set X position */
       
    84 #define SETY            0x60 /* set Y position */
       
    85 #define HSHIFT          0x70 /* set horizontal shift */
       
    86 #define VSHIFT          0xB0
       
    87 #define AUTOINCOFF      0x80 /* address auto increment off */
       
    88 #define SETPOSITION     0xff
       
    89 
       
    90 
       
    91 cDriverNoritake800::cDriverNoritake800(cDriverConfig * config)
       
    92 {
       
    93     int x = 0;
       
    94     m_bGraphScreen0_On = true;
       
    95     m_bGraphScreen1_On = false;
       
    96     // default initilaization for the wiring
       
    97     m_nWiring = WIRING_LIQUIDMP3;
       
    98 
       
    99     m_Config = config;
       
   100     m_oldConfig = new cDriverConfig(* config);
       
   101 
       
   102     m_pport = new cParallelPort();
       
   103 
       
   104     m_nTimingAdjustCmd = 0;
       
   105     m_nRefreshCounter = 0;
       
   106 
       
   107     width = m_Config->width;      // 128
       
   108     if (width <= 0)
       
   109         width = 128;
       
   110     height = m_Config->height;    //  64
       
   111     if (height <= 0)
       
   112         height = 64;
       
   113     m_iSizeYb = (height + 7)/8;   //   8
       
   114 
       
   115     //
       
   116     // initialize wiring
       
   117     //
       
   118     for (unsigned int i = 0; i < m_Config->options.size(); i++)
       
   119     {
       
   120         if (m_Config->options[i].name == "Wiring")
       
   121         {
       
   122             if (m_Config->options[i].value == kWiringLiquidmp3)
       
   123             {
       
   124                 m_nWiring = WIRING_LIQUIDMP3;
       
   125             }
       
   126             else if (m_Config->options[i].value == kWiringMZ)
       
   127             {
       
   128                 m_nWiring = WIRING_MZ;
       
   129             }
       
   130             else
       
   131                 syslog(LOG_ERR, "%s error: wiring %s not supported, using default wiring(%s)!\n",
       
   132                        config->name.c_str(), config->options[i].value.c_str(), kWiringLiquidmp3.c_str());
       
   133         }
       
   134     }
       
   135     // fill the wiring mask cache for all the 16 possibilities
       
   136     m_pWiringMaskCache = new unsigned char[16];
       
   137     for (unsigned int i = 0; i < 16; i++)
       
   138     {
       
   139         m_pWiringMaskCache[i] = N800LptWiringMask(i);
       
   140     }
       
   141 
       
   142     // setup linear lcd array
       
   143     m_pDrawMem = new unsigned char*[width];
       
   144     if (m_pDrawMem)
       
   145     {
       
   146         for (x = 0; x < width; x++)
       
   147         {
       
   148             m_pDrawMem[x] = new unsigned char[m_iSizeYb];
       
   149             memset(m_pDrawMem[x], 0, m_iSizeYb);
       
   150         }
       
   151     }
       
   152     Clear();
       
   153 
       
   154     // setup the lcd array for the "vertikal" mem
       
   155     m_pVFDMem = new unsigned char*[width];
       
   156     if (m_pVFDMem)
       
   157     {
       
   158         for (x = 0; x < width; x++)
       
   159         {
       
   160             m_pVFDMem[x] = new unsigned char[m_iSizeYb];
       
   161             memset(m_pVFDMem[x], 0, m_iSizeYb);
       
   162         }
       
   163     }
       
   164     ClearVFDMem();
       
   165 }
       
   166 
       
   167 cDriverNoritake800::~cDriverNoritake800()
       
   168 {
       
   169     int x;
       
   170 
       
   171     if (m_pVFDMem)
       
   172         for (x = 0; x < (width + 7) / 8; x++)
       
   173         {
       
   174             delete[] m_pVFDMem[x];
       
   175         }
       
   176     delete[] m_pVFDMem;
       
   177     if (m_pDrawMem)
       
   178         for (x = 0; x < (width + 7) / 8; x++)
       
   179         {
       
   180             delete[] m_pDrawMem[x];
       
   181         }
       
   182     delete[] m_pDrawMem;
       
   183     delete[] m_pWiringMaskCache;
       
   184     delete m_oldConfig;
       
   185     delete m_pport;
       
   186 }
       
   187 
       
   188 void cDriverNoritake800::Clear()
       
   189 {
       
   190     for (int x = 0; x < width; x++)
       
   191     {
       
   192         memset(m_pDrawMem[x], 0, m_iSizeYb);
       
   193     }
       
   194 }
       
   195 
       
   196 void cDriverNoritake800::ClearVFDMem()
       
   197 {
       
   198     for (int x = 0; x < width; x++)
       
   199     {
       
   200         memset(m_pVFDMem[x], 0, m_iSizeYb);
       
   201     }
       
   202 }
       
   203 
       
   204 int cDriverNoritake800::DeInit()
       
   205 {
       
   206     if (m_pport->Close() != 0)
       
   207         return -1;
       
   208     return 0;
       
   209 }
       
   210 
       
   211 int cDriverNoritake800::CheckSetup()
       
   212 {
       
   213     if (m_Config->device != m_oldConfig->device ||
       
   214         m_Config->port != m_oldConfig->port ||
       
   215         m_Config->width != m_oldConfig->width ||
       
   216         m_Config->height != m_oldConfig->height)
       
   217     {
       
   218         DeInit();
       
   219         Init();
       
   220         return 0;
       
   221     }
       
   222 
       
   223     if (m_Config->brightness != m_oldConfig->brightness)
       
   224     {
       
   225         m_oldConfig->brightness = m_Config->brightness;
       
   226         SetBrightness(m_Config->brightness);
       
   227     }
       
   228 
       
   229     if (m_Config->upsideDown != m_oldConfig->upsideDown ||
       
   230         m_Config->invert != m_oldConfig->invert)
       
   231     {
       
   232         m_oldConfig->upsideDown = m_Config->upsideDown;
       
   233         m_oldConfig->invert = m_Config->invert;
       
   234         return 1;
       
   235     }
       
   236     return 0;
       
   237 }
       
   238 
       
   239 int cDriverNoritake800::Init()
       
   240 {
       
   241     int x;
       
   242     struct timeval tv1, tv2;
       
   243 
       
   244     if (m_Config->device == "")
       
   245     {
       
   246         // use DirectIO
       
   247         if (m_pport->Open(m_Config->port) != 0)
       
   248             return -1;
       
   249         uSleep(10);
       
   250     }
       
   251     else
       
   252     {
       
   253         // use ppdev
       
   254         if (m_pport->Open(m_Config->device.c_str()) != 0)
       
   255             return -1;
       
   256     }
       
   257 
       
   258     if (nSleepInit() != 0)
       
   259     {
       
   260         syslog(LOG_ERR, "%s: INFO: cannot change wait parameters  Err: %s (cDriver::Init)\n", m_Config->name.c_str(), strerror(errno));
       
   261         m_bSleepIsInit = false;
       
   262     }
       
   263     else
       
   264     {
       
   265         m_bSleepIsInit = true;
       
   266     }
       
   267 
       
   268     // benchmark port access
       
   269     m_pport->Claim();
       
   270     syslog(LOG_DEBUG, "%s: benchmark started.\n", m_Config->name.c_str());
       
   271     gettimeofday(&tv1, 0);
       
   272     int nBenchFactor = 100000;
       
   273     for (x = 0; x < nBenchFactor; x++)
       
   274     {
       
   275         m_pport->WriteData(x % 0x100);
       
   276     }
       
   277     gettimeofday(&tv2, 0);
       
   278     nSleepDeInit();
       
   279     //m_nTimingAdjustCmd = ((tv2.tv_sec - tv1.tv_sec) * 10000 + (tv2.tv_usec - tv1.tv_usec)) / 1000;
       
   280     m_nTimingAdjustCmd = long(double((tv2.tv_sec - tv1.tv_sec) * 1000 + (tv2.tv_usec - tv1.tv_usec)) / double(nBenchFactor));
       
   281     syslog(LOG_DEBUG, "%s: benchmark stopped. Time for Port Command: %ldns\n", m_Config->name.c_str(), m_nTimingAdjustCmd);
       
   282     m_pport->Release();
       
   283 
       
   284 
       
   285     // initialize display
       
   286     N800Cmd(Init800A);
       
   287 
       
   288     int n;
       
   289     for (n=0; n < 15; n++)
       
   290     {
       
   291         N800Cmd(0x62);
       
   292         nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd);
       
   293         N800Cmd(n);
       
   294         nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd);
       
   295         N800Data(0xff);
       
   296         nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd);
       
   297     }
       
   298     nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd);
       
   299 
       
   300 
       
   301     N800Cmd(LAYERSOFF | LAYER0ON); // layer 0 of the graphic RAM on
       
   302     N800Cmd(ORON);     // OR the layers
       
   303     N800Cmd(HSHIFT);    // set horizontal shift
       
   304     N800Cmd(0x00);     // no shift
       
   305     N800Cmd(VSHIFT);    // Vertical shift =0
       
   306     N800Cmd(AUTOINCOFF);   // auto increment off
       
   307     N800Cmd(SETX);     // set x coord
       
   308     N800Cmd(0x40);     // to 0
       
   309     N800Cmd(SETY);     // set y coord
       
   310     N800Cmd(0);      // to 0
       
   311 
       
   312     m_pport->Release();
       
   313 
       
   314     *m_oldConfig = *m_Config;
       
   315 
       
   316     // Set Display SetBrightness
       
   317     SetBrightness(m_Config->brightness);
       
   318     // clear display
       
   319     ClearVFDMem();
       
   320     Refresh(true);
       
   321 
       
   322     syslog(LOG_INFO, "%s: initialization done.\n", m_Config->name.c_str());
       
   323     return 0;
       
   324 }
       
   325 
       
   326 void cDriverNoritake800::Refresh(bool refreshAll)
       
   327 {
       
   328     //
       
   329     // for VFD displays, we can safely ignore refreshAll, as they are "sticky"
       
   330     //
       
   331     int xb, yb;
       
   332 
       
   333     if (CheckSetup() > 0)
       
   334         refreshAll = true;  // we don't use it
       
   335 
       
   336     if (!m_pVFDMem || !m_pDrawMem)
       
   337         return;
       
   338 
       
   339 //  // just refresh if the time needed between refreshes is up
       
   340 //  m_nRefreshCounter = (m_nRefreshCounter + 1) % m_Config->refreshDisplay;
       
   341 //  if(!m_nRefreshCounter)
       
   342 //  {
       
   343     m_pport->Claim();
       
   344     for (xb = 0; xb < width; ++xb)
       
   345     {
       
   346         for (yb = 0; yb < m_iSizeYb; ++yb)
       
   347         {
       
   348             if (m_pVFDMem[xb][yb] != m_pDrawMem[xb][yb])
       
   349             {
       
   350                 m_pVFDMem[xb][yb] = m_pDrawMem[xb][yb];
       
   351                 // reset RefreshCounter
       
   352                 m_nRefreshCounter = 0;
       
   353                 // actually write to display
       
   354                 N800WriteByte(
       
   355                     (m_pVFDMem[xb][yb]) ^ ((m_Config->invert != 0) ? 0xff : 0x00),
       
   356                     xb,
       
   357                     yb,
       
   358                     0);
       
   359             }
       
   360         }
       
   361     }
       
   362     m_pport->Release();
       
   363 //  }
       
   364 }
       
   365 
       
   366 void cDriverNoritake800::N800Cmd(unsigned char data)
       
   367 {
       
   368     if (m_bSleepIsInit)
       
   369         nSleepInit();
       
   370 
       
   371     // set direction to "port_output" & C/D to C
       
   372     m_pport->WriteControl(m_pWiringMaskCache[0x00]);
       
   373     // write to data port
       
   374     m_pport->WriteData(data);
       
   375     //nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd);
       
   376     // set /WR on the control port
       
   377     m_pport->WriteControl(m_pWiringMaskCache[VFDSGN_WR]);
       
   378     //nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd);
       
   379     // reset /WR on the control port
       
   380     m_pport->WriteControl(m_pWiringMaskCache[0x00]);
       
   381     //nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd);
       
   382     // set direction to "port_input"
       
   383     m_pport->WriteControl(LPT_CTL_HI_DIR | m_pWiringMaskCache[0x00]);
       
   384 }
       
   385 
       
   386 void cDriverNoritake800::N800Data(unsigned char data)
       
   387 {
       
   388     if (m_bSleepIsInit)
       
   389         nSleepInit();
       
   390 
       
   391     // set direction to "port_output" & C/D to C
       
   392     m_pport->WriteControl(m_pWiringMaskCache[VFDSGN_CD]);
       
   393     // write to data port
       
   394     m_pport->WriteData(data);
       
   395     //nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd);
       
   396     // set /WR on the control port
       
   397     m_pport->WriteControl(m_pWiringMaskCache[VFDSGN_CD | VFDSGN_WR]);
       
   398     //nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd);
       
   399     // reset /WR on the control port
       
   400     m_pport->WriteControl(m_pWiringMaskCache[VFDSGN_CD]);
       
   401     //nSleep(100 + (100 * m_Config->adjustTiming) - m_nTimingAdjustCmd);
       
   402     // set direction to "port_input"
       
   403     m_pport->WriteControl(LPT_CTL_HI_DIR | m_pWiringMaskCache[0x00]);
       
   404 }
       
   405 
       
   406 void cDriverNoritake800::SetPixel(int x, int y)
       
   407 {
       
   408     unsigned char c;
       
   409 
       
   410     if (!m_pDrawMem)
       
   411         return;
       
   412 
       
   413     if (x >= width || x < 0)
       
   414         return;
       
   415     if (y >= height || y < 0)
       
   416         return;
       
   417 
       
   418     if (m_Config->upsideDown)
       
   419     {
       
   420         x = width - 1 - x;
       
   421         y = height - 1 - y;
       
   422     }
       
   423 
       
   424     c = 0x80 >> (y % 8);
       
   425 
       
   426     m_pDrawMem[x][y/8] |= c;
       
   427 }
       
   428 
       
   429 void cDriverNoritake800::Set8Pixels(int x, int y, unsigned char data)
       
   430 {
       
   431     int n;
       
   432 
       
   433     // x - pos is'nt mayby align to 8
       
   434     x &= 0xFFF8;
       
   435 
       
   436     for (n = 0; n < 8; ++n)
       
   437     {
       
   438         if (data & (0x80 >> n))      // if bit is set
       
   439             SetPixel(x + n, y);
       
   440     }
       
   441 }
       
   442 
       
   443 void cDriverNoritake800::SetBrightness(unsigned int percent)
       
   444 {
       
   445     // display can do 16 brightness levels,
       
   446     //  0 = light
       
   447     // 15 = dark
       
   448 
       
   449     // convert from "light percentage" into darkness values from 0 to 15
       
   450     if (percent > 100)
       
   451     {
       
   452         percent = 100;
       
   453     }
       
   454     unsigned int darkness = 16 - (unsigned int)((double)percent * 16.0 / 100.0);
       
   455 
       
   456     m_pport->Claim();
       
   457     N800Cmd(0x40 + (darkness & 0xf));
       
   458     m_pport->Release();
       
   459 }
       
   460 
       
   461 unsigned char cDriverNoritake800::N800LptWiringMask(unsigned char ctrl_bits)
       
   462 {
       
   463     unsigned char newstatus = 0x0;
       
   464 
       
   465     if (m_nWiring == WIRING_LIQUIDMP3)
       
   466     {
       
   467         if (ctrl_bits & VFDSGN_CSS)
       
   468             newstatus |= LPT_CTL_LO_STROBE;
       
   469         else
       
   470             newstatus &= ~LPT_CTL_LO_STROBE;
       
   471 
       
   472         if (ctrl_bits & VFDSGN_RD)
       
   473             newstatus |= LPT_CTL_LO_LFEED;
       
   474         else
       
   475             newstatus &= ~LPT_CTL_LO_LFEED;
       
   476 
       
   477         if (ctrl_bits & VFDSGN_WR)
       
   478             newstatus |= LPT_CTL_LO_INIT;
       
   479         else
       
   480             newstatus &= ~LPT_CTL_LO_INIT;
       
   481 
       
   482         if (ctrl_bits & VFDSGN_CD)
       
   483             newstatus |= LPT_CTL_LO_SELECT;
       
   484         else
       
   485             newstatus &= ~LPT_CTL_LO_SELECT;
       
   486 
       
   487         // control commands are XOR-ed with 0x5
       
   488         // to account for active lows and highs
       
   489         newstatus ^= 0x5;
       
   490     }
       
   491     else if (m_nWiring == WIRING_MZ)
       
   492     {
       
   493         if (ctrl_bits & VFDSGN_CSS)
       
   494             newstatus |= LPT_CTL_LO_INIT;
       
   495         else
       
   496             newstatus &= ~LPT_CTL_LO_INIT;
       
   497 
       
   498         if (ctrl_bits & VFDSGN_RD)
       
   499             newstatus |= LPT_CTL_LO_LFEED;
       
   500         else
       
   501             newstatus &= ~LPT_CTL_LO_LFEED;
       
   502 
       
   503         if (ctrl_bits & VFDSGN_WR)
       
   504             newstatus |= LPT_CTL_LO_STROBE;
       
   505         else
       
   506             newstatus &= ~LPT_CTL_LO_STROBE;
       
   507 
       
   508         if (ctrl_bits & VFDSGN_CD)
       
   509             newstatus |= LPT_CTL_LO_SELECT;
       
   510         else
       
   511             newstatus &= ~LPT_CTL_LO_SELECT;
       
   512     }
       
   513     return newstatus;
       
   514 }
       
   515 
       
   516 void cDriverNoritake800::N800WriteByte(unsigned char data, int nCol, int nRow, int layer)
       
   517 {
       
   518     /* set cursor to desired address */
       
   519     N800Cmd(SETX);           /* set upper cursor address */
       
   520     N800Cmd(nCol);
       
   521 
       
   522     if (layer==0)
       
   523     {
       
   524         N800Cmd(SETY);     /* set lower cursor address */
       
   525         N800Cmd(nRow);     /*layer0 */
       
   526     }
       
   527     else if (layer==1)
       
   528     {
       
   529         N800Cmd(SETY);      /* set lower cursor address */
       
   530         N800Cmd(nRow+8);    /* layer 1 */
       
   531     }
       
   532 
       
   533     N800Data(ReverseBits(data));
       
   534 }
       
   535 
       
   536 } // end of namespace
       
   537