FutabaGP1212A02.cpp
author StephaneLenclud
Sun, 31 Aug 2014 22:57:32 +0200
changeset 18 96c013c63595
parent 16 42ba42be810d
child 19 9f659cde9ee8
permissions -rw-r--r--
Display auto detect implementation.
     1 //
     2 //
     3 //
     4 
     5 #include "FutabaGP1212A02.h"
     6 
     7 #include <stdio.h>
     8 #include <time.h>
     9 
    10 const unsigned short KMaxDataMemoryAddress = 0x4FFF;
    11 const unsigned short KFrameSizeInBytes = 0x800;
    12 
    13 //
    14 // class GP1212A02A
    15 //
    16 
    17 GP1212A02A::GP1212A02A():
    18 	iDisplayPositionX(0),iDisplayPositionY(0),
    19     iOffScreenMode(true),
    20     iUseFrameDifferencing(true),
    21     iFrameNext(NULL),
    22     iFrameCurrent(NULL),
    23     iFramePrevious(NULL),
    24     iFrameAlpha(NULL),
    25     iFrameBeta(NULL),
    26     iFrameGamma(NULL),
    27     iNeedFullFrameUpdate(0),
    28     iPowerOn(false)
    29 	{
    30 	iDeviceId[0]=0;
    31 	iFirmwareRevision[0]=0;
    32 	//ResetBuffers();
    33 	}
    34 
    35 /**
    36 */
    37 GP1212A02A::~GP1212A02A()
    38 	{
    39     delete iFrameAlpha;
    40     iFrameAlpha=NULL;
    41     //
    42     delete iFrameBeta;
    43     iFrameBeta=NULL;
    44     //
    45     delete iFrameGamma;
    46     iFrameGamma=NULL;
    47     //
    48     iFrameNext=NULL;
    49     iFrameCurrent=NULL;
    50     iFramePrevious=NULL;
    51     //
    52     iNeedFullFrameUpdate=0;
    53 	}
    54 
    55 /**
    56 */
    57 int GP1212A02A::Open()
    58 	{
    59 	int success = HidDevice::Open(KFutabaVendorId,KFutabaProductIdGP1212A02A,NULL);
    60 	if (success)
    61 		{
    62         //Allocate both frames
    63         delete iFrameAlpha;
    64         iFrameAlpha=NULL;
    65         iFrameAlpha=new BitArrayLow(KGP12xFrameBufferPixelCount);
    66         //
    67         delete iFrameBeta;
    68         iFrameBeta=NULL;
    69         iFrameBeta=new BitArrayLow(KGP12xFrameBufferPixelCount);
    70         //
    71         delete iFrameGamma;
    72         iFrameGamma=NULL;
    73         iFrameGamma=new BitArrayLow(KGP12xFrameBufferPixelCount);
    74         //
    75         iFrameNext=iFrameAlpha;
    76         iFrameCurrent=iFrameBeta;
    77         iFramePrevious=iFrameGamma;
    78 
    79 
    80         //To make sure it is synced properly
    81         iNeedFullFrameUpdate=0;
    82         //
    83 		SetNonBlocking(1);
    84 		//
    85 		SendCommandClear();
    86 		//
    87 		SetClockSetting();
    88 
    89 		//BMP box setup could be removed if we don't use it anymore
    90 		//Setup BMP box
    91 		BmpBoxSetting(EBmpBoxIdOne,0x0000,256,64);
    92 		//Select current BMP box
    93 		BmpBoxSelect(EBmpBoxIdOne);
    94 		//
    95 		iNextFrameAddress = 0x0000;
    96 
    97 
    98 
    99 		}
   100 	return success;
   101 	}
   102 
   103 /**
   104  Setting the BMP box
   105 [Code] 1BH,5CH,42H,Pn,aL,aH,Pw,Ph
   106 [Function] Setting the BMP box. BMP box can be defined the 3 area to DW. The position of BMP 
   107 box is set based on the address of DW. 
   108 * To write data in BMP box, BMP box select is necessary. 
   109 * Specifiable horizontal size is 256dot (100H) MAX. If horizontal size specify 256dot, Pw = 00H 
   110 Pn = Number of a BMP box 
   111 aL = Lower byte of address 
   112 aH = Upper byte of address 
   113 Pw = BMP box width 
   114 Ph = BMP box height 
   115 
   116 [Definable area]
   117 Pn = 31H - BMP box 1
   118 Pn = 32H - BMP box 2
   119 Pn = 33H - BMP box 3
   120 0000H <= aL + aH * 100 <= 07FFH 
   121 01H <= Pw <= 00H (=100H) 
   122 01H <= Ph <= 08H
   123 */
   124 void GP1212A02A::BmpBoxSetting(TBmpBoxId aBoxId, unsigned short aAddress, int aWidth, int aHeight)
   125 	{
   126 	//TODO: check parameters validity
   127 	//1BH,5CH,42H,Pn,aL,aH,Pw,Ph
   128 	FutabaVfdReport report;
   129 	report[0]=0x00; //Report ID
   130 	report[1]=0x08; //Report length.
   131 	report[2]=0x1B; //Command ID
   132 	report[3]=0x5C; //Command ID
   133 	report[4]=0x42; //Command ID
   134 	report[5]=aBoxId; 
   135 	report[6]=(unsigned char)aAddress; //aL = DM lower byte
   136 	report[7]=aAddress>>8; //aH = DM upper byte
   137 	report[8]=(aWidth==256?0x00:aWidth); //Pw = BMP box width 00==256
   138 	report[9]=aHeight/8; //Ph = BMP box height.
   139 	Write(report);
   140 	}
   141 
   142 /**
   143 [Code]1BH,5CH,48H,Pn
   144 [Function]Select of BMP box 
   145 * Execution "BMP box select" is necessary before "Setting the Text box". 
   146 * In case of writing by the specified dot writing, it is necessary to cancel this command. 
   147 [Definable area]
   148 Pn = 30H - Remove the BMP box 
   149 Pn = 31H - BMP box 1
   150 Pn = 32H - BMP box 2
   151 Pn = 33H - BMP box 3
   152 */
   153 void GP1212A02A::BmpBoxSelect(TBmpBoxId aBoxId)
   154 	{
   155 	//TODO: check parameters validity 
   156 	FutabaVfdReport report;
   157 	report[0]=0x00; //Report ID
   158 	report[1]=0x04; //Report length.
   159 	report[2]=0x1B; //Command ID
   160 	report[3]=0x5C; //Command ID
   161 	report[4]=0x48; //Command ID
   162 	report[5]=aBoxId; //BMP box ID
   163 	Write(report);
   164 	}
   165 
   166 
   167 /**
   168 */
   169 void GP1212A02A::SetPixel(unsigned char aX, unsigned char aY, bool aOn)
   170 	{
   171 	//
   172 	//int byteOffset=(aX*HeightInPixels()+aY)/8;
   173 	//int bitOffset=(aX*HeightInPixels()+aY)%8;
   174     //iNextFrame[byteOffset] |= ( (aOn?0x01:0x00) << bitOffset );
   175 
   176     if (iOffScreenMode)
   177         {
   178         if (aOn)
   179             {
   180             iFrameNext->SetBit(aX*HeightInPixels()+aY);
   181             }
   182         else
   183             {
   184             iFrameNext->ClearBit(aX*HeightInPixels()+aY);
   185             }
   186         }
   187     else
   188         {
   189         //Just specify a one pixel block
   190         //TODO
   191         }
   192 	}
   193 
   194 /**
   195 */
   196 /*
   197 void GP1212A02A::BitBlit(const BitArray& aBitmap, int aSrcWidth, int aSrcHeight, int aTargetX, int aTargetY) const
   198 	{
   199 	//TODO: amend loop values so that we don't keep on looping past our frame buffer dimensions.
   200 	for (int i=0;i<aSrcWidth;i++)
   201 		{
   202 		for (int j=0;j<aSrcHeight;j++)
   203 			{
   204             iFrameNext->SetBitValue((aTargetX+i)*HeightInPixels()+aTargetY+j,aBitmap[+i*aSrcHeight+j]);
   205 			}
   206 		}
   207 	}
   208 */
   209 
   210 /**
   211 Clear our client side back buffer.
   212 Call to SwapBuffers must follow to actually clear the display.
   213 */
   214 void GP1212A02A::Clear()
   215     {
   216     //memset(iNextFrame->Ptr(),0x00,FrameBufferSizeInBytes());
   217     if (iOffScreenMode)
   218         {
   219         iFrameNext->ClearAll();
   220         }
   221     else
   222         {
   223         SendCommandClear();
   224         }
   225     }
   226 
   227 /**
   228 Turn on all pixels.
   229 Must be followed by a SwapBuffers call.
   230 */
   231 void GP1212A02A::Fill()
   232 	{
   233 	SetAllPixels(0xFF);
   234 	}
   235 
   236 /**
   237 Set all pixels on our screen to the desired value.
   238 This operation is performed off screen to avoid tearing.
   239 @param 8 pixels pattern
   240 */
   241 void GP1212A02A::SetAllPixels(unsigned char aPattern)
   242 	{
   243 	//With a single buffer
   244 	//unsigned char screen[2048]; //One screen worth of pixels
   245 	//memset(screen,0xFF,sizeof(screen));
   246 	//SetPixelBlock(0,0,63,sizeof(screen),screen);
   247 
   248 
   249     if (iOffScreenMode)
   250         {
   251         memset(iFrameNext->Ptr(),aPattern,FrameBufferSizeInBytes());
   252         }
   253     else
   254         {
   255         //Using pattern SetPixelBlock variant.
   256         //TODO
   257         }
   258 	//
   259 	}
   260 
   261 
   262 
   263 
   264 /**
   265 BMP data input 
   266 [Code] 1BH,4AH,Pm,aL,aH,Ps,nL,nH,Pd...Pd
   267 [Function] The BMP data is written in the DW(Display Window) or the Data memory. 
   268 Pm= DW or Data memory 
   269 aL = DW lower byte 
   270 aH = DW upper byte 
   271 Ps = Direction of writing 
   272 nL = number of BMP data length lower byte 
   273 nH = number of BMP data length upper byte 
   274 Pd = BMP data 
   275 * If X direction is selected as Ps and data is written in the last address, the data in the last address is 
   276 overwritten with the remaining data.  
   277 [Definable area] Pm = 30H : DW
   278  Pm = 31H: Data memory 
   279 0000H <= aL + aH * 100 <= 07FFH (DW)
   280 0000H <= aL + aH * 100 <= 4FFFH (Data memory) 
   281 Ps = 30H: Y direction 
   282 Ps = 31H: X direction 
   283 0001H <= nL + nH * 100 <= 0100H(DW: X direction) 
   284 0001H <= nL + nH * 100 <= 0800H(DW: Y direction) 
   285 0001H <= nL + nH * 100 <= 0A00H(Data memory: X direction) 
   286 0001H <= nL + nH * 100 <= 5000H(Data memory: Y direction) 
   287 */
   288 void GP1212A02A::BmpDataInput(TTarget aTarget, unsigned short aAddress, TDirection aDirection, unsigned short aSize, unsigned char* aPixels)
   289 {
   290 	FutabaVfdReport report;
   291     report[0]=0x00; //Report ID
   292     report[1]=(aSize<=report.Size()-10?aSize+0x08:64); //Report length. -10 is for our header first 10 bytes. +8 is for our Futaba header size
   293     report[2]=0x1B; //Command ID
   294     report[3]=0x4A; //Command ID
   295     report[4]=aTarget; //Display Window or Data Memory
   296     report[5]=(unsigned char)aAddress; //aL = DW lower byte
   297     report[6]=aAddress>>8; //aH = DW upper byte
   298     report[7]=aDirection; //Direction of writing: Y or X
   299 	report[8]=(unsigned char)aSize; //Size of pixel data in bytes (LSB)
   300 	report[9]=aSize>>8;	//Size of pixel data in bytes (MSB)
   301     int sizeWritten=MIN(aSize,report.Size()-10);
   302     memcpy(report.Buffer()+10, aPixels, sizeWritten);
   303     Write(report);
   304 
   305     int remainingSize=aSize;
   306     //We need to keep on sending our pixel data until we are done
   307     while (report[1]==64)
   308         {
   309         report.Reset();
   310         remainingSize-=sizeWritten;
   311         report[0]=0x00; //Report ID
   312         report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size
   313         sizeWritten=(report[1]==64?63:report[1]);
   314         memcpy(report.Buffer()+2, aPixels+(aSize-remainingSize), sizeWritten);
   315         Write(report);
   316         }
   317 }
   318 
   319 
   320 /**
   321 Data memory transfer
   322 [Code] 1BH,5CH,44H,aL,aH
   323 [Function] BMP data transfer from Data memory to DW. 
   324 Although source data is updated, data in BMP box is not updated. To reflect the update, 
   325 re-executing this command is necessary. 
   326 aL = Lower byte of address 
   327 aH = Upper byte of address 
   328 [Definable area]
   329 0000H <= aL + aH * 100 <= 4FFFH 
   330 */
   331 void GP1212A02A::BmpBoxDataMemoryTransfer(unsigned short aAddress)
   332 	{
   333 	FutabaVfdReport report;
   334 	report[0]=0x00; //Report ID
   335     report[1]=0x05; //Report length.
   336     report[2]=0x1B; //Command ID
   337     report[3]=0x5C; //Command ID
   338     report[4]=0x44; //Command ID
   339     report[5]=(unsigned char)aAddress; //aL = DM lower byte
   340     report[6]=aAddress>>8; //aH = DM upper byte
   341 	Write(report);
   342 	}
   343 
   344 /**
   345 Input BMP data in the BMP box 
   346 [Code] 1BH,5CH,5DH,nL,nH,Pd...Pd
   347 [Function] BMP data is written the BMP box 
   348 * Number of definable data is due to BMP box size. If the data is over range, the over range data is 
   349 rewritten the final address. 
   350 nL = Lower byte of number of definition byte 
   351 nH = Upper byte of number of definition byte 
   352 Pd = BMP data 
   353 [Definable area] Pn : BMP box size (Pw * Ph)
   354 */
   355 void GP1212A02A::BmpBoxDataInput(unsigned short aSize, unsigned char* aPixels)
   356 	{
   357 	FutabaVfdReport report;
   358     report[0]=0x00; //Report ID
   359     report[1]=(aSize<=report.Size()-7?aSize+0x05:64); //Report length. -7 is for our header first 10 bytes. +5 is for our Futaba header size
   360     report[2]=0x1B; //Command ID
   361     report[3]=0x5C; //Command ID
   362     report[4]=0x5D; //Display Window or Data Memory
   363 	report[5]=(unsigned char)aSize; //Size of pixel data in bytes (LSB)
   364 	report[6]=aSize>>8;	//Size of pixel data in bytes (MSB)
   365     int sizeWritten=MIN(aSize,report.Size()-7);
   366     memcpy(report.Buffer()+7, aPixels, sizeWritten);
   367     Write(report);
   368 
   369     int remainingSize=aSize;
   370     //We need to keep on sending our pixel data until we are done
   371     while (report[1]==64)
   372         {
   373         report.Reset();
   374         remainingSize-=sizeWritten;
   375         report[0]=0x00; //Report ID
   376         report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size
   377         sizeWritten=(report[1]==64?63:report[1]);
   378         memcpy(report.Buffer()+2, aPixels+(aSize-remainingSize), sizeWritten);
   379         Write(report);
   380         }
   381 	}
   382 
   383 /**
   384 Using this function is advised against as is causes tearing.
   385 Use Clear instead.
   386 */
   387 void GP1212A02A::SendCommandClear()
   388 	{
   389     //1BH,4AH,43H,44H
   390     //Send Clear Display Command
   391 	FutabaVfdReport report;
   392 	report[0]=0x00; //Report ID
   393 	report[1]=0x04; //Report length
   394 	report[2]=0x1B; //Command ID
   395 	report[3]=0x4A; //Command ID
   396 	report[4]=0x43; //Command ID
   397 	report[5]=0x44; //Command ID
   398 	Write(report);
   399 	}
   400 
   401 
   402 /**
   403 Provide Y coordinate of our off screen buffer.
   404 */
   405 unsigned char GP1212A02A::OffScreenY() const
   406 	{
   407 	//Overflowing is fine this is just what we want
   408 	return iDisplayPositionY+HeightInPixels();
   409 	}
   410 
   411 /**
   412 Put our off screen buffer on screen.
   413 On screen buffer goes off screen.
   414 */
   415 void GP1212A02A::SwapBuffers()
   416 	{
   417 	//Only perform buffer swapping if off screen mode is enabled
   418 	if (OffScreenMode())
   419 		{
   420 		//Send pixel directly into BMP box
   421 		//BmpBoxDataInput(FrameBufferSizeInBytes(),iFrameNext->Ptr());
   422 		//Send pixel data directly into the display window
   423 		//BmpDataInput(ETargetDisplayWindow,0x0000,EDirectionY, FrameBufferSizeInBytes(),iFrameNext->Ptr());
   424 		//Send pixel data first to Data Memory then copy into the selected BMP box	
   425 		//BmpDataInput(ETargetDataMemory,0x0000,EDirectionY, FrameBufferSizeInBytes(),iFrameNext->Ptr());
   426 		//BmpBoxDataMemoryTransfer(0x0000);
   427 		//Send pixel data first to Data Memory then copy into the selected BMP box, cycling through our Data Memory frmae
   428 		BmpDataInput(ETargetDataMemory,iNextFrameAddress,EDirectionY, FrameBufferSizeInBytes(),iFrameNext->Ptr());
   429 		BmpBoxDataMemoryTransfer(iNextFrameAddress);
   430 		iNextFrameAddress+=KFrameSizeInBytes;
   431 		if (iNextFrameAddress>KMaxDataMemoryAddress)
   432 		{
   433 			iNextFrameAddress=0x0000;
   434 		}
   435 
   436         //Cycle through our frame buffers
   437         //We keep track of previous frame which is in fact our device back buffer.
   438         //We can then compare previous and next frame and send only the differences to our device.
   439         //This mechanism allows us to reduce traffic over our USB bus thus improving our frame rate from 14 FPS to 30 FPS.
   440         //Keep our previous frame pointer
   441         BitArrayLow* previousFrame=iFramePrevious;
   442         //Current frame becomes the previous one
   443         iFramePrevious = iFrameCurrent;
   444         //Next frame becomes the current one
   445         iFrameCurrent = iFrameNext;
   446         //Next frame is now our former previous
   447         iFrameNext = previousFrame;
   448 		}
   449 	}
   450 
   451 
   452 //Define the edge of our pixel block
   453 //Pixel blocks of 32x32 seems to run almost as fast as full screen update in worse case scenarii.
   454 //Though I wonder if in some situations 16 could be better. Make this an attribute at some point if need be.
   455 const int KPixelBlockEdge = 32;
   456 const int KPixelBlockSizeInBits = KPixelBlockEdge*KPixelBlockEdge;
   457 const int KPixelBlockSizeInBytes = KPixelBlockSizeInBits/8;
   458 
   459 
   460 /**
   461 Translate the given pixel coordinate according to our off screen mode.
   462 */
   463 void GP1212A02A::OffScreenTranslation(unsigned char& aX, unsigned char& aY)
   464 	{
   465 	if (OffScreenMode())
   466 		{
   467 		aX+=WidthInPixels()-iDisplayPositionX;
   468 		aY+=HeightInPixels()-iDisplayPositionY;
   469 		}
   470 	}
   471 
   472 
   473 /**
   474 */
   475 void GP1212A02A::Request(TMiniDisplayRequest aRequest)
   476 	{
   477 	switch (aRequest)
   478 		{
   479 	case EMiniDisplayRequestDeviceId:
   480 		RequestDeviceId();
   481 		break;
   482 	case EMiniDisplayRequestFirmwareRevision:
   483 		RequestFirmwareRevision();
   484 		break;
   485 	case EMiniDisplayRequestPowerSupplyStatus:
   486 		RequestPowerSupplyStatus();
   487 		break;
   488 	default:
   489 		//Not supported
   490 		break;
   491 		};
   492 	}
   493 
   494 
   495 /**
   496 */
   497 void GP1212A02A::ResetBuffers()
   498 	{
   499     //iNextFrame->ClearAll();
   500     //memset(iFrameAlpha,0x00,sizeof(iFrameAlpha));
   501 	//memset(iFrameBeta,0x00,sizeof(iFrameBeta));
   502 	}
   503 
   504 /**
   505 */
   506 void GP1212A02A::RequestDeviceId()
   507     {
   508 	//Not supported
   509     }
   510 
   511 /**
   512 ID code 
   513 [Code] 1BH,6AH,49H,44H
   514 [Function] Send the ID code to the Host system. ID code is software version.
   515 */
   516 void GP1212A02A::RequestFirmwareRevision()
   517     {
   518     if (RequestPending())
   519         {
   520         //Abort silently for now
   521         return;
   522         }
   523 
   524     //1BH,6AH,49H,44H
   525     //Send Software Revision Read Command
   526     FutabaVfdReport report;
   527     report[0]=0x00; //Report ID
   528     report[1]=0x04; //Report length
   529     report[2]=0x1B; //Command ID
   530     report[3]=0x6A; //Command ID
   531     report[4]=0x49; //Command ID
   532     report[5]=0x44; //Command ID
   533     if (Write(report)==report.Size())
   534         {
   535         SetRequest(EMiniDisplayRequestFirmwareRevision);
   536         }
   537 
   538     }
   539 
   540 /**
   541 */
   542 void GP1212A02A::RequestPowerSupplyStatus()
   543     {
   544 	//Not supported
   545     }
   546 
   547 
   548 /**
   549 This is for development purposes only.
   550 Production application should stick to off-screen mode to avoid tearing.
   551 */
   552 void GP1212A02A::ToggleOffScreenMode()
   553 	{
   554     SetOffScreenMode(!iOffScreenMode);
   555 	}
   556 
   557 /**
   558  * @brief GP1212A02A::SetOffScreenMode
   559  * @param aOn
   560  * @return
   561  */
   562 void GP1212A02A::SetOffScreenMode(bool aOn)
   563     {
   564     if (aOn==iOffScreenMode)
   565     {
   566         //Nothing to do here
   567         return;
   568     }
   569 
   570     iOffScreenMode=aOn;
   571 
   572     //Clean up our buffers upon switching modes
   573     Clear();
   574     SwapBuffers();
   575     Clear();
   576     }
   577 
   578 /**
   579 Tries to complete our current request if we have one pending.
   580  */
   581 TMiniDisplayRequest GP1212A02A::AttemptRequestCompletion()
   582     {
   583     if (!RequestPending())
   584         {
   585         return EMiniDisplayRequestNone;
   586         }
   587 
   588     int res=Read(iInputReport);
   589 
   590     if (!res)
   591         {
   592         return EMiniDisplayRequestNone;
   593         }
   594 
   595     //Process our request
   596 	if (CurrentRequest()==EMiniDisplayRequestFirmwareRevision)
   597 		{
   598 			unsigned char* ptr=&iInputReport[2];
   599 			iInputReport[7]=0x00;
   600 			strcpy(iFirmwareRevision,(const char*)ptr);
   601 		}
   602 
   603     TMiniDisplayRequest completed=CurrentRequest();
   604     //Our request was completed
   605     SetRequest(EMiniDisplayRequestNone);
   606 
   607     return completed;
   608 	}
   609 
   610 
   611 /**
   612 Set our screen brightness.
   613 @param The desired brightness level. Must be between MinBrightness and MaxBrightness.
   614 */
   615 void GP1212A02A::SetBrightness(int aBrightness)
   616     {
   617     if (aBrightness<MinBrightness()||aBrightness>MaxBrightness())
   618         {
   619         //Brightness out of range.
   620         //Just ignore that request.
   621         return;
   622         }
   623 
   624     FutabaVfdReport report;
   625     report[0]=0x00; //Report ID
   626     report[1]=0x04; //Report size
   627     report[2]=0x1B; //Command ID
   628     report[3]=0x4A; //Command ID
   629     report[4]=0x44; //Command ID
   630     report[5]=0x30+aBrightness; //Brightness level
   631     Write(report);
   632     }
   633 
   634 /**
   635 */
   636 bool GP1212A02A::IsPowerOn()
   637 	{
   638 	return iPowerOn;
   639 	}
   640 
   641 /**
   642 */
   643 char* GP1212A02A::DeviceId()
   644 	{
   645 	return iDeviceId;
   646 	}
   647 
   648 /**
   649 */
   650 char* GP1212A02A::FirmwareRevision()
   651 	{
   652 	return iFirmwareRevision;
   653 	}
   654 
   655 /**
   656 VFD Power ON/OFF 
   657 [Code]1BH,4AH,42H,Ps
   658 [Function]Control of the power supply for VFD 
   659 * If VFD power ON or OFF, at interval of 10s or more. 
   660 * When the VFD power off, VFD display is turn off, but the module can receive a data and 
   661 process.
   662 Ps = VFD Power control 
   663 [Definable area]
   664 Ps = 30H : VFD Power OFF 
   665 Ps = 31H : VFD Power ON  (Default)
   666 */
   667 void GP1212A02A::SendCommandPower(TPowerStatus aPowerStatus)
   668 	{
   669 	FutabaVfdReport report;
   670     report[0]=0x00; //Report ID
   671     report[1]=0x04; //Report size
   672     report[2]=0x1B; //Command ID
   673     report[3]=0x4A; //Command ID
   674     report[4]=0x42; //Command ID
   675     report[5]=aPowerStatus; //ON or OFF
   676     Write(report);
   677 	}
   678 
   679 /**
   680 */
   681 void GP1212A02A::TurnPowerOn()
   682 	{
   683 	SendCommandPower(EPowerOn);
   684 	SetClockSetting();
   685 	}
   686 
   687 /**
   688 */
   689 void GP1212A02A::TurnPowerOff()
   690 	{
   691 	SendCommandPower(EPowerOff);
   692 	}
   693 
   694 
   695 /**
   696 Number of characters for the given clock format.
   697 @return 
   698 */
   699 int GP1212A02A::ClockCharCount(TClockFormat aFormat)
   700 	{
   701 	switch (aFormat)
   702 		{
   703 	case EClockDay12:
   704 	case EClockDay24:
   705 		return 10;
   706 	case EClock12:
   707 	case EClock24:
   708 		return 5;
   709 		}
   710 
   711 	return 10;
   712 	}
   713 
   714 /**
   715 @return 
   716 */
   717 int GP1212A02A::ClockCharWidthInPixels(TClockSize aSize)
   718 	{
   719 	switch (aSize)
   720 		{
   721 	case EClockTiny:
   722 		return 6;
   723 	case EClockSmall:
   724 		return 8;
   725 	case EClockMedium:
   726 		return 12;
   727 	case EClockLarge:
   728 		return 16;
   729 		}
   730 
   731 	return 16;
   732 	}
   733 
   734 /**
   735 @return 
   736 */
   737 int GP1212A02A::ClockCharHeightInPixels(TClockSize aSize)
   738 	{
   739 	switch (aSize)
   740 		{
   741 	case EClockTiny:
   742 		return 8;
   743 	case EClockSmall:
   744 		return 16;
   745 	case EClockMedium:
   746 		return 24;
   747 	case EClockLarge:
   748 		return 32;
   749 		}
   750 
   751 	return 32;
   752 	}
   753 
   754 /**
   755 Return the Display Window address for centering the clock corresponding to the given parameters.
   756 */
   757 unsigned short GP1212A02A::ClockCenterAddress(TClockFormat aFormat, TClockSize aSize)
   758 	{
   759 		int charCount=ClockCharCount(aFormat);
   760 		int halfWidth=(ClockCharWidthInPixels(aSize)*charCount)/2;
   761 		int halfHeight=(ClockCharHeightInPixels(aSize))/2;
   762 		int x=(WidthInPixels()/2)-halfWidth;
   763 		int y=(HeightInPixels()/2)-halfHeight;
   764 
   765 		int yOffset=y/8;
   766 		int xOffset=x*8; //Not sure why...
   767 
   768 		unsigned short address = yOffset+xOffset;
   769 		//
   770 		return address;
   771 	}
   772 
   773 /**
   774 */
   775 void GP1212A02A::ShowClock()
   776 	{
   777 	SendCommandClockDisplay(EClockDay24,ClockCenterAddress(EClockDay24,EClockLarge),EClockLarge);
   778 	}
   779 
   780 /**
   781 */
   782 void GP1212A02A::HideClock()
   783 	{
   784 	SendCommandClockCancel();
   785 	}
   786 
   787 
   788 /**
   789 Clock setting 
   790 [Code]1BH,6BH,53H,Pd,Ph,Pm 
   791 [Function]Setting the clock data. The setting data is cleared, if the Reset command is input or power 
   792 is turned off. 
   793 Pd = Day of the week 
   794 Ph = hour 
   795 Pm = minute 
   796 [Definable area]
   797 Pd = 00H : Sunday 
   798 Pd = 01H : Monday 
   799 ...
   800 Pd = 06H : Saturday 
   801 * Clock setting is canceled, when Pd is input value that is larger than 07H, or Ph is input value that is 
   802 larger than 18H,or Pm is input value that is larger than 3CH. 
   803 */
   804 void GP1212A02A::SendCommandClockSetting(TWeekDay aWeekDay, unsigned char aHour, unsigned char aMinute)
   805 	{
   806 	FutabaVfdReport report;
   807     report[0]=0x00; //Report ID
   808     report[1]=0x06; //Report size
   809     report[2]=0x1B; //Command ID
   810     report[3]=0x6B; //Command ID
   811     report[4]=0x53; //Command ID
   812     report[5]=aWeekDay; //Sunday to Saturday
   813 	report[6]=aHour;
   814 	report[7]=aMinute;
   815 
   816     Write(report);
   817 	}
   818 
   819 
   820 /**
   821 Set display clock settings according to local system time.
   822 This needs to be redone whenever we open or turn on our display.
   823 */
   824 void GP1212A02A::SetClockSetting()
   825 	{
   826 	time_t rawtime;
   827 	struct tm * timeinfo;
   828 
   829 	time ( &rawtime );
   830 	timeinfo = localtime ( &rawtime );
   831 	//
   832 	SendCommandClockSetting((TWeekDay)timeinfo->tm_wday,timeinfo->tm_hour,timeinfo->tm_min);
   833 	}
   834 
   835 
   836 /**
   837 Clock display
   838 [Code] 1BH,6BH,55H,Ps,aL,aH,Pf
   839 [Function] Clock is displayed. The display position and the font size can be freely decided. 
   840 Ps = Display type select 
   841 aL,aH = Address 
   842 Pf = Font size select 
   843 [Definable area]
   844 Ps = 00H : 24hour Ex.[12:34] 
   845 Ps = 01H : 24hour + day of the week  Ex.[Wed._12:34] 
   846 Ps = 10H : 12hour Ex.[PM_00:34] 
   847 Ps = 11H : 12hour + day of the week  Ex.[Wed._PM_00:34] 
   848 Pf = 30H : 6x8 dot 
   849 Pf = 31H : 8x16dot
   850 Pf = 32H : 12x24 dot 
   851 Pf = 33H : 16x32 dot 
   852 * When the clock data is not input, clock is not displayed. 
   853 * The clock display is maintained until Clock display cancel "Clear display" RESET command is input 
   854 or power is turned off. 
   855 The clock display area
   856 Graphic can be displayed excluding the clock display area.
   857 The self adjustment for the position 
   858 that cannot be displayed. 
   859 * Excluding the clock display area can be input other display commands.
   860 */
   861 void GP1212A02A::SendCommandClockDisplay(TClockFormat aClockFormat, unsigned short aAddress, TClockSize aSize)
   862 	{
   863 	FutabaVfdReport report;
   864     report[0]=0x00; //Report ID
   865     report[1]=0x07; //Report size
   866     report[2]=0x1B; //Command ID
   867     report[3]=0x6B; //Command ID
   868     report[4]=0x55; //Command ID
   869     report[5]=aClockFormat; //
   870 	report[6]=(unsigned char)aAddress;	//aL
   871 	report[7]=aAddress>>8;				//aH
   872 	report[8]=aSize;
   873 
   874     Write(report);
   875 	}
   876 
   877 
   878 /**
   879  Clock display cancel 
   880 [Code] 1BH,6BH,3DH,58H
   881 [Function] Clock display is canceled.
   882 */
   883 void GP1212A02A::SendCommandClockCancel()
   884 	{
   885 	FutabaVfdReport report;
   886     report[0]=0x00; //Report ID
   887     report[1]=0x04; //Report size
   888     report[2]=0x1B; //Command ID
   889     report[3]=0x6B; //Command ID
   890     report[4]=0x3D; //Command ID
   891     report[5]=0x58; //
   892 
   893     Write(report);
   894 	}