Implementing and C API to control our MiniDisplay.
7 #ifdef DEBUG_FRAME_DIFF
13 const int KNumberOfFrameBeforeDiffAlgo = 3;
19 FutabaVfdCommand::FutabaVfdCommand():/*iBuffer(NULL),*/iSize(0),iMaxSize(0)
23 FutabaVfdCommand::~FutabaVfdCommand()
32 void FutabaVfdCommand::Reset()
34 memset(iReports,0,sizeof(iReports));
43 void FutabaVfdCommand::Create(int aMaxSize)
45 iBuffer=new unsigned char[aMaxSize];
58 void FutabaVfdCommand::Delete()
74 GP1212A01A::GP1212A01A():
75 iDisplayPositionX(0),iDisplayPositionY(0),
77 iUseFrameDifferencing(true),
84 iNeedFullFrameUpdate(0),
85 iRequest(ERequestNone),iPowerOn(false)
92 GP1212A01A::~GP1212A01A()
107 iNeedFullFrameUpdate=0;
112 int GP1212A01A::Open()
114 int success = HidDevice::Open(KFutabaVendorId,KFutabaProductIdGP1212A01A,NULL);
117 //Allocate both frames
120 iFrameAlpha=new BitArray(KGP12xFrameBufferPixelCount);
124 iFrameBeta=new BitArray(KGP12xFrameBufferPixelCount);
128 iFrameGamma=new BitArray(KGP12xFrameBufferPixelCount);
130 iFrameNext=iFrameAlpha;
131 iFrameCurrent=iFrameBeta;
132 iFramePrevious=iFrameGamma;
135 //To make sure it is synced properly
136 iNeedFullFrameUpdate=0;
139 //Since we can't get our display position we force it to our default
140 //This makes sure frames are in sync from the start
141 //Clever clients will have taken care of putting back frame (0,0) before closing
142 SetDisplayPosition(iDisplayPositionX,iDisplayPositionY);
149 void GP1212A01A::SetPixel(unsigned char aX, unsigned char aY, bool aOn)
152 //int byteOffset=(aX*HeightInPixels()+aY)/8;
153 //int bitOffset=(aX*HeightInPixels()+aY)%8;
154 //iNextFrame[byteOffset] |= ( (aOn?0x01:0x00) << bitOffset );
160 iFrameNext->SetBit(aX*HeightInPixels()+aY);
164 iFrameNext->ClearBit(aX*HeightInPixels()+aY);
169 //Just specify a one pixel block
170 SetPixelBlock(aX,aY,0x00,0x01,aOn);
176 void GP1212A01A::BitBlit(const BitArray& aBitmap, int aSrcWidth, int aSrcHeight, int aTargetX, int aTargetY) const
178 //TODO: amend loop values so that we don't keep on looping past our frame buffer dimensions.
179 for (int i=0;i<aSrcWidth;i++)
181 for (int j=0;j<aSrcHeight;j++)
183 iFrameNext->SetBitValue((aTargetX+i)*HeightInPixels()+aTargetY+j,aBitmap[+i*aSrcHeight+j]);
189 Clear our client side back buffer.
190 Call to SwapBuffers must follow to actually clear the display.
192 void GP1212A01A::Clear()
194 //memset(iNextFrame->Ptr(),0x00,FrameBufferSizeInBytes());
197 iFrameNext->ClearAll();
206 Set all pixels on our screen to the desired value.
207 This operation is performed off screen to avoid tearing.
208 @param 8 pixels pattern
210 void GP1212A01A::SetAllPixels(unsigned char aPattern)
212 //With a single buffer
213 //unsigned char screen[2048]; //One screen worth of pixels
214 //memset(screen,0xFF,sizeof(screen));
215 //SetPixelBlock(0,0,63,sizeof(screen),screen);
220 memset(iFrameNext->Ptr(),aPattern,FrameBufferSizeInBytes());
224 //Using pattern SetPixelBlock variant.
225 SetPixelBlock(0,0,63,FrameBufferSizeInBytes(),aPattern);
232 Set the defined pixel block to the given value.
233 @param X coordinate of our pixel block starting point.
234 @param Y coordinate of our pixel block starting point.
235 @param The height of our pixel block.
236 @param The size of our pixel data. Number of pixels divided by 8.
237 @param The value set to 8 pixels used as a pattern.
239 void GP1212A01A::SetPixelBlock(unsigned char aX, unsigned char aY, int aHeight, int aSize, unsigned char aValue)
241 OffScreenTranslation(aX,aY);
242 FutabaVfdReport report;
243 report[0]=0x00; //Report ID
244 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
245 report[2]=0x1B; //Command ID
246 report[3]=0x5B; //Command ID
247 report[4]=0xF0; //Command ID
250 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.
251 report[8]=aSize>>8; //Size of pixel data in bytes (MSB)
252 report[9]=aSize; //Size of pixel data in bytes (LSB)
253 int sizeWritten=MIN(aSize,report.Size()-10);
254 memset(report.Buffer()+10, aValue, sizeWritten);
257 int remainingSize=aSize;
258 //We need to keep on sending our pixel data until we are done
259 while (report[1]==64)
262 remainingSize-=sizeWritten;
263 report[0]=0x00; //Report ID
264 report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size
265 sizeWritten=(report[1]==64?63:report[1]);
266 memset(report.Buffer()+2, aValue, sizeWritten);
272 Set the defined pixel block to the given value.
273 @param X coordinate of our pixel block starting point.
274 @param Y coordinate of our pixel block starting point.
275 @param The height of our pixel block.
276 @param The size of our pixel data. Number of pixels divided by 8.
277 @param Pointer to our pixel data.
279 void GP1212A01A::SetPixelBlock(unsigned char aX, unsigned char aY, int aHeight, int aSize, unsigned char* aPixels)
281 OffScreenTranslation(aX,aY);
282 FutabaVfdReport report;
283 report[0]=0x00; //Report ID
284 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
285 report[2]=0x1B; //Command ID
286 report[3]=0x5B; //Command ID
287 report[4]=0xF0; //Command ID
290 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.
291 report[8]=aSize>>8; //Size of pixel data in bytes (MSB)
292 report[9]=aSize; //Size of pixel data in bytes (LSB)
293 int sizeWritten=MIN(aSize,report.Size()-10);
294 memcpy(report.Buffer()+10, aPixels, sizeWritten);
297 int remainingSize=aSize;
298 //We need to keep on sending our pixel data until we are done
299 while (report[1]==64)
302 remainingSize-=sizeWritten;
303 report[0]=0x00; //Report ID
304 report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size
305 sizeWritten=(report[1]==64?63:report[1]);
306 memcpy(report.Buffer()+2, aPixels+(aSize-remainingSize), sizeWritten);
312 Using this function is advised against as is causes tearing.
315 void GP1212A01A::SendClearCommand()
318 //Send Clear Display Command
319 FutabaVfdReport report;
320 report[0]=0x00; //Report ID
321 report[1]=0x04; //Report length
322 report[2]=0x1B; //Command ID
323 report[3]=0x5B; //Command ID
324 report[4]=0x32; //Command ID
325 report[5]=0x4A; //Command ID
330 Change our display position within our buffer.
332 void GP1212A01A::SetDisplayPosition(DW aDw,unsigned char aX, unsigned char aY)
335 //Send Display Position Settings Command
336 FutabaVfdReport report;
337 report[0]=0x00; //Report ID
338 report[1]=0x05; //Report length
339 report[2]=0x1B; //Command ID
340 report[3]=0x5B; //Command ID
341 report[4]=aDw; //Specify our DW
342 report[5]=aX; //X coordinate of our DW top-left corner
343 report[6]=aY; //Y coordinate of our DW top-left corner
348 Change our display position within our buffer.
350 void GP1212A01A::SetDisplayPosition(unsigned char aX, unsigned char aY)
352 //Specs apparently says both DW should remain the same
354 SetDisplayPosition(GP1212A01A::DW1,aX,aY);
355 SetDisplayPosition(GP1212A01A::DW2,aX,aY);
356 iDisplayPositionX=aX;
357 iDisplayPositionY=aY;
361 Provide Y coordinate of our off screen buffer.
363 unsigned char GP1212A01A::OffScreenY() const
365 //Overflowing is fine this is just what we want
366 return iDisplayPositionY+HeightInPixels();
370 Put our off screen buffer on screen.
371 On screen buffer goes off screen.
373 void GP1212A01A::SwapBuffers()
375 //Only perform buffer swapping if off screen mode is enabled
378 //Send host back buffer to device back buffer
379 if (!iUseFrameDifferencing || iNeedFullFrameUpdate<KNumberOfFrameBeforeDiffAlgo)
381 iNeedFullFrameUpdate++;
382 SetPixelBlock(0,0,63,FrameBufferSizeInBytes(),iFrameNext->Ptr());
386 //Frame diff algo is enabled
387 //We are going to send to our device only the differences between next frame and previous frame
388 SendModifiedPixelBlocks();
390 //Swap device front and back buffer
391 SetDisplayPosition(iDisplayPositionX,OffScreenY());
393 //Cycle through our frame buffers
394 //We keep track of previous frame which is in fact our device back buffer.
395 //We can then compare previous and next frame and send only the differences to our device.
396 //This mechanism allows us to reduce traffic over our USB bus thus improving our frame rate from 14 FPS to 30 FPS.
397 //Keep our previous frame pointer
398 BitArray* previousFrame=iFramePrevious;
399 //Current frame becomes the previous one
400 iFramePrevious = iFrameCurrent;
401 //Next frame becomes the current one
402 iFrameCurrent = iFrameNext;
403 //Next frame is now our former previous
404 iFrameNext = previousFrame;
409 //Define the edge of our pixel block
410 //Pixel blocks of 32x32 seems to run almost as fast as full screen update in worse case scenarii.
411 //Though I wonder if in some situations 16 could be better. Make this an attribute at some point if need be.
412 const int KPixelBlockEdge = 32;
413 const int KPixelBlockSizeInBits = KPixelBlockEdge*KPixelBlockEdge;
414 const int KPixelBlockSizeInBytes = KPixelBlockSizeInBits/8;
418 * @brief GP1212A01A::SendModifiedPixelBlocks
419 * Compare our back and front buffer and send to the device only the modified pixels.
421 void GP1212A01A::SendModifiedPixelBlocks()
423 int w=WidthInPixels();
424 int h=HeightInPixels();
427 //TODO: optimize with memcmp and 16 inc
430 for (int i=0;i<w;i++)
432 for (int j=0;j<h;j++)
434 //aX*HeightInPixels()+aY
435 if ((*iFrameNext)[i*h+j]!=(*iFramePrevious)[i*h+j])
437 //We need to update that pixel
438 SetPixelBlock(i,j,0,1,((*iFrameNext)[i*h+j]?0x01:0x00));
439 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
440 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
442 //SetPixelBlock(i,j,15,32,iNextFrame->Ptr()+offset);
448 BitArray nextBlock(KPixelBlockSizeInBits);
449 BitArray previousBlock(KPixelBlockSizeInBits);
451 for (int i=0;i<w;i+=KPixelBlockEdge)
453 for (int j=0;j<h;j+=KPixelBlockEdge)
455 //aX*HeightInPixels()+aY
456 //int offset=(i*w/8)+(j/8);
458 #ifdef DEBUG_FRAME_DIFF
459 QImage imagePrevious(KPixelBlockEdge,KPixelBlockEdge,QImage::Format_RGB32);
460 QImage imageNext(KPixelBlockEdge,KPixelBlockEdge,QImage::Format_RGB32);
463 //Get both our blocks from our buffers
464 for (int x=i;x<i+KPixelBlockEdge;x++)
466 for (int y=j;y<j+KPixelBlockEdge;y++)
468 int blockOffset=(x-i)*KPixelBlockEdge+(y-j);
469 int frameOffset=x*h+y;
470 nextBlock.SetBitValue(blockOffset,(*iFrameNext)[frameOffset]);
471 previousBlock.SetBitValue(blockOffset,(*iFramePrevious)[frameOffset]);
473 #ifdef DEBUG_FRAME_DIFF
474 imageNext.setPixel(x-i,y-j,(nextBlock[blockOffset]?0xFFFFFFFF:0x00000000));
475 imagePrevious.setPixel(x-i,y-j,(previousBlock[blockOffset]?0xFFFFFFFF:0x00000000));
480 #ifdef DEBUG_FRAME_DIFF
481 QString previousName;
483 QTextStream(&previousName) << "p" << i << "x" << j << ".png";
484 QTextStream(&nextName) << "n" << i << "x" << j << ".png";
485 imagePrevious.save(previousName);
486 imageNext.save(nextName);
490 //if (memcmp(iFrameNext->Ptr()+offset,iFramePrevious->Ptr()+offset,32 )) //32=(16*16/8)
491 if (memcmp(nextBlock.Ptr(),previousBlock.Ptr(),KPixelBlockSizeInBytes)!=0)
493 //We need to update that block
494 SetPixelBlock(i,j,KPixelBlockEdge-1,KPixelBlockSizeInBytes,nextBlock.Ptr());
495 //SetPixelBlock(i,j,15,32,0xFF/*nextBlock.Ptr()*/);
496 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
497 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
499 //SetPixelBlock(i,j,15,32,iFrameNext->Ptr()+offset);
507 Translate the given pixel coordinate according to our off screen mode.
509 void GP1212A01A::OffScreenTranslation(unsigned char& aX, unsigned char& aY)
513 aX+=WidthInPixels()-iDisplayPositionX;
514 aY+=HeightInPixels()-iDisplayPositionY;
521 void GP1212A01A::ResetBuffers()
523 //iNextFrame->ClearAll();
524 //memset(iFrameAlpha,0x00,sizeof(iFrameAlpha));
525 //memset(iFrameBeta,0x00,sizeof(iFrameBeta));
530 void GP1212A01A::RequestDeviceId()
532 if (RequestPending())
534 //Abort silently for now
538 //1BH,5BH,63H,49H,44H
539 //Send Read ID 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]=0x49; //Command ID
547 report[6]=0x44; //Command ID
548 if (Write(report)==report.Size())
550 iRequest=ERequestDeviceId;
556 void GP1212A01A::RequestFirmwareRevision()
558 if (RequestPending())
560 //Abort silently for now
564 //1BH,5BH,63H,46H,52H
565 //Send Software Revision Read Command
566 FutabaVfdReport report;
567 report[0]=0x00; //Report ID
568 report[1]=0x05; //Report length
569 report[2]=0x1B; //Command ID
570 report[3]=0x5B; //Command ID
571 report[4]=0x63; //Command ID
572 report[5]=0x46; //Command ID
573 report[6]=0x52; //Command ID
574 if (Write(report)==report.Size())
576 iRequest=ERequestFirmwareRevision;
582 void GP1212A01A::RequestPowerSupplyStatus()
584 if (RequestPending())
586 //Abort silently for now
589 //1BH,5BH,63H,50H,4DH
590 //Send Power Suppply Monitor Command
591 FutabaVfdReport report;
592 report[0]=0x00; //Report ID
593 report[1]=0x05; //Report length
594 report[2]=0x1B; //Command ID
595 report[3]=0x5B; //Command ID
596 report[4]=0x63; //Command ID
597 report[5]=0x50; //Command ID
598 report[6]=0x4D; //Command ID
599 if (Write(report)==report.Size())
601 iRequest=ERequestPowerSupplyStatus;
607 This is for development purposes only.
608 Production application should stick to off-screen mode to avoid tearing.
610 void GP1212A01A::ToggleOffScreenMode()
612 SetOffScreenMode(!iOffScreenMode);
616 * @brief GP1212A01A::SetOffScreenMode
620 void GP1212A01A::SetOffScreenMode(bool aOn)
622 if (aOn==iOffScreenMode)
630 //Clean up our buffers upon switching modes
631 SetDisplayPosition(0,0);
639 GP1212A01A::Request GP1212A01A::AttemptRequestCompletion()
641 if (!RequestPending())
646 int res=Read(iInputReport);
653 //Process our request
654 if (CurrentRequest()==GP1212A01A::ERequestPowerSupplyStatus)
656 if (iInputReport[1]==0x4F && iInputReport[2]==0x4E)
660 else if (iInputReport[1]==0x4F && iInputReport[2]==0x46 && iInputReport[3]==0x46)
666 Request completed=iRequest;
667 //Our request was completed
668 iRequest=ERequestNone;
675 Set our screen brightness.
676 @param The desired brightness level. Must be between MinBrightness and MaxBrightness.
678 void GP1212A01A::SetBrightness(int aBrightness)
680 if (aBrightness<MinBrightness()||aBrightness>MaxBrightness())
682 //Brightness out of range.
683 //Just ignore that request.
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