graphlcd-base/glcddrivers/gu126x64D-K610A4.c
author root@rika
Wed, 06 Feb 2008 17:32:55 +0000
changeset 4 df6a40031aa5
permissions -rw-r--r--
added graphlcd-base
     1 /*
     2  * GraphLCD driver library
     3  *
     4  * gu126x64D-K610A4.c -  8-bit driver module for Noritake GU126x64D-K610A4 VFD
     5  *                       displays. The VFD is operating in its 8 bit-mode
     6  *                       connected to a single PC parallel port.
     7  *
     8  * based on: 
     9  *   gu256x64-372 driver module for graphlcd
    10  *     (c) 2004 Andreas 'randy' Weinberger (randy AT smue.org)
    11  *   gu256x64-3900 driver module for graphlcd
    12  *     (c) 2004 Ralf Mueller (ralf AT bj-ig.de)
    13  *   gu140x32f driver module for graphlcd
    14  *     (c) 2003 Andreas Brachold <vdr04 AT deltab.de>
    15  *   ks0108 driver module for graphlcd
    16  *     (c) 2004 Andreas 'randy' Weinberger (randy AT smue.org)
    17  *  
    18  * This file is released under the GNU General Public License. Refer
    19  * to the COPYING file distributed with this package.
    20  *
    21  * (c) 2007 Alexander Rieger (Alexander.Rieger AT inka.de)
    22  */
    23 
    24 #include <errno.h>
    25 #include <syslog.h>
    26 #include <unistd.h>
    27 #include <sys/time.h>
    28 
    29 #include "common.h"
    30 #include "config.h"
    31 #include "gu126x64D-K610A4.h"
    32 #include "port.h"
    33 
    34 namespace GLCD
    35 {
    36 //----- commands to the display -----------------------------------------------
    37 static const unsigned char CMD_RUN_MACRO_01  = 0x01; // run macro 1
    38 static const unsigned char CMD_RUN_MACRO_02  = 0x02; // run macro 2
    39 static const unsigned char CMD_RUN_MACRO_03  = 0x03; // run macro 3
    40 static const unsigned char CMD_RUN_MACRO_04  = 0x04; // run macro 4
    41 static const unsigned char CMD_RUN_MACRO_05  = 0x05; // run macro 5
    42 static const unsigned char CMD_RUN_MACRO_06  = 0x06; // run macro 6
    43 static const unsigned char CMD_RUN_MACRO_07  = 0x07; // run macro 7
    44 
    45 static const unsigned char CMD_CURSOR_POS    = 0x10; // Set cursor position
    46 static const unsigned char CMD_BOX_SET       = 0x11; // Set area
    47 static const unsigned char CMD_BOX_CLEAR     = 0x12; // Clear area
    48 static const unsigned char CMD_BOX_INVERT    = 0x13; // Invert area
    49 static const unsigned char CMD_RECT_SET      = 0x14; // Set outline
    50 static const unsigned char CMD_RECT_CLEAR    = 0x15; // Clear outline
    51 
    52 static const unsigned char CMD_PIXEL_SET     = 0x16; // Set pixel at current pos
    53 static const unsigned char CMD_PIXEL_CLEAR   = 0x17; // Clear pixel at current pos
    54 
    55 static const unsigned char CMD_GRAPHIC_WRITE = 0x18; // Write graphics data (args: len, data)
    56 static const unsigned char CMD_RESET         = 0x19; // Reset display
    57 static const unsigned char CMD_WRITE_MODE    = 0x1A; // Write mode 
    58 static const unsigned char CMD_INTRO         = 0x1B; // Intro for other commands (see CMA_*)
    59 static const unsigned char CMD_FONT_PROP_SML = 0x1C; // Select font: proportional mini
    60 static const unsigned char CMD_FONT_FIX_MED  = 0x1D; // Select font: fixed spaced 5x7
    61 static const unsigned char CMD_FONT_FIX_BIG  = 0x1E; // Select font: fixed spaced 10x14
    62 
    63 static const unsigned char CMA_MACROS_ERASE  = 0x4D; // Erase Macros  (usage: CMD_INTRO + this)
    64 static const unsigned char CMA_EPROM_LOCK    = 0x4C; // Lock EEPROM   (usage: CMD_INTRO + this)
    65 static const unsigned char CMA_EPROM_UNLOCK  = 0x55; // Unlock EEPROM (usage: CMD_INTRO + this)
    66 
    67 static const unsigned char CMA_POWER_OFF     = 0x46; // Power off     (usage: CMD_INTRO + this)
    68 static const unsigned char CMA_POWER_ON      = 0x50; // Power on      (usage: CMD_INTRO + this)
    69 
    70 //----- signal lines ----------------------------------------------------------
    71 static const unsigned char OUT_EN_HI         = kAutoLow ;
    72 static const unsigned char OUT_EN_LO         = kAutoHigh;
    73 static const unsigned char OUT_EN_MASK       = OUT_EN_HI;
    74 
    75 static const unsigned char IN_MB_HI          = 0x40;
    76 static const unsigned char IN_MB_LO          = 0x00;
    77 static const unsigned char IN_MB_MASK        = IN_MB_HI;
    78 
    79 //----- log flags -------------------------------------------------------------
    80 static const unsigned int  LL_REFRESH_START  = 0x0001;  //  1
    81 static const unsigned int  LL_REFRESH_END    = 0x0002;  //  2
    82 static const unsigned int  LL_REFRESH_MED    = 0x0004;  //  4
    83 static const unsigned int  LL_VFD_CMD        = 0x0008;  //  8
    84 static const unsigned int  LL_MAX_WAIT       = 0x0010;  // 16
    85 
    86 //----- mixed consts ----------------------------------------------------------
    87 static const long          ADJUST_FACTOR     =  100;    // used to adjust timing
    88 
    89 //-----------------------------------------------------------------------------
    90 cDriverGU126X64D_K610A4::cDriverGU126X64D_K610A4(cDriverConfig * config)
    91                        : port                (0)
    92                        , config              (config)
    93                        , oldConfig           (0)
    94                        , myNumRows           (0)
    95                        , myDrawMem           (0)
    96                        , myVFDMem            (0)
    97                        , myUseSleepInit      (false)
    98                        , myPortDelayNS       (0)
    99                        , myDelay125NS        (0)
   100                        , myRefreshCounter    (0)
   101                        , myClaimCounter      (0)
   102                        , myDataPendingCounter(0)
   103                        , myLogFlags          (0)
   104 {
   105     oldConfig = new cDriverConfig(*config);
   106 } // cDriverGU126X64D_K610A4::cDriverGU126X64D_K610A4()
   107 
   108 //-----------------------------------------------------------------------------
   109 cDriverGU126X64D_K610A4::~cDriverGU126X64D_K610A4()
   110 {
   111     delete oldConfig;
   112 } // cDriverGU126X64D_K610A4::cDriverGU126X64D_K610A4()
   113 
   114 //-----------------------------------------------------------------------------
   115 int cDriverGU126X64D_K610A4::Init()
   116 {
   117     width = config->width;
   118     if (width <= 0 || width > 256)   // don't allow unreasonable big sizes from config
   119     {
   120         width = 126;
   121     } // if
   122 
   123     height = config->height;
   124     if (height <= 0 || height > 256) // don't allow unreasonable big sizes from config
   125     {
   126         height = 64;
   127     } // if
   128 
   129     //----- parse config -----
   130     for (unsigned int i = 0; i < config->options.size(); i++)
   131     {
   132         if (config->options[i].name == "Debug")
   133         {
   134             myLogFlags = atoi(config->options[i].value.c_str());
   135         } // if
   136     } // for
   137 
   138     myNumRows = ((height + 7) / 8);
   139     port      = new cParallelPort();
   140 
   141     // setup drawing memory
   142     myDrawMem = new unsigned char *[width];
   143     for (int x = 0; x < width; x++)
   144     {
   145         myDrawMem[x] = new unsigned char[myNumRows];
   146         memset(myDrawMem[x], 0, myNumRows);
   147     } // for
   148 
   149     // setup vfd memory
   150     myVFDMem = new unsigned char *[width];
   151     for (int x = 0; x < width; x++)
   152     {
   153         myVFDMem[x] = new unsigned char[myNumRows];
   154         memset(myVFDMem[x], 0, myNumRows);
   155     } // for
   156 
   157     if (initParallelPort() < 0)
   158     {
   159         return -1;
   160     } // if
   161 
   162     initDisplay();
   163 
   164     *oldConfig = *config;
   165 
   166     // Set Display SetBrightness
   167     SetBrightness(config->brightness);
   168 
   169     // clear display
   170     Clear();
   171     clearVFDMem();
   172 
   173     syslog( LOG_INFO, "%s: initialized (width: %d  height: %d)"
   174           , config->name.c_str(), width, height
   175           );
   176 
   177     return 0;
   178 } // cDriverGU126X64D_K610A4::Init()
   179 
   180 //-----------------------------------------------------------------------------
   181 int cDriverGU126X64D_K610A4::DeInit()
   182 {
   183     if (myVFDMem)
   184     {
   185         for (int x = 0; x < width; x++)
   186         {
   187             delete[] myVFDMem[x];
   188         } // for
   189         delete[] myVFDMem;
   190         myVFDMem = 0;
   191     } // if
   192 
   193     if (myDrawMem)
   194     {
   195         for(int x = 0; x < width; x++)
   196         {
   197             delete[] myDrawMem[x];
   198         } // for
   199         delete[] myDrawMem;
   200         myDrawMem = 0;
   201     } // if
   202 
   203     if (port)
   204     {
   205         // claim port to avoid msg when closing the port
   206         port->Claim();
   207         if (port->Close() != 0)
   208         {
   209             return -1;
   210         } // if
   211         delete port;
   212         port = 0;
   213     } // if
   214 
   215     return 0;
   216 } // cDriverGU126X64D_K610A4::DeInit()
   217 
   218 //-----------------------------------------------------------------------------
   219 int cDriverGU126X64D_K610A4::checkSetup()
   220 {
   221     if ( config->device != oldConfig->device
   222       || config->port   != oldConfig->port
   223       || config->width  != oldConfig->width
   224       || config->height != oldConfig->height
   225        )
   226     {
   227         DeInit();
   228         Init();
   229         return 0;
   230     } // if
   231 
   232     if (config->brightness != oldConfig->brightness)
   233     {
   234         oldConfig->brightness = config->brightness;
   235         SetBrightness(config->brightness);
   236     } // if
   237 
   238     if ( config->upsideDown != oldConfig->upsideDown 
   239       || config->invert     != oldConfig->invert
   240        )
   241     {
   242         oldConfig->upsideDown = config->upsideDown;
   243         oldConfig->invert     = config->invert;
   244 
   245         return 1;
   246     } // if
   247 
   248     return 0;
   249 } // cDriverGU126X64D_K610A4::checkSetup()
   250 
   251 //-----------------------------------------------------------------------------
   252 int cDriverGU126X64D_K610A4::initParallelPort()
   253 {
   254     struct timeval tv1, tv2;
   255 
   256     if (config->device == "")
   257     {
   258         // use DirectIO
   259         if (port->Open(config->port) != 0)
   260         {
   261             syslog(LOG_ERR, "%s: unable to initialize gu256x64-3900!", config->name.c_str());
   262             return -1;
   263         } // if
   264         syslog(LOG_INFO, "%s: using direct IO!", config->name.c_str());
   265         uSleep(10);
   266     }
   267     else
   268     {
   269         // use ppdev
   270         if (port->Open(config->device.c_str()) != 0)
   271         {
   272             syslog(LOG_ERR, "%s: unable to initialize gu256x64-3900!", config->name.c_str());
   273             return -1;
   274         } // if
   275         syslog(LOG_INFO, "%s: using ppdev!", config->name.c_str());
   276     } // if
   277 
   278     if (nSleepInit() != 0)
   279     {
   280         syslog(LOG_ERR, "%s: INFO: cannot change wait parameters  Err: %s (cDriver::Init)", config->name.c_str(), strerror(errno));
   281         myUseSleepInit = false;
   282     }
   283     else
   284     {
   285         myUseSleepInit = true;
   286     } // if
   287 
   288     //----- measure the time to write to the port -----
   289     syslog(LOG_DEBUG, "%s: benchmark started.", config->name.c_str());
   290     gettimeofday(&tv1, 0);
   291 
   292     const int aBenchCount = 1000; // don't change this!
   293     for (int x = 0; x < aBenchCount; x++)
   294     {
   295         port->WriteData(x % 0x100);
   296     } // for
   297 
   298     gettimeofday(&tv2, 0);
   299 
   300     // release the port, which was implicitely claimed by open
   301     port->Release();
   302 
   303     if (myUseSleepInit) nSleepDeInit();
   304 
   305     myPortDelayNS  = (tv2.tv_sec - tv1.tv_sec) * 1000000 + (tv2.tv_usec - tv1.tv_usec);
   306 
   307     myDelay125NS = std::max(125 + (ADJUST_FACTOR * config->adjustTiming) - myPortDelayNS, 0L);
   308 
   309     syslog( LOG_DEBUG, "%s: benchmark stopped. Time for Port Command: %ldns, delay: %ldns"
   310           , config->name.c_str(), myPortDelayNS, myDelay125NS
   311           );
   312 
   313     return 0;
   314 } // cDriverGU126X64D_K610A4::initParallelPort()
   315 
   316 //-----------------------------------------------------------------------------
   317 void cDriverGU126X64D_K610A4::initDisplay()
   318 {
   319     claimPort();
   320     cmdReset();
   321     releasePort();
   322 } // cDriverGU126X64D_K610A4::initDisplay()
   323 
   324 //-----------------------------------------------------------------------------
   325 void cDriverGU126X64D_K610A4::clearVFDMem()
   326 {
   327     for (int x = 0; x < width; x++)
   328     {
   329         memset(myVFDMem[x], 0, myNumRows);
   330     } // for
   331 } // cDriverGU126X64D_K610A4::clearVFDMem()
   332 
   333 //-----------------------------------------------------------------------------
   334 void cDriverGU126X64D_K610A4::Clear()
   335 {
   336     for (int x = 0; x < width; x++)
   337     {
   338         memset(myDrawMem[x], 0, myNumRows);
   339     } // for
   340 } // cDriverGU126X64D_K610A4::Clear()
   341 
   342 //-----------------------------------------------------------------------------
   343 void cDriverGU126X64D_K610A4::SetBrightness(unsigned int percent)
   344 {
   345     claimPort();
   346     cmdSetBrightness(percent);
   347     releasePort();
   348 } // cDriverGU126X64D_K610A4::SetBrightness()
   349 
   350 //-----------------------------------------------------------------------------
   351 bool cDriverGU126X64D_K610A4::waitForStatus(unsigned char theMask, unsigned char theValue, int theMaxWait)
   352 {
   353     theValue = theValue & theMask;
   354 
   355     int status = port->ReadStatus();
   356 
   357     if ((status & theMask) != theValue)
   358     {
   359         // wait some time for MB go HI/LO but not forever
   360         int i = 0;
   361         for(i = 0; ((status & theMask) != theValue) && i < theMaxWait; i++)
   362         {
   363             status = port->ReadStatus();
   364         } // for
   365 
   366         if (isLogEnabled(LL_MAX_WAIT) && i >= theMaxWait)
   367         {
   368             syslog( LOG_INFO, "%s: slept for %5d times while waiting for MB = %d"
   369                   , config->name.c_str(), i, ((theMask & theValue) == 0 ? 0 : 1)
   370                   );
   371         } // 
   372     } // if
   373 
   374     return ((status & theMask) == theValue);
   375 } // cDriverGU126X64D_K610A4::waitForStatus()
   376 
   377 //-----------------------------------------------------------------------------
   378 void cDriverGU126X64D_K610A4::writeParallel(unsigned char data)
   379 {
   380     if (myUseSleepInit) nSleepInit();
   381 
   382     waitForStatus(IN_MB_MASK, IN_MB_LO, 500);                  // wait for MB == LO
   383 
   384     port->WriteData(data);                                     // write data
   385     nSleep(myDelay125NS);                                      // - sleep
   386 
   387     port->WriteControl(OUT_EN_LO & OUT_EN_MASK);               // set ENABLE to LO
   388     nSleep(myDelay125NS);                                      // - sleep
   389 
   390     port->WriteControl(OUT_EN_HI & OUT_EN_MASK);               // set ENABLE to HI
   391 
   392     waitForStatus(IN_MB_MASK, IN_MB_HI, 50);                   // wait for MB == HI
   393 
   394 //  the other drivers don't do this neither
   395 //  if (myUseSleepInit) nSleepDeInit();
   396 } // cDriverGU126X64D_K610A4::writeParallel()
   397 
   398 //-----------------------------------------------------------------------------
   399 int cDriverGU126X64D_K610A4::write(unsigned char data)
   400 {
   401     int b = 0;
   402 
   403     writeParallel(data);
   404     ++b; 
   405 
   406     // if data == 0x60 -> send 0x60 twice 
   407     // (0x60 switches to hex-mode)
   408     if (data == 0x60)
   409     {
   410         writeParallel(data);
   411         ++b;
   412     } // if
   413 
   414     return b;
   415 } // cDriverGU126X64D_K610A4::write()
   416 
   417 //-----------------------------------------------------------------------------
   418 void cDriverGU126X64D_K610A4::setPixel(int x, int y)
   419 {
   420     if (!myDrawMem          ) return;
   421     if (x >= width  || x < 0) return;
   422     if (y >= height || y < 0) return;
   423 
   424     if (config->upsideDown)
   425     {
   426         x = width  - 1 - x;
   427         y = height - 1 - y;
   428     } // if
   429 
   430     unsigned char c = 0x80 >> (y % 8);
   431 
   432     myDrawMem[x][y/8] = myDrawMem[x][y/8] | c;
   433 } // cDriverGU126X64D_K610A4::setPixel()
   434 
   435 //-----------------------------------------------------------------------------
   436 void cDriverGU126X64D_K610A4::Set8Pixels(int x, int y, unsigned char data)
   437 {
   438     // x - pos isn't maybe align to 8
   439     x &= 0xFFF8;
   440 
   441     for (int n = 0; n < 8; ++n)
   442     {
   443         if ((data & (0x80 >> n)) != 0) // if bit is set
   444         {
   445             setPixel(x + n, y);
   446         } // if
   447     } // for
   448 } // cDriverGU126X64D_K610A4::Set8Pixels()
   449 
   450 //-----------------------------------------------------------------------------
   451 void cDriverGU126X64D_K610A4::Refresh(bool refreshAll)
   452 {
   453     // no mem present -> return
   454     if (!myVFDMem || !myDrawMem)
   455     {
   456         return;
   457     } // if
   458 
   459     // create log
   460     if (isLogEnabled(LL_REFRESH_START))
   461     {
   462         syslog( LOG_INFO, "%s: > Refresh()  all = %d  RefreshDisplay = %d  RefreshCtr  = %d  Delay = %ld"
   463               , config->name.c_str()
   464               , refreshAll
   465               , config->refreshDisplay
   466               , myRefreshCounter
   467               , myDelay125NS
   468               );
   469     } // if
   470 
   471     // setup changed -> refresh all
   472     if (checkSetup() > 0)
   473     {
   474         syslog(LOG_DEBUG, "%s:   Refresh() checkSetup() returned != 0 -> refreshAll = true", config->name.c_str());
   475         refreshAll = true;
   476     } // if
   477 
   478     // refresh-counter exceeded -> refresh all
   479     if (!refreshAll && config->refreshDisplay != 0)
   480     {
   481         myRefreshCounter = (myRefreshCounter + 1) % config->refreshDisplay;
   482         refreshAll = myRefreshCounter == 0;
   483 
   484         if (refreshAll && isLogEnabled(LL_REFRESH_START))
   485         {
   486           syslog(LOG_DEBUG, "%s:   Refresh() refresh-count reached -> refreshAll = true", config->name.c_str());
   487         } // if
   488     } // if
   489 
   490     if (isLogEnabled(LL_REFRESH_START))
   491     {
   492         syslog( LOG_INFO, "%s:   Refresh()  all = %d  RefreshDisplay = %d  RefreshCtr  = %d  Delay = %ld"
   493               , config->name.c_str()
   494               , refreshAll
   495               , config->refreshDisplay
   496               , myRefreshCounter
   497               , myDelay125NS
   498               );
   499     } // if
   500 
   501     // time for logs
   502     struct timeval tv1, tv2;
   503     gettimeofday(&tv1, 0);
   504 
   505     claimPort();
   506 
   507     int  chunk = 128; // displays with more than 128 pixels width are written in chunks
   508                       // note: this driver isn't really prepared to handle displays
   509                       //       with other dimensions than 126x64
   510     int  xb    = 0;
   511     int  yb    = 0;
   512     long bc    = 0;
   513 
   514     for (yb = 0; yb < myNumRows; ++yb)
   515     {
   516         int  minX  = width;
   517         int  maxX  = 0;
   518 
   519         //----- if !refreshAll -> check modified bytes
   520         if (!refreshAll)
   521         {
   522             for (xb = 0; xb < width; ++xb)
   523             {
   524                 if (myVFDMem[xb][yb] != myDrawMem[xb][yb])
   525                 {
   526                     minX = std::min(minX, xb);
   527                     maxX = std::max(maxX, xb);
   528                 } // if
   529             } // for
   530         }
   531         else
   532         {
   533             minX = 0;
   534             maxX = width - 1;
   535         } // if
   536 
   537         // create log
   538         if (isLogEnabled(LL_REFRESH_MED))
   539         {
   540             if (minX <= maxX)
   541             {
   542                 syslog( LOG_INFO, "%s: Row[%d] %3d - %3d : %3d"
   543                       , config->name.c_str(), yb
   544                       , minX, maxX
   545                       , maxX - minX + 1
   546                       );
   547             }
   548             else
   549             {
   550                 syslog( LOG_INFO, "%s: Row[%d] --- - --- : ---"
   551                       , config->name.c_str(), yb
   552                       );
   553             } // if
   554         } // if
   555 
   556         // perform refresh
   557         if (minX <= maxX)
   558         {
   559             bc += cmdSetCursorPos(minX, yb * 8);
   560 
   561             for (xb = minX; xb <= maxX; ++xb)
   562             {
   563                 if ((xb - minX) % chunk == 0)
   564                 {
   565                     bc += cmdGraphicWrite(std::min((maxX - xb + 1), chunk));
   566                 } // if
   567 
   568                 bc += cmdGraphicData(myDrawMem[xb][yb]);
   569                 myVFDMem[xb][yb] = myDrawMem[xb][yb];
   570             } // for
   571         } // if
   572     } // for
   573 
   574     releasePort();
   575 
   576     // create log
   577     if (isLogEnabled(LL_REFRESH_END))
   578     {
   579         gettimeofday(&tv2, 0);
   580 
   581         long duration_ms = ((tv2.tv_sec - tv1.tv_sec) * 1000000 + (tv2.tv_usec - tv1.tv_usec)) 
   582                         / 1000 /* us -> ms */
   583                         ;
   584 
   585         syslog( LOG_INFO, "%s: < Refresh()  all = %d  took %3ld ms  %5ld bytes = %5ld bytes/sec = %5ld ns/byte"
   586               , config->name.c_str()
   587               , refreshAll
   588               , duration_ms
   589               , bc
   590               , duration_ms == 0 ? -1 : bc * 1000 / duration_ms
   591               , bc          == 0 ? -1 : duration_ms * 1000000 / bc
   592               );
   593     } // if
   594 } // cDriverGU126X64D_K610A4::Refresh()
   595 
   596 //-----------------------------------------------------------------------------
   597 int cDriverGU126X64D_K610A4::cmdReset()
   598 {
   599     ensureNotInGraphics();
   600     int b = 0;
   601 
   602     if (isLogEnabled(LL_VFD_CMD))
   603     {
   604         syslog(LOG_INFO, "- 1B: CMD_RESET        : 0x%02X ", int(CMD_RESET));
   605     } // if
   606 
   607     b += write(CMD_RESET);
   608 
   609     unsigned char aMode = 1 << 7  // data orientation : 0: horizontal, 1: vertical , default: 0
   610                         | 0 << 6  // cursor movement  : 0: horizontal, 1: vertical , default: 0
   611                         | 0 << 5  // cursor direction : 0: forwards  , 1: backwards, default: 0
   612                         | 0 << 4  // underscore cursor: 0: off       , 1: on       , default: 0
   613                         | 0 << 3  // underscore cursor: 0: static    , 1: flash    , default: 0
   614                         | 0 << 2  // not in documentation
   615                         | 0 << 0  // pen type: 0: overwrite, 1: AND, 2: OR, 3: XOR , default: 0
   616                         ;
   617 
   618     if (isLogEnabled(LL_VFD_CMD))
   619     {
   620         syslog(LOG_INFO, "- 2B: CMD_WRITE_MODE   : 0x%02X 0x%02X", int(CMD_RESET), int(aMode));
   621     } // if
   622 
   623     b += write(CMD_WRITE_MODE);
   624     b += write(aMode);
   625 
   626     return b;
   627 } // cDriverGU126X64D_K610A4::cmdReset()
   628 
   629 //-----------------------------------------------------------------------------
   630 int cDriverGU126X64D_K610A4::cmdPower(bool fOn)
   631 {
   632     ensureNotInGraphics();
   633     int b = 0;
   634 
   635     if (isLogEnabled(LL_VFD_CMD))
   636     {
   637         syslog( LOG_INFO, "- 2B: CMD_POWER        : 0x%02X 0x%02X"
   638               , int(CMD_INTRO), int(fOn ? CMA_POWER_ON : CMA_POWER_OFF)
   639               );
   640     } // if
   641 
   642     b += write(CMD_INTRO);
   643     b += write(fOn ? CMA_POWER_ON : CMA_POWER_OFF);
   644 
   645     return b;
   646 } // cDriverGU126X64D_K610A4::cmdPower()
   647 
   648 //-----------------------------------------------------------------------------
   649 int cDriverGU126X64D_K610A4::cmdLock(bool fLock)
   650 {
   651     ensureNotInGraphics();
   652     int b = 0;
   653 
   654     if (isLogEnabled(LL_VFD_CMD))
   655     {
   656         syslog( LOG_INFO, "- 2B: CMD_LOCK         : 0x%02X 0x%02X"
   657               , int(CMD_INTRO), int(fLock ? CMA_EPROM_LOCK : CMA_EPROM_UNLOCK)
   658               );
   659     } // if
   660 
   661     b += write(CMD_INTRO);
   662     b += write(fLock ? CMA_EPROM_LOCK : CMA_EPROM_UNLOCK);
   663 
   664     return b;
   665 } // cDriverGU126X64D_K610A4::cmdPower()
   666 
   667 //-----------------------------------------------------------------------------
   668 int cDriverGU126X64D_K610A4::cmdSetCursorPos(unsigned char x, unsigned char y)
   669 {
   670     ensureNotInGraphics();
   671     int b = 0;
   672 
   673     if (isLogEnabled(LL_VFD_CMD))
   674     {
   675         syslog( LOG_INFO, "- 3B: CMD_CURSOR_POS   : 0x%02X 0x%02X 0x%02X  (x = %3d, y = %3d)"
   676               , int(CMD_CURSOR_POS), int(x), int(y), int(x), int(y)
   677               );
   678     } // if
   679 
   680     b += write(CMD_CURSOR_POS); // cmd
   681     b += write(x             ); // xpos
   682     b += write(y             ); // ypos
   683 
   684     return b;
   685 } // cDriverGU126X64D_K610A4::cmdSetCursorPos();
   686 
   687 //-----------------------------------------------------------------------------
   688 int cDriverGU126X64D_K610A4::cmdGraphicWrite(unsigned char count)
   689 {
   690     ensureNotInGraphics();
   691     int b = 0;
   692 
   693     if (isLogEnabled(LL_VFD_CMD))
   694     {
   695         syslog( LOG_INFO, "- 2B: CMD_GRAPHIC_WRITE: 0x%02X 0x%02X (%d bytes)"
   696               , int(CMD_GRAPHIC_WRITE), int(count), int(count)
   697               );
   698     } // if
   699 
   700     b += write(CMD_GRAPHIC_WRITE); // cmd
   701     b += write(count            ); // len
   702 
   703     myDataPendingCounter = count;
   704 
   705     return b;
   706 } // cDriverGU126X64D_K610A4::cmdGraphicWrite()
   707 
   708 //-----------------------------------------------------------------------------
   709 int cDriverGU126X64D_K610A4::cmdGraphicData(unsigned char data)
   710 {
   711     int b = 0;
   712 
   713     myDataPendingCounter--;
   714     if (myDataPendingCounter < 0)
   715     {
   716         syslog( LOG_WARNING, "%s error: more graphic data written than announced -> ignored"
   717               , config->name.c_str()
   718               );
   719     }
   720     else
   721     {
   722         if (isLogEnabled(LL_VFD_CMD))
   723         {
   724             syslog( LOG_INFO, "- 1B: CMD_GRAPHIC_DATA : 0x%02X  (expecting another %d bytes)"
   725                   , int(data), myDataPendingCounter
   726                   );
   727         } // if
   728 
   729         b += write(data ^ (config->invert ? 0xFF : 0x00));
   730     } // if
   731 
   732     return b;
   733 } // cDriverGU126X64D_K610A4::cmdGraphicData()
   734 
   735 //-----------------------------------------------------------------------------
   736 int cDriverGU126X64D_K610A4::cmdSetBrightness(unsigned int percent)
   737 {
   738     ensureNotInGraphics();
   739     int b = 0;
   740 
   741     unsigned char bright = 0;
   742     if      (percent >= 85) bright = 0xFF;
   743     else if (percent >= 71) bright = 0xFE;
   744     else if (percent >= 57) bright = 0xFD;
   745     else if (percent >= 43) bright = 0xFC;
   746     else if (percent >= 29) bright = 0xFB;
   747     else if (percent >= 15) bright = 0xFA;
   748     else if (percent >=  1) bright = 0xF9;
   749     else                    bright = 0xF8;
   750 
   751     if (isLogEnabled(LL_VFD_CMD))
   752     {
   753         syslog( LOG_INFO, "- 2B: CMD_INTRO        : 0x%02X 0x%02X = set brightness"
   754               , int(CMD_INTRO), int(bright)
   755               );
   756     } // if
   757 
   758     b += write(CMD_INTRO);
   759     b += write(bright);
   760 
   761     return b;
   762 } // cDriverGU126X64D_K610A4::cmdSetBrightness()
   763 
   764 //-----------------------------------------------------------------------------
   765 int cDriverGU126X64D_K610A4::cmdSetFont(FontType theFont)
   766 {
   767     ensureNotInGraphics();
   768     int b = 0;
   769 
   770     unsigned char aCmd = 0;
   771     switch (theFont)
   772     {
   773         case FONT_PROP_SML: aCmd = CMD_FONT_PROP_SML; break;
   774         case FONT_FIX_BIG : aCmd = CMD_FONT_FIX_BIG ; break;
   775         case FONT_FIX_MED : 
   776         default           : aCmd = CMD_FONT_FIX_MED ; break;
   777     } // switch
   778 
   779     if (isLogEnabled(LL_VFD_CMD))
   780     {
   781         syslog(LOG_INFO, "- 1B: CMD_SET_FONT     : 0x%02X", int(aCmd));
   782     } // if
   783 
   784     b += write(aCmd);
   785 
   786     return b;
   787 } // cDriverGU126X64D_K610A4::cmdSetFont()
   788 
   789 //-----------------------------------------------------------------------------
   790 int cDriverGU126X64D_K610A4::cmdWriteText(const char *theText)
   791 {
   792     ensureNotInGraphics();
   793     int b = 0;
   794 
   795     if (isLogEnabled(LL_VFD_CMD))
   796     {
   797         syslog(LOG_INFO, "-%2dB: WRITE_TEXT       : '%s'", strlen(theText), theText);
   798     } // if
   799 
   800     for (const char *p = theText; *p != '\0'; ++p)
   801     {
   802         b += write(*p);
   803     } // for
   804 
   805     return b;
   806 } // cDriverGU126X64D_K610A4::cmdWriteText()
   807 
   808 //-----------------------------------------------------------------------------
   809 int cDriverGU126X64D_K610A4::cmdDrawRect(unsigned char x1, unsigned char y1, unsigned char x2, unsigned char y2)
   810 {
   811     ensureNotInGraphics();
   812     int b = 0;
   813 
   814     if (isLogEnabled(LL_VFD_CMD))
   815     {
   816         syslog( LOG_INFO, "- 5B: CMD_SET_OUTLINE  : 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X  (x1 = %3d, y1 = %3d, x2 = %3d, y2 = %3d)"
   817               , int(CMD_CURSOR_POS)
   818               , int(x1), int(y1), int(x2), int(y2)
   819               , int(x1), int(y1), int(x2), int(y2)
   820               );
   821     } // if
   822 
   823     b += write(CMD_RECT_SET  );
   824     b += write(x1            );
   825     b += write(y1            );
   826     b += write(x2            );
   827     b += write(y2            );
   828 
   829     return b;
   830 } // cDriverGU126X64D_K610A4::cmdDrawRect()
   831 
   832 //-----------------------------------------------------------------------------
   833 int cDriverGU126X64D_K610A4::cmdSetMacro(unsigned char theMacroNum, unsigned char theCountBytes)
   834 {
   835     if (theMacroNum > 7)
   836     {
   837         return 0;
   838     } // if
   839 
   840     ensureNotInGraphics();
   841     int b = 0;
   842 
   843     if (isLogEnabled(LL_VFD_CMD))
   844     {
   845         syslog( LOG_INFO, "- 3B: CMD_INTRO        : 0x%02X 0x%02X 0x%02X (define macro %d with length %d)"
   846               , int(CMD_INTRO)
   847               , int(theMacroNum), int(theCountBytes)
   848               , int(theMacroNum), int(theCountBytes)
   849               );
   850     } // if
   851 
   852     b += write(CMD_INTRO     );
   853     b += write(theMacroNum   );
   854     b += write(theCountBytes );
   855 
   856     return b;
   857 } // cDriverGU126X64D_K610A4::cmdSetMacro()
   858 
   859 //-----------------------------------------------------------------------------
   860 int cDriverGU126X64D_K610A4::cmdSetPixel(bool fSet)
   861 {
   862     int b = 0;
   863 
   864     if (fSet)
   865     {
   866         ensureNotInGraphics();
   867 
   868         if (isLogEnabled(LL_VFD_CMD))
   869         {
   870             syslog(LOG_INFO, "- 1B: SET_PIXEL        : 0x%02X", 0x16);
   871         } // if
   872 
   873         b += write(CMD_PIXEL_SET);
   874     }
   875     else
   876     {
   877         b = cmdClrPixel();
   878     } // if
   879 
   880     return b;
   881 } // cDriverGU126X64D_K610A4::cmdSetPixel()
   882 
   883 //-----------------------------------------------------------------------------
   884 int cDriverGU126X64D_K610A4::cmdClrPixel()
   885 {
   886     ensureNotInGraphics();
   887     int b = 0;
   888 
   889     if (isLogEnabled(LL_VFD_CMD))
   890     {
   891         syslog(LOG_INFO, "- 1B: CLR_PIXEL        : 0x%02X", 0x17);
   892     } // if
   893 
   894     b += write(CMD_PIXEL_CLEAR);
   895 
   896     return b;
   897 } // cDriverGU126X64D_K610A4::cmdClrPixel()
   898 
   899 //-----------------------------------------------------------------------------
   900 void cDriverGU126X64D_K610A4::ensureNotInGraphics()
   901 {
   902     if (myClaimCounter <= 0)
   903     {
   904         syslog(LOG_ERR, "%s: ERROR: port not claimed (%d)", config->name.c_str(), myClaimCounter);
   905     } // if
   906 
   907     if (myDataPendingCounter > 0)
   908     {
   909         syslog( LOG_WARNING, "%s error: expected another %d bytes graphic data, filling with 0x00"
   910               , config->name.c_str(), myDataPendingCounter
   911               );
   912     } // if
   913     while (myDataPendingCounter > 0)
   914     {
   915         cmdGraphicData(0);
   916     } // while
   917 } // cDriverGU126X64D_K610A4::ensureNotInGraphics()
   918 
   919 
   920 //-----------------------------------------------------------------------------
   921 void cDriverGU126X64D_K610A4::claimPort()
   922 {
   923     if (myClaimCounter == 0) 
   924     {
   925         port->Claim();
   926     } // if
   927 
   928     myClaimCounter++;
   929 
   930     if (myClaimCounter > 1)
   931     {
   932         syslog( LOG_WARNING, "%s: port claimed more than once (%d)"
   933               , config->name.c_str(), myClaimCounter
   934               );
   935     } // if
   936 
   937 } // cDriverGU126X64D_K610A4::claimPort()
   938 
   939 //-----------------------------------------------------------------------------
   940 void cDriverGU126X64D_K610A4::releasePort()
   941 {
   942     if (myClaimCounter == 1) 
   943     {
   944         port->Release();
   945     } // if
   946 
   947     myClaimCounter--;
   948 
   949     if (myClaimCounter < 0)
   950     {
   951         syslog( LOG_WARNING, "%s: port released more often than claimed"
   952               , config->name.c_str()
   953               );
   954         myClaimCounter = 0;
   955     } // if
   956 
   957 } // cDriverGU126X64D_K610A4::releasePort()
   958 
   959 //-----------------------------------------------------------------------------
   960 bool cDriverGU126X64D_K610A4::isLogEnabled(int theLevel) const
   961 {
   962     return (theLevel & myLogFlags) != 0;
   963 } // cDriverGU126X64D_K610A4::isLogEnabled()
   964 
   965 //-----------------------------------------------------------------------------
   966 } // end of namespace
   967