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