Working on GP1212A02 support.
5 #include "FutabaGP1212A02.h"
8 const int KNumberOfFrameBeforeDiffAlgo = 3;
14 GP1212A02A::GP1212A02A():
15 iDisplayPositionX(0),iDisplayPositionY(0),
17 iUseFrameDifferencing(true),
24 iNeedFullFrameUpdate(0),
25 iRequest(EMiniDisplayRequestNone),iPowerOn(false)
28 iFirmwareRevision[0]=0;
34 GP1212A02A::~GP1212A02A()
49 iNeedFullFrameUpdate=0;
54 int GP1212A02A::Open()
56 int success = HidDevice::Open(KFutabaVendorId,KFutabaProductIdGP1212A02A,NULL);
59 //Allocate both frames
62 iFrameAlpha=new BitArray(KGP12xFrameBufferPixelCount);
66 iFrameBeta=new BitArray(KGP12xFrameBufferPixelCount);
70 iFrameGamma=new BitArray(KGP12xFrameBufferPixelCount);
72 iFrameNext=iFrameAlpha;
73 iFrameCurrent=iFrameBeta;
74 iFramePrevious=iFrameGamma;
77 //To make sure it is synced properly
78 iNeedFullFrameUpdate=0;
81 //Since we can't get our display position we force it to our default
82 //This makes sure frames are in sync from the start
83 //Clever clients will have taken care of putting back frame (0,0) before closing
84 SetDisplayPosition(iDisplayPositionX,iDisplayPositionY);
91 void GP1212A02A::SetPixel(unsigned char aX, unsigned char aY, bool aOn)
94 //int byteOffset=(aX*HeightInPixels()+aY)/8;
95 //int bitOffset=(aX*HeightInPixels()+aY)%8;
96 //iNextFrame[byteOffset] |= ( (aOn?0x01:0x00) << bitOffset );
102 iFrameNext->SetBit(aX*HeightInPixels()+aY);
106 iFrameNext->ClearBit(aX*HeightInPixels()+aY);
111 //Just specify a one pixel block
112 SetPixelBlock(aX,aY,0x00,0x01,aOn);
118 void GP1212A02A::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]);
131 Clear our client side back buffer.
132 Call to SwapBuffers must follow to actually clear the display.
134 void GP1212A02A::Clear()
136 //memset(iNextFrame->Ptr(),0x00,FrameBufferSizeInBytes());
139 iFrameNext->ClearAll();
149 Must be followed by a SwapBuffers call.
151 void GP1212A02A::Fill()
157 Set all pixels on our screen to the desired value.
158 This operation is performed off screen to avoid tearing.
159 @param 8 pixels pattern
161 void GP1212A02A::SetAllPixels(unsigned char aPattern)
163 //With a single buffer
164 //unsigned char screen[2048]; //One screen worth of pixels
165 //memset(screen,0xFF,sizeof(screen));
166 //SetPixelBlock(0,0,63,sizeof(screen),screen);
171 memset(iFrameNext->Ptr(),aPattern,FrameBufferSizeInBytes());
175 //Using pattern SetPixelBlock variant.
176 SetPixelBlock(0,0,63,FrameBufferSizeInBytes(),aPattern);
183 Set the defined pixel block to the given value.
184 @param X coordinate of our pixel block starting point.
185 @param Y coordinate of our pixel block starting point.
186 @param The height of our pixel block.
187 @param The size of our pixel data. Number of pixels divided by 8.
188 @param The value set to 8 pixels used as a pattern.
190 void GP1212A02A::SetPixelBlock(unsigned char aX, unsigned char aY, int aHeight, int aSize, unsigned char aValue)
192 OffScreenTranslation(aX,aY);
193 FutabaVfdReport report;
194 report[0]=0x00; //Report ID
195 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
196 report[2]=0x1B; //Command ID
197 report[3]=0x5B; //Command ID
198 report[4]=0xF0; //Command ID
201 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.
202 report[8]=aSize>>8; //Size of pixel data in bytes (MSB)
203 report[9]=aSize; //Size of pixel data in bytes (LSB)
204 int sizeWritten=MIN(aSize,report.Size()-10);
205 memset(report.Buffer()+10, aValue, sizeWritten);
208 int remainingSize=aSize;
209 //We need to keep on sending our pixel data until we are done
210 while (report[1]==64)
213 remainingSize-=sizeWritten;
214 report[0]=0x00; //Report ID
215 report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size
216 sizeWritten=(report[1]==64?63:report[1]);
217 memset(report.Buffer()+2, aValue, sizeWritten);
223 Set the defined pixel block to the given value.
224 @param X coordinate of our pixel block starting point.
225 @param Y coordinate of our pixel block starting point.
226 @param The height of our pixel block.
227 @param The size of our pixel data. Number of pixels divided by 8.
228 @param Pointer to our pixel data.
230 void GP1212A02A::SetPixelBlock(unsigned char aX, unsigned char aY, int aHeight, int aSize, unsigned char* aPixels)
232 OffScreenTranslation(aX,aY);
233 FutabaVfdReport report;
234 report[0]=0x00; //Report ID
235 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
236 report[2]=0x1B; //Command ID
237 report[3]=0x5B; //Command ID
238 report[4]=0xF0; //Command ID
241 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.
242 report[8]=aSize>>8; //Size of pixel data in bytes (MSB)
243 report[9]=aSize; //Size of pixel data in bytes (LSB)
244 int sizeWritten=MIN(aSize,report.Size()-10);
245 memcpy(report.Buffer()+10, aPixels, sizeWritten);
248 int remainingSize=aSize;
249 //We need to keep on sending our pixel data until we are done
250 while (report[1]==64)
253 remainingSize-=sizeWritten;
254 report[0]=0x00; //Report ID
255 report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size
256 sizeWritten=(report[1]==64?63:report[1]);
257 memcpy(report.Buffer()+2, aPixels+(aSize-remainingSize), sizeWritten);
263 Using this function is advised against as is causes tearing.
266 void GP1212A02A::SendClearCommand()
269 //Send Clear Display Command
270 FutabaVfdReport report;
271 report[0]=0x00; //Report ID
272 report[1]=0x04; //Report length
273 report[2]=0x1B; //Command ID
274 report[3]=0x5B; //Command ID
275 report[4]=0x32; //Command ID
276 report[5]=0x4A; //Command ID
281 Change our display position within our buffer.
283 void GP1212A02A::SetDisplayPosition(DW aDw,unsigned char aX, unsigned char aY)
286 //Send Display Position Settings Command
287 FutabaVfdReport report;
288 report[0]=0x00; //Report ID
289 report[1]=0x05; //Report length
290 report[2]=0x1B; //Command ID
291 report[3]=0x5B; //Command ID
292 report[4]=aDw; //Specify our DW
293 report[5]=aX; //X coordinate of our DW top-left corner
294 report[6]=aY; //Y coordinate of our DW top-left corner
299 Change our display position within our buffer.
301 void GP1212A02A::SetDisplayPosition(unsigned char aX, unsigned char aY)
303 //Specs apparently says both DW should remain the same
305 SetDisplayPosition(GP1212A02A::DW1,aX,aY);
306 SetDisplayPosition(GP1212A02A::DW2,aX,aY);
307 iDisplayPositionX=aX;
308 iDisplayPositionY=aY;
312 Provide Y coordinate of our off screen buffer.
314 unsigned char GP1212A02A::OffScreenY() const
316 //Overflowing is fine this is just what we want
317 return iDisplayPositionY+HeightInPixels();
321 Put our off screen buffer on screen.
322 On screen buffer goes off screen.
324 void GP1212A02A::SwapBuffers()
326 //Only perform buffer swapping if off screen mode is enabled
329 //Send host back buffer to device back buffer
330 if (!iUseFrameDifferencing || iNeedFullFrameUpdate<KNumberOfFrameBeforeDiffAlgo)
332 iNeedFullFrameUpdate++;
333 SetPixelBlock(0,0,63,FrameBufferSizeInBytes(),iFrameNext->Ptr());
337 //Frame diff algo is enabled
338 //We are going to send to our device only the differences between next frame and previous frame
339 SendModifiedPixelBlocks();
341 //Swap device front and back buffer
342 SetDisplayPosition(iDisplayPositionX,OffScreenY());
344 //Cycle through our frame buffers
345 //We keep track of previous frame which is in fact our device back buffer.
346 //We can then compare previous and next frame and send only the differences to our device.
347 //This mechanism allows us to reduce traffic over our USB bus thus improving our frame rate from 14 FPS to 30 FPS.
348 //Keep our previous frame pointer
349 BitArray* previousFrame=iFramePrevious;
350 //Current frame becomes the previous one
351 iFramePrevious = iFrameCurrent;
352 //Next frame becomes the current one
353 iFrameCurrent = iFrameNext;
354 //Next frame is now our former previous
355 iFrameNext = previousFrame;
360 //Define the edge of our pixel block
361 //Pixel blocks of 32x32 seems to run almost as fast as full screen update in worse case scenarii.
362 //Though I wonder if in some situations 16 could be better. Make this an attribute at some point if need be.
363 const int KPixelBlockEdge = 32;
364 const int KPixelBlockSizeInBits = KPixelBlockEdge*KPixelBlockEdge;
365 const int KPixelBlockSizeInBytes = KPixelBlockSizeInBits/8;
369 * @brief GP1212A02A::SendModifiedPixelBlocks
370 * Compare our back and front buffer and send to the device only the modified pixels.
372 void GP1212A02A::SendModifiedPixelBlocks()
374 int w=WidthInPixels();
375 int h=HeightInPixels();
378 //TODO: optimize with memcmp and 16 inc
381 for (int i=0;i<w;i++)
383 for (int j=0;j<h;j++)
385 //aX*HeightInPixels()+aY
386 if ((*iFrameNext)[i*h+j]!=(*iFramePrevious)[i*h+j])
388 //We need to update that pixel
389 SetPixelBlock(i,j,0,1,((*iFrameNext)[i*h+j]?0x01:0x00));
390 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
391 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
393 //SetPixelBlock(i,j,15,32,iNextFrame->Ptr()+offset);
399 BitArray nextBlock(KPixelBlockSizeInBits);
400 BitArray previousBlock(KPixelBlockSizeInBits);
402 for (int i=0;i<w;i+=KPixelBlockEdge)
404 for (int j=0;j<h;j+=KPixelBlockEdge)
406 //aX*HeightInPixels()+aY
407 //int offset=(i*w/8)+(j/8);
409 #ifdef DEBUG_FRAME_DIFF
410 QImage imagePrevious(KPixelBlockEdge,KPixelBlockEdge,QImage::Format_RGB32);
411 QImage imageNext(KPixelBlockEdge,KPixelBlockEdge,QImage::Format_RGB32);
414 //Get both our blocks from our buffers
415 for (int x=i;x<i+KPixelBlockEdge;x++)
417 for (int y=j;y<j+KPixelBlockEdge;y++)
419 int blockOffset=(x-i)*KPixelBlockEdge+(y-j);
420 int frameOffset=x*h+y;
421 nextBlock.SetBitValue(blockOffset,(*iFrameNext)[frameOffset]);
422 previousBlock.SetBitValue(blockOffset,(*iFramePrevious)[frameOffset]);
424 #ifdef DEBUG_FRAME_DIFF
425 imageNext.setPixel(x-i,y-j,(nextBlock[blockOffset]?0xFFFFFFFF:0x00000000));
426 imagePrevious.setPixel(x-i,y-j,(previousBlock[blockOffset]?0xFFFFFFFF:0x00000000));
431 #ifdef DEBUG_FRAME_DIFF
432 QString previousName;
434 QTextStream(&previousName) << "p" << i << "x" << j << ".png";
435 QTextStream(&nextName) << "n" << i << "x" << j << ".png";
436 imagePrevious.save(previousName);
437 imageNext.save(nextName);
441 //if (memcmp(iFrameNext->Ptr()+offset,iFramePrevious->Ptr()+offset,32 )) //32=(16*16/8)
442 if (memcmp(nextBlock.Ptr(),previousBlock.Ptr(),KPixelBlockSizeInBytes)!=0)
444 //We need to update that block
445 SetPixelBlock(i,j,KPixelBlockEdge-1,KPixelBlockSizeInBytes,nextBlock.Ptr());
446 //SetPixelBlock(i,j,15,32,0xFF/*nextBlock.Ptr()*/);
447 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
448 //SetDisplayPosition(iDisplayPositionX,OffScreenY());
450 //SetPixelBlock(i,j,15,32,iFrameNext->Ptr()+offset);
458 Translate the given pixel coordinate according to our off screen mode.
460 void GP1212A02A::OffScreenTranslation(unsigned char& aX, unsigned char& aY)
464 aX+=WidthInPixels()-iDisplayPositionX;
465 aY+=HeightInPixels()-iDisplayPositionY;
472 void GP1212A02A::ResetBuffers()
474 //iNextFrame->ClearAll();
475 //memset(iFrameAlpha,0x00,sizeof(iFrameAlpha));
476 //memset(iFrameBeta,0x00,sizeof(iFrameBeta));
481 void GP1212A02A::RequestDeviceId()
483 if (RequestPending())
485 //Abort silently for now
489 //1BH,5BH,63H,49H,44H
490 //Send Read ID command
491 FutabaVfdReport report;
492 report[0]=0x00; //Report ID
493 report[1]=0x05; //Report length
494 report[2]=0x1B; //Command ID
495 report[3]=0x5B; //Command ID
496 report[4]=0x63; //Command ID
497 report[5]=0x49; //Command ID
498 report[6]=0x44; //Command ID
499 if (Write(report)==report.Size())
501 iRequest=EMiniDisplayRequestDeviceId;
507 void GP1212A02A::RequestFirmwareRevision()
509 if (RequestPending())
511 //Abort silently for now
515 //1BH,5BH,63H,46H,52H
516 //Send Software Revision Read Command
517 FutabaVfdReport report;
518 report[0]=0x00; //Report ID
519 report[1]=0x05; //Report length
520 report[2]=0x1B; //Command ID
521 report[3]=0x5B; //Command ID
522 report[4]=0x63; //Command ID
523 report[5]=0x46; //Command ID
524 report[6]=0x52; //Command ID
525 if (Write(report)==report.Size())
527 iRequest=EMiniDisplayRequestFirmwareRevision;
533 void GP1212A02A::RequestPowerSupplyStatus()
535 if (RequestPending())
537 //Abort silently for now
540 //1BH,5BH,63H,50H,4DH
541 //Send Power Suppply Monitor 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]=0x50; //Command ID
549 report[6]=0x4D; //Command ID
550 if (Write(report)==report.Size())
552 iRequest=EMiniDisplayRequestPowerSupplyStatus;
558 This is for development purposes only.
559 Production application should stick to off-screen mode to avoid tearing.
561 void GP1212A02A::ToggleOffScreenMode()
563 SetOffScreenMode(!iOffScreenMode);
567 * @brief GP1212A02A::SetOffScreenMode
571 void GP1212A02A::SetOffScreenMode(bool aOn)
573 if (aOn==iOffScreenMode)
581 //Clean up our buffers upon switching modes
582 SetDisplayPosition(0,0);
589 Tries to complete our current request if we have one pending.
591 TMiniDisplayRequest GP1212A02A::AttemptRequestCompletion()
593 if (!RequestPending())
595 return EMiniDisplayRequestNone;
598 int res=Read(iInputReport);
602 return EMiniDisplayRequestNone;
605 //Process our request
606 if (CurrentRequest()==EMiniDisplayRequestPowerSupplyStatus)
608 if (iInputReport[1]==0x4F && iInputReport[2]==0x4E)
612 else if (iInputReport[1]==0x4F && iInputReport[2]==0x46 && iInputReport[3]==0x46)
617 else if (CurrentRequest()==EMiniDisplayRequestDeviceId)
619 unsigned char* ptr=&iInputReport[1];
620 strcpy(iDeviceId,(const char*)ptr);
622 else if (CurrentRequest()==EMiniDisplayRequestFirmwareRevision)
624 unsigned char* ptr=&iInputReport[1];
625 strcpy(iFirmwareRevision,(const char*)ptr);
628 TMiniDisplayRequest completed=iRequest;
629 //Our request was completed
630 iRequest=EMiniDisplayRequestNone;
637 Set our screen brightness.
638 @param The desired brightness level. Must be between MinBrightness and MaxBrightness.
640 void GP1212A02A::SetBrightness(int aBrightness)
642 if (aBrightness<MinBrightness()||aBrightness>MaxBrightness())
644 //Brightness out of range.
645 //Just ignore that request.
649 FutabaVfdReport report;
650 report[0]=0x00; //Report ID
651 report[1]=0x04; //Report size
652 report[2]=0x1B; //Command ID
653 report[3]=0x4A; //Command ID
654 report[4]=0x44; //Command ID
655 report[7]=0x30+aBrightness; //Brightness level
661 bool GP1212A02A::PowerOn()
668 char* GP1212A02A::DeviceId()
675 char* GP1212A02A::FirmwareRevision()
677 return iFirmwareRevision;