FutabaGP1212A02.cpp
changeset 10 1c3a4964a5bd
parent 8 5a9dbbc40c6b
child 11 2d749a2bea34
     1.1 --- a/FutabaGP1212A02.cpp	Thu Aug 21 21:53:35 2014 +0200
     1.2 +++ b/FutabaGP1212A02.cpp	Mon Aug 25 00:07:01 2014 +0200
     1.3 @@ -2,4 +2,677 @@
     1.4  //
     1.5  //
     1.6  
     1.7 -#include "FutabaGP1212A02.h"
     1.8 \ No newline at end of file
     1.9 +#include "FutabaGP1212A02.h"
    1.10 +
    1.11 +
    1.12 +const int KNumberOfFrameBeforeDiffAlgo = 3;
    1.13 +
    1.14 +//
    1.15 +// class GP1212A02A
    1.16 +//
    1.17 +
    1.18 +GP1212A02A::GP1212A02A():
    1.19 +	iDisplayPositionX(0),iDisplayPositionY(0),
    1.20 +    iOffScreenMode(true),
    1.21 +    iUseFrameDifferencing(true),
    1.22 +    iFrameNext(NULL),
    1.23 +    iFrameCurrent(NULL),
    1.24 +    iFramePrevious(NULL),
    1.25 +    iFrameAlpha(NULL),
    1.26 +    iFrameBeta(NULL),
    1.27 +    iFrameGamma(NULL),
    1.28 +    iNeedFullFrameUpdate(0),
    1.29 +    iRequest(EMiniDisplayRequestNone),iPowerOn(false)
    1.30 +	{
    1.31 +	iDeviceId[0]=0;
    1.32 +	iFirmwareRevision[0]=0;
    1.33 +	//ResetBuffers();
    1.34 +	}
    1.35 +
    1.36 +/**
    1.37 +*/
    1.38 +GP1212A02A::~GP1212A02A()
    1.39 +	{
    1.40 +    delete iFrameAlpha;
    1.41 +    iFrameAlpha=NULL;
    1.42 +    //
    1.43 +    delete iFrameBeta;
    1.44 +    iFrameBeta=NULL;
    1.45 +    //
    1.46 +    delete iFrameGamma;
    1.47 +    iFrameGamma=NULL;
    1.48 +    //
    1.49 +    iFrameNext=NULL;
    1.50 +    iFrameCurrent=NULL;
    1.51 +    iFramePrevious=NULL;
    1.52 +    //
    1.53 +    iNeedFullFrameUpdate=0;
    1.54 +	}
    1.55 +
    1.56 +/**
    1.57 +*/
    1.58 +int GP1212A02A::Open()
    1.59 +	{
    1.60 +	int success = HidDevice::Open(KFutabaVendorId,KFutabaProductIdGP1212A02A,NULL);
    1.61 +	if (success)
    1.62 +		{
    1.63 +        //Allocate both frames
    1.64 +        delete iFrameAlpha;
    1.65 +        iFrameAlpha=NULL;
    1.66 +        iFrameAlpha=new BitArray(KGP12xFrameBufferPixelCount);
    1.67 +        //
    1.68 +        delete iFrameBeta;
    1.69 +        iFrameBeta=NULL;
    1.70 +        iFrameBeta=new BitArray(KGP12xFrameBufferPixelCount);
    1.71 +        //
    1.72 +        delete iFrameGamma;
    1.73 +        iFrameGamma=NULL;
    1.74 +        iFrameGamma=new BitArray(KGP12xFrameBufferPixelCount);
    1.75 +        //
    1.76 +        iFrameNext=iFrameAlpha;
    1.77 +        iFrameCurrent=iFrameBeta;
    1.78 +        iFramePrevious=iFrameGamma;
    1.79 +
    1.80 +
    1.81 +        //To make sure it is synced properly
    1.82 +        iNeedFullFrameUpdate=0;
    1.83 +        //
    1.84 +		SetNonBlocking(1);
    1.85 +        //Since we can't get our display position we force it to our default
    1.86 +		//This makes sure frames are in sync from the start
    1.87 +        //Clever clients will have taken care of putting back frame (0,0) before closing
    1.88 +		SetDisplayPosition(iDisplayPositionX,iDisplayPositionY);
    1.89 +		}
    1.90 +	return success;
    1.91 +	}
    1.92 +
    1.93 +/**
    1.94 +*/
    1.95 +void GP1212A02A::SetPixel(unsigned char aX, unsigned char aY, bool aOn)
    1.96 +	{
    1.97 +	//
    1.98 +	//int byteOffset=(aX*HeightInPixels()+aY)/8;
    1.99 +	//int bitOffset=(aX*HeightInPixels()+aY)%8;
   1.100 +    //iNextFrame[byteOffset] |= ( (aOn?0x01:0x00) << bitOffset );
   1.101 +
   1.102 +    if (iOffScreenMode)
   1.103 +        {
   1.104 +        if (aOn)
   1.105 +            {
   1.106 +            iFrameNext->SetBit(aX*HeightInPixels()+aY);
   1.107 +            }
   1.108 +        else
   1.109 +            {
   1.110 +            iFrameNext->ClearBit(aX*HeightInPixels()+aY);
   1.111 +            }
   1.112 +        }
   1.113 +    else
   1.114 +        {
   1.115 +        //Just specify a one pixel block
   1.116 +        SetPixelBlock(aX,aY,0x00,0x01,aOn);
   1.117 +        }
   1.118 +	}
   1.119 +
   1.120 +/**
   1.121 +*/
   1.122 +void GP1212A02A::BitBlit(const BitArray& aBitmap, int aSrcWidth, int aSrcHeight, int aTargetX, int aTargetY) const
   1.123 +	{
   1.124 +	//TODO: amend loop values so that we don't keep on looping past our frame buffer dimensions.
   1.125 +	for (int i=0;i<aSrcWidth;i++)
   1.126 +		{
   1.127 +		for (int j=0;j<aSrcHeight;j++)
   1.128 +			{
   1.129 +            iFrameNext->SetBitValue((aTargetX+i)*HeightInPixels()+aTargetY+j,aBitmap[+i*aSrcHeight+j]);
   1.130 +			}
   1.131 +		}
   1.132 +	}
   1.133 +
   1.134 +/**
   1.135 +Clear our client side back buffer.
   1.136 +Call to SwapBuffers must follow to actually clear the display.
   1.137 +*/
   1.138 +void GP1212A02A::Clear()
   1.139 +    {
   1.140 +    //memset(iNextFrame->Ptr(),0x00,FrameBufferSizeInBytes());
   1.141 +    if (iOffScreenMode)
   1.142 +        {
   1.143 +        iFrameNext->ClearAll();
   1.144 +        }
   1.145 +    else
   1.146 +        {
   1.147 +        SendClearCommand();
   1.148 +        }
   1.149 +    }
   1.150 +
   1.151 +/**
   1.152 +Turn on all pixels.
   1.153 +Must be followed by a SwapBuffers call.
   1.154 +*/
   1.155 +void GP1212A02A::Fill()
   1.156 +	{
   1.157 +	SetAllPixels(0xFF);
   1.158 +	}
   1.159 +
   1.160 +/**
   1.161 +Set all pixels on our screen to the desired value.
   1.162 +This operation is performed off screen to avoid tearing.
   1.163 +@param 8 pixels pattern
   1.164 +*/
   1.165 +void GP1212A02A::SetAllPixels(unsigned char aPattern)
   1.166 +	{
   1.167 +	//With a single buffer
   1.168 +	//unsigned char screen[2048]; //One screen worth of pixels
   1.169 +	//memset(screen,0xFF,sizeof(screen));
   1.170 +	//SetPixelBlock(0,0,63,sizeof(screen),screen);
   1.171 +
   1.172 +
   1.173 +    if (iOffScreenMode)
   1.174 +        {
   1.175 +        memset(iFrameNext->Ptr(),aPattern,FrameBufferSizeInBytes());
   1.176 +        }
   1.177 +    else
   1.178 +        {
   1.179 +        //Using pattern SetPixelBlock variant.
   1.180 +        SetPixelBlock(0,0,63,FrameBufferSizeInBytes(),aPattern);
   1.181 +        }
   1.182 +	//
   1.183 +	}
   1.184 +
   1.185 +
   1.186 +/**
   1.187 +Set the defined pixel block to the given value.
   1.188 +@param X coordinate of our pixel block starting point.
   1.189 +@param Y coordinate of our pixel block starting point.
   1.190 +@param The height of our pixel block.
   1.191 +@param The size of our pixel data. Number of pixels divided by 8.
   1.192 +@param The value set to 8 pixels used as a pattern.
   1.193 +*/
   1.194 +void GP1212A02A::SetPixelBlock(unsigned char aX, unsigned char aY, int aHeight, int aSize, unsigned char aValue)
   1.195 +	{
   1.196 +	OffScreenTranslation(aX,aY);
   1.197 +    FutabaVfdReport report;
   1.198 +    report[0]=0x00; //Report ID
   1.199 +    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
   1.200 +    report[2]=0x1B; //Command ID
   1.201 +    report[3]=0x5B; //Command ID
   1.202 +    report[4]=0xF0; //Command ID
   1.203 +    report[5]=aX;   //X
   1.204 +    report[6]=aY;   //Y
   1.205 +    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.
   1.206 +	report[8]=aSize>>8; //Size of pixel data in bytes (MSB)
   1.207 +	report[9]=aSize;	//Size of pixel data in bytes (LSB)
   1.208 +    int sizeWritten=MIN(aSize,report.Size()-10);
   1.209 +    memset(report.Buffer()+10, aValue, sizeWritten);
   1.210 +    Write(report);
   1.211 +
   1.212 +    int remainingSize=aSize;
   1.213 +    //We need to keep on sending our pixel data until we are done
   1.214 +    while (report[1]==64)
   1.215 +        {
   1.216 +        report.Reset();
   1.217 +        remainingSize-=sizeWritten;
   1.218 +        report[0]=0x00; //Report ID
   1.219 +        report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size
   1.220 +        sizeWritten=(report[1]==64?63:report[1]);
   1.221 +        memset(report.Buffer()+2, aValue, sizeWritten);
   1.222 +        Write(report);
   1.223 +        }
   1.224 +	}
   1.225 +
   1.226 +/**
   1.227 +Set the defined pixel block to the given value.
   1.228 +@param X coordinate of our pixel block starting point.
   1.229 +@param Y coordinate of our pixel block starting point.
   1.230 +@param The height of our pixel block.
   1.231 +@param The size of our pixel data. Number of pixels divided by 8.
   1.232 +@param Pointer to our pixel data.
   1.233 +*/
   1.234 +void GP1212A02A::SetPixelBlock(unsigned char aX, unsigned char aY, int aHeight, int aSize, unsigned char* aPixels)
   1.235 +    {
   1.236 +	OffScreenTranslation(aX,aY);
   1.237 +    FutabaVfdReport report;
   1.238 +    report[0]=0x00; //Report ID
   1.239 +    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
   1.240 +    report[2]=0x1B; //Command ID
   1.241 +    report[3]=0x5B; //Command ID
   1.242 +    report[4]=0xF0; //Command ID
   1.243 +    report[5]=aX;   //X
   1.244 +    report[6]=aY;   //Y
   1.245 +    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.
   1.246 +	report[8]=aSize>>8; //Size of pixel data in bytes (MSB)
   1.247 +	report[9]=aSize;	//Size of pixel data in bytes (LSB)
   1.248 +    int sizeWritten=MIN(aSize,report.Size()-10);
   1.249 +    memcpy(report.Buffer()+10, aPixels, sizeWritten);
   1.250 +    Write(report);
   1.251 +
   1.252 +    int remainingSize=aSize;
   1.253 +    //We need to keep on sending our pixel data until we are done
   1.254 +    while (report[1]==64)
   1.255 +        {
   1.256 +        report.Reset();
   1.257 +        remainingSize-=sizeWritten;
   1.258 +        report[0]=0x00; //Report ID
   1.259 +        report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size
   1.260 +        sizeWritten=(report[1]==64?63:report[1]);
   1.261 +        memcpy(report.Buffer()+2, aPixels+(aSize-remainingSize), sizeWritten);
   1.262 +        Write(report);
   1.263 +        }
   1.264 +    }
   1.265 +
   1.266 +/**
   1.267 +Using this function is advised against as is causes tearing.
   1.268 +Use Clear instead.
   1.269 +*/
   1.270 +void GP1212A02A::SendClearCommand()
   1.271 +	{
   1.272 +    //1BH,5BH,32H,4AH
   1.273 +    //Send Clear Display Command
   1.274 +	FutabaVfdReport report;
   1.275 +	report[0]=0x00; //Report ID
   1.276 +	report[1]=0x04; //Report length
   1.277 +	report[2]=0x1B; //Command ID
   1.278 +	report[3]=0x5B; //Command ID
   1.279 +	report[4]=0x32; //Command ID
   1.280 +	report[5]=0x4A; //Command ID
   1.281 +	Write(report);
   1.282 +	}
   1.283 +
   1.284 +/**
   1.285 +Change our display position within our buffer.
   1.286 +*/
   1.287 +void GP1212A02A::SetDisplayPosition(DW aDw,unsigned char aX, unsigned char aY)
   1.288 +    {
   1.289 +    //1BH,5BH,Dw,Px,Py
   1.290 +    //Send Display Position Settings Command
   1.291 +    FutabaVfdReport report;
   1.292 +    report[0]=0x00; //Report ID
   1.293 +    report[1]=0x05; //Report length
   1.294 +    report[2]=0x1B; //Command ID
   1.295 +    report[3]=0x5B; //Command ID
   1.296 +    report[4]=aDw;  //Specify our DW
   1.297 +    report[5]=aX;   //X coordinate of our DW top-left corner
   1.298 +    report[6]=aY;   //Y coordinate of our DW top-left corner
   1.299 +    Write(report);
   1.300 +    }
   1.301 +
   1.302 +/**
   1.303 +Change our display position within our buffer.
   1.304 +*/
   1.305 +void GP1212A02A::SetDisplayPosition(unsigned char aX, unsigned char aY)
   1.306 +	{
   1.307 +	//Specs apparently says both DW should remain the same
   1.308 +	//Just don't ask
   1.309 +    SetDisplayPosition(GP1212A02A::DW1,aX,aY);
   1.310 +    SetDisplayPosition(GP1212A02A::DW2,aX,aY);
   1.311 +	iDisplayPositionX=aX;
   1.312 +	iDisplayPositionY=aY;
   1.313 +	}
   1.314 +
   1.315 +/**
   1.316 +Provide Y coordinate of our off screen buffer.
   1.317 +*/
   1.318 +unsigned char GP1212A02A::OffScreenY() const
   1.319 +	{
   1.320 +	//Overflowing is fine this is just what we want
   1.321 +	return iDisplayPositionY+HeightInPixels();
   1.322 +	}
   1.323 +
   1.324 +/**
   1.325 +Put our off screen buffer on screen.
   1.326 +On screen buffer goes off screen.
   1.327 +*/
   1.328 +void GP1212A02A::SwapBuffers()
   1.329 +	{
   1.330 +	//Only perform buffer swapping if off screen mode is enabled
   1.331 +	if (OffScreenMode())
   1.332 +		{
   1.333 +		//Send host back buffer to device back buffer
   1.334 +        if (!iUseFrameDifferencing || iNeedFullFrameUpdate<KNumberOfFrameBeforeDiffAlgo)
   1.335 +            {
   1.336 +            iNeedFullFrameUpdate++;
   1.337 +            SetPixelBlock(0,0,63,FrameBufferSizeInBytes(),iFrameNext->Ptr());
   1.338 +            }
   1.339 +        else
   1.340 +            {
   1.341 +            //Frame diff algo is enabled
   1.342 +            //We are going to send to our device only the differences between next frame and previous frame
   1.343 +            SendModifiedPixelBlocks();
   1.344 +            }
   1.345 +		//Swap device front and back buffer
   1.346 +		SetDisplayPosition(iDisplayPositionX,OffScreenY());
   1.347 +
   1.348 +        //Cycle through our frame buffers
   1.349 +        //We keep track of previous frame which is in fact our device back buffer.
   1.350 +        //We can then compare previous and next frame and send only the differences to our device.
   1.351 +        //This mechanism allows us to reduce traffic over our USB bus thus improving our frame rate from 14 FPS to 30 FPS.
   1.352 +        //Keep our previous frame pointer
   1.353 +        BitArray* previousFrame=iFramePrevious;
   1.354 +        //Current frame becomes the previous one
   1.355 +        iFramePrevious = iFrameCurrent;
   1.356 +        //Next frame becomes the current one
   1.357 +        iFrameCurrent = iFrameNext;
   1.358 +        //Next frame is now our former previous
   1.359 +        iFrameNext = previousFrame;
   1.360 +		}
   1.361 +	}
   1.362 +
   1.363 +
   1.364 +//Define the edge of our pixel block
   1.365 +//Pixel blocks of 32x32 seems to run almost as fast as full screen update in worse case scenarii.
   1.366 +//Though I wonder if in some situations 16 could be better. Make this an attribute at some point if need be.
   1.367 +const int KPixelBlockEdge = 32;
   1.368 +const int KPixelBlockSizeInBits = KPixelBlockEdge*KPixelBlockEdge;
   1.369 +const int KPixelBlockSizeInBytes = KPixelBlockSizeInBits/8;
   1.370 +
   1.371 +
   1.372 +/**
   1.373 + * @brief GP1212A02A::SendModifiedPixelBlocks
   1.374 + * Compare our back and front buffer and send to the device only the modified pixels.
   1.375 + */
   1.376 +void GP1212A02A::SendModifiedPixelBlocks()
   1.377 +    {
   1.378 +    int w=WidthInPixels();
   1.379 +    int h=HeightInPixels();
   1.380 +
   1.381 +
   1.382 +    //TODO: optimize with memcmp and 16 inc
   1.383 +    /*
   1.384 +
   1.385 +    for (int i=0;i<w;i++)
   1.386 +        {
   1.387 +        for (int j=0;j<h;j++)
   1.388 +            {
   1.389 +            //aX*HeightInPixels()+aY
   1.390 +            if ((*iFrameNext)[i*h+j]!=(*iFramePrevious)[i*h+j])
   1.391 +                {
   1.392 +                //We need to update that pixel
   1.393 +                SetPixelBlock(i,j,0,1,((*iFrameNext)[i*h+j]?0x01:0x00));
   1.394 +                //SetDisplayPosition(iDisplayPositionX,OffScreenY());
   1.395 +                //SetDisplayPosition(iDisplayPositionX,OffScreenY());
   1.396 +
   1.397 +                //SetPixelBlock(i,j,15,32,iNextFrame->Ptr()+offset);
   1.398 +                }
   1.399 +            }
   1.400 +        }
   1.401 +    */
   1.402 +
   1.403 +    BitArray nextBlock(KPixelBlockSizeInBits);
   1.404 +    BitArray previousBlock(KPixelBlockSizeInBits);
   1.405 +
   1.406 +    for (int i=0;i<w;i+=KPixelBlockEdge)
   1.407 +        {
   1.408 +        for (int j=0;j<h;j+=KPixelBlockEdge)
   1.409 +            {
   1.410 +            //aX*HeightInPixels()+aY
   1.411 +            //int offset=(i*w/8)+(j/8);
   1.412 +
   1.413 +#ifdef DEBUG_FRAME_DIFF
   1.414 +            QImage imagePrevious(KPixelBlockEdge,KPixelBlockEdge,QImage::Format_RGB32);
   1.415 +            QImage imageNext(KPixelBlockEdge,KPixelBlockEdge,QImage::Format_RGB32);
   1.416 +#endif
   1.417 +
   1.418 +            //Get both our blocks from our buffers
   1.419 +            for (int x=i;x<i+KPixelBlockEdge;x++)
   1.420 +                {
   1.421 +                for (int y=j;y<j+KPixelBlockEdge;y++)
   1.422 +                    {
   1.423 +                    int blockOffset=(x-i)*KPixelBlockEdge+(y-j);
   1.424 +                    int frameOffset=x*h+y;
   1.425 +                    nextBlock.SetBitValue(blockOffset,(*iFrameNext)[frameOffset]);
   1.426 +                    previousBlock.SetBitValue(blockOffset,(*iFramePrevious)[frameOffset]);
   1.427 +
   1.428 +#ifdef DEBUG_FRAME_DIFF
   1.429 +                    imageNext.setPixel(x-i,y-j,(nextBlock[blockOffset]?0xFFFFFFFF:0x00000000));
   1.430 +                    imagePrevious.setPixel(x-i,y-j,(previousBlock[blockOffset]?0xFFFFFFFF:0x00000000));
   1.431 +#endif
   1.432 +                    }
   1.433 +                }
   1.434 +
   1.435 +#ifdef DEBUG_FRAME_DIFF
   1.436 +            QString previousName;
   1.437 +            QString nextName;
   1.438 +            QTextStream(&previousName) << "p" << i << "x" << j << ".png";
   1.439 +            QTextStream(&nextName) << "n" << i << "x" << j << ".png";
   1.440 +            imagePrevious.save(previousName);
   1.441 +            imageNext.save(nextName);
   1.442 +#endif
   1.443 +
   1.444 +
   1.445 +            //if (memcmp(iFrameNext->Ptr()+offset,iFramePrevious->Ptr()+offset,32 )) //32=(16*16/8)
   1.446 +            if (memcmp(nextBlock.Ptr(),previousBlock.Ptr(),KPixelBlockSizeInBytes)!=0)
   1.447 +                {
   1.448 +                //We need to update that block
   1.449 +                SetPixelBlock(i,j,KPixelBlockEdge-1,KPixelBlockSizeInBytes,nextBlock.Ptr());
   1.450 +                //SetPixelBlock(i,j,15,32,0xFF/*nextBlock.Ptr()*/);
   1.451 +                //SetDisplayPosition(iDisplayPositionX,OffScreenY());
   1.452 +                //SetDisplayPosition(iDisplayPositionX,OffScreenY());
   1.453 +
   1.454 +                //SetPixelBlock(i,j,15,32,iFrameNext->Ptr()+offset);
   1.455 +                }
   1.456 +            }
   1.457 +        }
   1.458 +
   1.459 +    }
   1.460 +
   1.461 +/**
   1.462 +Translate the given pixel coordinate according to our off screen mode.
   1.463 +*/
   1.464 +void GP1212A02A::OffScreenTranslation(unsigned char& aX, unsigned char& aY)
   1.465 +	{
   1.466 +	if (OffScreenMode())
   1.467 +		{
   1.468 +		aX+=WidthInPixels()-iDisplayPositionX;
   1.469 +		aY+=HeightInPixels()-iDisplayPositionY;
   1.470 +		}
   1.471 +	}
   1.472 +
   1.473 +
   1.474 +/**
   1.475 +*/
   1.476 +void GP1212A02A::ResetBuffers()
   1.477 +	{
   1.478 +    //iNextFrame->ClearAll();
   1.479 +    //memset(iFrameAlpha,0x00,sizeof(iFrameAlpha));
   1.480 +	//memset(iFrameBeta,0x00,sizeof(iFrameBeta));
   1.481 +	}
   1.482 +
   1.483 +/**
   1.484 +*/
   1.485 +void GP1212A02A::RequestDeviceId()
   1.486 +    {
   1.487 +    if (RequestPending())
   1.488 +        {
   1.489 +        //Abort silently for now
   1.490 +        return;
   1.491 +        }
   1.492 +
   1.493 +    //1BH,5BH,63H,49H,44H
   1.494 +    //Send Read ID command
   1.495 +    FutabaVfdReport report;
   1.496 +    report[0]=0x00; //Report ID
   1.497 +    report[1]=0x05; //Report length
   1.498 +    report[2]=0x1B; //Command ID
   1.499 +    report[3]=0x5B; //Command ID
   1.500 +    report[4]=0x63; //Command ID
   1.501 +    report[5]=0x49; //Command ID
   1.502 +    report[6]=0x44; //Command ID
   1.503 +    if (Write(report)==report.Size())
   1.504 +        {
   1.505 +        iRequest=EMiniDisplayRequestDeviceId;
   1.506 +        }
   1.507 +    }
   1.508 +
   1.509 +/**
   1.510 +*/
   1.511 +void GP1212A02A::RequestFirmwareRevision()
   1.512 +    {
   1.513 +    if (RequestPending())
   1.514 +        {
   1.515 +        //Abort silently for now
   1.516 +        return;
   1.517 +        }
   1.518 +
   1.519 +    //1BH,5BH,63H,46H,52H
   1.520 +    //Send Software Revision Read Command
   1.521 +    FutabaVfdReport report;
   1.522 +    report[0]=0x00; //Report ID
   1.523 +    report[1]=0x05; //Report length
   1.524 +    report[2]=0x1B; //Command ID
   1.525 +    report[3]=0x5B; //Command ID
   1.526 +    report[4]=0x63; //Command ID
   1.527 +    report[5]=0x46; //Command ID
   1.528 +    report[6]=0x52; //Command ID
   1.529 +    if (Write(report)==report.Size())
   1.530 +        {
   1.531 +        iRequest=EMiniDisplayRequestFirmwareRevision;
   1.532 +        }
   1.533 +    }
   1.534 +
   1.535 +/**
   1.536 +*/
   1.537 +void GP1212A02A::RequestPowerSupplyStatus()
   1.538 +    {
   1.539 +    if (RequestPending())
   1.540 +        {
   1.541 +        //Abort silently for now
   1.542 +        return;
   1.543 +        }
   1.544 +    //1BH,5BH,63H,50H,4DH
   1.545 +    //Send Power Suppply Monitor Command
   1.546 +    FutabaVfdReport report;
   1.547 +    report[0]=0x00; //Report ID
   1.548 +    report[1]=0x05; //Report length
   1.549 +    report[2]=0x1B; //Command ID
   1.550 +    report[3]=0x5B; //Command ID
   1.551 +    report[4]=0x63; //Command ID
   1.552 +    report[5]=0x50; //Command ID
   1.553 +    report[6]=0x4D; //Command ID
   1.554 +    if (Write(report)==report.Size())
   1.555 +        {
   1.556 +        iRequest=EMiniDisplayRequestPowerSupplyStatus;
   1.557 +        }
   1.558 +    }
   1.559 +
   1.560 +
   1.561 +/**
   1.562 +This is for development purposes only.
   1.563 +Production application should stick to off-screen mode to avoid tearing.
   1.564 +*/
   1.565 +void GP1212A02A::ToggleOffScreenMode()
   1.566 +	{
   1.567 +    SetOffScreenMode(!iOffScreenMode);
   1.568 +	}
   1.569 +
   1.570 +/**
   1.571 + * @brief GP1212A02A::SetOffScreenMode
   1.572 + * @param aOn
   1.573 + * @return
   1.574 + */
   1.575 +void GP1212A02A::SetOffScreenMode(bool aOn)
   1.576 +    {
   1.577 +    if (aOn==iOffScreenMode)
   1.578 +    {
   1.579 +        //Nothing to do here
   1.580 +        return;
   1.581 +    }
   1.582 +
   1.583 +    iOffScreenMode=aOn;
   1.584 +
   1.585 +    //Clean up our buffers upon switching modes
   1.586 +    SetDisplayPosition(0,0);
   1.587 +    Clear();
   1.588 +    SwapBuffers();
   1.589 +    Clear();
   1.590 +    }
   1.591 +
   1.592 +/**
   1.593 +Tries to complete our current request if we have one pending.
   1.594 + */
   1.595 +TMiniDisplayRequest GP1212A02A::AttemptRequestCompletion()
   1.596 +    {
   1.597 +    if (!RequestPending())
   1.598 +        {
   1.599 +        return EMiniDisplayRequestNone;
   1.600 +        }
   1.601 +
   1.602 +    int res=Read(iInputReport);
   1.603 +
   1.604 +    if (!res)
   1.605 +        {
   1.606 +        return EMiniDisplayRequestNone;
   1.607 +        }
   1.608 +
   1.609 +    //Process our request
   1.610 +    if (CurrentRequest()==EMiniDisplayRequestPowerSupplyStatus)
   1.611 +        {
   1.612 +        if (iInputReport[1]==0x4F && iInputReport[2]==0x4E)
   1.613 +            {
   1.614 +            iPowerOn = true;
   1.615 +            }
   1.616 +        else if (iInputReport[1]==0x4F && iInputReport[2]==0x46 && iInputReport[3]==0x46)
   1.617 +            {
   1.618 +            iPowerOn = false;
   1.619 +            }
   1.620 +        }
   1.621 +	else if (CurrentRequest()==EMiniDisplayRequestDeviceId)
   1.622 +		{
   1.623 +			unsigned char* ptr=&iInputReport[1];
   1.624 +			strcpy(iDeviceId,(const char*)ptr);
   1.625 +		}
   1.626 +	else if (CurrentRequest()==EMiniDisplayRequestFirmwareRevision)
   1.627 +		{
   1.628 +			unsigned char* ptr=&iInputReport[1];
   1.629 +			strcpy(iFirmwareRevision,(const char*)ptr);
   1.630 +		}
   1.631 +
   1.632 +    TMiniDisplayRequest completed=iRequest;
   1.633 +    //Our request was completed
   1.634 +    iRequest=EMiniDisplayRequestNone;
   1.635 +
   1.636 +    return completed;
   1.637 +    }
   1.638 +
   1.639 +
   1.640 +/**
   1.641 +Set our screen brightness.
   1.642 +@param The desired brightness level. Must be between MinBrightness and MaxBrightness.
   1.643 +*/
   1.644 +void GP1212A02A::SetBrightness(int aBrightness)
   1.645 +    {
   1.646 +    if (aBrightness<MinBrightness()||aBrightness>MaxBrightness())
   1.647 +        {
   1.648 +        //Brightness out of range.
   1.649 +        //Just ignore that request.
   1.650 +        return;
   1.651 +        }
   1.652 +
   1.653 +    FutabaVfdReport report;
   1.654 +    report[0]=0x00; //Report ID
   1.655 +    report[1]=0x04; //Report size
   1.656 +    report[2]=0x1B; //Command ID
   1.657 +    report[3]=0x4A; //Command ID
   1.658 +    report[4]=0x44; //Command ID
   1.659 +    report[7]=0x30+aBrightness; //Brightness level
   1.660 +    Write(report);
   1.661 +    }
   1.662 +
   1.663 +/**
   1.664 +*/
   1.665 +bool GP1212A02A::PowerOn()
   1.666 +	{
   1.667 +	return iPowerOn;
   1.668 +	}
   1.669 +
   1.670 +/**
   1.671 +*/
   1.672 +char* GP1212A02A::DeviceId()
   1.673 +	{
   1.674 +	return iDeviceId;
   1.675 +	}
   1.676 +
   1.677 +/**
   1.678 +*/
   1.679 +char* GP1212A02A::FirmwareRevision()
   1.680 +	{
   1.681 +	return iFirmwareRevision;
   1.682 +	}