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