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