FutabaGP1212A02.cpp
author sl
Mon, 25 Aug 2014 00:07:01 +0200
changeset 10 1c3a4964a5bd
parent 8 5a9dbbc40c6b
child 11 2d749a2bea34
permissions -rw-r--r--
Working on GP1212A02 support.
     1 //
     2 //
     3 //
     4 
     5 #include "FutabaGP1212A02.h"
     6 
     7 
     8 const int KNumberOfFrameBeforeDiffAlgo = 3;
     9 
    10 //
    11 // class GP1212A02A
    12 //
    13 
    14 GP1212A02A::GP1212A02A():
    15 	iDisplayPositionX(0),iDisplayPositionY(0),
    16     iOffScreenMode(true),
    17     iUseFrameDifferencing(true),
    18     iFrameNext(NULL),
    19     iFrameCurrent(NULL),
    20     iFramePrevious(NULL),
    21     iFrameAlpha(NULL),
    22     iFrameBeta(NULL),
    23     iFrameGamma(NULL),
    24     iNeedFullFrameUpdate(0),
    25     iRequest(EMiniDisplayRequestNone),iPowerOn(false)
    26 	{
    27 	iDeviceId[0]=0;
    28 	iFirmwareRevision[0]=0;
    29 	//ResetBuffers();
    30 	}
    31 
    32 /**
    33 */
    34 GP1212A02A::~GP1212A02A()
    35 	{
    36     delete iFrameAlpha;
    37     iFrameAlpha=NULL;
    38     //
    39     delete iFrameBeta;
    40     iFrameBeta=NULL;
    41     //
    42     delete iFrameGamma;
    43     iFrameGamma=NULL;
    44     //
    45     iFrameNext=NULL;
    46     iFrameCurrent=NULL;
    47     iFramePrevious=NULL;
    48     //
    49     iNeedFullFrameUpdate=0;
    50 	}
    51 
    52 /**
    53 */
    54 int GP1212A02A::Open()
    55 	{
    56 	int success = HidDevice::Open(KFutabaVendorId,KFutabaProductIdGP1212A02A,NULL);
    57 	if (success)
    58 		{
    59         //Allocate both frames
    60         delete iFrameAlpha;
    61         iFrameAlpha=NULL;
    62         iFrameAlpha=new BitArray(KGP12xFrameBufferPixelCount);
    63         //
    64         delete iFrameBeta;
    65         iFrameBeta=NULL;
    66         iFrameBeta=new BitArray(KGP12xFrameBufferPixelCount);
    67         //
    68         delete iFrameGamma;
    69         iFrameGamma=NULL;
    70         iFrameGamma=new BitArray(KGP12xFrameBufferPixelCount);
    71         //
    72         iFrameNext=iFrameAlpha;
    73         iFrameCurrent=iFrameBeta;
    74         iFramePrevious=iFrameGamma;
    75 
    76 
    77         //To make sure it is synced properly
    78         iNeedFullFrameUpdate=0;
    79         //
    80 		SetNonBlocking(1);
    81         //Since we can't get our display position we force it to our default
    82 		//This makes sure frames are in sync from the start
    83         //Clever clients will have taken care of putting back frame (0,0) before closing
    84 		SetDisplayPosition(iDisplayPositionX,iDisplayPositionY);
    85 		}
    86 	return success;
    87 	}
    88 
    89 /**
    90 */
    91 void GP1212A02A::SetPixel(unsigned char aX, unsigned char aY, bool aOn)
    92 	{
    93 	//
    94 	//int byteOffset=(aX*HeightInPixels()+aY)/8;
    95 	//int bitOffset=(aX*HeightInPixels()+aY)%8;
    96     //iNextFrame[byteOffset] |= ( (aOn?0x01:0x00) << bitOffset );
    97 
    98     if (iOffScreenMode)
    99         {
   100         if (aOn)
   101             {
   102             iFrameNext->SetBit(aX*HeightInPixels()+aY);
   103             }
   104         else
   105             {
   106             iFrameNext->ClearBit(aX*HeightInPixels()+aY);
   107             }
   108         }
   109     else
   110         {
   111         //Just specify a one pixel block
   112         SetPixelBlock(aX,aY,0x00,0x01,aOn);
   113         }
   114 	}
   115 
   116 /**
   117 */
   118 void GP1212A02A::BitBlit(const BitArray& aBitmap, int aSrcWidth, int aSrcHeight, int aTargetX, int aTargetY) const
   119 	{
   120 	//TODO: amend loop values so that we don't keep on looping past our frame buffer dimensions.
   121 	for (int i=0;i<aSrcWidth;i++)
   122 		{
   123 		for (int j=0;j<aSrcHeight;j++)
   124 			{
   125             iFrameNext->SetBitValue((aTargetX+i)*HeightInPixels()+aTargetY+j,aBitmap[+i*aSrcHeight+j]);
   126 			}
   127 		}
   128 	}
   129 
   130 /**
   131 Clear our client side back buffer.
   132 Call to SwapBuffers must follow to actually clear the display.
   133 */
   134 void GP1212A02A::Clear()
   135     {
   136     //memset(iNextFrame->Ptr(),0x00,FrameBufferSizeInBytes());
   137     if (iOffScreenMode)
   138         {
   139         iFrameNext->ClearAll();
   140         }
   141     else
   142         {
   143         SendClearCommand();
   144         }
   145     }
   146 
   147 /**
   148 Turn on all pixels.
   149 Must be followed by a SwapBuffers call.
   150 */
   151 void GP1212A02A::Fill()
   152 	{
   153 	SetAllPixels(0xFF);
   154 	}
   155 
   156 /**
   157 Set all pixels on our screen to the desired value.
   158 This operation is performed off screen to avoid tearing.
   159 @param 8 pixels pattern
   160 */
   161 void GP1212A02A::SetAllPixels(unsigned char aPattern)
   162 	{
   163 	//With a single buffer
   164 	//unsigned char screen[2048]; //One screen worth of pixels
   165 	//memset(screen,0xFF,sizeof(screen));
   166 	//SetPixelBlock(0,0,63,sizeof(screen),screen);
   167 
   168 
   169     if (iOffScreenMode)
   170         {
   171         memset(iFrameNext->Ptr(),aPattern,FrameBufferSizeInBytes());
   172         }
   173     else
   174         {
   175         //Using pattern SetPixelBlock variant.
   176         SetPixelBlock(0,0,63,FrameBufferSizeInBytes(),aPattern);
   177         }
   178 	//
   179 	}
   180 
   181 
   182 /**
   183 Set the defined pixel block to the given value.
   184 @param X coordinate of our pixel block starting point.
   185 @param Y coordinate of our pixel block starting point.
   186 @param The height of our pixel block.
   187 @param The size of our pixel data. Number of pixels divided by 8.
   188 @param The value set to 8 pixels used as a pattern.
   189 */
   190 void GP1212A02A::SetPixelBlock(unsigned char aX, unsigned char aY, int aHeight, int aSize, unsigned char aValue)
   191 	{
   192 	OffScreenTranslation(aX,aY);
   193     FutabaVfdReport report;
   194     report[0]=0x00; //Report ID
   195     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
   196     report[2]=0x1B; //Command ID
   197     report[3]=0x5B; //Command ID
   198     report[4]=0xF0; //Command ID
   199     report[5]=aX;   //X
   200     report[6]=aY;   //Y
   201     report[7]=aHeight; //Y length before return. Though outside the specs, setting this to zero apparently allows us to modify a single pixel without touching any other.
   202 	report[8]=aSize>>8; //Size of pixel data in bytes (MSB)
   203 	report[9]=aSize;	//Size of pixel data in bytes (LSB)
   204     int sizeWritten=MIN(aSize,report.Size()-10);
   205     memset(report.Buffer()+10, aValue, sizeWritten);
   206     Write(report);
   207 
   208     int remainingSize=aSize;
   209     //We need to keep on sending our pixel data until we are done
   210     while (report[1]==64)
   211         {
   212         report.Reset();
   213         remainingSize-=sizeWritten;
   214         report[0]=0x00; //Report ID
   215         report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size
   216         sizeWritten=(report[1]==64?63:report[1]);
   217         memset(report.Buffer()+2, aValue, sizeWritten);
   218         Write(report);
   219         }
   220 	}
   221 
   222 /**
   223 Set the defined pixel block to the given value.
   224 @param X coordinate of our pixel block starting point.
   225 @param Y coordinate of our pixel block starting point.
   226 @param The height of our pixel block.
   227 @param The size of our pixel data. Number of pixels divided by 8.
   228 @param Pointer to our pixel data.
   229 */
   230 void GP1212A02A::SetPixelBlock(unsigned char aX, unsigned char aY, int aHeight, int aSize, unsigned char* aPixels)
   231     {
   232 	OffScreenTranslation(aX,aY);
   233     FutabaVfdReport report;
   234     report[0]=0x00; //Report ID
   235     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
   236     report[2]=0x1B; //Command ID
   237     report[3]=0x5B; //Command ID
   238     report[4]=0xF0; //Command ID
   239     report[5]=aX;   //X
   240     report[6]=aY;   //Y
   241     report[7]=aHeight; //Y length before return. Though outside the specs, setting this to zero apparently allows us to modify a single pixel without touching any other.
   242 	report[8]=aSize>>8; //Size of pixel data in bytes (MSB)
   243 	report[9]=aSize;	//Size of pixel data in bytes (LSB)
   244     int sizeWritten=MIN(aSize,report.Size()-10);
   245     memcpy(report.Buffer()+10, aPixels, sizeWritten);
   246     Write(report);
   247 
   248     int remainingSize=aSize;
   249     //We need to keep on sending our pixel data until we are done
   250     while (report[1]==64)
   251         {
   252         report.Reset();
   253         remainingSize-=sizeWritten;
   254         report[0]=0x00; //Report ID
   255         report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size
   256         sizeWritten=(report[1]==64?63:report[1]);
   257         memcpy(report.Buffer()+2, aPixels+(aSize-remainingSize), sizeWritten);
   258         Write(report);
   259         }
   260     }
   261 
   262 /**
   263 Using this function is advised against as is causes tearing.
   264 Use Clear instead.
   265 */
   266 void GP1212A02A::SendClearCommand()
   267 	{
   268     //1BH,5BH,32H,4AH
   269     //Send Clear Display Command
   270 	FutabaVfdReport report;
   271 	report[0]=0x00; //Report ID
   272 	report[1]=0x04; //Report length
   273 	report[2]=0x1B; //Command ID
   274 	report[3]=0x5B; //Command ID
   275 	report[4]=0x32; //Command ID
   276 	report[5]=0x4A; //Command ID
   277 	Write(report);
   278 	}
   279 
   280 /**
   281 Change our display position within our buffer.
   282 */
   283 void GP1212A02A::SetDisplayPosition(DW aDw,unsigned char aX, unsigned char aY)
   284     {
   285     //1BH,5BH,Dw,Px,Py
   286     //Send Display Position Settings Command
   287     FutabaVfdReport report;
   288     report[0]=0x00; //Report ID
   289     report[1]=0x05; //Report length
   290     report[2]=0x1B; //Command ID
   291     report[3]=0x5B; //Command ID
   292     report[4]=aDw;  //Specify our DW
   293     report[5]=aX;   //X coordinate of our DW top-left corner
   294     report[6]=aY;   //Y coordinate of our DW top-left corner
   295     Write(report);
   296     }
   297 
   298 /**
   299 Change our display position within our buffer.
   300 */
   301 void GP1212A02A::SetDisplayPosition(unsigned char aX, unsigned char aY)
   302 	{
   303 	//Specs apparently says both DW should remain the same
   304 	//Just don't ask
   305     SetDisplayPosition(GP1212A02A::DW1,aX,aY);
   306     SetDisplayPosition(GP1212A02A::DW2,aX,aY);
   307 	iDisplayPositionX=aX;
   308 	iDisplayPositionY=aY;
   309 	}
   310 
   311 /**
   312 Provide Y coordinate of our off screen buffer.
   313 */
   314 unsigned char GP1212A02A::OffScreenY() const
   315 	{
   316 	//Overflowing is fine this is just what we want
   317 	return iDisplayPositionY+HeightInPixels();
   318 	}
   319 
   320 /**
   321 Put our off screen buffer on screen.
   322 On screen buffer goes off screen.
   323 */
   324 void GP1212A02A::SwapBuffers()
   325 	{
   326 	//Only perform buffer swapping if off screen mode is enabled
   327 	if (OffScreenMode())
   328 		{
   329 		//Send host back buffer to device back buffer
   330         if (!iUseFrameDifferencing || iNeedFullFrameUpdate<KNumberOfFrameBeforeDiffAlgo)
   331             {
   332             iNeedFullFrameUpdate++;
   333             SetPixelBlock(0,0,63,FrameBufferSizeInBytes(),iFrameNext->Ptr());
   334             }
   335         else
   336             {
   337             //Frame diff algo is enabled
   338             //We are going to send to our device only the differences between next frame and previous frame
   339             SendModifiedPixelBlocks();
   340             }
   341 		//Swap device front and back buffer
   342 		SetDisplayPosition(iDisplayPositionX,OffScreenY());
   343 
   344         //Cycle through our frame buffers
   345         //We keep track of previous frame which is in fact our device back buffer.
   346         //We can then compare previous and next frame and send only the differences to our device.
   347         //This mechanism allows us to reduce traffic over our USB bus thus improving our frame rate from 14 FPS to 30 FPS.
   348         //Keep our previous frame pointer
   349         BitArray* previousFrame=iFramePrevious;
   350         //Current frame becomes the previous one
   351         iFramePrevious = iFrameCurrent;
   352         //Next frame becomes the current one
   353         iFrameCurrent = iFrameNext;
   354         //Next frame is now our former previous
   355         iFrameNext = previousFrame;
   356 		}
   357 	}
   358 
   359 
   360 //Define the edge of our pixel block
   361 //Pixel blocks of 32x32 seems to run almost as fast as full screen update in worse case scenarii.
   362 //Though I wonder if in some situations 16 could be better. Make this an attribute at some point if need be.
   363 const int KPixelBlockEdge = 32;
   364 const int KPixelBlockSizeInBits = KPixelBlockEdge*KPixelBlockEdge;
   365 const int KPixelBlockSizeInBytes = KPixelBlockSizeInBits/8;
   366 
   367 
   368 /**
   369  * @brief GP1212A02A::SendModifiedPixelBlocks
   370  * Compare our back and front buffer and send to the device only the modified pixels.
   371  */
   372 void GP1212A02A::SendModifiedPixelBlocks()
   373     {
   374     int w=WidthInPixels();
   375     int h=HeightInPixels();
   376 
   377 
   378     //TODO: optimize with memcmp and 16 inc
   379     /*
   380 
   381     for (int i=0;i<w;i++)
   382         {
   383         for (int j=0;j<h;j++)
   384             {
   385             //aX*HeightInPixels()+aY
   386             if ((*iFrameNext)[i*h+j]!=(*iFramePrevious)[i*h+j])
   387                 {
   388                 //We need to update that pixel
   389                 SetPixelBlock(i,j,0,1,((*iFrameNext)[i*h+j]?0x01:0x00));
   390                 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
   391                 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
   392 
   393                 //SetPixelBlock(i,j,15,32,iNextFrame->Ptr()+offset);
   394                 }
   395             }
   396         }
   397     */
   398 
   399     BitArray nextBlock(KPixelBlockSizeInBits);
   400     BitArray previousBlock(KPixelBlockSizeInBits);
   401 
   402     for (int i=0;i<w;i+=KPixelBlockEdge)
   403         {
   404         for (int j=0;j<h;j+=KPixelBlockEdge)
   405             {
   406             //aX*HeightInPixels()+aY
   407             //int offset=(i*w/8)+(j/8);
   408 
   409 #ifdef DEBUG_FRAME_DIFF
   410             QImage imagePrevious(KPixelBlockEdge,KPixelBlockEdge,QImage::Format_RGB32);
   411             QImage imageNext(KPixelBlockEdge,KPixelBlockEdge,QImage::Format_RGB32);
   412 #endif
   413 
   414             //Get both our blocks from our buffers
   415             for (int x=i;x<i+KPixelBlockEdge;x++)
   416                 {
   417                 for (int y=j;y<j+KPixelBlockEdge;y++)
   418                     {
   419                     int blockOffset=(x-i)*KPixelBlockEdge+(y-j);
   420                     int frameOffset=x*h+y;
   421                     nextBlock.SetBitValue(blockOffset,(*iFrameNext)[frameOffset]);
   422                     previousBlock.SetBitValue(blockOffset,(*iFramePrevious)[frameOffset]);
   423 
   424 #ifdef DEBUG_FRAME_DIFF
   425                     imageNext.setPixel(x-i,y-j,(nextBlock[blockOffset]?0xFFFFFFFF:0x00000000));
   426                     imagePrevious.setPixel(x-i,y-j,(previousBlock[blockOffset]?0xFFFFFFFF:0x00000000));
   427 #endif
   428                     }
   429                 }
   430 
   431 #ifdef DEBUG_FRAME_DIFF
   432             QString previousName;
   433             QString nextName;
   434             QTextStream(&previousName) << "p" << i << "x" << j << ".png";
   435             QTextStream(&nextName) << "n" << i << "x" << j << ".png";
   436             imagePrevious.save(previousName);
   437             imageNext.save(nextName);
   438 #endif
   439 
   440 
   441             //if (memcmp(iFrameNext->Ptr()+offset,iFramePrevious->Ptr()+offset,32 )) //32=(16*16/8)
   442             if (memcmp(nextBlock.Ptr(),previousBlock.Ptr(),KPixelBlockSizeInBytes)!=0)
   443                 {
   444                 //We need to update that block
   445                 SetPixelBlock(i,j,KPixelBlockEdge-1,KPixelBlockSizeInBytes,nextBlock.Ptr());
   446                 //SetPixelBlock(i,j,15,32,0xFF/*nextBlock.Ptr()*/);
   447                 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
   448                 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
   449 
   450                 //SetPixelBlock(i,j,15,32,iFrameNext->Ptr()+offset);
   451                 }
   452             }
   453         }
   454 
   455     }
   456 
   457 /**
   458 Translate the given pixel coordinate according to our off screen mode.
   459 */
   460 void GP1212A02A::OffScreenTranslation(unsigned char& aX, unsigned char& aY)
   461 	{
   462 	if (OffScreenMode())
   463 		{
   464 		aX+=WidthInPixels()-iDisplayPositionX;
   465 		aY+=HeightInPixels()-iDisplayPositionY;
   466 		}
   467 	}
   468 
   469 
   470 /**
   471 */
   472 void GP1212A02A::ResetBuffers()
   473 	{
   474     //iNextFrame->ClearAll();
   475     //memset(iFrameAlpha,0x00,sizeof(iFrameAlpha));
   476 	//memset(iFrameBeta,0x00,sizeof(iFrameBeta));
   477 	}
   478 
   479 /**
   480 */
   481 void GP1212A02A::RequestDeviceId()
   482     {
   483     if (RequestPending())
   484         {
   485         //Abort silently for now
   486         return;
   487         }
   488 
   489     //1BH,5BH,63H,49H,44H
   490     //Send Read ID command
   491     FutabaVfdReport report;
   492     report[0]=0x00; //Report ID
   493     report[1]=0x05; //Report length
   494     report[2]=0x1B; //Command ID
   495     report[3]=0x5B; //Command ID
   496     report[4]=0x63; //Command ID
   497     report[5]=0x49; //Command ID
   498     report[6]=0x44; //Command ID
   499     if (Write(report)==report.Size())
   500         {
   501         iRequest=EMiniDisplayRequestDeviceId;
   502         }
   503     }
   504 
   505 /**
   506 */
   507 void GP1212A02A::RequestFirmwareRevision()
   508     {
   509     if (RequestPending())
   510         {
   511         //Abort silently for now
   512         return;
   513         }
   514 
   515     //1BH,5BH,63H,46H,52H
   516     //Send Software Revision Read Command
   517     FutabaVfdReport report;
   518     report[0]=0x00; //Report ID
   519     report[1]=0x05; //Report length
   520     report[2]=0x1B; //Command ID
   521     report[3]=0x5B; //Command ID
   522     report[4]=0x63; //Command ID
   523     report[5]=0x46; //Command ID
   524     report[6]=0x52; //Command ID
   525     if (Write(report)==report.Size())
   526         {
   527         iRequest=EMiniDisplayRequestFirmwareRevision;
   528         }
   529     }
   530 
   531 /**
   532 */
   533 void GP1212A02A::RequestPowerSupplyStatus()
   534     {
   535     if (RequestPending())
   536         {
   537         //Abort silently for now
   538         return;
   539         }
   540     //1BH,5BH,63H,50H,4DH
   541     //Send Power Suppply Monitor Command
   542     FutabaVfdReport report;
   543     report[0]=0x00; //Report ID
   544     report[1]=0x05; //Report length
   545     report[2]=0x1B; //Command ID
   546     report[3]=0x5B; //Command ID
   547     report[4]=0x63; //Command ID
   548     report[5]=0x50; //Command ID
   549     report[6]=0x4D; //Command ID
   550     if (Write(report)==report.Size())
   551         {
   552         iRequest=EMiniDisplayRequestPowerSupplyStatus;
   553         }
   554     }
   555 
   556 
   557 /**
   558 This is for development purposes only.
   559 Production application should stick to off-screen mode to avoid tearing.
   560 */
   561 void GP1212A02A::ToggleOffScreenMode()
   562 	{
   563     SetOffScreenMode(!iOffScreenMode);
   564 	}
   565 
   566 /**
   567  * @brief GP1212A02A::SetOffScreenMode
   568  * @param aOn
   569  * @return
   570  */
   571 void GP1212A02A::SetOffScreenMode(bool aOn)
   572     {
   573     if (aOn==iOffScreenMode)
   574     {
   575         //Nothing to do here
   576         return;
   577     }
   578 
   579     iOffScreenMode=aOn;
   580 
   581     //Clean up our buffers upon switching modes
   582     SetDisplayPosition(0,0);
   583     Clear();
   584     SwapBuffers();
   585     Clear();
   586     }
   587 
   588 /**
   589 Tries to complete our current request if we have one pending.
   590  */
   591 TMiniDisplayRequest GP1212A02A::AttemptRequestCompletion()
   592     {
   593     if (!RequestPending())
   594         {
   595         return EMiniDisplayRequestNone;
   596         }
   597 
   598     int res=Read(iInputReport);
   599 
   600     if (!res)
   601         {
   602         return EMiniDisplayRequestNone;
   603         }
   604 
   605     //Process our request
   606     if (CurrentRequest()==EMiniDisplayRequestPowerSupplyStatus)
   607         {
   608         if (iInputReport[1]==0x4F && iInputReport[2]==0x4E)
   609             {
   610             iPowerOn = true;
   611             }
   612         else if (iInputReport[1]==0x4F && iInputReport[2]==0x46 && iInputReport[3]==0x46)
   613             {
   614             iPowerOn = false;
   615             }
   616         }
   617 	else if (CurrentRequest()==EMiniDisplayRequestDeviceId)
   618 		{
   619 			unsigned char* ptr=&iInputReport[1];
   620 			strcpy(iDeviceId,(const char*)ptr);
   621 		}
   622 	else if (CurrentRequest()==EMiniDisplayRequestFirmwareRevision)
   623 		{
   624 			unsigned char* ptr=&iInputReport[1];
   625 			strcpy(iFirmwareRevision,(const char*)ptr);
   626 		}
   627 
   628     TMiniDisplayRequest completed=iRequest;
   629     //Our request was completed
   630     iRequest=EMiniDisplayRequestNone;
   631 
   632     return completed;
   633     }
   634 
   635 
   636 /**
   637 Set our screen brightness.
   638 @param The desired brightness level. Must be between MinBrightness and MaxBrightness.
   639 */
   640 void GP1212A02A::SetBrightness(int aBrightness)
   641     {
   642     if (aBrightness<MinBrightness()||aBrightness>MaxBrightness())
   643         {
   644         //Brightness out of range.
   645         //Just ignore that request.
   646         return;
   647         }
   648 
   649     FutabaVfdReport report;
   650     report[0]=0x00; //Report ID
   651     report[1]=0x04; //Report size
   652     report[2]=0x1B; //Command ID
   653     report[3]=0x4A; //Command ID
   654     report[4]=0x44; //Command ID
   655     report[7]=0x30+aBrightness; //Brightness level
   656     Write(report);
   657     }
   658 
   659 /**
   660 */
   661 bool GP1212A02A::PowerOn()
   662 	{
   663 	return iPowerOn;
   664 	}
   665 
   666 /**
   667 */
   668 char* GP1212A02A::DeviceId()
   669 	{
   670 	return iDeviceId;
   671 	}
   672 
   673 /**
   674 */
   675 char* GP1212A02A::FirmwareRevision()
   676 	{
   677 	return iFirmwareRevision;
   678 	}