GP1212A02: Implementing DataMemory frame cycle experiment, no frame rate improvement.
Adding support for firmware revision query.
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, bool aOn)
90 //int byteOffset=(aX*HeightInPixels()+aY)/8;
91 //int bitOffset=(aX*HeightInPixels()+aY)%8;
92 //iNextFrame[byteOffset] |= ( (aOn?0x01:0x00) << bitOffset );
98 iFrameNext->SetBit(aX*HeightInPixels()+aY);
102 iFrameNext->ClearBit(aX*HeightInPixels()+aY);
107 //Just specify a one pixel block
108 SetPixelBlock(aX,aY,0x00,0x01,aOn);
115 void GP1212A01A::BitBlit(const BitArray& aBitmap, int aSrcWidth, int aSrcHeight, int aTargetX, int aTargetY) const
117 //TODO: amend loop values so that we don't keep on looping past our frame buffer dimensions.
118 for (int i=0;i<aSrcWidth;i++)
120 for (int j=0;j<aSrcHeight;j++)
122 iFrameNext->SetBitValue((aTargetX+i)*HeightInPixels()+aTargetY+j,aBitmap[+i*aSrcHeight+j]);
129 Clear our client side back buffer.
130 Call to SwapBuffers must follow to actually clear the display.
132 void GP1212A01A::Clear()
134 //memset(iNextFrame->Ptr(),0x00,FrameBufferSizeInBytes());
137 iFrameNext->ClearAll();
147 Must be followed by a SwapBuffers call.
149 void GP1212A01A::Fill()
155 Set all pixels on our screen to the desired value.
156 This operation is performed off screen to avoid tearing.
157 @param 8 pixels pattern
159 void GP1212A01A::SetAllPixels(unsigned char aPattern)
161 //With a single buffer
162 //unsigned char screen[2048]; //One screen worth of pixels
163 //memset(screen,0xFF,sizeof(screen));
164 //SetPixelBlock(0,0,63,sizeof(screen),screen);
169 memset(iFrameNext->Ptr(),aPattern,FrameBufferSizeInBytes());
173 //Using pattern SetPixelBlock variant.
174 SetPixelBlock(0,0,63,FrameBufferSizeInBytes(),aPattern);
181 Set the defined pixel block to the given value.
182 @param X coordinate of our pixel block starting point.
183 @param Y coordinate of our pixel block starting point.
184 @param The height of our pixel block.
185 @param The size of our pixel data. Number of pixels divided by 8.
186 @param The value set to 8 pixels used as a pattern.
188 void GP1212A01A::SetPixelBlock(unsigned char aX, unsigned char aY, int aHeight, int aSize, unsigned char aValue)
190 OffScreenTranslation(aX,aY);
191 FutabaVfdReport report;
192 report[0]=0x00; //Report ID
193 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
194 report[2]=0x1B; //Command ID
195 report[3]=0x5B; //Command ID
196 report[4]=0xF0; //Command ID
199 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.
200 report[8]=aSize>>8; //Size of pixel data in bytes (MSB)
201 report[9]=aSize; //Size of pixel data in bytes (LSB)
202 int sizeWritten=MIN(aSize,report.Size()-10);
203 memset(report.Buffer()+10, aValue, sizeWritten);
206 int remainingSize=aSize;
207 //We need to keep on sending our pixel data until we are done
208 while (report[1]==64)
211 remainingSize-=sizeWritten;
212 report[0]=0x00; //Report ID
213 report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size
214 sizeWritten=(report[1]==64?63:report[1]);
215 memset(report.Buffer()+2, aValue, sizeWritten);
221 Set the defined pixel block to the given value.
222 @param X coordinate of our pixel block starting point.
223 @param Y coordinate of our pixel block starting point.
224 @param The height of our pixel block.
225 @param The size of our pixel data. Number of pixels divided by 8.
226 @param Pointer to our pixel data.
228 void GP1212A01A::SetPixelBlock(unsigned char aX, unsigned char aY, int aHeight, int aSize, unsigned char* aPixels)
230 OffScreenTranslation(aX,aY);
231 FutabaVfdReport report;
232 report[0]=0x00; //Report ID
233 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
234 report[2]=0x1B; //Command ID
235 report[3]=0x5B; //Command ID
236 report[4]=0xF0; //Command ID
239 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.
240 report[8]=aSize>>8; //Size of pixel data in bytes (MSB)
241 report[9]=aSize; //Size of pixel data in bytes (LSB)
242 int sizeWritten=MIN(aSize,report.Size()-10);
243 memcpy(report.Buffer()+10, aPixels, sizeWritten);
246 int remainingSize=aSize;
247 //We need to keep on sending our pixel data until we are done
248 while (report[1]==64)
251 remainingSize-=sizeWritten;
252 report[0]=0x00; //Report ID
253 report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size
254 sizeWritten=(report[1]==64?63:report[1]);
255 memcpy(report.Buffer()+2, aPixels+(aSize-remainingSize), sizeWritten);
261 Using this function is advised against as is causes tearing.
264 void GP1212A01A::SendClearCommand()
267 //Send Clear Display Command
268 FutabaVfdReport report;
269 report[0]=0x00; //Report ID
270 report[1]=0x04; //Report length
271 report[2]=0x1B; //Command ID
272 report[3]=0x5B; //Command ID
273 report[4]=0x32; //Command ID
274 report[5]=0x4A; //Command ID
279 Change our display position within our buffer.
281 void GP1212A01A::SetDisplayPosition(DW aDw,unsigned char aX, unsigned char aY)
284 //Send Display Position Settings Command
285 FutabaVfdReport report;
286 report[0]=0x00; //Report ID
287 report[1]=0x05; //Report length
288 report[2]=0x1B; //Command ID
289 report[3]=0x5B; //Command ID
290 report[4]=aDw; //Specify our DW
291 report[5]=aX; //X coordinate of our DW top-left corner
292 report[6]=aY; //Y coordinate of our DW top-left corner
297 Change our display position within our buffer.
299 void GP1212A01A::SetDisplayPosition(unsigned char aX, unsigned char aY)
301 //Specs apparently says both DW should remain the same
303 SetDisplayPosition(GP1212A01A::DW1,aX,aY);
304 SetDisplayPosition(GP1212A01A::DW2,aX,aY);
305 iDisplayPositionX=aX;
306 iDisplayPositionY=aY;
310 Provide Y coordinate of our off screen buffer.
312 unsigned char GP1212A01A::OffScreenY() const
314 //Overflowing is fine this is just what we want
315 return iDisplayPositionY+HeightInPixels();
319 Put our off screen buffer on screen.
320 On screen buffer goes off screen.
322 void GP1212A01A::SwapBuffers()
324 //Only perform buffer swapping if off screen mode is enabled
327 //Send host back buffer to device back buffer
328 if (!iUseFrameDifferencing || iNeedFullFrameUpdate<KNumberOfFrameBeforeDiffAlgo)
330 iNeedFullFrameUpdate++;
331 SetPixelBlock(0,0,63,FrameBufferSizeInBytes(),iFrameNext->Ptr());
335 //Frame diff algo is enabled
336 //We are going to send to our device only the differences between next frame and previous frame
337 SendModifiedPixelBlocks();
339 //Swap device front and back buffer
340 SetDisplayPosition(iDisplayPositionX,OffScreenY());
342 //Cycle through our frame buffers
343 //We keep track of previous frame which is in fact our device back buffer.
344 //We can then compare previous and next frame and send only the differences to our device.
345 //This mechanism allows us to reduce traffic over our USB bus thus improving our frame rate from 14 FPS to 30 FPS.
346 //Keep our previous frame pointer
347 BitArrayHigh* previousFrame=iFramePrevious;
348 //Current frame becomes the previous one
349 iFramePrevious = iFrameCurrent;
350 //Next frame becomes the current one
351 iFrameCurrent = iFrameNext;
352 //Next frame is now our former previous
353 iFrameNext = previousFrame;
358 //Define the edge of our pixel block
359 //Pixel blocks of 32x32 seems to run almost as fast as full screen update in worse case scenarii.
360 //Though I wonder if in some situations 16 could be better. Make this an attribute at some point if need be.
361 const int KPixelBlockEdge = 32;
362 const int KPixelBlockSizeInBits = KPixelBlockEdge*KPixelBlockEdge;
363 const int KPixelBlockSizeInBytes = KPixelBlockSizeInBits/8;
367 * @brief GP1212A01A::SendModifiedPixelBlocks
368 * Compare our back and front buffer and send to the device only the modified pixels.
370 void GP1212A01A::SendModifiedPixelBlocks()
372 int w=WidthInPixels();
373 int h=HeightInPixels();
376 //TODO: optimize with memcmp and 16 inc
379 for (int i=0;i<w;i++)
381 for (int j=0;j<h;j++)
383 //aX*HeightInPixels()+aY
384 if ((*iFrameNext)[i*h+j]!=(*iFramePrevious)[i*h+j])
386 //We need to update that pixel
387 SetPixelBlock(i,j,0,1,((*iFrameNext)[i*h+j]?0x01:0x00));
388 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
389 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
391 //SetPixelBlock(i,j,15,32,iNextFrame->Ptr()+offset);
397 BitArrayHigh nextBlock(KPixelBlockSizeInBits);
398 BitArrayHigh previousBlock(KPixelBlockSizeInBits);
400 for (int i=0;i<w;i+=KPixelBlockEdge)
402 for (int j=0;j<h;j+=KPixelBlockEdge)
404 //aX*HeightInPixels()+aY
405 //int offset=(i*w/8)+(j/8);
407 #ifdef DEBUG_FRAME_DIFF
408 QImage imagePrevious(KPixelBlockEdge,KPixelBlockEdge,QImage::Format_RGB32);
409 QImage imageNext(KPixelBlockEdge,KPixelBlockEdge,QImage::Format_RGB32);
412 //Get both our blocks from our buffers
413 for (int x=i;x<i+KPixelBlockEdge;x++)
415 for (int y=j;y<j+KPixelBlockEdge;y++)
417 int blockOffset=(x-i)*KPixelBlockEdge+(y-j);
418 int frameOffset=x*h+y;
419 nextBlock.SetBitValue(blockOffset,(*iFrameNext)[frameOffset]);
420 previousBlock.SetBitValue(blockOffset,(*iFramePrevious)[frameOffset]);
422 #ifdef DEBUG_FRAME_DIFF
423 imageNext.setPixel(x-i,y-j,(nextBlock[blockOffset]?0xFFFFFFFF:0x00000000));
424 imagePrevious.setPixel(x-i,y-j,(previousBlock[blockOffset]?0xFFFFFFFF:0x00000000));
429 #ifdef DEBUG_FRAME_DIFF
430 QString previousName;
432 QTextStream(&previousName) << "p" << i << "x" << j << ".png";
433 QTextStream(&nextName) << "n" << i << "x" << j << ".png";
434 imagePrevious.save(previousName);
435 imageNext.save(nextName);
439 //if (memcmp(iFrameNext->Ptr()+offset,iFramePrevious->Ptr()+offset,32 )) //32=(16*16/8)
440 if (memcmp(nextBlock.Ptr(),previousBlock.Ptr(),KPixelBlockSizeInBytes)!=0)
442 //We need to update that block
443 SetPixelBlock(i,j,KPixelBlockEdge-1,KPixelBlockSizeInBytes,nextBlock.Ptr());
444 //SetPixelBlock(i,j,15,32,0xFF/*nextBlock.Ptr()*/);
445 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
446 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
448 //SetPixelBlock(i,j,15,32,iFrameNext->Ptr()+offset);
456 Translate the given pixel coordinate according to our off screen mode.
458 void GP1212A01A::OffScreenTranslation(unsigned char& aX, unsigned char& aY)
462 aX+=WidthInPixels()-iDisplayPositionX;
463 aY+=HeightInPixels()-iDisplayPositionY;
470 void GP1212A01A::ResetBuffers()
472 //iNextFrame->ClearAll();
473 //memset(iFrameAlpha,0x00,sizeof(iFrameAlpha));
474 //memset(iFrameBeta,0x00,sizeof(iFrameBeta));
480 void GP1212A01A::Request(TMiniDisplayRequest aRequest)
484 case EMiniDisplayRequestDeviceId:
487 case EMiniDisplayRequestFirmwareRevision:
488 RequestFirmwareRevision();
490 case EMiniDisplayRequestPowerSupplyStatus:
491 RequestPowerSupplyStatus();
501 void GP1212A01A::RequestDeviceId()
503 if (RequestPending())
505 //Abort silently for now
509 //1BH,5BH,63H,49H,44H
510 //Send Read ID command
511 FutabaVfdReport report;
512 report[0]=0x00; //Report ID
513 report[1]=0x05; //Report length
514 report[2]=0x1B; //Command ID
515 report[3]=0x5B; //Command ID
516 report[4]=0x63; //Command ID
517 report[5]=0x49; //Command ID
518 report[6]=0x44; //Command ID
519 if (Write(report)==report.Size())
521 SetRequest(EMiniDisplayRequestDeviceId);
527 void GP1212A01A::RequestFirmwareRevision()
529 if (RequestPending())
531 //Abort silently for now
535 //1BH,5BH,63H,46H,52H
536 //Send Software Revision Read Command
537 FutabaVfdReport report;
538 report[0]=0x00; //Report ID
539 report[1]=0x05; //Report length
540 report[2]=0x1B; //Command ID
541 report[3]=0x5B; //Command ID
542 report[4]=0x63; //Command ID
543 report[5]=0x46; //Command ID
544 report[6]=0x52; //Command ID
545 if (Write(report)==report.Size())
547 SetRequest(EMiniDisplayRequestFirmwareRevision);
553 void GP1212A01A::RequestPowerSupplyStatus()
555 if (RequestPending())
557 //Abort silently for now
560 //1BH,5BH,63H,50H,4DH
561 //Send Power Suppply Monitor Command
562 FutabaVfdReport report;
563 report[0]=0x00; //Report ID
564 report[1]=0x05; //Report length
565 report[2]=0x1B; //Command ID
566 report[3]=0x5B; //Command ID
567 report[4]=0x63; //Command ID
568 report[5]=0x50; //Command ID
569 report[6]=0x4D; //Command ID
570 if (Write(report)==report.Size())
572 SetRequest(EMiniDisplayRequestPowerSupplyStatus);
578 This is for development purposes only.
579 Production application should stick to off-screen mode to avoid tearing.
581 void GP1212A01A::ToggleOffScreenMode()
583 SetOffScreenMode(!iOffScreenMode);
587 * @brief GP1212A01A::SetOffScreenMode
591 void GP1212A01A::SetOffScreenMode(bool aOn)
593 if (aOn==iOffScreenMode)
601 //Clean up our buffers upon switching modes
602 SetDisplayPosition(0,0);
609 Tries to complete our current request if we have one pending.
611 TMiniDisplayRequest GP1212A01A::AttemptRequestCompletion()
613 if (!RequestPending())
615 return EMiniDisplayRequestNone;
618 int res=Read(iInputReport);
622 return EMiniDisplayRequestNone;
625 //Process our request
626 if (CurrentRequest()==EMiniDisplayRequestPowerSupplyStatus)
628 if (iInputReport[1]==0x4F && iInputReport[2]==0x4E)
632 else if (iInputReport[1]==0x4F && iInputReport[2]==0x46 && iInputReport[3]==0x46)
637 else if (CurrentRequest()==EMiniDisplayRequestDeviceId)
639 unsigned char* ptr=&iInputReport[1];
640 strcpy(iDeviceId,(const char*)ptr);
642 else if (CurrentRequest()==EMiniDisplayRequestFirmwareRevision)
644 unsigned char* ptr=&iInputReport[1];
645 strcpy(iFirmwareRevision,(const char*)ptr);
648 TMiniDisplayRequest completed=CurrentRequest();
649 //Our request was completed
650 SetRequest(EMiniDisplayRequestNone);
657 Set our screen brightness.
658 @param The desired brightness level. Must be between MinBrightness and MaxBrightness.
660 void GP1212A01A::SetBrightness(int aBrightness)
662 if (aBrightness<MinBrightness()||aBrightness>MaxBrightness())
664 //Brightness out of range.
665 //Just ignore that request.
669 FutabaVfdReport report;
670 report[0]=0x00; //Report ID
671 report[1]=0x06; //Report size
672 report[2]=0x1B; //Command ID
673 report[3]=0x5C; //Command ID
674 report[4]=0x3F; //Command ID
675 report[5]=0x4C; //Command ID
676 report[6]=0x44; //Command ID
677 report[7]=0x30+aBrightness; //Brightness level