graphlcd-base/glcddrivers/hd61830.c
author root@rika
Thu, 23 Apr 2009 20:55:41 +0200
changeset 33 7a0c4b0354ba
parent 4 df6a40031aa5
permissions -rw-r--r--
updated documentation
     1 /*
     2  * GraphLCD driver library
     3  *
     4  * hd61830.c  -  HD61830 driver class
     5  *
     6  * This file is released under the GNU General Public License. Refer
     7  * to the COPYING file distributed with this package.
     8  *
     9  * (c) 2001-2004 Carsten Siebholz <c.siebholz AT t-online.de>
    10  */
    11 
    12 #include <syslog.h>
    13 #include <sys/time.h>
    14 
    15 #include "common.h"
    16 #include "config.h"
    17 #include "hd61830.h"
    18 #include "port.h"
    19 
    20 
    21 namespace GLCD
    22 {
    23 
    24 // commands
    25 #define MCNT    0x00
    26 #define CPIT    0x01
    27 #define NOCH    0x02
    28 #define NOTD    0x03
    29 #define CPOS    0x04
    30 
    31 #define DSAL    0x08
    32 #define DSAH    0x09
    33 #define CACL    0x0A
    34 #define CACH    0x0B
    35 
    36 #define WDDI    0x0C
    37 #define RDDI    0x0D
    38 
    39 #define CBIT    0x0E
    40 #define SBIT    0x0F
    41 
    42 // control bits for DirectIO
    43 #define EN      0x01
    44 #define ENHI    0x00
    45 #define ENLO    0x01
    46 
    47 #define RW      0x02
    48 #define RWHI    0x00
    49 #define RWLO    0x02
    50 
    51 #define RS      0x04
    52 #define RSHI    0x04
    53 #define RSLO    0x00
    54 
    55 
    56 cDriverHD61830::cDriverHD61830(cDriverConfig * config)
    57 :   config(config)
    58 {
    59     oldConfig = new cDriverConfig(*config);
    60 
    61     port = new cParallelPort();
    62 
    63     useSleepInit = false;
    64 
    65     refreshCounter = 0;
    66     timeForPortCmdInNs = 0;
    67 }
    68 
    69 cDriverHD61830::~cDriverHD61830()
    70 {
    71     delete port;
    72     delete oldConfig;
    73 }
    74 
    75 int cDriverHD61830::Init()
    76 {
    77     int i;
    78     int x;
    79     struct timeval tv1, tv2;
    80 
    81     width = config->width;
    82     if (width <= 0)
    83         width = 240;
    84     height = config->height;
    85     if (height <= 0)
    86         height = 128;
    87 
    88     for (unsigned int i = 0; i < config->options.size(); i++)
    89     {
    90         if (config->options[i].name == "")
    91         {
    92         }
    93     }
    94 
    95     // setup lcd array (wanted state)
    96     newLCD = new unsigned char *[(width + 7) / 8];
    97     if (newLCD)
    98     {
    99         for (x = 0; x < (width + 7) / 8; x++)
   100         {
   101             newLCD[x] = new unsigned char[height];
   102             memset(newLCD[x], 0, height);
   103         }
   104     }
   105     // setup lcd array (current state)
   106     oldLCD = new unsigned char*[(width + 7) / 8];
   107     if (oldLCD)
   108     {
   109         for (x = 0; x < (width + 7) / 8; x++)
   110         {
   111             oldLCD[x] = new unsigned char[height];
   112             memset(oldLCD[x], 0, height);
   113         }
   114     }
   115 
   116     if (config->device == "")
   117     {
   118         // use DirectIO
   119         if (port->Open(config->port) != 0)
   120             return -1;
   121         uSleep(10);
   122     }
   123     else
   124     {
   125         // use ppdev
   126         if (port->Open(config->device.c_str()) != 0)
   127             return -1;
   128     }
   129 
   130     if (nSleepInit() != 0)
   131     {
   132         syslog(LOG_DEBUG, "%s: INFO: cannot change wait parameters (cDriver::Init)\n", config->name.c_str());
   133         useSleepInit = false;
   134     }
   135     else
   136     {
   137         useSleepInit = true;
   138     }
   139 
   140     syslog(LOG_DEBUG, "%s: benchmark started.\n", config->name.c_str());
   141     gettimeofday(&tv1, 0);
   142     for (i = 0; i < 1000; i++)
   143     {
   144         port->WriteData(1 % 0x100);
   145     }
   146     gettimeofday(&tv2, 0);
   147     if (useSleepInit)
   148         nSleepDeInit();
   149     timeForPortCmdInNs = (tv2.tv_sec-tv1.tv_sec) * 1000000 + (tv2.tv_usec-tv1.tv_usec);
   150     syslog(LOG_DEBUG, "%s: benchmark stopped. Time for Port Command: %ldns\n", config->name.c_str(), timeForPortCmdInNs);
   151 
   152     // initialize graphic mode
   153     InitGraphic();
   154 
   155     port->Release();
   156 
   157     *oldConfig = *config;
   158 
   159     // clear display
   160     Clear();
   161 
   162     syslog(LOG_INFO, "%s: HD61830 initialized.\n", config->name.c_str());
   163     return 0;
   164 }
   165 
   166 int cDriverHD61830::DeInit()
   167 {
   168     int x;
   169 
   170     // free lcd array (wanted state)
   171     if (newLCD)
   172     {
   173         for (x = 0; x < (width + 7) / 8; x++)
   174         {
   175             delete[] newLCD[x];
   176         }
   177         delete[] newLCD;
   178     }
   179     // free lcd array (current state)
   180     if (oldLCD)
   181     {
   182         for (x = 0; x < (width + 7) / 8; x++)
   183         {
   184             delete[] oldLCD[x];
   185         }
   186         delete[] oldLCD;
   187     }
   188     if (port->Close() != 0)
   189         return -1;
   190     return 0;
   191 }
   192 
   193 int cDriverHD61830::CheckSetup()
   194 {
   195     if (config->device != oldConfig->device ||
   196         config->port != oldConfig->port ||
   197         config->width != oldConfig->width ||
   198         config->height != oldConfig->height)
   199     {
   200         DeInit();
   201         Init();
   202         return 0;
   203     }
   204 
   205     if (config->upsideDown != oldConfig->upsideDown ||
   206         config->invert != oldConfig->invert)
   207     {
   208         oldConfig->upsideDown = config->upsideDown;
   209         oldConfig->invert = config->invert;
   210         return 1;
   211     }
   212     return 0;
   213 }
   214 
   215 int cDriverHD61830::InitGraphic()
   216 {
   217     Write(MCNT, 0x32); // set Mode Control Register
   218     // DISP ON, MASTER ON, BLINK OFF, CURSOR OFF, GRAPHIC-Mode, int.Clock
   219     Write(CPIT, 0x07); // set Character Pitch Register
   220     // 8 pixels per byte
   221     Write(NOCH, std::max(1, (width + 7) / 8 - 1)); // set Number-Of-Characters Register
   222     // (width - 1) / 8 bytes per line horizontally
   223     Write(NOTD, std::max(1, height - 1)); // set Number-Of-Time-Divisions Register
   224     // height - 1
   225     Write(CPOS, 0x00); // set Cursor Position Register
   226     // optional, because we havn't enabled a cursor
   227     Write(DSAL, 0x00); // set Display Start Address Register (Low Order Byte)
   228     Write(DSAH, 0x00); // set Display Start Address Register (High Order Byte)
   229     Write(CACL, 0x00); // set Cursor Address Counter Register (Low Order Byte)
   230     Write(CACH, 0x00); // set Cursor Address Counter Register (High Order Byte)
   231 
   232     return 0;
   233 }
   234 
   235 void cDriverHD61830::Write(unsigned char cmd, unsigned char data)
   236 {
   237     if (useSleepInit)
   238         nSleepInit();
   239 
   240     // set RS high (instruction), RW low (write) and E low
   241     port->WriteControl(RSHI | RWLO | ENLO);
   242     nSleep(140 - timeForPortCmdInNs + 100 * config->adjustTiming);
   243 
   244     // Output the actual command
   245     port->WriteData(cmd);
   246 
   247     // set E high
   248     port->WriteControl(RSHI | RWLO | ENHI);
   249     nSleep(450 - timeForPortCmdInNs + 100 * config->adjustTiming);
   250 
   251     // set E low
   252     port->WriteControl(RSHI | RWLO | ENLO);
   253     nSleep(450 - timeForPortCmdInNs + 100 * config->adjustTiming);
   254 
   255 
   256     // set RS low (data), RW low (write) and E low
   257     port->WriteControl(RSLO | RWLO | ENLO);
   258     nSleep(140 - timeForPortCmdInNs + 100 * config->adjustTiming);
   259 
   260     // Output the actual data
   261     port->WriteData(data);
   262 
   263     // set E high
   264     port->WriteControl(RSLO | RWLO | ENHI);
   265     nSleep(450 - timeForPortCmdInNs + 100 * config->adjustTiming);
   266 
   267     // set E low
   268     port->WriteControl(RSLO | RWLO | ENLO);
   269     nSleep(450 - timeForPortCmdInNs + 100 * config->adjustTiming);
   270 
   271     switch (cmd)
   272     {
   273         case MCNT:
   274         case CPIT:
   275         case NOCH:
   276         case NOTD:
   277         case CPOS:
   278         case DSAL:
   279         case DSAH:
   280         case CACL:
   281         case CACH:
   282             nSleep(4000 - std::max(450l, timeForPortCmdInNs) + 100 * config->adjustTiming);
   283             break;
   284         case WDDI:
   285         case RDDI:
   286             nSleep(6000 - std::max(450l, timeForPortCmdInNs) + 100 * config->adjustTiming);
   287             break;
   288         case CBIT:
   289         case SBIT:
   290             nSleep(36000 - std::max(450l, timeForPortCmdInNs) + 100 * config->adjustTiming);
   291             break;
   292     }
   293     if (useSleepInit)
   294         nSleepDeInit();
   295 }
   296 
   297 void cDriverHD61830::Clear()
   298 {
   299     for (int x = 0; x < (width + 7) / 8; x++)
   300         memset(newLCD[x], 0, height);
   301 }
   302 
   303 void cDriverHD61830::Set8Pixels(int x, int y, unsigned char data)
   304 {
   305     if (x >= width || y >= height)
   306         return;
   307 
   308     if (!config->upsideDown)
   309     {
   310         // normal orientation
   311         newLCD[x / 8][y] = newLCD[x / 8][y] | ReverseBits(data);
   312     }
   313     else
   314     {
   315         // upside down orientation
   316         x = width - 1 - x;
   317         y = height - 1 - y;
   318         newLCD[x / 8][y] = newLCD[x / 8][y] | data;
   319     }
   320 }
   321 
   322 void cDriverHD61830::Refresh(bool refreshAll)
   323 {
   324     int x;
   325     int y;
   326     int pos = 0;
   327 
   328     if (CheckSetup() > 0)
   329         refreshAll = true;
   330 
   331     if (config->refreshDisplay > 0)
   332     {
   333         refreshCounter = (refreshCounter + 1) % config->refreshDisplay;
   334         if (!refreshAll && !refreshCounter)
   335             refreshAll = true;
   336     }
   337 
   338     port->Claim();
   339 
   340     if (refreshAll)
   341     {
   342         // draw all
   343 
   344         for (y = 0; y < height; y++)
   345         {
   346             for (x = 0; x < (width + 7) / 8; x++)
   347             {
   348                 // (re-setting the cursor position
   349                 //  might be removed, when the graphic glitches are solved)
   350                 Write(CACL, (pos % 0x100));
   351                 Write(CACH, (pos / 0x100));
   352                 Write(WDDI, (newLCD[x][y]) ^ (config->invert ? 0xff : 0x00));
   353                 oldLCD[x][y] = newLCD[x][y];
   354                 pos++;
   355             }
   356         }
   357         // and reset RefreshCounter
   358         refreshCounter = 0;
   359     }
   360     else
   361     {
   362         // draw only the changed bytes
   363 
   364         bool cs = false;
   365         for (y = 0; y < height; y++)
   366         {
   367             for (x = 0; x < (width + 7) / 8; x++)
   368             {
   369                 if (newLCD[x][y] != oldLCD[x][y])
   370                 {
   371                     if (!cs)
   372                     {
   373                         Write(CACL, (pos % 0x100));
   374                         Write(CACH, (pos / 0x100));
   375                         cs = true;
   376                     }
   377                     Write(WDDI, (newLCD[x][y]) ^ (config->invert ? 0xff : 0x00));
   378                     oldLCD[x][y] = newLCD[x][y];
   379                 }
   380                 else
   381                 {
   382                     cs = false;
   383                 }
   384                 pos++;
   385             }
   386         }
   387     }
   388     port->Release();
   389 }
   390 
   391 } // end of namespace