MiniDisplay/FutabaVfd.cpp
author sl
Thu, 29 May 2014 21:42:07 +0200
changeset 19 be04ffbb561c
parent 18 79801cc3bc94
child 20 cd6d76b9b47e
permissions -rw-r--r--
Trying to optimize our swap buffer implementation by sending only the pixel
block which ave changed. No joy so far.
     1 
     2 #include "FutabaVfd.h"
     3 //#include <stdlib.h>
     4 #include <string.h>
     5 
     6 
     7 //
     8 //
     9 //
    10 
    11 
    12 
    13 
    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     iFrameAlpha(NULL),
    78     iFrameBeta(NULL),
    79     iCurrentFrame(NULL),
    80     iNextFrame(NULL),
    81     iNeedFullFrameUpdate(true),
    82     iRequest(ERequestNone),iPowerOn(false)
    83 	{
    84 	//ResetBuffers();
    85 	}
    86 
    87 /**
    88 */
    89 GP1212A01A::~GP1212A01A()
    90 	{
    91     delete iFrameAlpha;
    92     iFrameAlpha=NULL;
    93     //
    94     delete iFrameBeta;
    95     iFrameBeta=NULL;
    96     //
    97     iNextFrame=NULL;
    98     iCurrentFrame=NULL;
    99     //
   100     iNeedFullFrameUpdate=true;
   101 	}
   102 
   103 /**
   104 */
   105 int GP1212A01A::Open()
   106 	{
   107 	int success = HidDevice::Open(KFutabaVendorId,KFutabaProductIdGP1212A01A,NULL);
   108 	if (success)
   109 		{
   110         //Allocate both frames
   111         delete iFrameAlpha;
   112         iFrameAlpha=NULL;
   113         iFrameAlpha=new BitArray(KGP12xFrameBufferPixelCount);
   114         //
   115         delete iFrameBeta;
   116         iFrameBeta=NULL;
   117         iFrameBeta=new BitArray(KGP12xFrameBufferPixelCount);
   118         //
   119         iCurrentFrame=iFrameAlpha;
   120         iNextFrame=iFrameBeta;
   121         //To make sure it is synced properly
   122         iNeedFullFrameUpdate=true;
   123         //
   124 		SetNonBlocking(1);
   125         //Since we can't get our display position we force it to our default
   126 		//This makes sure frames are in sync from the start
   127         //Clever clients will have taken care of putting back frame (0,0) before closing
   128 		SetDisplayPosition(iDisplayPositionX,iDisplayPositionY);
   129 		}
   130 	return success;
   131 	}
   132 
   133 /**
   134 */
   135 void GP1212A01A::SetPixel(unsigned char aX, unsigned char aY, bool aOn)
   136 	{
   137 	//
   138 	//int byteOffset=(aX*HeightInPixels()+aY)/8;
   139 	//int bitOffset=(aX*HeightInPixels()+aY)%8;
   140     //iNextFrame[byteOffset] |= ( (aOn?0x01:0x00) << bitOffset );
   141 
   142     if (iOffScreenMode)
   143         {
   144         if (aOn)
   145             {
   146             iNextFrame->SetBit(aX*HeightInPixels()+aY);
   147             }
   148         else
   149             {
   150             iNextFrame->ClearBit(aX*HeightInPixels()+aY);
   151             }
   152         }
   153     else
   154         {
   155         //Just specify a one pixel block
   156         SetPixelBlock(aX,aY,0x00,0x01,aOn);
   157         }
   158 	}
   159 
   160 /**
   161 */
   162 void GP1212A01A::BitBlit(const BitArray& aBitmap, int aSrcWidth, int aSrcHeight, int aTargetX, int aTargetY) const
   163 	{
   164 	//TODO: amend loop values so that we don't keep on looping past our frame buffer dimensions.
   165 	for (int i=0;i<aSrcWidth;i++)
   166 		{
   167 		for (int j=0;j<aSrcHeight;j++)
   168 			{
   169             iNextFrame->SetBitValue((aTargetX+i)*HeightInPixels()+aTargetY+j,aBitmap[+i*aSrcHeight+j]);
   170 			}
   171 		}
   172 	}
   173 
   174 /**
   175 Set all pixels on our screen to the desired value.
   176 This operation is performed off screen to avoid tearing.
   177 @param 8 pixels pattern
   178 */
   179 void GP1212A01A::SetAllPixels(unsigned char aPattern)
   180 	{
   181 	//With a single buffer
   182 	//unsigned char screen[2048]; //One screen worth of pixels
   183 	//memset(screen,0xFF,sizeof(screen));
   184 	//SetPixelBlock(0,0,63,sizeof(screen),screen);
   185 
   186 
   187     if (iOffScreenMode)
   188         {
   189         memset(iNextFrame->Ptr(),aPattern,FrameBufferSizeInBytes());
   190         }
   191     else
   192         {
   193         //Using pattern SetPixelBlock variant.
   194         SetPixelBlock(0,0,63,FrameBufferSizeInBytes(),aPattern);
   195         }
   196 	//
   197 	}
   198 
   199 
   200 /**
   201 Set the defined pixel block to the given value.
   202 @param X coordinate of our pixel block starting point.
   203 @param Y coordinate of our pixel block starting point.
   204 @param The height of our pixel block.
   205 @param The size of our pixel data. Number of pixels divided by 8.
   206 @param The value set to 8 pixels used as a pattern.
   207 */
   208 void GP1212A01A::SetPixelBlock(unsigned char aX, unsigned char aY, int aHeight, int aSize, unsigned char aValue)
   209 	{
   210 	OffScreenTranslation(aX,aY);
   211     FutabaVfdReport report;
   212     report[0]=0x00; //Report ID
   213     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
   214     report[2]=0x1B; //Command ID
   215     report[3]=0x5B; //Command ID
   216     report[4]=0xF0; //Command ID
   217     report[5]=aX;   //X
   218     report[6]=aY;   //Y
   219     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.
   220 	report[8]=aSize>>8; //Size of pixel data in bytes (MSB)
   221 	report[9]=aSize;	//Size of pixel data in bytes (LSB)
   222     int sizeWritten=MIN(aSize,report.Size()-10);
   223     memset(report.Buffer()+10, aValue, sizeWritten);
   224     Write(report);
   225 
   226     int remainingSize=aSize;
   227     //We need to keep on sending our pixel data until we are done
   228     while (report[1]==64)
   229         {
   230         report.Reset();
   231         remainingSize-=sizeWritten;
   232         report[0]=0x00; //Report ID
   233         report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size
   234         sizeWritten=(report[1]==64?63:report[1]);
   235         memset(report.Buffer()+2, aValue, sizeWritten);
   236         Write(report);
   237         }
   238 	}
   239 
   240 /**
   241 Set the defined pixel block to the given value.
   242 @param X coordinate of our pixel block starting point.
   243 @param Y coordinate of our pixel block starting point.
   244 @param The height of our pixel block.
   245 @param The size of our pixel data. Number of pixels divided by 8.
   246 @param Pointer to our pixel data.
   247 */
   248 void GP1212A01A::SetPixelBlock(unsigned char aX, unsigned char aY, int aHeight, int aSize, unsigned char* aPixels)
   249     {
   250 	OffScreenTranslation(aX,aY);
   251     FutabaVfdReport report;
   252     report[0]=0x00; //Report ID
   253     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
   254     report[2]=0x1B; //Command ID
   255     report[3]=0x5B; //Command ID
   256     report[4]=0xF0; //Command ID
   257     report[5]=aX;   //X
   258     report[6]=aY;   //Y
   259     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.
   260 	report[8]=aSize>>8; //Size of pixel data in bytes (MSB)
   261 	report[9]=aSize;	//Size of pixel data in bytes (LSB)
   262     int sizeWritten=MIN(aSize,report.Size()-10);
   263     memcpy(report.Buffer()+10, aPixels, sizeWritten);
   264     Write(report);
   265 
   266     int remainingSize=aSize;
   267     //We need to keep on sending our pixel data until we are done
   268     while (report[1]==64)
   269         {
   270         report.Reset();
   271         remainingSize-=sizeWritten;
   272         report[0]=0x00; //Report ID
   273         report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size
   274         sizeWritten=(report[1]==64?63:report[1]);
   275         memcpy(report.Buffer()+2, aPixels+(aSize-remainingSize), sizeWritten);
   276         Write(report);
   277         }
   278     }
   279 
   280 
   281 /**
   282 Clear our client side back buffer.
   283 Call to SwapBuffers must follow to actually clear the display.
   284 */
   285 void GP1212A01A::Clear()
   286 	{
   287     //memset(iNextFrame->Ptr(),0x00,FrameBufferSizeInBytes());
   288     if (iOffScreenMode)
   289         {
   290         iNextFrame->ClearAll();
   291         }
   292     else
   293         {
   294         SendClearCommand();
   295         }
   296 	}
   297 
   298 /**
   299 Using this function is advised against as is causes tearing.
   300 Use Clear instead.
   301 */
   302 void GP1212A01A::SendClearCommand()
   303 	{
   304     //1BH,5BH,32H,4AH
   305     //Send Clear Display Command
   306 	FutabaVfdReport report;
   307 	report[0]=0x00; //Report ID
   308 	report[1]=0x04; //Report length
   309 	report[2]=0x1B; //Command ID
   310 	report[3]=0x5B; //Command ID
   311 	report[4]=0x32; //Command ID
   312 	report[5]=0x4A; //Command ID
   313 	Write(report);
   314 	}
   315 
   316 /**
   317 Change our display position within our buffer.
   318 */
   319 void GP1212A01A::SetDisplayPosition(DW aDw,unsigned char aX, unsigned char aY)
   320     {
   321     //1BH,5BH,Dw,Px,Py
   322     //Send Display Position Settings Command
   323     FutabaVfdReport report;
   324     report[0]=0x00; //Report ID
   325     report[1]=0x05; //Report length
   326     report[2]=0x1B; //Command ID
   327     report[3]=0x5B; //Command ID
   328     report[4]=aDw;  //Specify our DW
   329     report[5]=aX;   //X coordinate of our DW top-left corner
   330     report[6]=aY;   //Y coordinate of our DW top-left corner
   331     Write(report);
   332     }
   333 
   334 /**
   335 Change our display position within our buffer.
   336 */
   337 void GP1212A01A::SetDisplayPosition(unsigned char aX, unsigned char aY)
   338 	{
   339 	//Specs apparently says both DW should remain the same
   340 	//Just don't ask
   341     SetDisplayPosition(GP1212A01A::DW1,aX,aY);
   342     SetDisplayPosition(GP1212A01A::DW2,aX,aY);
   343 	iDisplayPositionX=aX;
   344 	iDisplayPositionY=aY;
   345 	}
   346 
   347 /**
   348 Provide Y coordinate of our off screen buffer.
   349 */
   350 unsigned char GP1212A01A::OffScreenY() const
   351 	{
   352 	//Overflowing is fine this is just what we want
   353 	return iDisplayPositionY+HeightInPixels();
   354 	}
   355 
   356 /**
   357 Put our off screen buffer on screen.
   358 On screen buffer goes off screen.
   359 */
   360 void GP1212A01A::SwapBuffers()
   361 	{
   362 	//Only perform buffer swapping if off screen mode is enabled
   363 	if (OffScreenMode())
   364 		{
   365 		//Send host back buffer to device back buffer
   366         if (iNeedFullFrameUpdate)
   367             {
   368             //TODO: enable this once SendModifiedPixelBlocks works
   369             //iNeedFullFrameUpdate=false;
   370             SetPixelBlock(0,0,63,FrameBufferSizeInBytes(),iNextFrame->Ptr());
   371             }
   372         else
   373             {
   374             SendModifiedPixelBlocks();
   375             }
   376 		//Swap device front and back buffer
   377 		SetDisplayPosition(iDisplayPositionX,OffScreenY());
   378 		//Swap host buffers
   379         BitArray* nextFrame=iCurrentFrame;
   380         iCurrentFrame = iNextFrame;
   381         iNextFrame = nextFrame;
   382 		}
   383 	}
   384 
   385 
   386 /**
   387  * @brief GP1212A01A::SendModifiedPixelBlocks
   388  * Compare our back and front buffer and send to the device only the modified pixels.
   389  * TODO: Get this working at some point.
   390  */
   391 void GP1212A01A::SendModifiedPixelBlocks()
   392     {
   393     //The largest pixel block we can sanely send with one report is 16*16
   394     //const int KBlocksPerRow = WidthInPixels()/16; //16
   395     //const int KBlocksPerColumn = HeightInPixels()/16; //4
   396 
   397     int w=WidthInPixels();
   398     int h=HeightInPixels();
   399 
   400     BitArray block(16*16);
   401     //TODO: optimize with memcmp and 16 inc
   402     for (int i=0;i<w;i+=16)
   403         {
   404         for (int j=0;j<h;j+=16)
   405             {
   406             //aX*HeightInPixels()+aY
   407             int offset=(i*w/8)+(j/8);
   408             if (memcmp(iNextFrame->Ptr()+offset,iCurrentFrame->Ptr()+offset,32 /*(16*16/8)*/))
   409                 {
   410                 //We need to update that block
   411 
   412                 for (int x=i;x<i+16;x++)
   413                     {
   414                     for (int y=i;y<j+16;y++)
   415                         {
   416                         block.SetBitValue((x-i)*h+(y-j),(*iNextFrame)[x*h+y]);
   417                         }
   418                     }
   419                 SetPixelBlock(i,j,15,32,block.Ptr());
   420                 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
   421                 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
   422 
   423                 //SetPixelBlock(i,j,15,32,iNextFrame->Ptr()+offset);
   424                 }
   425             }
   426         }
   427 
   428     }
   429 
   430 /**
   431 Translate the given pixel coordinate according to our off screen mode.
   432 */
   433 void GP1212A01A::OffScreenTranslation(unsigned char& aX, unsigned char& aY)
   434 	{
   435 	if (OffScreenMode())
   436 		{
   437 		aX+=WidthInPixels()-iDisplayPositionX;
   438 		aY+=HeightInPixels()-iDisplayPositionY;
   439 		}
   440 	}
   441 
   442 
   443 /**
   444 */
   445 void GP1212A01A::ResetBuffers()
   446 	{
   447     //iNextFrame->ClearAll();
   448     //memset(iFrameAlpha,0x00,sizeof(iFrameAlpha));
   449 	//memset(iFrameBeta,0x00,sizeof(iFrameBeta));
   450 	}
   451 
   452 /**
   453 */
   454 void GP1212A01A::RequestDeviceId()
   455     {
   456     if (RequestPending())
   457         {
   458         //Abort silently for now
   459         return;
   460         }
   461 
   462     //1BH,5BH,63H,49H,44H
   463     //Send Read ID command
   464     FutabaVfdReport report;
   465     report[0]=0x00; //Report ID
   466     report[1]=0x05; //Report length
   467     report[2]=0x1B; //Command ID
   468     report[3]=0x5B; //Command ID
   469     report[4]=0x63; //Command ID
   470     report[5]=0x49; //Command ID
   471     report[6]=0x44; //Command ID
   472     if (Write(report)==report.Size())
   473         {
   474         iRequest=ERequestDeviceId;
   475         }
   476     }
   477 
   478 /**
   479 */
   480 void GP1212A01A::RequestFirmwareRevision()
   481     {
   482     if (RequestPending())
   483         {
   484         //Abort silently for now
   485         return;
   486         }
   487 
   488     //1BH,5BH,63H,46H,52H
   489     //Send Software Revision Read Command
   490     FutabaVfdReport report;
   491     report[0]=0x00; //Report ID
   492     report[1]=0x05; //Report length
   493     report[2]=0x1B; //Command ID
   494     report[3]=0x5B; //Command ID
   495     report[4]=0x63; //Command ID
   496     report[5]=0x46; //Command ID
   497     report[6]=0x52; //Command ID
   498     if (Write(report)==report.Size())
   499         {
   500         iRequest=ERequestFirmwareRevision;
   501         }
   502     }
   503 
   504 /**
   505 */
   506 void GP1212A01A::RequestPowerSupplyStatus()
   507     {
   508     if (RequestPending())
   509         {
   510         //Abort silently for now
   511         return;
   512         }
   513     //1BH,5BH,63H,50H,4DH
   514     //Send Power Suppply Monitor Command
   515     FutabaVfdReport report;
   516     report[0]=0x00; //Report ID
   517     report[1]=0x05; //Report length
   518     report[2]=0x1B; //Command ID
   519     report[3]=0x5B; //Command ID
   520     report[4]=0x63; //Command ID
   521     report[5]=0x50; //Command ID
   522     report[6]=0x4D; //Command ID
   523     if (Write(report)==report.Size())
   524         {
   525         iRequest=ERequestPowerSupplyStatus;
   526         }
   527     }
   528 
   529 
   530 /**
   531 This is for development purposes only.
   532 Production application should stick to off-screen mode to avoid tearing.
   533 */
   534 void GP1212A01A::ToggleOffScreenMode()
   535 	{
   536 	iOffScreenMode=!iOffScreenMode;
   537 	//Clean up our buffers upon switching modes	
   538 	SetDisplayPosition(0,0);
   539 	Clear();
   540 	SwapBuffers();
   541 	Clear();
   542 	}
   543 
   544 /**
   545  * @brief GP1212A01A::SetOffScreenMode
   546  * @param aOn
   547  * @return
   548  */
   549 void GP1212A01A::SetOffScreenMode(bool aOn)
   550     {
   551     if (aOn==iOffScreenMode)
   552     {
   553         //Nothing to do here
   554         return;
   555     }
   556 
   557     iOffScreenMode=aOn;
   558 
   559     //Clean up our buffers upon switching modes
   560     SetDisplayPosition(0,0);
   561     Clear();
   562     SwapBuffers();
   563     Clear();
   564     }
   565 
   566 /**
   567  */
   568 GP1212A01A::Request GP1212A01A::AttemptRequestCompletion()
   569     {
   570     if (!RequestPending())
   571         {
   572         return ERequestNone;
   573         }
   574 
   575     int res=Read(iInputReport);
   576 
   577     if (!res)
   578         {
   579         return ERequestNone;
   580         }
   581 
   582     //Process our request
   583     if (CurrentRequest()==GP1212A01A::ERequestPowerSupplyStatus)
   584         {
   585         if (iInputReport[1]==0x4F && iInputReport[2]==0x4E)
   586             {
   587             iPowerOn = true;
   588             }
   589         else if (iInputReport[1]==0x4F && iInputReport[2]==0x46 && iInputReport[3]==0x46)
   590             {
   591             iPowerOn = false;
   592             }
   593         }
   594 
   595     Request completed=iRequest;
   596     //Our request was completed
   597     iRequest=ERequestNone;
   598 
   599     return completed;
   600     }
   601 
   602 
   603 /**
   604 Set our screen brightness.
   605 @param The desired brightness level. Must be between MinBrightness and MaxBrightness.
   606 */
   607 void GP1212A01A::SetBrightness(int aBrightness)
   608     {
   609     if (aBrightness<MinBrightness()||aBrightness>MaxBrightness())
   610         {
   611         //Brightness out of range.
   612         //Just ignore that request.
   613         return;
   614         }
   615 
   616     FutabaVfdReport report;
   617     report[0]=0x00; //Report ID
   618     report[1]=0x06; //Report size
   619     report[2]=0x1B; //Command ID
   620     report[3]=0x5C; //Command ID
   621     report[4]=0x3F; //Command ID
   622     report[5]=0x4C; //Command ID
   623     report[6]=0x44; //Command ID
   624     report[7]=0x30+aBrightness; //Brightness level
   625     Write(report);
   626     }
   627 
   628