graphlcd-base/glcddrivers/t6963c.c
changeset 4 df6a40031aa5
equal deleted inserted replaced
3:d0e62fc47285 4:df6a40031aa5
       
     1 /*
       
     2  * GraphLCD driver library
       
     3  *
       
     4  * t6963c.c  -  T6963C driver class
       
     5  *
       
     6  * low level routines based on lcdproc 0.5 driver, (c) 2001 Manuel Stahl
       
     7  *
       
     8  * This file is released under the GNU General Public License. Refer
       
     9  * to the COPYING file distributed with this package.
       
    10  *
       
    11  * (c) 2003, 2004 Andreas Regel <andreas.regel AT powarman.de>
       
    12  */
       
    13 
       
    14 #include <syslog.h>
       
    15 
       
    16 #include "common.h"
       
    17 #include "config.h"
       
    18 #include "port.h"
       
    19 #include "t6963c.h"
       
    20 
       
    21 
       
    22 namespace GLCD
       
    23 {
       
    24 
       
    25 // T6963 commands
       
    26 const unsigned char kSetCursorPointer  = 0x21;
       
    27 const unsigned char kSetOffsetRegister = 0x22;
       
    28 const unsigned char kSetAddressPointer = 0x24;
       
    29 
       
    30 const unsigned char kSetTextHomeAddress    = 0x40;
       
    31 const unsigned char kSetTextArea           = 0x41;
       
    32 const unsigned char kSetGraphicHomeAddress = 0x42;
       
    33 const unsigned char kSetGraphicArea        = 0x43;
       
    34 
       
    35 const unsigned char kSetMode          = 0x80;
       
    36 const unsigned char kSetDisplayMode   = 0x90;
       
    37 const unsigned char kSetCursorPattern = 0xA0;
       
    38 
       
    39 const unsigned char kDataWriteInc = 0xC0;
       
    40 const unsigned char kDataReadInc  = 0xC1;
       
    41 const unsigned char kDataWriteDec = 0xC2;
       
    42 const unsigned char kDataReadDec  = 0xC3;
       
    43 const unsigned char kDataWrite    = 0xC4;
       
    44 const unsigned char kDataRead     = 0xC5;
       
    45 
       
    46 const unsigned char kAutoWrite = 0xB0;
       
    47 const unsigned char kAutoRead  = 0xB1;
       
    48 const unsigned char kAutoReset = 0xB2;
       
    49 
       
    50 
       
    51 // T6963 Parameters
       
    52 const unsigned char kModeOr            = 0x00;
       
    53 const unsigned char kModeXor           = 0x01;
       
    54 const unsigned char kModeAnd           = 0x03;
       
    55 const unsigned char kModeTextAttribute = 0x04;
       
    56 const unsigned char kModeInternalCG    = 0x00;
       
    57 const unsigned char kModeExternalCG    = 0x08;
       
    58 
       
    59 const unsigned char kTextAttributeNormal    = 0x00;
       
    60 const unsigned char kTextAttributeInverse   = 0x05;
       
    61 const unsigned char kTextAttributeNoDisplay = 0x03;
       
    62 const unsigned char kTextAttributeBlink     = 0x08;
       
    63 
       
    64 const unsigned char kDisplayModeBlink   = 0x01;
       
    65 const unsigned char kDisplayModeCursor  = 0x02;
       
    66 const unsigned char kDisplayModeText    = 0x04;
       
    67 const unsigned char kDisplayModeGraphic = 0x08;
       
    68 
       
    69 const unsigned short kGraphicBase = 0x0000;
       
    70 const unsigned short kTextBase    = 0x1500;
       
    71 const unsigned short kCGRAMBase   = 0x1800;
       
    72 
       
    73 
       
    74 // T6963 Wirings
       
    75 static const std::string kWiringStandard = "Standard";
       
    76 static const std::string kWiringWindows  = "Windows";
       
    77 static const std::string kWiringSerial   = "Serial";
       
    78 
       
    79 const unsigned char kStandardWRHI = 0x00; // 01 / nSTRB
       
    80 const unsigned char kStandardWRLO = 0x01; //
       
    81 const unsigned char kStandardRDHI = 0x00; // 17 / nSELECT
       
    82 const unsigned char kStandardRDLO = 0x08; //
       
    83 const unsigned char kStandardCEHI = 0x00; // 14 / nLINEFEED
       
    84 const unsigned char kStandardCELO = 0x02; //
       
    85 const unsigned char kStandardCDHI = 0x04; // 16 / INIT
       
    86 const unsigned char kStandardCDLO = 0x00; //
       
    87 
       
    88 const unsigned char kWindowsWRHI = 0x04; // 16 / INIT
       
    89 const unsigned char kWindowsWRLO = 0x00; //
       
    90 const unsigned char kWindowsRDHI = 0x00; // 14 / nLINEFEED
       
    91 const unsigned char kWindowsRDLO = 0x02; //
       
    92 const unsigned char kWindowsCEHI = 0x00; // 01 / nSTRB
       
    93 const unsigned char kWindowsCELO = 0x01; //
       
    94 const unsigned char kWindowsCDHI = 0x00; // 17 / nSELECT
       
    95 const unsigned char kWindowsCDLO = 0x08; //
       
    96 
       
    97 const unsigned char kSerialWRHI = 0x01; // 01 / nSTRB
       
    98 const unsigned char kSerialWRLO = 0x00; //
       
    99 const unsigned char kSerialRDHI = 0x08; // 17 / nSELECT
       
   100 const unsigned char kSerialRDLO = 0x00; //
       
   101 const unsigned char kSerialCEHI = 0x02; // 14 / nLINEFEED
       
   102 const unsigned char kSerialCELO = 0x00; //
       
   103 const unsigned char kSerialCDHI = 0x00; // 16 / INIT
       
   104 const unsigned char kSerialCDLO = 0x04; //
       
   105 
       
   106 
       
   107 cDriverT6963C::cDriverT6963C(cDriverConfig * config)
       
   108 :   config(config)
       
   109 {
       
   110     oldConfig = new cDriverConfig(*config);
       
   111 
       
   112     port = new cParallelPort();
       
   113 
       
   114     //width = config->width;
       
   115     //height = config->height;
       
   116     refreshCounter = 0;
       
   117     displayMode = 0;
       
   118     bidirectLPT = 1;
       
   119     autoWrite = false;
       
   120     serial = 0;
       
   121 }
       
   122 
       
   123 cDriverT6963C::~cDriverT6963C()
       
   124 {
       
   125     delete port;
       
   126     delete oldConfig;
       
   127 }
       
   128 
       
   129 int cDriverT6963C::Init()
       
   130 {
       
   131     int x;
       
   132 
       
   133     width = config->width;
       
   134     if (width <= 0)
       
   135         width = 240;
       
   136     height = config->height;
       
   137     if (height <= 0)
       
   138         height = 128;
       
   139 
       
   140     // default values
       
   141     FS = 6;
       
   142     WRHI = kStandardWRHI;
       
   143     WRLO = kStandardWRLO;
       
   144     RDHI = kStandardRDHI;
       
   145     RDLO = kStandardRDLO;
       
   146     CEHI = kStandardCEHI;
       
   147     CELO = kStandardCELO;
       
   148     CDHI = kStandardCDHI;
       
   149     CDLO = kStandardCDLO;
       
   150     useAutoMode = true;
       
   151     useStatusCheck = true;
       
   152 
       
   153     for (unsigned int i = 0; i < config->options.size(); i++)
       
   154     {
       
   155         if (config->options[i].name == "FontSelect")
       
   156         {
       
   157             int fontSelect = atoi(config->options[i].value.c_str());
       
   158             if (fontSelect == 6)
       
   159                 FS = 6;
       
   160             else if (fontSelect == 8)
       
   161                 FS = 8;
       
   162             else
       
   163                 syslog(LOG_ERR, "%s error: font select %d not supported, using default (%d)!\n",
       
   164                        config->name.c_str(), fontSelect, FS);
       
   165         }
       
   166         else if (config->options[i].name == "Wiring")
       
   167         {
       
   168             if (config->options[i].value == kWiringStandard)
       
   169             {
       
   170                 WRHI = kStandardWRHI;
       
   171                 WRLO = kStandardWRLO;
       
   172                 RDHI = kStandardRDHI;
       
   173                 RDLO = kStandardRDLO;
       
   174                 CEHI = kStandardCEHI;
       
   175                 CELO = kStandardCELO;
       
   176                 CDHI = kStandardCDHI;
       
   177                 CDLO = kStandardCDLO;
       
   178             }
       
   179             else if (config->options[i].value == kWiringWindows)
       
   180             {
       
   181                 WRHI = kWindowsWRHI;
       
   182                 WRLO = kWindowsWRLO;
       
   183                 RDHI = kWindowsRDHI;
       
   184                 RDLO = kWindowsRDLO;
       
   185                 CEHI = kWindowsCEHI;
       
   186                 CELO = kWindowsCELO;
       
   187                 CDHI = kWindowsCDHI;
       
   188                 CDLO = kWindowsCDLO;
       
   189             }
       
   190             else if (config->options[i].value == kWiringSerial)
       
   191             {
       
   192                 serial = 1;
       
   193                 WRHI = kSerialWRHI;
       
   194                 WRLO = kSerialWRLO;
       
   195                 RDHI = kSerialRDHI;
       
   196                 RDLO = kSerialRDLO;
       
   197                 CEHI = kSerialCEHI;
       
   198                 CELO = kSerialCELO;
       
   199                 CDHI = kSerialCDHI;
       
   200                 CDLO = kSerialCDLO;
       
   201             }
       
   202             else
       
   203                 syslog(LOG_ERR, "%s error: wiring %s not supported, using default (Standard)!\n",
       
   204                        config->name.c_str(), config->options[i].value.c_str());
       
   205         }
       
   206         else if (config->options[i].name == "AutoMode")
       
   207         {
       
   208             if (config->options[i].value == "yes")
       
   209                 useAutoMode = true;
       
   210             else if (config->options[i].value == "no")
       
   211                 useAutoMode = false;
       
   212             else
       
   213                 syslog(LOG_ERR, "%s error: unknown auto mode setting %s, using default (%s)!\n",
       
   214                        config->name.c_str(), config->options[i].value.c_str(), useAutoMode ? "yes" : "no");
       
   215         }
       
   216         else if (config->options[i].name == "StatusCheck")
       
   217         {
       
   218             if (config->options[i].value == "yes")
       
   219                 useStatusCheck = true;
       
   220             else if (config->options[i].value == "no")
       
   221                 useStatusCheck = false;
       
   222             else
       
   223                 syslog(LOG_ERR, "%s error: unknown status check setting %s, using default (%s)!\n",
       
   224                        config->name.c_str(), config->options[i].value.c_str(), useStatusCheck ? "yes" : "no");
       
   225         }
       
   226     }
       
   227 
       
   228     // setup lcd array (wanted state)
       
   229     newLCD = new unsigned char*[(width + (FS - 1)) / FS];
       
   230     if (newLCD)
       
   231     {
       
   232         for (x = 0; x < (width + (FS - 1)) / FS; x++)
       
   233         {
       
   234             newLCD[x] = new unsigned char[height];
       
   235             memset(newLCD[x], 0, height);
       
   236         }
       
   237     }
       
   238     // setup lcd array (current state)
       
   239     oldLCD = new unsigned char*[(width + (FS - 1)) / FS];
       
   240     if (oldLCD)
       
   241     {
       
   242         for (x = 0; x < (width + (FS - 1)) / FS; x++)
       
   243         {
       
   244             oldLCD[x] = new unsigned char[height];
       
   245             memset(oldLCD[x], 0, height);
       
   246         }
       
   247     }
       
   248 
       
   249     if (config->device == "")
       
   250     {
       
   251         // use DirectIO
       
   252         if (port->Open(config->port) != 0)
       
   253             return -1;
       
   254         uSleep(10);
       
   255     }
       
   256     else
       
   257     {
       
   258         // use ppdev
       
   259         if (port->Open(config->device.c_str()) != 0)
       
   260             return -1;
       
   261     }
       
   262 
       
   263     // disable chip
       
   264     // disable reading from LCD
       
   265     // disable writing to LCD
       
   266     // command/status mode
       
   267     T6963CSetControl(WRHI | CEHI | CDHI | RDHI);
       
   268     port->SetDirection(kForward); // make 8-bit parallel port an output port
       
   269 
       
   270     // Test ECP mode
       
   271     if (bidirectLPT == 1)
       
   272     {
       
   273         syslog(LOG_DEBUG, "%s: Testing ECP mode...\n", config->name.c_str());
       
   274         int i = 0;
       
   275         int ecp_input;
       
   276         port->SetDirection(kReverse);
       
   277         for (int i = 0; i < 100; i++)
       
   278         {
       
   279             T6963CSetControl(WRHI | CEHI | CDHI | RDHI);    // wr, ce, cd, rd
       
   280             T6963CSetControl(WRHI | CELO | CDHI | RDLO);
       
   281             T6963CSetControl(WRHI | CELO | CDHI | RDLO);
       
   282             T6963CSetControl(WRHI | CELO | CDHI | RDLO);
       
   283             ecp_input = port->ReadData();
       
   284             T6963CSetControl(WRHI | CEHI | CDHI | RDHI);
       
   285             if ((ecp_input & 0x03) == 0x03)
       
   286                 break;
       
   287         }
       
   288         port->SetDirection(kForward);
       
   289         if (i >= 100)
       
   290         {
       
   291             syslog(LOG_DEBUG, "%s: ECP mode not working! -> is now disabled\n", config->name.c_str());
       
   292             bidirectLPT = 0;
       
   293         }
       
   294         else
       
   295             syslog(LOG_DEBUG, "%s: working!\n", config->name.c_str());
       
   296     }
       
   297 
       
   298     T6963CCommandWord(kSetGraphicHomeAddress, kGraphicBase);
       
   299     if (width % FS == 0)
       
   300         T6963CCommandWord(kSetGraphicArea, width / FS);
       
   301     else
       
   302         T6963CCommandWord(kSetGraphicArea, width / FS + 1);
       
   303 
       
   304     T6963CCommand(kSetMode | kModeOr | kModeInternalCG);
       
   305 
       
   306     T6963CDisplayMode(kDisplayModeText, false);
       
   307     T6963CDisplayMode(kDisplayModeGraphic, true);
       
   308     T6963CDisplayMode(kDisplayModeCursor, false);
       
   309     T6963CDisplayMode(kDisplayModeBlink, false);
       
   310 
       
   311     port->Release();
       
   312 
       
   313     *oldConfig = *config;
       
   314 
       
   315     // clear display
       
   316     Clear();
       
   317 
       
   318     syslog(LOG_INFO, "%s: T6963 initialized.\n", config->name.c_str());
       
   319     return 0;
       
   320 }
       
   321 
       
   322 int cDriverT6963C::DeInit()
       
   323 {
       
   324     int x;
       
   325     // free lcd array (wanted state)
       
   326     if (newLCD)
       
   327     {
       
   328         for (x = 0; x < (width + (FS - 1)) / FS; x++)
       
   329         {
       
   330             delete[] newLCD[x];
       
   331         }
       
   332         delete[] newLCD;
       
   333     }
       
   334     // free lcd array (current state)
       
   335     if (oldLCD)
       
   336     {
       
   337         for (x = 0; x < (width + (FS - 1)) / FS; x++)
       
   338         {
       
   339             delete[] oldLCD[x];
       
   340         }
       
   341         delete[] oldLCD;
       
   342     }
       
   343 
       
   344     if (port->Close() != 0)
       
   345         return -1;
       
   346     return 0;
       
   347 }
       
   348 
       
   349 int cDriverT6963C::CheckSetup()
       
   350 {
       
   351     if (config->device != oldConfig->device ||
       
   352         config->port != oldConfig->port ||
       
   353         config->width != oldConfig->width ||
       
   354         config->height != oldConfig->height)
       
   355     {
       
   356         DeInit();
       
   357         Init();
       
   358         return 0;
       
   359     }
       
   360 
       
   361     if (config->upsideDown != oldConfig->upsideDown ||
       
   362         config->invert != oldConfig->invert)
       
   363     {
       
   364         oldConfig->upsideDown = config->upsideDown;
       
   365         oldConfig->invert = config->invert;
       
   366         return 1;
       
   367     }
       
   368     return 0;
       
   369 }
       
   370 
       
   371 void cDriverT6963C::Clear()
       
   372 {
       
   373     for (int x = 0; x < (width + (FS - 1)) / FS; x++)
       
   374         memset(newLCD[x], 0, height);
       
   375 }
       
   376 
       
   377 void cDriverT6963C::Set8Pixels(int x, int y, unsigned char data)
       
   378 {
       
   379     if (x >= width || y >= height)
       
   380         return;
       
   381 
       
   382     if (FS == 6)
       
   383     {
       
   384         unsigned char data1 = 0;
       
   385         unsigned char data2 = 0;
       
   386         unsigned char data3 = 0;
       
   387 
       
   388         if (!config->upsideDown)
       
   389         {
       
   390             // normal orientation
       
   391             x = x - (x % 8);
       
   392             data1 = data >> (2 + (x % 6));
       
   393             if (x % 6 == 5)
       
   394             {
       
   395                 data2 = data >> 1;
       
   396                 data3 = data << 5;
       
   397             }
       
   398             else
       
   399                 data2 = data << (4 - (x % 6));
       
   400 
       
   401             newLCD[x / 6][y] |= data1;
       
   402             if (x / 6 + 1 < (width + 5) / 6)
       
   403                 newLCD[x / 6 + 1][y] |= data2;
       
   404             if (x / 6 + 2 < (width + 5) / 6)
       
   405                 if (x % 6 == 5)
       
   406                     newLCD[x / 6 + 2][y] |= data3;
       
   407         }
       
   408         else
       
   409         {
       
   410             // upside down orientation
       
   411             x = width - 1 - x;
       
   412             y = height - 1 - y;
       
   413             x = x - (x % 8);
       
   414             data = ReverseBits(data);
       
   415 
       
   416             data1 = data >> (2 + (x % 6));
       
   417             if (x % 6 == 5)
       
   418             {
       
   419                 data2 = data >> 1;
       
   420                 data3 = data << 5;
       
   421             }
       
   422             else
       
   423                 data2 = data << (4 - (x % 6));
       
   424 
       
   425             newLCD[x / 6][y] |= data1;
       
   426             if (x / 6 + 1 < (width + 5) / 6)
       
   427                 newLCD[x / 6 + 1][y] |= data2;
       
   428             if (x / 6 + 2 < (width + 5) / 6)
       
   429                 if (x % 6 == 5)
       
   430                     newLCD[x / 6 + 2][y] |= data3;
       
   431         }
       
   432     }
       
   433     else
       
   434     {
       
   435         if (!config->upsideDown)
       
   436         {
       
   437             newLCD[x / 8][y] |= data;
       
   438         }
       
   439         else
       
   440         {
       
   441             x = width - 1 - x;
       
   442             y = height - 1 - y;
       
   443             newLCD[x / 8][y] |= ReverseBits(data);
       
   444         }
       
   445     }
       
   446 }
       
   447 
       
   448 void cDriverT6963C::Refresh(bool refreshAll)
       
   449 {
       
   450     int x,y;
       
   451     int addr = 0;
       
   452 
       
   453     if (CheckSetup() == 1)
       
   454         refreshAll = true;
       
   455 
       
   456     if (config->refreshDisplay > 0)
       
   457     {
       
   458         refreshCounter = (refreshCounter + 1) % config->refreshDisplay;
       
   459         if (!refreshAll && !refreshCounter)
       
   460             refreshAll = true;
       
   461     }
       
   462 
       
   463     port->Claim();
       
   464     if (refreshAll)
       
   465     {
       
   466         // draw all
       
   467         T6963CCommandWord(kSetAddressPointer, kGraphicBase);
       
   468         if (useAutoMode)
       
   469         {
       
   470             T6963CCommand(kAutoWrite);
       
   471             autoWrite = true;
       
   472         }
       
   473         for (y = 0; y < height; y++)
       
   474         {
       
   475             for (x = 0; x < (width + (FS - 1)) / FS; x++)
       
   476             {
       
   477                 if (autoWrite)
       
   478                     T6963CData((newLCD[x][y]) ^ (config->invert ? 0xff : 0x00));
       
   479                 else
       
   480                     T6963CCommandByte(kDataWriteInc, (newLCD[x][y]) ^ (config->invert ? 0xff : 0x00));
       
   481                 oldLCD[x][y] = newLCD[x][y];
       
   482             }
       
   483         }
       
   484         if (autoWrite)
       
   485         {
       
   486             T6963CCommand(kAutoReset);
       
   487             autoWrite = false;
       
   488         }
       
   489         // and reset RefreshCounter
       
   490         refreshCounter = 0;
       
   491     }
       
   492     else
       
   493     {
       
   494         // draw only the changed bytes
       
   495 
       
   496         bool cs = false;
       
   497         for (y = 0; y < height; y++)
       
   498         {
       
   499             for (x = 0; x < (width + (FS - 1)) / FS; x++)
       
   500             {
       
   501                 if (oldLCD[x][y] != newLCD[x][y])
       
   502                 {
       
   503                     if (!cs)
       
   504                     {
       
   505                         if (width % FS == 0)
       
   506                             addr = (y * (width / FS)) + x;
       
   507                         else
       
   508                             addr = (y * (width / FS + 1)) + x;
       
   509                         T6963CCommandWord(kSetAddressPointer, kGraphicBase + addr);
       
   510                         if (useAutoMode)
       
   511                         {
       
   512                             T6963CCommand(kAutoWrite);
       
   513                             autoWrite = true;
       
   514                         }
       
   515                         cs = true;
       
   516                     }
       
   517                     if (autoWrite)
       
   518                         T6963CData((newLCD[x][y]) ^ (config->invert ? 0xff : 0x00));
       
   519                     else
       
   520                         T6963CCommandByte(kDataWriteInc, (newLCD[x][y]) ^ (config->invert ? 0xff : 0x00));
       
   521                     oldLCD[x][y] = newLCD[x][y];
       
   522                 }
       
   523                 else
       
   524                 {
       
   525                     if (autoWrite)
       
   526                     {
       
   527                         T6963CCommand(kAutoReset);
       
   528                         autoWrite = false;
       
   529                     }
       
   530                     cs = false;
       
   531                 }
       
   532             }
       
   533         }
       
   534         if (autoWrite)
       
   535         {
       
   536             T6963CCommand(kAutoReset);
       
   537             autoWrite = false;
       
   538         }
       
   539     }
       
   540     port->Release();
       
   541 }
       
   542 
       
   543 void cDriverT6963C::T6963CSetControl(unsigned char flags)
       
   544 {
       
   545     unsigned char status = port->ReadControl();
       
   546     status &= 0xF0; // mask 4 bits
       
   547     status |= flags; // add new flags
       
   548     port->WriteControl(status);
       
   549 }
       
   550 
       
   551 void cDriverT6963C::T6963CDSPReady()
       
   552 {
       
   553     int input = 0;
       
   554 
       
   555     port->SetDirection(kReverse);
       
   556     if (bidirectLPT == 1)
       
   557     {
       
   558         for (int i = 0; i < 10; i++)
       
   559         {
       
   560             T6963CSetControl(WRHI | CEHI | CDHI | RDHI);
       
   561             T6963CSetControl(WRHI | CELO | CDHI | RDLO);
       
   562             input = port->ReadData();
       
   563             T6963CSetControl(WRHI | CEHI | CDHI | RDHI);
       
   564             if (!autoWrite && (input & 3) == 3)
       
   565                 break;
       
   566             if (autoWrite && (input & 8) == 8)
       
   567                 break;
       
   568         }
       
   569     }
       
   570     else
       
   571     {
       
   572         T6963CSetControl(WRHI | CEHI | CDHI | RDHI);
       
   573         T6963CSetControl(WRHI | CELO | CDHI | RDLO);
       
   574         T6963CSetControl(WRHI | CEHI | CDHI | RDHI);
       
   575     }
       
   576     port->SetDirection(kForward);
       
   577 }
       
   578 
       
   579 void cDriverT6963C::T6963CData(unsigned char data)
       
   580 {
       
   581     if (serial)
       
   582     {
       
   583         T6963CSetControl(WRLO | CEHI | CDLO | RDLO);
       
   584         for (int i = 128; i; i>>=1)
       
   585         {
       
   586             if (data & i)
       
   587             {
       
   588                 T6963CSetControl(WRLO | CEHI | CDHI | RDLO);
       
   589                 T6963CSetControl(WRHI | CEHI | CDHI | RDLO);
       
   590             }
       
   591             else
       
   592             {
       
   593                 T6963CSetControl(WRLO | CEHI | CDLO | RDLO);
       
   594                 T6963CSetControl(WRHI | CEHI | CDLO | RDLO);
       
   595             }
       
   596         }
       
   597         T6963CSetControl(WRLO | CEHI | CDLO | RDLO); // CD down (data)
       
   598         T6963CSetControl(WRLO | CELO | CDLO | RDLO); // CE down
       
   599         T6963CSetControl(WRLO | CEHI | CDLO | RDLO); // CE up
       
   600     }
       
   601     else
       
   602     {
       
   603         if (useStatusCheck)
       
   604             T6963CDSPReady();
       
   605         T6963CSetControl(WRHI | CEHI | CDLO | RDHI); // CD down (data)
       
   606         T6963CSetControl(WRLO | CELO | CDLO | RDHI); // CE & WR down
       
   607         port->WriteData(data);
       
   608         T6963CSetControl(WRHI | CEHI | CDLO | RDHI); // CE & WR up again
       
   609         T6963CSetControl(WRHI | CEHI | CDHI | RDHI); // CD up again
       
   610     }
       
   611 }
       
   612 
       
   613 void cDriverT6963C::T6963CCommand(unsigned char cmd)
       
   614 {
       
   615     if (serial)
       
   616     {
       
   617         syslog(LOG_DEBUG, "Serial cmd out: ");
       
   618         T6963CSetControl(WRLO | CEHI | CDLO | RDLO);
       
   619         for (int i = 128; i; i>>=1)
       
   620         {
       
   621             if (cmd & i)
       
   622             {
       
   623                 T6963CSetControl(WRLO | CEHI | CDHI | RDLO);
       
   624                 T6963CSetControl(WRHI | CEHI | CDHI | RDLO);
       
   625             }
       
   626             else
       
   627             {
       
   628                 T6963CSetControl(WRLO | CEHI | CDLO | RDLO);
       
   629                 T6963CSetControl(WRHI | CEHI | CDLO | RDLO);
       
   630             }
       
   631         }
       
   632         T6963CSetControl(WRLO | CEHI | CDHI | RDLO); // CD up (command)
       
   633         T6963CSetControl(WRLO | CELO | CDHI | RDLO); // CE down
       
   634         T6963CSetControl(WRLO | CEHI | CDHI | RDLO); // CE up
       
   635         T6963CSetControl(WRLO | CEHI | CDLO | RDLO); // CD down
       
   636     }
       
   637     else
       
   638     {
       
   639         if (useStatusCheck)
       
   640             T6963CDSPReady();
       
   641         T6963CSetControl(WRHI | CEHI | CDHI | RDHI); // CD up (command)
       
   642         T6963CSetControl(WRLO | CELO | CDHI | RDHI); // CE & WR down
       
   643         port->WriteData(cmd);
       
   644         T6963CSetControl(WRHI | CEHI | CDHI | RDHI); // CE & WR up again
       
   645         T6963CSetControl(WRHI | CEHI | CDLO | RDHI); // CD down again
       
   646     }
       
   647 }
       
   648 
       
   649 void cDriverT6963C::T6963CCommandByte(unsigned char cmd, unsigned char data)
       
   650 {
       
   651     T6963CData(data);
       
   652     T6963CCommand(cmd);
       
   653 }
       
   654 
       
   655 void cDriverT6963C::T6963CCommand2Bytes(unsigned char cmd, unsigned char data1, unsigned char data2)
       
   656 {
       
   657     T6963CData(data1);
       
   658     T6963CData(data2);
       
   659     T6963CCommand(cmd);
       
   660 }
       
   661 
       
   662 void cDriverT6963C::T6963CCommandWord(unsigned char cmd, unsigned short data)
       
   663 {
       
   664     T6963CData(data % 256);
       
   665     T6963CData(data >> 8);
       
   666     T6963CCommand(cmd);
       
   667 }
       
   668 
       
   669 void cDriverT6963C::T6963CDisplayMode(unsigned char mode, bool enable)
       
   670 {
       
   671     if (enable)
       
   672         displayMode |= mode;
       
   673     else
       
   674         displayMode &= ~mode;
       
   675     T6963CCommand(kSetDisplayMode | displayMode);
       
   676 }
       
   677 
       
   678 }