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