5 #include "FutabaGP1212A01.h"
7 const int KNumberOfFrameBeforeDiffAlgo = 3;
13 GP1212A01A::GP1212A01A():
14 iDisplayPositionX(0),iDisplayPositionY(0),
16 iUseFrameDifferencing(true),
23 iNeedFullFrameUpdate(0)
30 GP1212A01A::~GP1212A01A()
45 iNeedFullFrameUpdate=0;
50 int GP1212A01A::Open()
52 int success = HidDevice::Open(KFutabaVendorId,KFutabaProductIdGP1212A01A,NULL);
55 //Allocate both frames
58 iFrameAlpha=new BitArrayHigh(KGP12xFrameBufferPixelCount);
62 iFrameBeta=new BitArrayHigh(KGP12xFrameBufferPixelCount);
66 iFrameGamma=new BitArrayHigh(KGP12xFrameBufferPixelCount);
68 iFrameNext=iFrameAlpha;
69 iFrameCurrent=iFrameBeta;
70 iFramePrevious=iFrameGamma;
73 //To make sure it is synced properly
74 iNeedFullFrameUpdate=0;
77 //Since we can't get our display position we force it to our default
78 //This makes sure frames are in sync from the start
79 //Clever clients will have taken care of putting back frame (0,0) before closing
80 SetDisplayPosition(iDisplayPositionX,iDisplayPositionY);
87 void GP1212A01A::SetPixel(unsigned char aX, unsigned char aY, unsigned int aPixel)
90 //int byteOffset=(aX*HeightInPixels()+aY)/8;
91 //int bitOffset=(aX*HeightInPixels()+aY)%8;
92 //iNextFrame[byteOffset] |= ( (aOn?0x01:0x00) << bitOffset );
94 //Pixel is on if any of the non-alpha component is not null
95 bool on = (aPixel&0x00FFFFFF)!=0x00000000;
101 iFrameNext->SetBit(aX*HeightInPixels()+aY);
105 iFrameNext->ClearBit(aX*HeightInPixels()+aY);
110 //Just specify a one pixel block
111 SetPixelBlock(aX,aY,0x00,0x01,on);
118 void GP1212A01A::BitBlit(const BitArray& aBitmap, int aSrcWidth, int aSrcHeight, int aTargetX, int aTargetY) const
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++)
123 for (int j=0;j<aSrcHeight;j++)
125 iFrameNext->SetBitValue((aTargetX+i)*HeightInPixels()+aTargetY+j,aBitmap[+i*aSrcHeight+j]);
132 Clear our client side back buffer.
133 Call to SwapBuffers must follow to actually clear the display.
135 void GP1212A01A::Clear()
137 //memset(iNextFrame->Ptr(),0x00,FrameBufferSizeInBytes());
140 iFrameNext->ClearAll();
150 Must be followed by a SwapBuffers call.
152 void GP1212A01A::Fill()
158 Set all pixels on our screen to the desired value.
159 This operation is performed off screen to avoid tearing.
160 @param 8 pixels pattern
162 void GP1212A01A::SetAllPixels(unsigned char aPattern)
164 //With a single buffer
165 //unsigned char screen[2048]; //One screen worth of pixels
166 //memset(screen,0xFF,sizeof(screen));
167 //SetPixelBlock(0,0,63,sizeof(screen),screen);
172 memset(iFrameNext->Ptr(),aPattern,FrameBufferSizeInBytes());
176 //Using pattern SetPixelBlock variant.
177 SetPixelBlock(0,0,63,FrameBufferSizeInBytes(),aPattern);
184 Set the defined pixel block to the given value.
185 @param X coordinate of our pixel block starting point.
186 @param Y coordinate of our pixel block starting point.
187 @param The height of our pixel block.
188 @param The size of our pixel data. Number of pixels divided by 8.
189 @param The value set to 8 pixels used as a pattern.
191 void GP1212A01A::SetPixelBlock(unsigned char aX, unsigned char aY, int aHeight, int aSize, unsigned char aValue)
193 OffScreenTranslation(aX,aY);
194 FutabaVfdReport report;
195 report[0]=0x00; //Report ID
196 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
197 report[2]=0x1B; //Command ID
198 report[3]=0x5B; //Command ID
199 report[4]=0xF0; //Command ID
202 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.
203 report[8]=aSize>>8; //Size of pixel data in bytes (MSB)
204 report[9]=aSize; //Size of pixel data in bytes (LSB)
205 int sizeWritten=MIN(aSize,report.Size()-10);
206 memset(report.Buffer()+10, aValue, sizeWritten);
209 int remainingSize=aSize;
210 //We need to keep on sending our pixel data until we are done
211 while (report[1]==64)
214 remainingSize-=sizeWritten;
215 report[0]=0x00; //Report ID
216 report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size
217 sizeWritten=(report[1]==64?63:report[1]);
218 memset(report.Buffer()+2, aValue, sizeWritten);
224 Set the defined pixel block to the given value.
225 @param X coordinate of our pixel block starting point.
226 @param Y coordinate of our pixel block starting point.
227 @param The height of our pixel block.
228 @param The size of our pixel data. Number of pixels divided by 8.
229 @param Pointer to our pixel data.
231 void GP1212A01A::SetPixelBlock(unsigned char aX, unsigned char aY, int aHeight, int aSize, unsigned char* aPixels)
233 OffScreenTranslation(aX,aY);
234 FutabaVfdReport report;
235 report[0]=0x00; //Report ID
236 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
237 report[2]=0x1B; //Command ID
238 report[3]=0x5B; //Command ID
239 report[4]=0xF0; //Command ID
242 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.
243 report[8]=aSize>>8; //Size of pixel data in bytes (MSB)
244 report[9]=aSize; //Size of pixel data in bytes (LSB)
245 int sizeWritten=MIN(aSize,report.Size()-10);
246 memcpy(report.Buffer()+10, aPixels, sizeWritten);
249 int remainingSize=aSize;
250 //We need to keep on sending our pixel data until we are done
251 while (report[1]==64)
254 remainingSize-=sizeWritten;
255 report[0]=0x00; //Report ID
256 report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size
257 sizeWritten=(report[1]==64?63:report[1]);
258 memcpy(report.Buffer()+2, aPixels+(aSize-remainingSize), sizeWritten);
264 Using this function is advised against as is causes tearing.
267 void GP1212A01A::SendClearCommand()
270 //Send Clear Display Command
271 FutabaVfdReport report;
272 report[0]=0x00; //Report ID
273 report[1]=0x04; //Report length
274 report[2]=0x1B; //Command ID
275 report[3]=0x5B; //Command ID
276 report[4]=0x32; //Command ID
277 report[5]=0x4A; //Command ID
282 Change our display position within our buffer.
284 void GP1212A01A::SetDisplayPosition(DW aDw,unsigned char aX, unsigned char aY)
287 //Send Display Position Settings Command
288 FutabaVfdReport report;
289 report[0]=0x00; //Report ID
290 report[1]=0x05; //Report length
291 report[2]=0x1B; //Command ID
292 report[3]=0x5B; //Command ID
293 report[4]=aDw; //Specify our DW
294 report[5]=aX; //X coordinate of our DW top-left corner
295 report[6]=aY; //Y coordinate of our DW top-left corner
300 Change our display position within our buffer.
302 void GP1212A01A::SetDisplayPosition(unsigned char aX, unsigned char aY)
304 //Specs apparently says both DW should remain the same
306 SetDisplayPosition(GP1212A01A::DW1,aX,aY);
307 SetDisplayPosition(GP1212A01A::DW2,aX,aY);
308 iDisplayPositionX=aX;
309 iDisplayPositionY=aY;
313 Provide Y coordinate of our off screen buffer.
315 unsigned char GP1212A01A::OffScreenY() const
317 //Overflowing is fine this is just what we want
318 return iDisplayPositionY+HeightInPixels();
322 Put our off screen buffer on screen.
323 On screen buffer goes off screen.
325 void GP1212A01A::SwapBuffers()
327 //Only perform buffer swapping if off screen mode is enabled
330 //Send host back buffer to device back buffer
331 if (!iUseFrameDifferencing || iNeedFullFrameUpdate<KNumberOfFrameBeforeDiffAlgo)
333 iNeedFullFrameUpdate++;
334 SetPixelBlock(0,0,63,FrameBufferSizeInBytes(),iFrameNext->Ptr());
338 //Frame diff algo is enabled
339 //We are going to send to our device only the differences between next frame and previous frame
340 SendModifiedPixelBlocks();
342 //Swap device front and back buffer
343 SetDisplayPosition(iDisplayPositionX,OffScreenY());
345 //Cycle through our frame buffers
346 //We keep track of previous frame which is in fact our device back buffer.
347 //We can then compare previous and next frame and send only the differences to our device.
348 //This mechanism allows us to reduce traffic over our USB bus thus improving our frame rate from 14 FPS to 30 FPS.
349 //Keep our previous frame pointer
350 BitArrayHigh* previousFrame=iFramePrevious;
351 //Current frame becomes the previous one
352 iFramePrevious = iFrameCurrent;
353 //Next frame becomes the current one
354 iFrameCurrent = iFrameNext;
355 //Next frame is now our former previous
356 iFrameNext = previousFrame;
361 //Define the edge of our pixel block
362 //Pixel blocks of 32x32 seems to run almost as fast as full screen update in worse case scenarii.
363 //Though I wonder if in some situations 16 could be better. Make this an attribute at some point if need be.
364 const int KPixelBlockEdge = 32;
365 const int KPixelBlockSizeInBits = KPixelBlockEdge*KPixelBlockEdge;
366 const int KPixelBlockSizeInBytes = KPixelBlockSizeInBits/8;
370 * @brief GP1212A01A::SendModifiedPixelBlocks
371 * Compare our back and front buffer and send to the device only the modified pixels.
373 void GP1212A01A::SendModifiedPixelBlocks()
375 int w=WidthInPixels();
376 int h=HeightInPixels();
379 //TODO: optimize with memcmp and 16 inc
382 for (int i=0;i<w;i++)
384 for (int j=0;j<h;j++)
386 //aX*HeightInPixels()+aY
387 if ((*iFrameNext)[i*h+j]!=(*iFramePrevious)[i*h+j])
389 //We need to update that pixel
390 SetPixelBlock(i,j,0,1,((*iFrameNext)[i*h+j]?0x01:0x00));
391 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
392 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
394 //SetPixelBlock(i,j,15,32,iNextFrame->Ptr()+offset);
400 BitArrayHigh nextBlock(KPixelBlockSizeInBits);
401 BitArrayHigh previousBlock(KPixelBlockSizeInBits);
403 for (int i=0;i<w;i+=KPixelBlockEdge)
405 for (int j=0;j<h;j+=KPixelBlockEdge)
407 //aX*HeightInPixels()+aY
408 //int offset=(i*w/8)+(j/8);
410 #ifdef DEBUG_FRAME_DIFF
411 QImage imagePrevious(KPixelBlockEdge,KPixelBlockEdge,QImage::Format_RGB32);
412 QImage imageNext(KPixelBlockEdge,KPixelBlockEdge,QImage::Format_RGB32);
415 //Get both our blocks from our buffers
416 for (int x=i;x<i+KPixelBlockEdge;x++)
418 for (int y=j;y<j+KPixelBlockEdge;y++)
420 int blockOffset=(x-i)*KPixelBlockEdge+(y-j);
421 int frameOffset=x*h+y;
422 nextBlock.SetBitValue(blockOffset,(*iFrameNext)[frameOffset]);
423 previousBlock.SetBitValue(blockOffset,(*iFramePrevious)[frameOffset]);
425 #ifdef DEBUG_FRAME_DIFF
426 imageNext.setPixel(x-i,y-j,(nextBlock[blockOffset]?0xFFFFFFFF:0x00000000));
427 imagePrevious.setPixel(x-i,y-j,(previousBlock[blockOffset]?0xFFFFFFFF:0x00000000));
432 #ifdef DEBUG_FRAME_DIFF
433 QString previousName;
435 QTextStream(&previousName) << "p" << i << "x" << j << ".png";
436 QTextStream(&nextName) << "n" << i << "x" << j << ".png";
437 imagePrevious.save(previousName);
438 imageNext.save(nextName);
442 //if (memcmp(iFrameNext->Ptr()+offset,iFramePrevious->Ptr()+offset,32 )) //32=(16*16/8)
443 if (memcmp(nextBlock.Ptr(),previousBlock.Ptr(),KPixelBlockSizeInBytes)!=0)
445 //We need to update that block
446 SetPixelBlock(i,j,KPixelBlockEdge-1,KPixelBlockSizeInBytes,nextBlock.Ptr());
447 //SetPixelBlock(i,j,15,32,0xFF/*nextBlock.Ptr()*/);
448 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
449 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
451 //SetPixelBlock(i,j,15,32,iFrameNext->Ptr()+offset);
459 Translate the given pixel coordinate according to our off screen mode.
461 void GP1212A01A::OffScreenTranslation(unsigned char& aX, unsigned char& aY)
465 aX+=WidthInPixels()-iDisplayPositionX;
466 aY+=HeightInPixels()-iDisplayPositionY;
473 void GP1212A01A::ResetBuffers()
475 //iNextFrame->ClearAll();
476 //memset(iFrameAlpha,0x00,sizeof(iFrameAlpha));
477 //memset(iFrameBeta,0x00,sizeof(iFrameBeta));
483 void GP1212A01A::Request(TMiniDisplayRequest aRequest)
487 case EMiniDisplayRequestDeviceId:
490 case EMiniDisplayRequestFirmwareRevision:
491 RequestFirmwareRevision();
493 case EMiniDisplayRequestPowerSupplyStatus:
494 RequestPowerSupplyStatus();
504 void GP1212A01A::RequestDeviceId()
506 if (RequestPending())
508 //Abort silently for now
512 //1BH,5BH,63H,49H,44H
513 //Send Read ID command
514 FutabaVfdReport report;
515 report[0]=0x00; //Report ID
516 report[1]=0x05; //Report length
517 report[2]=0x1B; //Command ID
518 report[3]=0x5B; //Command ID
519 report[4]=0x63; //Command ID
520 report[5]=0x49; //Command ID
521 report[6]=0x44; //Command ID
522 if (Write(report)==report.Size())
524 SetRequest(EMiniDisplayRequestDeviceId);
530 void GP1212A01A::RequestFirmwareRevision()
532 if (RequestPending())
534 //Abort silently for now
538 //1BH,5BH,63H,46H,52H
539 //Send Software Revision Read Command
540 FutabaVfdReport report;
541 report[0]=0x00; //Report ID
542 report[1]=0x05; //Report length
543 report[2]=0x1B; //Command ID
544 report[3]=0x5B; //Command ID
545 report[4]=0x63; //Command ID
546 report[5]=0x46; //Command ID
547 report[6]=0x52; //Command ID
548 if (Write(report)==report.Size())
550 SetRequest(EMiniDisplayRequestFirmwareRevision);
556 void GP1212A01A::RequestPowerSupplyStatus()
558 if (RequestPending())
560 //Abort silently for now
563 //1BH,5BH,63H,50H,4DH
564 //Send Power Suppply Monitor Command
565 FutabaVfdReport report;
566 report[0]=0x00; //Report ID
567 report[1]=0x05; //Report length
568 report[2]=0x1B; //Command ID
569 report[3]=0x5B; //Command ID
570 report[4]=0x63; //Command ID
571 report[5]=0x50; //Command ID
572 report[6]=0x4D; //Command ID
573 if (Write(report)==report.Size())
575 SetRequest(EMiniDisplayRequestPowerSupplyStatus);
581 This is for development purposes only.
582 Production application should stick to off-screen mode to avoid tearing.
584 void GP1212A01A::ToggleOffScreenMode()
586 SetOffScreenMode(!iOffScreenMode);
590 * @brief GP1212A01A::SetOffScreenMode
594 void GP1212A01A::SetOffScreenMode(bool aOn)
596 if (aOn==iOffScreenMode)
604 //Clean up our buffers upon switching modes
605 SetDisplayPosition(0,0);
612 Tries to complete our current request if we have one pending.
614 TMiniDisplayRequest GP1212A01A::AttemptRequestCompletion()
616 if (!RequestPending())
618 return EMiniDisplayRequestNone;
621 int res=Read(iInputReport);
625 return EMiniDisplayRequestNone;
628 //Process our request
629 if (CurrentRequest()==EMiniDisplayRequestPowerSupplyStatus)
631 if (iInputReport[1]==0x4F && iInputReport[2]==0x4E)
635 else if (iInputReport[1]==0x4F && iInputReport[2]==0x46 && iInputReport[3]==0x46)
640 else if (CurrentRequest()==EMiniDisplayRequestDeviceId)
642 unsigned char* ptr=&iInputReport[1];
643 strcpy(iDeviceId,(const char*)ptr);
645 else if (CurrentRequest()==EMiniDisplayRequestFirmwareRevision)
647 unsigned char* ptr=&iInputReport[1];
648 strcpy(iFirmwareRevision,(const char*)ptr);
651 TMiniDisplayRequest completed=CurrentRequest();
652 //Our request was completed
653 SetRequest(EMiniDisplayRequestNone);
660 Set our screen brightness.
661 @param The desired brightness level. Must be between MinBrightness and MaxBrightness.
663 void GP1212A01A::SetBrightness(int aBrightness)
665 if (aBrightness<MinBrightness()||aBrightness>MaxBrightness())
667 //Brightness out of range.
668 //Just ignore that request.
672 FutabaVfdReport report;
673 report[0]=0x00; //Report ID
674 report[1]=0x06; //Report size
675 report[2]=0x1B; //Command ID
676 report[3]=0x5C; //Command ID
677 report[4]=0x3F; //Command ID
678 report[5]=0x4C; //Command ID
679 report[6]=0x44; //Command ID
680 report[7]=0x30+aBrightness; //Brightness level