Improving support for device ID and Firmware revision. Untested.
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(EMiniDisplayRequestNone),iPowerOn(false)
88 iFirmwareRevision[0]=0;
94 GP1212A01A::~GP1212A01A()
109 iNeedFullFrameUpdate=0;
114 int GP1212A01A::Open()
116 int success = HidDevice::Open(KFutabaVendorId,KFutabaProductIdGP1212A01A,NULL);
119 //Allocate both frames
122 iFrameAlpha=new BitArray(KGP12xFrameBufferPixelCount);
126 iFrameBeta=new BitArray(KGP12xFrameBufferPixelCount);
130 iFrameGamma=new BitArray(KGP12xFrameBufferPixelCount);
132 iFrameNext=iFrameAlpha;
133 iFrameCurrent=iFrameBeta;
134 iFramePrevious=iFrameGamma;
137 //To make sure it is synced properly
138 iNeedFullFrameUpdate=0;
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);
151 void GP1212A01A::SetPixel(unsigned char aX, unsigned char aY, bool aOn)
154 //int byteOffset=(aX*HeightInPixels()+aY)/8;
155 //int bitOffset=(aX*HeightInPixels()+aY)%8;
156 //iNextFrame[byteOffset] |= ( (aOn?0x01:0x00) << bitOffset );
162 iFrameNext->SetBit(aX*HeightInPixels()+aY);
166 iFrameNext->ClearBit(aX*HeightInPixels()+aY);
171 //Just specify a one pixel block
172 SetPixelBlock(aX,aY,0x00,0x01,aOn);
178 void GP1212A01A::BitBlit(const BitArray& aBitmap, int aSrcWidth, int aSrcHeight, int aTargetX, int aTargetY) const
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++)
183 for (int j=0;j<aSrcHeight;j++)
185 iFrameNext->SetBitValue((aTargetX+i)*HeightInPixels()+aTargetY+j,aBitmap[+i*aSrcHeight+j]);
191 Clear our client side back buffer.
192 Call to SwapBuffers must follow to actually clear the display.
194 void GP1212A01A::Clear()
196 //memset(iNextFrame->Ptr(),0x00,FrameBufferSizeInBytes());
199 iFrameNext->ClearAll();
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
212 void GP1212A01A::SetAllPixels(unsigned char aPattern)
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);
222 memset(iFrameNext->Ptr(),aPattern,FrameBufferSizeInBytes());
226 //Using pattern SetPixelBlock variant.
227 SetPixelBlock(0,0,63,FrameBufferSizeInBytes(),aPattern);
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.
241 void GP1212A01A::SetPixelBlock(unsigned char aX, unsigned char aY, int aHeight, int aSize, unsigned char aValue)
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
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);
259 int remainingSize=aSize;
260 //We need to keep on sending our pixel data until we are done
261 while (report[1]==64)
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);
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.
281 void GP1212A01A::SetPixelBlock(unsigned char aX, unsigned char aY, int aHeight, int aSize, unsigned char* aPixels)
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
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);
299 int remainingSize=aSize;
300 //We need to keep on sending our pixel data until we are done
301 while (report[1]==64)
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);
314 Using this function is advised against as is causes tearing.
317 void GP1212A01A::SendClearCommand()
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
332 Change our display position within our buffer.
334 void GP1212A01A::SetDisplayPosition(DW aDw,unsigned char aX, unsigned char aY)
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
350 Change our display position within our buffer.
352 void GP1212A01A::SetDisplayPosition(unsigned char aX, unsigned char aY)
354 //Specs apparently says both DW should remain the same
356 SetDisplayPosition(GP1212A01A::DW1,aX,aY);
357 SetDisplayPosition(GP1212A01A::DW2,aX,aY);
358 iDisplayPositionX=aX;
359 iDisplayPositionY=aY;
363 Provide Y coordinate of our off screen buffer.
365 unsigned char GP1212A01A::OffScreenY() const
367 //Overflowing is fine this is just what we want
368 return iDisplayPositionY+HeightInPixels();
372 Put our off screen buffer on screen.
373 On screen buffer goes off screen.
375 void GP1212A01A::SwapBuffers()
377 //Only perform buffer swapping if off screen mode is enabled
380 //Send host back buffer to device back buffer
381 if (!iUseFrameDifferencing || iNeedFullFrameUpdate<KNumberOfFrameBeforeDiffAlgo)
383 iNeedFullFrameUpdate++;
384 SetPixelBlock(0,0,63,FrameBufferSizeInBytes(),iFrameNext->Ptr());
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();
392 //Swap device front and back buffer
393 SetDisplayPosition(iDisplayPositionX,OffScreenY());
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;
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;
420 * @brief GP1212A01A::SendModifiedPixelBlocks
421 * Compare our back and front buffer and send to the device only the modified pixels.
423 void GP1212A01A::SendModifiedPixelBlocks()
425 int w=WidthInPixels();
426 int h=HeightInPixels();
429 //TODO: optimize with memcmp and 16 inc
432 for (int i=0;i<w;i++)
434 for (int j=0;j<h;j++)
436 //aX*HeightInPixels()+aY
437 if ((*iFrameNext)[i*h+j]!=(*iFramePrevious)[i*h+j])
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());
444 //SetPixelBlock(i,j,15,32,iNextFrame->Ptr()+offset);
450 BitArray nextBlock(KPixelBlockSizeInBits);
451 BitArray previousBlock(KPixelBlockSizeInBits);
453 for (int i=0;i<w;i+=KPixelBlockEdge)
455 for (int j=0;j<h;j+=KPixelBlockEdge)
457 //aX*HeightInPixels()+aY
458 //int offset=(i*w/8)+(j/8);
460 #ifdef DEBUG_FRAME_DIFF
461 QImage imagePrevious(KPixelBlockEdge,KPixelBlockEdge,QImage::Format_RGB32);
462 QImage imageNext(KPixelBlockEdge,KPixelBlockEdge,QImage::Format_RGB32);
465 //Get both our blocks from our buffers
466 for (int x=i;x<i+KPixelBlockEdge;x++)
468 for (int y=j;y<j+KPixelBlockEdge;y++)
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]);
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));
482 #ifdef DEBUG_FRAME_DIFF
483 QString previousName;
485 QTextStream(&previousName) << "p" << i << "x" << j << ".png";
486 QTextStream(&nextName) << "n" << i << "x" << j << ".png";
487 imagePrevious.save(previousName);
488 imageNext.save(nextName);
492 //if (memcmp(iFrameNext->Ptr()+offset,iFramePrevious->Ptr()+offset,32 )) //32=(16*16/8)
493 if (memcmp(nextBlock.Ptr(),previousBlock.Ptr(),KPixelBlockSizeInBytes)!=0)
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());
501 //SetPixelBlock(i,j,15,32,iFrameNext->Ptr()+offset);
509 Translate the given pixel coordinate according to our off screen mode.
511 void GP1212A01A::OffScreenTranslation(unsigned char& aX, unsigned char& aY)
515 aX+=WidthInPixels()-iDisplayPositionX;
516 aY+=HeightInPixels()-iDisplayPositionY;
523 void GP1212A01A::ResetBuffers()
525 //iNextFrame->ClearAll();
526 //memset(iFrameAlpha,0x00,sizeof(iFrameAlpha));
527 //memset(iFrameBeta,0x00,sizeof(iFrameBeta));
532 void GP1212A01A::RequestDeviceId()
534 if (RequestPending())
536 //Abort silently for now
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())
552 iRequest=EMiniDisplayRequestDeviceId;
558 void GP1212A01A::RequestFirmwareRevision()
560 if (RequestPending())
562 //Abort silently for now
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())
578 iRequest=EMiniDisplayRequestFirmwareRevision;
584 void GP1212A01A::RequestPowerSupplyStatus()
586 if (RequestPending())
588 //Abort silently for now
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())
603 iRequest=EMiniDisplayRequestPowerSupplyStatus;
609 This is for development purposes only.
610 Production application should stick to off-screen mode to avoid tearing.
612 void GP1212A01A::ToggleOffScreenMode()
614 SetOffScreenMode(!iOffScreenMode);
618 * @brief GP1212A01A::SetOffScreenMode
622 void GP1212A01A::SetOffScreenMode(bool aOn)
624 if (aOn==iOffScreenMode)
632 //Clean up our buffers upon switching modes
633 SetDisplayPosition(0,0);
641 TMiniDisplayRequest GP1212A01A::AttemptRequestCompletion()
643 if (!RequestPending())
645 return EMiniDisplayRequestNone;
648 int res=Read(iInputReport);
652 return EMiniDisplayRequestNone;
655 //Process our request
656 if (CurrentRequest()==EMiniDisplayRequestPowerSupplyStatus)
658 if (iInputReport[1]==0x4F && iInputReport[2]==0x4E)
662 else if (iInputReport[1]==0x4F && iInputReport[2]==0x46 && iInputReport[3]==0x46)
667 else if (CurrentRequest()==EMiniDisplayRequestDeviceId)
669 unsigned char* ptr=&iInputReport[1];
670 strcpy(iDeviceId,(const char*)ptr);
672 else if (CurrentRequest()==EMiniDisplayRequestFirmwareRevision)
674 unsigned char* ptr=&iInputReport[1];
675 strcpy(iFirmwareRevision,(const char*)ptr);
678 TMiniDisplayRequest completed=iRequest;
679 //Our request was completed
680 iRequest=EMiniDisplayRequestNone;
687 Set our screen brightness.
688 @param The desired brightness level. Must be between MinBrightness and MaxBrightness.
690 void GP1212A01A::SetBrightness(int aBrightness)
692 if (aBrightness<MinBrightness()||aBrightness>MaxBrightness())
694 //Brightness out of range.
695 //Just ignore that request.
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