sl@0: sl@0: #include "FutabaVfd.h" sl@0: //#include sl@0: #include sl@0: sl@0: sl@0: #ifdef DEBUG_FRAME_DIFF sl@0: #include sl@0: #include sl@0: #endif sl@0: sl@0: sl@0: const int KNumberOfFrameBeforeDiffAlgo = 3; sl@0: sl@0: // sl@0: // sl@0: // sl@0: sl@0: FutabaVfdCommand::FutabaVfdCommand():/*iBuffer(NULL),*/iSize(0),iMaxSize(0) sl@0: { sl@0: } sl@0: sl@0: FutabaVfdCommand::~FutabaVfdCommand() sl@0: { sl@0: //Delete(); sl@0: } sl@0: sl@0: sl@0: /** sl@0: sl@0: */ sl@0: void FutabaVfdCommand::Reset() sl@0: { sl@0: memset(iReports,0,sizeof(iReports)); sl@0: } sl@0: sl@0: sl@0: sl@0: /** sl@0: sl@0: */ sl@0: /* sl@0: void FutabaVfdCommand::Create(int aMaxSize) sl@0: { sl@0: iBuffer=new unsigned char[aMaxSize]; sl@0: if (iBuffer) sl@0: { sl@0: iMaxSize = aMaxSize; sl@0: iSize = 0; sl@0: } sl@0: } sl@0: */ sl@0: sl@0: /** sl@0: sl@0: */ sl@0: /* sl@0: void FutabaVfdCommand::Delete() sl@0: { sl@0: delete[] iBuffer; sl@0: iBuffer = NULL; sl@0: iMaxSize = 0; sl@0: iSize = 0; sl@0: } sl@0: */ sl@0: sl@0: sl@0: sl@0: sl@0: // sl@0: // class GP1212A01A sl@0: // sl@0: sl@0: GP1212A01A::GP1212A01A(): sl@0: iDisplayPositionX(0),iDisplayPositionY(0), sl@0: iOffScreenMode(true), sl@0: iUseFrameDifferencing(true), sl@0: iFrameNext(NULL), sl@0: iFrameCurrent(NULL), sl@0: iFramePrevious(NULL), sl@0: iFrameAlpha(NULL), sl@0: iFrameBeta(NULL), sl@0: iFrameGamma(NULL), sl@0: iNeedFullFrameUpdate(0), sl@0: iRequest(ERequestNone),iPowerOn(false) sl@0: { sl@0: //ResetBuffers(); sl@0: } sl@0: sl@0: /** sl@0: */ sl@0: GP1212A01A::~GP1212A01A() sl@0: { sl@0: delete iFrameAlpha; sl@0: iFrameAlpha=NULL; sl@0: // sl@0: delete iFrameBeta; sl@0: iFrameBeta=NULL; sl@0: // sl@0: delete iFrameGamma; sl@0: iFrameGamma=NULL; sl@0: // sl@0: iFrameNext=NULL; sl@0: iFrameCurrent=NULL; sl@0: iFramePrevious=NULL; sl@0: // sl@0: iNeedFullFrameUpdate=0; sl@0: } sl@0: sl@0: /** sl@0: */ sl@0: int GP1212A01A::Open() sl@0: { sl@0: int success = HidDevice::Open(KFutabaVendorId,KFutabaProductIdGP1212A01A,NULL); sl@0: if (success) sl@0: { sl@0: //Allocate both frames sl@0: delete iFrameAlpha; sl@0: iFrameAlpha=NULL; sl@0: iFrameAlpha=new BitArray(KGP12xFrameBufferPixelCount); sl@0: // sl@0: delete iFrameBeta; sl@0: iFrameBeta=NULL; sl@0: iFrameBeta=new BitArray(KGP12xFrameBufferPixelCount); sl@0: // sl@0: delete iFrameGamma; sl@0: iFrameGamma=NULL; sl@0: iFrameGamma=new BitArray(KGP12xFrameBufferPixelCount); sl@0: // sl@0: iFrameNext=iFrameAlpha; sl@0: iFrameCurrent=iFrameBeta; sl@0: iFramePrevious=iFrameGamma; sl@0: sl@0: sl@0: //To make sure it is synced properly sl@0: iNeedFullFrameUpdate=0; sl@0: // sl@0: SetNonBlocking(1); sl@0: //Since we can't get our display position we force it to our default sl@0: //This makes sure frames are in sync from the start sl@0: //Clever clients will have taken care of putting back frame (0,0) before closing sl@0: SetDisplayPosition(iDisplayPositionX,iDisplayPositionY); sl@0: } sl@0: return success; sl@0: } sl@0: sl@0: /** sl@0: */ sl@0: void GP1212A01A::SetPixel(unsigned char aX, unsigned char aY, bool aOn) sl@0: { sl@0: // sl@0: //int byteOffset=(aX*HeightInPixels()+aY)/8; sl@0: //int bitOffset=(aX*HeightInPixels()+aY)%8; sl@0: //iNextFrame[byteOffset] |= ( (aOn?0x01:0x00) << bitOffset ); sl@0: sl@0: if (iOffScreenMode) sl@0: { sl@0: if (aOn) sl@0: { sl@0: iFrameNext->SetBit(aX*HeightInPixels()+aY); sl@0: } sl@0: else sl@0: { sl@0: iFrameNext->ClearBit(aX*HeightInPixels()+aY); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: //Just specify a one pixel block sl@0: SetPixelBlock(aX,aY,0x00,0x01,aOn); sl@0: } sl@0: } sl@0: sl@0: /** sl@0: */ sl@0: void GP1212A01A::BitBlit(const BitArray& aBitmap, int aSrcWidth, int aSrcHeight, int aTargetX, int aTargetY) const sl@0: { sl@0: //TODO: amend loop values so that we don't keep on looping past our frame buffer dimensions. sl@0: for (int i=0;iSetBitValue((aTargetX+i)*HeightInPixels()+aTargetY+j,aBitmap[+i*aSrcHeight+j]); sl@0: } sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Clear our client side back buffer. sl@0: Call to SwapBuffers must follow to actually clear the display. sl@0: */ sl@0: void GP1212A01A::Clear() sl@0: { sl@0: //memset(iNextFrame->Ptr(),0x00,FrameBufferSizeInBytes()); sl@0: if (iOffScreenMode) sl@0: { sl@0: iFrameNext->ClearAll(); sl@0: } sl@0: else sl@0: { sl@0: SendClearCommand(); sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Set all pixels on our screen to the desired value. sl@0: This operation is performed off screen to avoid tearing. sl@0: @param 8 pixels pattern sl@0: */ sl@0: void GP1212A01A::SetAllPixels(unsigned char aPattern) sl@0: { sl@0: //With a single buffer sl@0: //unsigned char screen[2048]; //One screen worth of pixels sl@0: //memset(screen,0xFF,sizeof(screen)); sl@0: //SetPixelBlock(0,0,63,sizeof(screen),screen); sl@0: sl@0: sl@0: if (iOffScreenMode) sl@0: { sl@0: memset(iFrameNext->Ptr(),aPattern,FrameBufferSizeInBytes()); sl@0: } sl@0: else sl@0: { sl@0: //Using pattern SetPixelBlock variant. sl@0: SetPixelBlock(0,0,63,FrameBufferSizeInBytes(),aPattern); sl@0: } sl@0: // sl@0: } sl@0: sl@0: sl@0: /** sl@0: Set the defined pixel block to the given value. sl@0: @param X coordinate of our pixel block starting point. sl@0: @param Y coordinate of our pixel block starting point. sl@0: @param The height of our pixel block. sl@0: @param The size of our pixel data. Number of pixels divided by 8. sl@0: @param The value set to 8 pixels used as a pattern. sl@0: */ sl@0: void GP1212A01A::SetPixelBlock(unsigned char aX, unsigned char aY, int aHeight, int aSize, unsigned char aValue) sl@0: { sl@0: OffScreenTranslation(aX,aY); sl@0: FutabaVfdReport report; sl@0: report[0]=0x00; //Report ID sl@0: 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 sl@0: report[2]=0x1B; //Command ID sl@0: report[3]=0x5B; //Command ID sl@0: report[4]=0xF0; //Command ID sl@0: report[5]=aX; //X sl@0: report[6]=aY; //Y sl@0: 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. sl@0: report[8]=aSize>>8; //Size of pixel data in bytes (MSB) sl@0: report[9]=aSize; //Size of pixel data in bytes (LSB) sl@0: int sizeWritten=MIN(aSize,report.Size()-10); sl@0: memset(report.Buffer()+10, aValue, sizeWritten); sl@0: Write(report); sl@0: sl@0: int remainingSize=aSize; sl@0: //We need to keep on sending our pixel data until we are done sl@0: while (report[1]==64) sl@0: { sl@0: report.Reset(); sl@0: remainingSize-=sizeWritten; sl@0: report[0]=0x00; //Report ID sl@0: report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size sl@0: sizeWritten=(report[1]==64?63:report[1]); sl@0: memset(report.Buffer()+2, aValue, sizeWritten); sl@0: Write(report); sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Set the defined pixel block to the given value. sl@0: @param X coordinate of our pixel block starting point. sl@0: @param Y coordinate of our pixel block starting point. sl@0: @param The height of our pixel block. sl@0: @param The size of our pixel data. Number of pixels divided by 8. sl@0: @param Pointer to our pixel data. sl@0: */ sl@0: void GP1212A01A::SetPixelBlock(unsigned char aX, unsigned char aY, int aHeight, int aSize, unsigned char* aPixels) sl@0: { sl@0: OffScreenTranslation(aX,aY); sl@0: FutabaVfdReport report; sl@0: report[0]=0x00; //Report ID sl@0: 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 sl@0: report[2]=0x1B; //Command ID sl@0: report[3]=0x5B; //Command ID sl@0: report[4]=0xF0; //Command ID sl@0: report[5]=aX; //X sl@0: report[6]=aY; //Y sl@0: 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. sl@0: report[8]=aSize>>8; //Size of pixel data in bytes (MSB) sl@0: report[9]=aSize; //Size of pixel data in bytes (LSB) sl@0: int sizeWritten=MIN(aSize,report.Size()-10); sl@0: memcpy(report.Buffer()+10, aPixels, sizeWritten); sl@0: Write(report); sl@0: sl@0: int remainingSize=aSize; sl@0: //We need to keep on sending our pixel data until we are done sl@0: while (report[1]==64) sl@0: { sl@0: report.Reset(); sl@0: remainingSize-=sizeWritten; sl@0: report[0]=0x00; //Report ID sl@0: report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size sl@0: sizeWritten=(report[1]==64?63:report[1]); sl@0: memcpy(report.Buffer()+2, aPixels+(aSize-remainingSize), sizeWritten); sl@0: Write(report); sl@0: } sl@0: } sl@0: sl@0: /** sl@0: Using this function is advised against as is causes tearing. sl@0: Use Clear instead. sl@0: */ sl@0: void GP1212A01A::SendClearCommand() sl@0: { sl@0: //1BH,5BH,32H,4AH sl@0: //Send Clear Display Command sl@0: FutabaVfdReport report; sl@0: report[0]=0x00; //Report ID sl@0: report[1]=0x04; //Report length sl@0: report[2]=0x1B; //Command ID sl@0: report[3]=0x5B; //Command ID sl@0: report[4]=0x32; //Command ID sl@0: report[5]=0x4A; //Command ID sl@0: Write(report); sl@0: } sl@0: sl@0: /** sl@0: Change our display position within our buffer. sl@0: */ sl@0: void GP1212A01A::SetDisplayPosition(DW aDw,unsigned char aX, unsigned char aY) sl@0: { sl@0: //1BH,5BH,Dw,Px,Py sl@0: //Send Display Position Settings Command sl@0: FutabaVfdReport report; sl@0: report[0]=0x00; //Report ID sl@0: report[1]=0x05; //Report length sl@0: report[2]=0x1B; //Command ID sl@0: report[3]=0x5B; //Command ID sl@0: report[4]=aDw; //Specify our DW sl@0: report[5]=aX; //X coordinate of our DW top-left corner sl@0: report[6]=aY; //Y coordinate of our DW top-left corner sl@0: Write(report); sl@0: } sl@0: sl@0: /** sl@0: Change our display position within our buffer. sl@0: */ sl@0: void GP1212A01A::SetDisplayPosition(unsigned char aX, unsigned char aY) sl@0: { sl@0: //Specs apparently says both DW should remain the same sl@0: //Just don't ask sl@0: SetDisplayPosition(GP1212A01A::DW1,aX,aY); sl@0: SetDisplayPosition(GP1212A01A::DW2,aX,aY); sl@0: iDisplayPositionX=aX; sl@0: iDisplayPositionY=aY; sl@0: } sl@0: sl@0: /** sl@0: Provide Y coordinate of our off screen buffer. sl@0: */ sl@0: unsigned char GP1212A01A::OffScreenY() const sl@0: { sl@0: //Overflowing is fine this is just what we want sl@0: return iDisplayPositionY+HeightInPixels(); sl@0: } sl@0: sl@0: /** sl@0: Put our off screen buffer on screen. sl@0: On screen buffer goes off screen. sl@0: */ sl@0: void GP1212A01A::SwapBuffers() sl@0: { sl@0: //Only perform buffer swapping if off screen mode is enabled sl@0: if (OffScreenMode()) sl@0: { sl@0: //Send host back buffer to device back buffer sl@0: if (!iUseFrameDifferencing || iNeedFullFrameUpdatePtr()); sl@0: } sl@0: else sl@0: { sl@0: //Frame diff algo is enabled sl@0: //We are going to send to our device only the differences between next frame and previous frame sl@0: SendModifiedPixelBlocks(); sl@0: } sl@0: //Swap device front and back buffer sl@0: SetDisplayPosition(iDisplayPositionX,OffScreenY()); sl@0: sl@0: //Cycle through our frame buffers sl@0: //We keep track of previous frame which is in fact our device back buffer. sl@0: //We can then compare previous and next frame and send only the differences to our device. sl@0: //This mechanism allows us to reduce traffic over our USB bus thus improving our frame rate from 14 FPS to 30 FPS. sl@0: //Keep our previous frame pointer sl@0: BitArray* previousFrame=iFramePrevious; sl@0: //Current frame becomes the previous one sl@0: iFramePrevious = iFrameCurrent; sl@0: //Next frame becomes the current one sl@0: iFrameCurrent = iFrameNext; sl@0: //Next frame is now our former previous sl@0: iFrameNext = previousFrame; sl@0: } sl@0: } sl@0: sl@0: sl@0: //Define the edge of our pixel block sl@0: //Pixel blocks of 32x32 seems to run almost as fast as full screen update in worse case scenarii. sl@0: //Though I wonder if in some situations 16 could be better. Make this an attribute at some point if need be. sl@0: const int KPixelBlockEdge = 32; sl@0: const int KPixelBlockSizeInBits = KPixelBlockEdge*KPixelBlockEdge; sl@0: const int KPixelBlockSizeInBytes = KPixelBlockSizeInBits/8; sl@0: sl@0: sl@0: /** sl@0: * @brief GP1212A01A::SendModifiedPixelBlocks sl@0: * Compare our back and front buffer and send to the device only the modified pixels. sl@0: */ sl@0: void GP1212A01A::SendModifiedPixelBlocks() sl@0: { sl@0: int w=WidthInPixels(); sl@0: int h=HeightInPixels(); sl@0: sl@0: sl@0: //TODO: optimize with memcmp and 16 inc sl@0: /* sl@0: sl@0: for (int i=0;iPtr()+offset); sl@0: } sl@0: } sl@0: } sl@0: */ sl@0: sl@0: BitArray nextBlock(KPixelBlockSizeInBits); sl@0: BitArray previousBlock(KPixelBlockSizeInBits); sl@0: sl@0: for (int i=0;iPtr()+offset,iFramePrevious->Ptr()+offset,32 )) //32=(16*16/8) sl@0: if (memcmp(nextBlock.Ptr(),previousBlock.Ptr(),KPixelBlockSizeInBytes)!=0) sl@0: { sl@0: //We need to update that block sl@0: SetPixelBlock(i,j,KPixelBlockEdge-1,KPixelBlockSizeInBytes,nextBlock.Ptr()); sl@0: //SetPixelBlock(i,j,15,32,0xFF/*nextBlock.Ptr()*/); sl@0: //SetDisplayPosition(iDisplayPositionX,OffScreenY()); sl@0: //SetDisplayPosition(iDisplayPositionX,OffScreenY()); sl@0: sl@0: //SetPixelBlock(i,j,15,32,iFrameNext->Ptr()+offset); sl@0: } sl@0: } sl@0: } sl@0: sl@0: } sl@0: sl@0: /** sl@0: Translate the given pixel coordinate according to our off screen mode. sl@0: */ sl@0: void GP1212A01A::OffScreenTranslation(unsigned char& aX, unsigned char& aY) sl@0: { sl@0: if (OffScreenMode()) sl@0: { sl@0: aX+=WidthInPixels()-iDisplayPositionX; sl@0: aY+=HeightInPixels()-iDisplayPositionY; sl@0: } sl@0: } sl@0: sl@0: sl@0: /** sl@0: */ sl@0: void GP1212A01A::ResetBuffers() sl@0: { sl@0: //iNextFrame->ClearAll(); sl@0: //memset(iFrameAlpha,0x00,sizeof(iFrameAlpha)); sl@0: //memset(iFrameBeta,0x00,sizeof(iFrameBeta)); sl@0: } sl@0: sl@0: /** sl@0: */ sl@0: void GP1212A01A::RequestDeviceId() sl@0: { sl@0: if (RequestPending()) sl@0: { sl@0: //Abort silently for now sl@0: return; sl@0: } sl@0: sl@0: //1BH,5BH,63H,49H,44H sl@0: //Send Read ID command sl@0: FutabaVfdReport report; sl@0: report[0]=0x00; //Report ID sl@0: report[1]=0x05; //Report length sl@0: report[2]=0x1B; //Command ID sl@0: report[3]=0x5B; //Command ID sl@0: report[4]=0x63; //Command ID sl@0: report[5]=0x49; //Command ID sl@0: report[6]=0x44; //Command ID sl@0: if (Write(report)==report.Size()) sl@0: { sl@0: iRequest=ERequestDeviceId; sl@0: } sl@0: } sl@0: sl@0: /** sl@0: */ sl@0: void GP1212A01A::RequestFirmwareRevision() sl@0: { sl@0: if (RequestPending()) sl@0: { sl@0: //Abort silently for now sl@0: return; sl@0: } sl@0: sl@0: //1BH,5BH,63H,46H,52H sl@0: //Send Software Revision Read Command sl@0: FutabaVfdReport report; sl@0: report[0]=0x00; //Report ID sl@0: report[1]=0x05; //Report length sl@0: report[2]=0x1B; //Command ID sl@0: report[3]=0x5B; //Command ID sl@0: report[4]=0x63; //Command ID sl@0: report[5]=0x46; //Command ID sl@0: report[6]=0x52; //Command ID sl@0: if (Write(report)==report.Size()) sl@0: { sl@0: iRequest=ERequestFirmwareRevision; sl@0: } sl@0: } sl@0: sl@0: /** sl@0: */ sl@0: void GP1212A01A::RequestPowerSupplyStatus() sl@0: { sl@0: if (RequestPending()) sl@0: { sl@0: //Abort silently for now sl@0: return; sl@0: } sl@0: //1BH,5BH,63H,50H,4DH sl@0: //Send Power Suppply Monitor Command sl@0: FutabaVfdReport report; sl@0: report[0]=0x00; //Report ID sl@0: report[1]=0x05; //Report length sl@0: report[2]=0x1B; //Command ID sl@0: report[3]=0x5B; //Command ID sl@0: report[4]=0x63; //Command ID sl@0: report[5]=0x50; //Command ID sl@0: report[6]=0x4D; //Command ID sl@0: if (Write(report)==report.Size()) sl@0: { sl@0: iRequest=ERequestPowerSupplyStatus; sl@0: } sl@0: } sl@0: sl@0: sl@0: /** sl@0: This is for development purposes only. sl@0: Production application should stick to off-screen mode to avoid tearing. sl@0: */ sl@0: void GP1212A01A::ToggleOffScreenMode() sl@0: { sl@0: SetOffScreenMode(!iOffScreenMode); sl@0: } sl@0: sl@0: /** sl@0: * @brief GP1212A01A::SetOffScreenMode sl@0: * @param aOn sl@0: * @return sl@0: */ sl@0: void GP1212A01A::SetOffScreenMode(bool aOn) sl@0: { sl@0: if (aOn==iOffScreenMode) sl@0: { sl@0: //Nothing to do here sl@0: return; sl@0: } sl@0: sl@0: iOffScreenMode=aOn; sl@0: sl@0: //Clean up our buffers upon switching modes sl@0: SetDisplayPosition(0,0); sl@0: Clear(); sl@0: SwapBuffers(); sl@0: Clear(); sl@0: } sl@0: sl@0: /** sl@0: */ sl@0: GP1212A01A::Request GP1212A01A::AttemptRequestCompletion() sl@0: { sl@0: if (!RequestPending()) sl@0: { sl@0: return ERequestNone; sl@0: } sl@0: sl@0: int res=Read(iInputReport); sl@0: sl@0: if (!res) sl@0: { sl@0: return ERequestNone; sl@0: } sl@0: sl@0: //Process our request sl@0: if (CurrentRequest()==GP1212A01A::ERequestPowerSupplyStatus) sl@0: { sl@0: if (iInputReport[1]==0x4F && iInputReport[2]==0x4E) sl@0: { sl@0: iPowerOn = true; sl@0: } sl@0: else if (iInputReport[1]==0x4F && iInputReport[2]==0x46 && iInputReport[3]==0x46) sl@0: { sl@0: iPowerOn = false; sl@0: } sl@0: } sl@0: sl@0: Request completed=iRequest; sl@0: //Our request was completed sl@0: iRequest=ERequestNone; sl@0: sl@0: return completed; sl@0: } sl@0: sl@0: sl@0: /** sl@0: Set our screen brightness. sl@0: @param The desired brightness level. Must be between MinBrightness and MaxBrightness. sl@0: */ sl@0: void GP1212A01A::SetBrightness(int aBrightness) sl@0: { sl@0: if (aBrightnessMaxBrightness()) sl@0: { sl@0: //Brightness out of range. sl@0: //Just ignore that request. sl@0: return; sl@0: } sl@0: sl@0: FutabaVfdReport report; sl@0: report[0]=0x00; //Report ID sl@0: report[1]=0x06; //Report size sl@0: report[2]=0x1B; //Command ID sl@0: report[3]=0x5C; //Command ID sl@0: report[4]=0x3F; //Command ID sl@0: report[5]=0x4C; //Command ID sl@0: report[6]=0x44; //Command ID sl@0: report[7]=0x30+aBrightness; //Brightness level sl@0: Write(report); sl@0: } sl@0: sl@0: