sl@8: // sl@8: // sl@8: // sl@8: sl@10: #include "FutabaGP1212A02.h" sl@10: sl@17: #include sl@17: #include sl@10: sl@15: const unsigned short KMaxDataMemoryAddress = 0x4FFF; sl@15: const unsigned short KFrameSizeInBytes = 0x800; sl@15: sl@20: StephaneLenclud@25: static void sleep(unsigned int mseconds) sl@20: { sl@20: clock_t goal = mseconds + clock(); sl@20: while (goal > clock()); sl@20: } sl@20: sl@10: // sl@10: // class GP1212A02A sl@10: // sl@10: sl@10: GP1212A02A::GP1212A02A(): sl@10: iDisplayPositionX(0),iDisplayPositionY(0), sl@10: iOffScreenMode(true), sl@10: iUseFrameDifferencing(true), sl@10: iFrameNext(NULL), sl@10: iFrameCurrent(NULL), sl@10: iFramePrevious(NULL), sl@10: iFrameAlpha(NULL), sl@10: iFrameBeta(NULL), sl@10: iFrameGamma(NULL), StephaneLenclud@29: iNeedFullFrameUpdate(0) sl@10: { sl@10: iDeviceId[0]=0; sl@10: iFirmwareRevision[0]=0; sl@10: //ResetBuffers(); sl@10: } sl@10: sl@10: /** sl@10: */ sl@10: GP1212A02A::~GP1212A02A() sl@10: { sl@10: delete iFrameAlpha; sl@10: iFrameAlpha=NULL; sl@10: // sl@10: delete iFrameBeta; sl@10: iFrameBeta=NULL; sl@10: // sl@10: delete iFrameGamma; sl@10: iFrameGamma=NULL; sl@10: // sl@10: iFrameNext=NULL; sl@10: iFrameCurrent=NULL; sl@10: iFramePrevious=NULL; sl@10: // sl@10: iNeedFullFrameUpdate=0; sl@10: } sl@10: sl@10: /** sl@10: */ sl@10: int GP1212A02A::Open() sl@10: { sl@10: int success = HidDevice::Open(KFutabaVendorId,KFutabaProductIdGP1212A02A,NULL); sl@10: if (success) sl@10: { sl@10: //Allocate both frames sl@10: delete iFrameAlpha; sl@10: iFrameAlpha=NULL; sl@13: iFrameAlpha=new BitArrayLow(KGP12xFrameBufferPixelCount); sl@10: // sl@10: delete iFrameBeta; sl@10: iFrameBeta=NULL; sl@13: iFrameBeta=new BitArrayLow(KGP12xFrameBufferPixelCount); sl@10: // sl@10: delete iFrameGamma; sl@10: iFrameGamma=NULL; sl@13: iFrameGamma=new BitArrayLow(KGP12xFrameBufferPixelCount); sl@10: // sl@10: iFrameNext=iFrameAlpha; sl@10: iFrameCurrent=iFrameBeta; sl@10: iFramePrevious=iFrameGamma; sl@10: sl@10: sl@10: //To make sure it is synced properly sl@10: iNeedFullFrameUpdate=0; sl@10: // sl@10: SetNonBlocking(1); sl@13: // sl@17: SendCommandClear(); sl@17: // sl@17: SetClockSetting(); sl@14: sl@17: //BMP box setup could be removed if we don't use it anymore sl@14: //Setup BMP box sl@14: BmpBoxSetting(EBmpBoxIdOne,0x0000,256,64); sl@14: //Select current BMP box sl@14: BmpBoxSelect(EBmpBoxIdOne); sl@17: // sl@17: iNextFrameAddress = 0x0000; sl@14: sl@20: //Beta font test sl@21: /* sl@21: SendCommandFontAction(EFontDelete); sl@21: sl@21: //SendCommandSelectFontSize(EFontLarge); sl@20: //SendCommandReset(); sl@20: sl@21: sl@21: sl@20: unsigned char charPixels[]={ 0xFF,0xFF,0xFF,0xFF, sl@20: 0x80,0x00,0x00,0x01, sl@20: 0x80,0x00,0x00,0x01, sl@20: 0x80,0x00,0x00,0x01, sl@20: 0x80,0x00,0x00,0x01, sl@20: 0x80,0x00,0x00,0x01, sl@20: 0x80,0x00,0x00,0x01, sl@20: 0x81,0xFF,0xFF,0xE1, sl@20: 0x80,0x00,0x00,0x01, sl@20: 0x80,0x00,0x00,0x01, sl@20: 0x80,0x00,0x00,0x01, sl@20: 0x80,0x00,0x00,0x01, sl@20: 0x80,0x00,0x00,0x01, sl@20: 0x80,0x00,0x00,0x01, sl@20: 0x80,0x00,0x00,0x01, sl@20: 0xFF,0xFF,0xFF,0xFF}; sl@20: sl@20: sl@21: //SendCommandFontAction(EFontStore); sl@20: for (unsigned short i=0;i<16;i++) sl@20: { sl@21: //SendCommandFontAction(EFontDelete); sl@21: sl@20: SendCommandDefineCharacter(EFont16x32,0x0030+i,charPixels); sl@20: //SendCommandFontAction(EFontStore); sl@21: sl@21: sl@20: //sleep(100); sl@20: } sl@20: */ sl@21: //SendCommandFontAction(EFontTransfer); sl@20: sl@20: sl@20: //SendCommandDefineCharacter(EFont16x32,0x0031,charPixels); sl@20: //SendCommandFontAction(EFontStore); sl@20: sl@20: sl@20: // sl@20: sl@17: sl@14: sl@10: } sl@10: return success; sl@10: } sl@10: sl@10: /** sl@14: Setting the BMP box sl@14: [Code] 1BH,5CH,42H,Pn,aL,aH,Pw,Ph sl@14: [Function] Setting the BMP box. BMP box can be defined the 3 area to DW. The position of BMP sl@14: box is set based on the address of DW. sl@14: * To write data in BMP box, BMP box select is necessary. sl@14: * Specifiable horizontal size is 256dot (100H) MAX. If horizontal size specify 256dot, Pw = 00H sl@14: Pn = Number of a BMP box sl@14: aL = Lower byte of address sl@14: aH = Upper byte of address sl@14: Pw = BMP box width sl@14: Ph = BMP box height sl@14: sl@14: [Definable area] sl@14: Pn = 31H - BMP box 1 sl@14: Pn = 32H - BMP box 2 sl@14: Pn = 33H - BMP box 3 sl@14: 0000H <= aL + aH * 100 <= 07FFH sl@14: 01H <= Pw <= 00H (=100H) sl@14: 01H <= Ph <= 08H sl@14: */ sl@14: void GP1212A02A::BmpBoxSetting(TBmpBoxId aBoxId, unsigned short aAddress, int aWidth, int aHeight) sl@14: { sl@14: //TODO: check parameters validity sl@14: //1BH,5CH,42H,Pn,aL,aH,Pw,Ph sl@14: FutabaVfdReport report; sl@14: report[0]=0x00; //Report ID sl@14: report[1]=0x08; //Report length. sl@14: report[2]=0x1B; //Command ID sl@14: report[3]=0x5C; //Command ID sl@14: report[4]=0x42; //Command ID sl@14: report[5]=aBoxId; sl@14: report[6]=(unsigned char)aAddress; //aL = DM lower byte sl@14: report[7]=aAddress>>8; //aH = DM upper byte sl@14: report[8]=(aWidth==256?0x00:aWidth); //Pw = BMP box width 00==256 sl@14: report[9]=aHeight/8; //Ph = BMP box height. sl@14: Write(report); sl@14: } sl@14: sl@14: /** sl@14: [Code]1BH,5CH,48H,Pn sl@14: [Function]Select of BMP box sl@14: * Execution "BMP box select" is necessary before "Setting the Text box". sl@14: * In case of writing by the specified dot writing, it is necessary to cancel this command. sl@14: [Definable area] sl@14: Pn = 30H - Remove the BMP box sl@14: Pn = 31H - BMP box 1 sl@14: Pn = 32H - BMP box 2 sl@14: Pn = 33H - BMP box 3 sl@14: */ sl@14: void GP1212A02A::BmpBoxSelect(TBmpBoxId aBoxId) sl@14: { sl@14: //TODO: check parameters validity sl@14: FutabaVfdReport report; sl@14: report[0]=0x00; //Report ID sl@14: report[1]=0x04; //Report length. sl@14: report[2]=0x1B; //Command ID sl@14: report[3]=0x5C; //Command ID sl@14: report[4]=0x48; //Command ID sl@14: report[5]=aBoxId; //BMP box ID sl@14: Write(report); sl@14: } sl@14: sl@14: sl@14: /** sl@10: */ sl@22: void GP1212A02A::SetPixel(unsigned char aX, unsigned char aY, unsigned int aPixel) sl@10: { sl@10: // sl@10: //int byteOffset=(aX*HeightInPixels()+aY)/8; sl@10: //int bitOffset=(aX*HeightInPixels()+aY)%8; sl@10: //iNextFrame[byteOffset] |= ( (aOn?0x01:0x00) << bitOffset ); sl@10: sl@22: //Pixel is on if any of the non-alpha component is not null sl@22: bool on = (aPixel&0x00FFFFFF)!=0x00000000; sl@22: sl@10: if (iOffScreenMode) sl@10: { sl@22: if (on) sl@10: { sl@10: iFrameNext->SetBit(aX*HeightInPixels()+aY); sl@10: } sl@10: else sl@10: { sl@10: iFrameNext->ClearBit(aX*HeightInPixels()+aY); sl@10: } sl@10: } sl@10: else sl@10: { sl@10: //Just specify a one pixel block sl@13: //TODO sl@10: } sl@10: } sl@10: sl@10: /** sl@10: */ sl@13: /* sl@10: void GP1212A02A::BitBlit(const BitArray& aBitmap, int aSrcWidth, int aSrcHeight, int aTargetX, int aTargetY) const sl@10: { sl@10: //TODO: amend loop values so that we don't keep on looping past our frame buffer dimensions. sl@10: for (int i=0;iSetBitValue((aTargetX+i)*HeightInPixels()+aTargetY+j,aBitmap[+i*aSrcHeight+j]); sl@10: } sl@10: } sl@10: } sl@13: */ sl@10: sl@10: /** sl@10: Clear our client side back buffer. sl@10: Call to SwapBuffers must follow to actually clear the display. sl@10: */ sl@10: void GP1212A02A::Clear() sl@10: { sl@10: //memset(iNextFrame->Ptr(),0x00,FrameBufferSizeInBytes()); sl@10: if (iOffScreenMode) sl@10: { sl@10: iFrameNext->ClearAll(); sl@10: } sl@10: else sl@10: { sl@17: SendCommandClear(); sl@10: } sl@10: } sl@10: sl@10: /** sl@10: Turn on all pixels. sl@10: Must be followed by a SwapBuffers call. sl@10: */ sl@10: void GP1212A02A::Fill() sl@10: { sl@10: SetAllPixels(0xFF); sl@10: } sl@10: sl@10: /** sl@10: Set all pixels on our screen to the desired value. sl@10: This operation is performed off screen to avoid tearing. sl@10: @param 8 pixels pattern sl@10: */ sl@10: void GP1212A02A::SetAllPixels(unsigned char aPattern) sl@10: { sl@10: //With a single buffer sl@10: //unsigned char screen[2048]; //One screen worth of pixels sl@10: //memset(screen,0xFF,sizeof(screen)); sl@10: //SetPixelBlock(0,0,63,sizeof(screen),screen); sl@10: sl@10: sl@10: if (iOffScreenMode) sl@10: { sl@10: memset(iFrameNext->Ptr(),aPattern,FrameBufferSizeInBytes()); sl@10: } sl@10: else sl@10: { sl@10: //Using pattern SetPixelBlock variant. sl@13: //TODO sl@10: } sl@10: // sl@10: } sl@10: sl@10: sl@13: sl@13: sl@10: /** sl@14: BMP data input sl@14: [Code] 1BH,4AH,Pm,aL,aH,Ps,nL,nH,Pd...Pd sl@14: [Function] The BMP data is written in the DW(Display Window) or the Data memory. sl@14: Pm= DW or Data memory sl@14: aL = DW lower byte sl@14: aH = DW upper byte sl@14: Ps = Direction of writing sl@14: nL = number of BMP data length lower byte sl@14: nH = number of BMP data length upper byte sl@14: Pd = BMP data sl@14: * If X direction is selected as Ps and data is written in the last address, the data in the last address is sl@14: overwritten with the remaining data. sl@14: [Definable area] Pm = 30H : DW sl@14: Pm = 31H: Data memory sl@14: 0000H <= aL + aH * 100 <= 07FFH (DW) sl@14: 0000H <= aL + aH * 100 <= 4FFFH (Data memory) sl@14: Ps = 30H: Y direction sl@14: Ps = 31H: X direction sl@14: 0001H <= nL + nH * 100 <= 0100H(DW: X direction) sl@14: 0001H <= nL + nH * 100 <= 0800H(DW: Y direction) sl@14: 0001H <= nL + nH * 100 <= 0A00H(Data memory: X direction) sl@14: 0001H <= nL + nH * 100 <= 5000H(Data memory: Y direction) sl@10: */ sl@14: void GP1212A02A::BmpDataInput(TTarget aTarget, unsigned short aAddress, TDirection aDirection, unsigned short aSize, unsigned char* aPixels) sl@13: { sl@13: FutabaVfdReport report; sl@10: report[0]=0x00; //Report ID sl@10: 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@10: report[2]=0x1B; //Command ID sl@13: report[3]=0x4A; //Command ID sl@14: report[4]=aTarget; //Display Window or Data Memory sl@14: report[5]=(unsigned char)aAddress; //aL = DW lower byte sl@14: report[6]=aAddress>>8; //aH = DW upper byte sl@14: report[7]=aDirection; //Direction of writing: Y or X sl@14: report[8]=(unsigned char)aSize; //Size of pixel data in bytes (LSB) sl@13: report[9]=aSize>>8; //Size of pixel data in bytes (MSB) sl@10: int sizeWritten=MIN(aSize,report.Size()-10); sl@10: memcpy(report.Buffer()+10, aPixels, sizeWritten); sl@10: Write(report); sl@10: sl@10: int remainingSize=aSize; sl@10: //We need to keep on sending our pixel data until we are done sl@10: while (report[1]==64) sl@10: { sl@10: report.Reset(); sl@10: remainingSize-=sizeWritten; sl@10: report[0]=0x00; //Report ID sl@10: report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size sl@10: sizeWritten=(report[1]==64?63:report[1]); sl@10: memcpy(report.Buffer()+2, aPixels+(aSize-remainingSize), sizeWritten); sl@10: Write(report); sl@10: } sl@13: } sl@10: sl@14: sl@14: /** sl@14: Data memory transfer sl@14: [Code] 1BH,5CH,44H,aL,aH sl@14: [Function] BMP data transfer from Data memory to DW. sl@14: Although source data is updated, data in BMP box is not updated. To reflect the update, sl@14: re-executing this command is necessary. sl@14: aL = Lower byte of address sl@14: aH = Upper byte of address sl@14: [Definable area] sl@14: 0000H <= aL + aH * 100 <= 4FFFH sl@14: */ sl@14: void GP1212A02A::BmpBoxDataMemoryTransfer(unsigned short aAddress) sl@14: { sl@14: FutabaVfdReport report; sl@14: report[0]=0x00; //Report ID sl@14: report[1]=0x05; //Report length. sl@14: report[2]=0x1B; //Command ID sl@14: report[3]=0x5C; //Command ID sl@14: report[4]=0x44; //Command ID sl@14: report[5]=(unsigned char)aAddress; //aL = DM lower byte sl@14: report[6]=aAddress>>8; //aH = DM upper byte sl@14: Write(report); sl@14: } sl@14: sl@14: /** sl@14: Input BMP data in the BMP box sl@14: [Code] 1BH,5CH,5DH,nL,nH,Pd...Pd sl@14: [Function] BMP data is written the BMP box sl@14: * Number of definable data is due to BMP box size. If the data is over range, the over range data is sl@14: rewritten the final address. sl@14: nL = Lower byte of number of definition byte sl@14: nH = Upper byte of number of definition byte sl@14: Pd = BMP data sl@14: [Definable area] Pn : BMP box size (Pw * Ph) sl@14: */ sl@14: void GP1212A02A::BmpBoxDataInput(unsigned short aSize, unsigned char* aPixels) sl@14: { sl@14: FutabaVfdReport report; sl@14: report[0]=0x00; //Report ID sl@14: report[1]=(aSize<=report.Size()-7?aSize+0x05:64); //Report length. -7 is for our header first 10 bytes. +5 is for our Futaba header size sl@14: report[2]=0x1B; //Command ID sl@14: report[3]=0x5C; //Command ID sl@14: report[4]=0x5D; //Display Window or Data Memory sl@14: report[5]=(unsigned char)aSize; //Size of pixel data in bytes (LSB) sl@14: report[6]=aSize>>8; //Size of pixel data in bytes (MSB) sl@14: int sizeWritten=MIN(aSize,report.Size()-7); sl@14: memcpy(report.Buffer()+7, aPixels, sizeWritten); sl@14: Write(report); sl@14: sl@14: int remainingSize=aSize; sl@14: //We need to keep on sending our pixel data until we are done sl@14: while (report[1]==64) sl@14: { sl@14: report.Reset(); sl@14: remainingSize-=sizeWritten; sl@14: report[0]=0x00; //Report ID sl@14: report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size sl@14: sizeWritten=(report[1]==64?63:report[1]); sl@14: memcpy(report.Buffer()+2, aPixels+(aSize-remainingSize), sizeWritten); sl@14: Write(report); sl@14: } sl@14: } sl@14: sl@10: /** sl@10: Using this function is advised against as is causes tearing. sl@10: Use Clear instead. sl@10: */ sl@17: void GP1212A02A::SendCommandClear() sl@10: { sl@13: //1BH,4AH,43H,44H sl@10: //Send Clear Display Command sl@10: FutabaVfdReport report; sl@10: report[0]=0x00; //Report ID sl@10: report[1]=0x04; //Report length sl@10: report[2]=0x1B; //Command ID sl@13: report[3]=0x4A; //Command ID sl@13: report[4]=0x43; //Command ID sl@13: report[5]=0x44; //Command ID sl@10: Write(report); sl@10: } sl@10: sl@10: sl@10: /** sl@20: Returns to default setting. sl@20: * The other command is not receive until this command complete. Please don’t send the any data sl@20: from a host during “BUSY” sl@20: * Delete the User definable font to the RAM. sl@20: * If the VFD Power Off, VFD Power turn ON after the RESET command. sl@20: */ sl@20: void GP1212A02A::SendCommandReset() sl@20: { sl@20: //1BH,4AH,43H,44H sl@20: //Send Clear Display Command sl@20: FutabaVfdReport report; sl@20: report[0]=0x00; //Report ID sl@20: report[1]=0x04; //Report length sl@20: report[2]=0x1B; //Command ID sl@20: report[3]=0x4A; //Command ID sl@20: report[4]=0x52; //Command ID sl@20: report[5]=0x53; //Command ID sl@20: Write(report); sl@20: //Wait until reset is done. Is that needed? sl@20: sleep(2000); sl@20: sl@20: } sl@20: sl@20: sl@20: /** sl@10: Provide Y coordinate of our off screen buffer. sl@10: */ sl@10: unsigned char GP1212A02A::OffScreenY() const sl@10: { sl@10: //Overflowing is fine this is just what we want sl@10: return iDisplayPositionY+HeightInPixels(); sl@10: } sl@10: sl@10: /** sl@10: Put our off screen buffer on screen. sl@10: On screen buffer goes off screen. sl@10: */ sl@10: void GP1212A02A::SwapBuffers() sl@10: { sl@10: //Only perform buffer swapping if off screen mode is enabled sl@10: if (OffScreenMode()) sl@10: { sl@14: //Send pixel directly into BMP box sl@15: //BmpBoxDataInput(FrameBufferSizeInBytes(),iFrameNext->Ptr()); StephaneLenclud@23: StephaneLenclud@23: //This appears to be the fastest scheme when running on our HTPC sl@14: //Send pixel data directly into the display window StephaneLenclud@23: BmpDataInput(ETargetDisplayWindow,0x0000,EDirectionY, FrameBufferSizeInBytes(),iFrameNext->Ptr()); StephaneLenclud@23: sl@14: //Send pixel data first to Data Memory then copy into the selected BMP box sl@14: //BmpDataInput(ETargetDataMemory,0x0000,EDirectionY, FrameBufferSizeInBytes(),iFrameNext->Ptr()); sl@14: //BmpBoxDataMemoryTransfer(0x0000); StephaneLenclud@23: StephaneLenclud@23: //Send pixel data first to Data Memory then copy into the selected BMP box, cycling through our Data Memory frame StephaneLenclud@23: #if 0 sl@15: BmpDataInput(ETargetDataMemory,iNextFrameAddress,EDirectionY, FrameBufferSizeInBytes(),iFrameNext->Ptr()); sl@15: BmpBoxDataMemoryTransfer(iNextFrameAddress); sl@15: iNextFrameAddress+=KFrameSizeInBytes; sl@15: if (iNextFrameAddress>KMaxDataMemoryAddress) sl@15: { sl@15: iNextFrameAddress=0x0000; sl@15: } StephaneLenclud@23: #endif sl@10: sl@10: //Cycle through our frame buffers sl@10: //We keep track of previous frame which is in fact our device back buffer. sl@10: //We can then compare previous and next frame and send only the differences to our device. sl@10: //This mechanism allows us to reduce traffic over our USB bus thus improving our frame rate from 14 FPS to 30 FPS. sl@10: //Keep our previous frame pointer sl@13: BitArrayLow* previousFrame=iFramePrevious; sl@10: //Current frame becomes the previous one sl@10: iFramePrevious = iFrameCurrent; sl@10: //Next frame becomes the current one sl@10: iFrameCurrent = iFrameNext; sl@10: //Next frame is now our former previous sl@10: iFrameNext = previousFrame; sl@10: } sl@10: } sl@10: sl@10: sl@10: //Define the edge of our pixel block sl@10: //Pixel blocks of 32x32 seems to run almost as fast as full screen update in worse case scenarii. sl@10: //Though I wonder if in some situations 16 could be better. Make this an attribute at some point if need be. sl@10: const int KPixelBlockEdge = 32; sl@10: const int KPixelBlockSizeInBits = KPixelBlockEdge*KPixelBlockEdge; sl@10: const int KPixelBlockSizeInBytes = KPixelBlockSizeInBits/8; sl@10: sl@10: sl@10: /** sl@10: Translate the given pixel coordinate according to our off screen mode. sl@10: */ sl@10: void GP1212A02A::OffScreenTranslation(unsigned char& aX, unsigned char& aY) sl@10: { sl@10: if (OffScreenMode()) sl@10: { sl@10: aX+=WidthInPixels()-iDisplayPositionX; sl@10: aY+=HeightInPixels()-iDisplayPositionY; sl@10: } sl@10: } sl@10: sl@10: sl@10: /** sl@10: */ sl@15: void GP1212A02A::Request(TMiniDisplayRequest aRequest) sl@15: { sl@15: switch (aRequest) sl@15: { sl@15: case EMiniDisplayRequestDeviceId: sl@15: RequestDeviceId(); sl@15: break; sl@15: case EMiniDisplayRequestFirmwareRevision: sl@15: RequestFirmwareRevision(); sl@15: break; sl@15: case EMiniDisplayRequestPowerSupplyStatus: sl@15: RequestPowerSupplyStatus(); sl@15: break; sl@15: default: sl@15: //Not supported sl@15: break; sl@15: }; sl@15: } sl@15: sl@15: sl@15: /** sl@15: */ sl@10: void GP1212A02A::ResetBuffers() sl@10: { sl@10: //iNextFrame->ClearAll(); sl@10: //memset(iFrameAlpha,0x00,sizeof(iFrameAlpha)); sl@10: //memset(iFrameBeta,0x00,sizeof(iFrameBeta)); sl@10: } sl@10: sl@10: /** sl@10: */ sl@10: void GP1212A02A::RequestDeviceId() sl@10: { sl@11: //Not supported sl@10: } sl@10: sl@10: /** sl@15: ID code sl@15: [Code] 1BH,6AH,49H,44H sl@15: [Function] Send the ID code to the Host system. ID code is software version. sl@10: */ sl@10: void GP1212A02A::RequestFirmwareRevision() sl@10: { sl@15: if (RequestPending()) sl@15: { sl@15: //Abort silently for now sl@15: return; sl@15: } sl@15: sl@15: //1BH,6AH,49H,44H sl@15: //Send Software Revision Read Command sl@15: FutabaVfdReport report; sl@15: report[0]=0x00; //Report ID sl@15: report[1]=0x04; //Report length sl@15: report[2]=0x1B; //Command ID sl@15: report[3]=0x6A; //Command ID sl@15: report[4]=0x49; //Command ID sl@15: report[5]=0x44; //Command ID sl@15: if (Write(report)==report.Size()) sl@15: { sl@15: SetRequest(EMiniDisplayRequestFirmwareRevision); sl@15: } sl@15: sl@10: } sl@10: sl@10: /** sl@10: */ sl@10: void GP1212A02A::RequestPowerSupplyStatus() sl@10: { sl@11: //Not supported sl@10: } sl@10: sl@10: sl@10: /** sl@10: This is for development purposes only. sl@10: Production application should stick to off-screen mode to avoid tearing. sl@10: */ sl@10: void GP1212A02A::ToggleOffScreenMode() sl@10: { sl@10: SetOffScreenMode(!iOffScreenMode); sl@10: } sl@10: sl@10: /** sl@10: * @brief GP1212A02A::SetOffScreenMode sl@10: * @param aOn sl@10: * @return sl@10: */ sl@10: void GP1212A02A::SetOffScreenMode(bool aOn) sl@10: { sl@10: if (aOn==iOffScreenMode) sl@10: { sl@10: //Nothing to do here sl@10: return; sl@10: } sl@10: sl@10: iOffScreenMode=aOn; sl@10: sl@10: //Clean up our buffers upon switching modes sl@10: Clear(); sl@10: SwapBuffers(); sl@10: Clear(); sl@10: } sl@10: sl@10: /** sl@10: Tries to complete our current request if we have one pending. sl@10: */ sl@10: TMiniDisplayRequest GP1212A02A::AttemptRequestCompletion() sl@10: { sl@15: if (!RequestPending()) sl@15: { sl@15: return EMiniDisplayRequestNone; sl@15: } sl@15: sl@15: int res=Read(iInputReport); sl@15: sl@15: if (!res) sl@15: { sl@15: return EMiniDisplayRequestNone; sl@15: } sl@15: sl@15: //Process our request sl@15: if (CurrentRequest()==EMiniDisplayRequestFirmwareRevision) sl@15: { sl@15: unsigned char* ptr=&iInputReport[2]; sl@15: iInputReport[7]=0x00; sl@15: strcpy(iFirmwareRevision,(const char*)ptr); sl@15: } sl@15: sl@15: TMiniDisplayRequest completed=CurrentRequest(); sl@15: //Our request was completed sl@15: SetRequest(EMiniDisplayRequestNone); sl@15: sl@15: return completed; sl@11: } sl@10: sl@10: sl@10: /** sl@10: Set our screen brightness. sl@10: @param The desired brightness level. Must be between MinBrightness and MaxBrightness. sl@10: */ sl@10: void GP1212A02A::SetBrightness(int aBrightness) sl@10: { sl@10: if (aBrightnessMaxBrightness()) sl@10: { sl@10: //Brightness out of range. sl@10: //Just ignore that request. sl@10: return; sl@10: } sl@10: sl@10: FutabaVfdReport report; sl@10: report[0]=0x00; //Report ID sl@10: report[1]=0x04; //Report size sl@10: report[2]=0x1B; //Command ID sl@10: report[3]=0x4A; //Command ID sl@10: report[4]=0x44; //Command ID sl@13: report[5]=0x30+aBrightness; //Brightness level sl@10: Write(report); sl@10: } sl@10: sl@10: /** sl@16: VFD Power ON/OFF sl@16: [Code]1BH,4AH,42H,Ps sl@16: [Function]Control of the power supply for VFD sl@16: * If VFD power ON or OFF, at interval of 10s or more. sl@16: * When the VFD power off, VFD display is turn off, but the module can receive a data and sl@16: process. sl@16: Ps = VFD Power control sl@16: [Definable area] sl@16: Ps = 30H : VFD Power OFF sl@16: Ps = 31H : VFD Power ON (Default) sl@16: */ sl@16: void GP1212A02A::SendCommandPower(TPowerStatus aPowerStatus) sl@16: { sl@16: FutabaVfdReport report; sl@16: report[0]=0x00; //Report ID sl@16: report[1]=0x04; //Report size sl@16: report[2]=0x1B; //Command ID sl@16: report[3]=0x4A; //Command ID sl@16: report[4]=0x42; //Command ID sl@16: report[5]=aPowerStatus; //ON or OFF sl@16: Write(report); sl@16: } sl@16: sl@16: /** sl@16: */ sl@16: void GP1212A02A::TurnPowerOn() sl@16: { sl@16: SendCommandPower(EPowerOn); sl@17: SetClockSetting(); sl@16: } sl@16: sl@16: /** sl@16: */ sl@16: void GP1212A02A::TurnPowerOff() sl@16: { sl@16: SendCommandPower(EPowerOff); sl@16: } sl@17: sl@17: sl@17: /** sl@20: Provide the length of our character string for the given clock format. sl@20: @param The clock format to evaluate. sl@20: @return Number of characters for the given clock format. sl@17: */ sl@17: int GP1212A02A::ClockCharCount(TClockFormat aFormat) sl@17: { sl@17: switch (aFormat) sl@17: { sl@17: case EClockDay12: sl@19: return 13; sl@17: case EClockDay24: sl@17: return 10; sl@17: case EClock12: sl@19: return 8; sl@17: case EClock24: sl@17: return 5; sl@17: } sl@17: sl@17: return 10; sl@17: } sl@17: sl@17: /** sl@20: @return Clock character width in pixels. sl@17: */ sl@21: int GP1212A02A::ClockCharWidthInPixels(TFontSizeLogical aSize) sl@17: { sl@17: switch (aSize) sl@17: { sl@21: case EFontTiny: sl@17: return 6; sl@21: case EFontSmall: sl@17: return 8; sl@21: case EFontMedium: sl@17: return 12; sl@21: case EFontLarge: sl@17: return 16; sl@17: } sl@17: sl@17: return 16; sl@17: } sl@17: sl@17: /** sl@20: @return Clock character height in pixels. sl@17: */ sl@21: int GP1212A02A::ClockCharHeightInPixels(TFontSizeLogical aSize) sl@17: { sl@17: switch (aSize) sl@17: { sl@21: case EFontTiny: sl@17: return 8; sl@21: case EFontSmall: sl@17: return 16; sl@21: case EFontMedium: sl@17: return 24; sl@21: case EFontLarge: sl@17: return 32; sl@17: } sl@17: sl@17: return 32; sl@17: } sl@17: sl@17: /** sl@17: Return the Display Window address for centering the clock corresponding to the given parameters. sl@17: */ sl@21: unsigned short GP1212A02A::ClockCenterAddress(TClockFormat aFormat, TFontSizeLogical aSize) sl@17: { sl@17: int charCount=ClockCharCount(aFormat); sl@17: int halfWidth=(ClockCharWidthInPixels(aSize)*charCount)/2; sl@17: int halfHeight=(ClockCharHeightInPixels(aSize))/2; sl@17: int x=(WidthInPixels()/2)-halfWidth; sl@17: int y=(HeightInPixels()/2)-halfHeight; sl@17: sl@17: int yOffset=y/8; sl@17: int xOffset=x*8; //Not sure why... sl@17: sl@17: unsigned short address = yOffset+xOffset; sl@17: // sl@17: return address; sl@17: } sl@17: sl@17: /** sl@17: */ sl@17: void GP1212A02A::ShowClock() sl@17: { sl@21: SendCommandClockDisplay(EClock24,ClockCenterAddress(EClock24,EFontLarge),EFontLarge); sl@17: } sl@17: sl@17: /** sl@17: */ sl@17: void GP1212A02A::HideClock() sl@17: { sl@17: SendCommandClockCancel(); sl@17: } sl@17: sl@17: sl@17: /** sl@17: Clock setting sl@17: [Code]1BH,6BH,53H,Pd,Ph,Pm sl@17: [Function]Setting the clock data. The setting data is cleared, if the Reset command is input or power sl@17: is turned off. sl@17: Pd = Day of the week sl@17: Ph = hour sl@17: Pm = minute sl@17: [Definable area] sl@17: Pd = 00H : Sunday sl@17: Pd = 01H : Monday sl@17: ... sl@17: Pd = 06H : Saturday sl@17: * Clock setting is canceled, when Pd is input value that is larger than 07H, or Ph is input value that is sl@17: larger than 18H,or Pm is input value that is larger than 3CH. sl@17: */ sl@17: void GP1212A02A::SendCommandClockSetting(TWeekDay aWeekDay, unsigned char aHour, unsigned char aMinute) sl@17: { sl@17: FutabaVfdReport report; sl@17: report[0]=0x00; //Report ID sl@17: report[1]=0x06; //Report size sl@17: report[2]=0x1B; //Command ID sl@17: report[3]=0x6B; //Command ID sl@17: report[4]=0x53; //Command ID sl@17: report[5]=aWeekDay; //Sunday to Saturday sl@17: report[6]=aHour; sl@17: report[7]=aMinute; sl@17: sl@17: Write(report); sl@17: } sl@17: sl@17: sl@17: /** sl@17: Set display clock settings according to local system time. sl@17: This needs to be redone whenever we open or turn on our display. sl@17: */ sl@17: void GP1212A02A::SetClockSetting() sl@17: { sl@17: time_t rawtime; sl@17: struct tm * timeinfo; sl@17: sl@17: time ( &rawtime ); sl@17: timeinfo = localtime ( &rawtime ); sl@17: // sl@17: SendCommandClockSetting((TWeekDay)timeinfo->tm_wday,timeinfo->tm_hour,timeinfo->tm_min); sl@17: } sl@17: sl@17: sl@17: /** sl@17: Clock display sl@17: [Code] 1BH,6BH,55H,Ps,aL,aH,Pf sl@17: [Function] Clock is displayed. The display position and the font size can be freely decided. sl@17: Ps = Display type select sl@17: aL,aH = Address sl@17: Pf = Font size select sl@17: [Definable area] sl@17: Ps = 00H : 24hour Ex.[12:34] sl@17: Ps = 01H : 24hour + day of the week Ex.[Wed._12:34] sl@17: Ps = 10H : 12hour Ex.[PM_00:34] sl@17: Ps = 11H : 12hour + day of the week Ex.[Wed._PM_00:34] sl@17: Pf = 30H : 6x8 dot sl@17: Pf = 31H : 8x16dot sl@17: Pf = 32H : 12x24 dot sl@17: Pf = 33H : 16x32 dot sl@17: * When the clock data is not input, clock is not displayed. sl@17: * The clock display is maintained until Clock display cancel "Clear display" RESET command is input sl@17: or power is turned off. sl@17: The clock display area sl@17: Graphic can be displayed excluding the clock display area. sl@17: The self adjustment for the position sl@17: that cannot be displayed. sl@17: * Excluding the clock display area can be input other display commands. sl@17: */ sl@21: void GP1212A02A::SendCommandClockDisplay(TClockFormat aClockFormat, unsigned short aAddress, TFontSizeLogical aSize) sl@17: { sl@17: FutabaVfdReport report; sl@17: report[0]=0x00; //Report ID sl@17: report[1]=0x07; //Report size sl@17: report[2]=0x1B; //Command ID sl@17: report[3]=0x6B; //Command ID sl@17: report[4]=0x55; //Command ID sl@17: report[5]=aClockFormat; // sl@17: report[6]=(unsigned char)aAddress; //aL sl@17: report[7]=aAddress>>8; //aH sl@17: report[8]=aSize; sl@17: sl@17: Write(report); sl@17: } sl@17: sl@17: sl@17: /** sl@17: Clock display cancel sl@17: [Code] 1BH,6BH,3DH,58H sl@17: [Function] Clock display is canceled. sl@17: */ sl@17: void GP1212A02A::SendCommandClockCancel() sl@17: { sl@17: FutabaVfdReport report; sl@17: report[0]=0x00; //Report ID sl@17: report[1]=0x04; //Report size sl@17: report[2]=0x1B; //Command ID sl@17: report[3]=0x6B; //Command ID sl@17: report[4]=0x3D; //Command ID sl@17: report[5]=0x58; // sl@17: sl@17: Write(report); sl@17: } sl@20: sl@20: sl@20: /** sl@20: @return Size in bytes of a character for a given font size. sl@20: */ sl@20: int GP1212A02A::CharacterSizeInBytes(TFontSize aFontSize) sl@20: { sl@20: switch (aFontSize) sl@20: { sl@20: case EFont6x8: sl@20: return 6; sl@20: case EFont8x16: sl@20: return 16; sl@20: case EFont12x24: sl@20: return 36; sl@20: case EFont16x32: sl@20: return 64; sl@20: case EFont16x16: sl@20: return 32; sl@20: case EFont24x24: sl@20: return 72; sl@20: case EFont32x32: sl@20: return 128; sl@20: } sl@20: sl@20: return 0; sl@20: } sl@20: sl@20: /** sl@20: Define the User definable font (RAM) sl@20: [Code] 1BH,6AH,47H,Pf,cL,(cH),Pd...Pd sl@20: [Function] Define the User definable font into RAM. A maximum 16 characters can be defined sl@20: within each font size. sl@20: The User definable fonts are displayed the defined code. It is a same process to normal fonts. sl@20: The User definable fonts are valid until they redefined, Reset command, or the power off. sl@20: If define the user definable font over 16 characters, at first defined user definable font is removed sl@20: If the defined code is specified, existing data is re-written. sl@20: If the 16x16, 24x24, 32x32 size define, it must specify the “cH” sl@20: Pf = Font size sl@20: cL = Lower byte of User definable font code sl@20: cH = Upper byte of User definable font code sl@20: Pd = Definition data sl@20: [Definable area] sl@20: Pf = 30H : 6x8 dot (Pd=6 byte) sl@20: Pf = 31H : 8x16 dot (Pd=16 byte) sl@20: Pf = 32H : 12x24 dot (Pd=36 byte) sl@20: Pf = 33H : 16x32 dot (Pd=64 byte) sl@20: Pf = 34H : 16x16 dot (Pd=32 byte) sl@20: Pf = 35H : 24x24 dot (Pd=72 byte) sl@20: Pf = 36H : 32x32 dot (Pd=128 byte) sl@20: cL = ANK code (Pf=30H~33H : 1 byte code) sl@20: cL,cH = Shift-JIS code (Pf=34H~36H : 2 byte code) sl@20: */ sl@20: void GP1212A02A::SendCommandDefineCharacter(TFontSize aFontSize, unsigned short aCharacterCode, unsigned char* aPixels) sl@20: { sl@20: unsigned char reportSize=0; sl@20: unsigned char headerSize=0; sl@20: unsigned char dataSize=CharacterSizeInBytes(aFontSize); sl@20: FutabaVfdReport report; sl@20: sl@20: if (aFontSize>=EFont16x16) sl@20: { sl@20: //16 bits char code sl@20: headerSize=8; sl@20: reportSize = (dataSize<=report.Size()-headerSize?dataSize+0x06:64); //Report length. -7 is for our header first 7 bytes. +5 is for our Futaba header size sl@20: report[7] = aCharacterCode>>8; sl@20: } sl@20: else sl@20: { sl@20: //8 bits char code sl@20: headerSize=7; sl@20: reportSize = (dataSize<=report.Size()-headerSize?dataSize+0x05:64); //Report length. -7 is for our header first 7 bytes. +5 is for our Futaba header size sl@20: } sl@20: sl@20: sl@20: report[0]=0x00; //Report ID sl@20: report[1]=reportSize; //Report size sl@20: report[2]=0x1B; //Command ID sl@20: report[3]=0x6A; //Command ID sl@20: report[4]=0x47; //Command ID sl@20: report[5]=aFontSize; // sl@20: report[6] = (unsigned char) aCharacterCode; sl@20: //7th byte was set above already sl@20: int sizeWritten=MIN(dataSize,report.Size()-headerSize); sl@20: memcpy(report.Buffer()+headerSize, aPixels, sizeWritten); sl@20: Write(report); sl@20: sl@20: int remainingSize=dataSize; sl@20: //We need to keep on sending our pixel data until we are done sl@20: while (report[1]==64) sl@20: { sl@20: report.Reset(); sl@20: remainingSize-=sizeWritten; sl@20: report[0]=0x00; //Report ID sl@20: report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size sl@20: sizeWritten=(report[1]==64?63:report[1]); sl@20: memcpy(report.Buffer()+2, aPixels+(dataSize-remainingSize), sizeWritten); sl@20: Write(report); sl@20: } sl@20: } sl@20: sl@20: sl@20: /** sl@20: User definable font store / transfer / delete sl@20: [Code] 1BH,6AH,45H,Ps sl@20: [Function] Store, transfer, or delete the User definable font to FROM. sl@20: * Define the user definable font, after the user definable font is stored sl@20: * The user definable font store is stored the all defined user definable font data. sl@20: * The use definable font delete is deleted the all defined to FROM and RAM user definable font data. sl@20: Ps = store / transfer / delete sl@20: [Definable area] sl@20: Ps = 30H : Store sl@20: Ps = 31H : Transfer sl@20: Ps = 32H : Delete sl@20: */ sl@20: void GP1212A02A::SendCommandFontAction(TFontAction aFontAction) sl@20: { sl@20: FutabaVfdReport report; sl@20: report[0]=0x00; //Report ID sl@20: report[1]=0x04; //Report size sl@20: report[2]=0x1B; //Command ID sl@20: report[3]=0x6A; //Command ID sl@20: report[4]=0x45; //Command ID sl@20: report[5]=aFontAction; //Ps sl@20: sl@20: Write(report); sl@20: } sl@21: sl@21: sl@21: /** sl@21: [Code]1BH,4AH,46H,Pf sl@21: [Function]Setting the font size sl@21: Pf = Font size sl@21: [Definable area] sl@21: Pf = 30H?6x8 dot sl@21: Pf = 31H?8x16dot and 16x16 dot sl@21: Pf = 32H?12x24 dot and 24x24 dot sl@21: Pf = 33H?16x32 dot and 32x32 dot sl@21: */ sl@21: void GP1212A02A::SendCommandSelectFontSize(TFontSizeLogical aFontSoze) sl@21: { sl@21: FutabaVfdReport report; sl@21: report[0]=0x00; //Report ID sl@21: report[1]=0x04; //Report size sl@21: report[2]=0x1B; //Command ID sl@21: report[3]=0x4A; //Command ID sl@21: report[4]=0x46; //Command ID sl@21: report[5]=aFontSoze; //Pf sl@21: sl@21: Write(report); sl@21: }