graphlcd-base/glcddrivers/gu140x32f.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  * gu140x32f.c  -  8-bit driver module for Noritake GU140x32-F7806 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  *   HD61830 device
    10  *     (c) 2001-2003 by Carsten Siebholz <c.siebholz AT t-online.de>
    11  *   lcdproc 0.4 driver hd44780-ext8bit
    12  *     (c) 1999, 1995 Benjamin Tse <blt AT Comports com>
    13  *
    14  * This file is released under the GNU General Public License. Refer
    15  * to the COPYING file distributed with this package.
    16  *
    17  * (c) 2003 Andreas Brachold <vdr04 AT deltab.de>
    18  */
    19 
    20 #include <errno.h>
    21 #include <syslog.h>
    22 #include <unistd.h>
    23 #include <sys/time.h>
    24 
    25 #include "common.h"
    26 #include "config.h"
    27 #include "gu140x32f.h"
    28 #include "port.h"
    29 
    30 
    31 namespace GLCD
    32 {
    33 
    34 // Defines for hd44780 Displays
    35 #define RS_DAT      0x00
    36 #define RS_CMD      0x01
    37 
    38 #define CLEAR       0x01
    39 
    40 #define HOMECURSOR  0x02
    41 
    42 #define ENTRYMODE   0x04
    43 #define E_MOVERIGHT 0x02
    44 #define E_MOVELEFT  0x00
    45 #define EDGESCROLL  0x01
    46 #define NOSCROLL    0x00
    47 
    48 #define ONOFFCTRL   0x08
    49 #define DISPON      0x04
    50 #define DISPOFF     0x00
    51 #define CURSORON    0x02
    52 #define CURSOROFF   0x00
    53 #define CURSORBLINK 0x01
    54 #define CURSORNOBLINK 0x00
    55 
    56 #define CURSORSHIFT 0x10
    57 #define SCROLLDISP  0x08
    58 #define MOVECURSOR  0x00
    59 #define MOVERIGHT   0x04
    60 #define MOVELEFT    0x00
    61 
    62 #define FUNCSET     0x20
    63 #define IF_8BIT     0x10
    64 #define IF_4BIT     0x00
    65 
    66 // Control output lines
    67 // Write to baseaddress+2
    68 #define nSTRB       0x01    // pin 1; negative logic
    69 #define nLF         0x02    // pin 14
    70 #define nINIT       0x04    // pin 16; the only positive logic output line
    71 #define nSEL        0x08    // pin 17
    72 #define ENIRQ       0x10    // Enable IRQ via ACK line (don't enable this withouT
    73                             // setting up interrupt stuff too)
    74 #define ENBI        0x20    // Enable bi-directional port (is nice to play with!
    75                             // I first didn't know a SPP could do this)
    76 
    77 #define OUTMASK     0x0B    // SEL, LF and STRB are hardware inverted
    78                             // Use this mask only for the control output lines
    79                             // XOR with this mask ( ^ OUTMASK )
    80 
    81 static const std::string kWiringStandard = "Standard";
    82 static const std::string kWiringWindows  = "Windows";
    83 
    84 // standard wiring
    85 // #define RS        nSTRB
    86 // #define RW        nLF
    87 // #define EN1       nINIT
    88 // #define BL        nSEL
    89 
    90 // windows wiring
    91 // #define RS        nINIT
    92 // #define RW        nLF
    93 // #define EN1       nSTRB
    94 // #define BL        nSEL
    95 
    96 
    97 cDriverGU140X32F::cDriverGU140X32F(cDriverConfig * config)
    98 :   config(config),
    99     m_pDrawMem(0),
   100     m_pVFDMem(0)
   101 {
   102     oldConfig = new cDriverConfig(*config);
   103 
   104     port = new cParallelPort();
   105 
   106     m_nRefreshCounter = 0;
   107 }
   108 
   109 cDriverGU140X32F::~cDriverGU140X32F()
   110 {
   111     delete port;
   112     delete oldConfig;
   113 }
   114 
   115 int cDriverGU140X32F::Init()
   116 {
   117     int x;
   118     struct timeval tv1, tv2;
   119 
   120     // default values
   121     width = config->width;
   122     if (width <= 0)
   123         width = 140;
   124     height = config->height;
   125     if (height <= 0)
   126         height = 32;
   127     m_iSizeYb = ((height + 7) / 8); // 4
   128     m_WiringRS = nSTRB;
   129     m_WiringEN1 = nINIT;
   130 
   131     for (unsigned int i = 0; i < config->options.size(); i++)
   132     {
   133         if (config->options[i].name == "Wiring")
   134         {
   135             if (config->options[i].value == kWiringStandard)
   136             {
   137                 m_WiringRS = nSTRB;
   138                 m_WiringEN1 = nINIT;
   139             }
   140             else if (config->options[i].value == kWiringWindows)
   141             {
   142                 m_WiringRS = nINIT;
   143                 m_WiringEN1 = nSTRB;
   144             }
   145             else
   146                 syslog(LOG_ERR, "%s error: wiring %s not supported, using default (Standard)!\n",
   147                        config->name.c_str(), config->options[i].value.c_str());
   148         }
   149     }
   150 
   151     // setup the memory array for the drawing array gu140x32f
   152     m_pDrawMem = new unsigned char[width * m_iSizeYb];
   153     Clear();
   154 
   155     // setup the memory array for the display array gu140x32f
   156     m_pVFDMem = new unsigned char[width * m_iSizeYb];
   157     ClearVFDMem();
   158 
   159     if (config->device == "")
   160     {
   161         // use DirectIO
   162         if (port->Open(config->port) != 0)
   163             return -1;
   164         uSleep(10);
   165     }
   166     else
   167     {
   168         // use ppdev
   169         if (port->Open(config->device.c_str()) != 0)
   170             return -1;
   171     }
   172 
   173     if (nSleepInit() != 0)
   174     {
   175         syslog(LOG_ERR, "%s: INFO: cannot change wait parameters  Err: %s (cDriver::Init)\n", config->name.c_str(), strerror(errno));
   176         m_bSleepIsInit = false;
   177     }
   178     else
   179     {
   180         m_bSleepIsInit = true;
   181     }
   182 
   183     syslog(LOG_DEBUG, "%s: benchmark started.\n", config->name.c_str());
   184     gettimeofday(&tv1, 0);
   185     for (x = 0; x < 10000; x++)
   186     {
   187         port->WriteData(x % 0x100);
   188     }
   189     gettimeofday(&tv2, 0);
   190     nSleepDeInit();
   191     m_nTimingAdjustCmd = ((tv2.tv_sec - tv1.tv_sec) * 10000 + (tv2.tv_usec - tv1.tv_usec)) / 1000;
   192     syslog(LOG_DEBUG, "%s: benchmark stopped. Time for Port Command: %ldns\n", config->name.c_str(), m_nTimingAdjustCmd);
   193 
   194 
   195     // setup the lcd in 8 bit mode
   196     Write(RS_CMD, FUNCSET | IF_8BIT, 4100);
   197     Write(RS_CMD, FUNCSET | IF_8BIT, 100);
   198     Write(RS_CMD, FUNCSET | IF_8BIT, 40);
   199 
   200     Write(RS_CMD, ONOFFCTRL | DISPON | CURSOROFF | CURSORNOBLINK, 40);
   201     Write(RS_CMD, CLEAR, 1600);
   202     Write(RS_CMD, HOMECURSOR, 1600);
   203 
   204     port->Release();
   205 
   206     *oldConfig = *config;
   207 
   208     // Set Display SetBrightness
   209     SetBrightness(config->brightness);
   210     // clear display
   211     ClearVFDMem();
   212     Clear();
   213 
   214     syslog(LOG_INFO, "%s: gu140x32f initialized.\n", config->name.c_str());
   215     return 0;
   216 }
   217 
   218 int cDriverGU140X32F::DeInit()
   219 {
   220     if (m_pVFDMem)
   221         delete[] m_pVFDMem;
   222     if (m_pDrawMem)
   223         delete[] m_pDrawMem;
   224 
   225     if (port->Close() != 0)
   226         return -1;
   227     return 0;
   228 }
   229 
   230 int cDriverGU140X32F::CheckSetup()
   231 {
   232     if (config->device != oldConfig->device ||
   233         config->port != oldConfig->port ||
   234         config->width != oldConfig->width ||
   235         config->height != oldConfig->height)
   236     {
   237         DeInit();
   238         Init();
   239         return 0;
   240     }
   241 
   242     if (config->brightness != oldConfig->brightness)
   243     {
   244         oldConfig->brightness = config->brightness;
   245         SetBrightness(config->brightness);
   246     }
   247 
   248     if (config->upsideDown != oldConfig->upsideDown ||
   249         config->invert != oldConfig->invert)
   250     {
   251         oldConfig->upsideDown = config->upsideDown;
   252         oldConfig->invert = config->invert;
   253         return 1;
   254     }
   255     return 0;
   256 }
   257 
   258 void cDriverGU140X32F::ClearVFDMem()
   259 {
   260     for (int n = 0; m_pVFDMem && n < (width * m_iSizeYb); n++)
   261         m_pVFDMem[n] = 0x00;
   262 }
   263 
   264 void cDriverGU140X32F::Clear()
   265 {
   266     for (int n = 0; m_pDrawMem && n < (width * m_iSizeYb); n++)
   267         m_pDrawMem[n] = 0x00;
   268 }
   269 
   270 void cDriverGU140X32F::SetBrightness(unsigned int percent)
   271 {
   272     port->Claim();
   273 
   274     unsigned char level;
   275     if (percent > 100)
   276         percent = 100;
   277     level = percent / 25;
   278     if (level < 1)
   279         level = 1;
   280     level = (4 - level) & 0x03;
   281     // Set Brightness
   282     // 00 - 100%
   283     // 01 -  75%
   284     // 02 -  50%
   285     // 03 -  25%
   286     Write(RS_CMD, FUNCSET | IF_8BIT, 40);
   287     Write(RS_DAT, level, 40);
   288 
   289     port->Release();
   290 }
   291 
   292 void cDriverGU140X32F::Write(unsigned char nFlags, unsigned char bData, unsigned int nMicroSecBusyTime)
   293 {
   294     if (m_bSleepIsInit)
   295         nSleepInit();
   296 
   297     unsigned char enableLines = 0, portControl;
   298 
   299     // Only one controller is supported
   300     enableLines = m_WiringEN1;
   301 
   302     if (nFlags == RS_CMD)
   303         portControl = 0;
   304     else // if (nFlags == RS_DAT)
   305         portControl = m_WiringRS;
   306 
   307     // portControl |= m_WiringBL;
   308 
   309     port->WriteControl(portControl ^ OUTMASK);                  //Reset controlbits
   310     port->WriteData(bData);                                     //Set data
   311     port->WriteControl((enableLines | portControl) ^ OUTMASK);  //Set controlbits
   312 
   313     // How long hold the data active
   314     if (m_bSleepIsInit && (25 + (100 * config->adjustTiming) - m_nTimingAdjustCmd > 0))
   315     {
   316         // Wait 50ns
   317         nSleep(std::max(25L, 50 + (100 * config->adjustTiming) - m_nTimingAdjustCmd));
   318     }
   319 
   320     port->WriteControl(portControl ^ OUTMASK);                  //Reset controlbits
   321 
   322     nSleep((nMicroSecBusyTime * 1000) + (100 * config->adjustTiming) - m_nTimingAdjustCmd);
   323 
   324     if (m_bSleepIsInit)
   325         nSleepDeInit();
   326 }
   327 
   328 void cDriverGU140X32F::SetPixel(int x, int y)
   329 {
   330     unsigned char c;
   331     int n;
   332 
   333     if (!m_pDrawMem)
   334         return;
   335 
   336     if (x >= width || x < 0)
   337         return;
   338     if (y >= height || y < 0)
   339         return;
   340 
   341     if (config->upsideDown)
   342     {
   343         x = width - 1 - x;
   344         y = height - 1 - y;
   345     }
   346 
   347     n = x + ((y / 8) * width);
   348     c = 0x80 >> (y % 8);
   349 
   350     m_pDrawMem[n] |= c;
   351 }
   352 
   353 void cDriverGU140X32F::Set8Pixels(int x, int y, unsigned char data)
   354 {
   355     int n;
   356 
   357     // x - pos is'nt mayby align to 8
   358     x &= 0xFFF8;
   359 
   360     for (n = 0; n < 8; ++n)
   361     {
   362         if (data & (0x80 >> n))      // if bit is set
   363             SetPixel(x + n, y);
   364     }
   365 }
   366 
   367 void cDriverGU140X32F::Refresh(bool refreshAll)
   368 {
   369     int n, x, yb;
   370 
   371     if (!m_pVFDMem || !m_pDrawMem)
   372         return;
   373 
   374     bool doRefresh = false;
   375     int minX = width;
   376     int maxX = 0;
   377     int minYb = m_iSizeYb;
   378     int maxYb = 0;
   379 
   380     if (CheckSetup() > 0)
   381         refreshAll = true;
   382 
   383     for (yb = 0; yb < m_iSizeYb; ++yb)
   384         for (x = 0; x < width; ++x)
   385         {
   386             n = x + (yb * width);
   387             if (m_pVFDMem[n] != m_pDrawMem[n])
   388             {
   389                 m_pVFDMem[n] = m_pDrawMem[n];
   390                 minX = std::min(minX, x);
   391                 maxX = std::max(maxX, x);
   392                 minYb = std::min(minYb, yb);
   393                 maxYb = std::max(maxYb, yb + 1);
   394                 doRefresh = true;
   395             }
   396         }
   397 
   398     m_nRefreshCounter = (m_nRefreshCounter + 1) % config->refreshDisplay;
   399 
   400     if (!refreshAll && !m_nRefreshCounter)
   401         refreshAll = true;
   402 
   403     if (refreshAll || doRefresh)
   404     {
   405         if (refreshAll)
   406         {
   407             minX = 0;
   408             maxX = width;
   409             minYb = 0;
   410             maxYb = m_iSizeYb;
   411             // and reset RefreshCounter
   412             m_nRefreshCounter = 0;
   413         }
   414 
   415         minX = std::max(minX, 0);
   416         maxX = std::min(maxX, width - 1);
   417         minYb = std::max(minYb, 0);
   418         maxYb = std::min(maxYb, m_iSizeYb);
   419 
   420         port->Claim();
   421         // send lcd data to display, controller
   422         Write(RS_CMD, 0xF1, 40);
   423         Write(RS_DAT, minX, 40);
   424         Write(RS_DAT, (minYb * 8) & 0xFFF8, 40);
   425         Write(RS_DAT, maxX, 40);
   426         Write(RS_DAT, (maxYb * 8), 40);
   427 
   428         Write(RS_DAT, 'v', 500);
   429 
   430         for (yb = minYb; yb <= maxYb; ++yb)
   431             for (x = minX; x <= maxX; ++x)
   432             {
   433                 n = x + (yb * width);
   434 
   435                 if (n >= (width * m_iSizeYb))
   436                     break;
   437                 Write(RS_DAT, (m_pVFDMem[n]) ^ (config->invert ? 0xff : 0x00), 40);
   438             }
   439         port->Release();
   440     }
   441 }
   442 
   443 } // end of namespace