MiniDisplay/FutabaVfd.cpp
author sl
Wed, 28 May 2014 18:44:15 +0200
changeset 11 b935de604982
parent 9 52372bbbc0f8
child 18 79801cc3bc94
permissions -rw-r--r--
Sorting out start-up/shutdown sequence to avoid flashing old frame upon connection.
Adding a couple of option to clear our frames upong close and/or open.
sl@4
     1
sl@4
     2
#include "FutabaVfd.h"
sl@4
     3
//#include <stdlib.h>
sl@4
     4
#include <string.h>
sl@4
     5
sl@4
     6
sl@4
     7
//
sl@4
     8
//
sl@4
     9
//
sl@4
    10
sl@4
    11
sl@4
    12
sl@4
    13
sl@4
    14
sl@4
    15
//
sl@4
    16
//
sl@4
    17
//
sl@4
    18
sl@4
    19
FutabaVfdCommand::FutabaVfdCommand():/*iBuffer(NULL),*/iSize(0),iMaxSize(0)
sl@4
    20
    {
sl@4
    21
    }
sl@4
    22
sl@4
    23
FutabaVfdCommand::~FutabaVfdCommand()
sl@4
    24
    {
sl@4
    25
    //Delete();
sl@4
    26
    }
sl@4
    27
sl@4
    28
sl@4
    29
/**
sl@4
    30
sl@4
    31
*/
sl@4
    32
void FutabaVfdCommand::Reset()
sl@4
    33
    {
sl@4
    34
    memset(iReports,0,sizeof(iReports));
sl@4
    35
    }
sl@4
    36
sl@4
    37
sl@4
    38
sl@4
    39
/**
sl@4
    40
sl@4
    41
*/
sl@4
    42
/*
sl@4
    43
void FutabaVfdCommand::Create(int aMaxSize)
sl@4
    44
    {
sl@4
    45
    iBuffer=new unsigned char[aMaxSize];
sl@4
    46
    if (iBuffer)
sl@4
    47
        {
sl@4
    48
        iMaxSize = aMaxSize;
sl@4
    49
        iSize = 0;
sl@4
    50
        }
sl@4
    51
    }
sl@4
    52
*/
sl@4
    53
sl@4
    54
/**
sl@4
    55
sl@4
    56
*/
sl@4
    57
/*
sl@4
    58
void FutabaVfdCommand::Delete()
sl@4
    59
{
sl@4
    60
    delete[] iBuffer;
sl@4
    61
    iBuffer = NULL;
sl@4
    62
    iMaxSize = 0;
sl@4
    63
    iSize = 0;
sl@4
    64
}
sl@4
    65
*/
sl@4
    66
sl@4
    67
sl@4
    68
sl@4
    69
sl@4
    70
//
sl@4
    71
// class GP1212A01A
sl@4
    72
//
sl@4
    73
sl@4
    74
GP1212A01A::GP1212A01A():
sl@4
    75
	iDisplayPositionX(0),iDisplayPositionY(0),
sl@9
    76
    iOffScreenMode(true),iFrameBuffer(NULL),iRequest(ERequestNone),iPowerOn(false)
sl@4
    77
	{
sl@4
    78
	//ResetBuffers();
sl@4
    79
	}
sl@4
    80
sl@4
    81
/**
sl@4
    82
*/
sl@4
    83
GP1212A01A::~GP1212A01A()
sl@4
    84
	{
sl@4
    85
	delete iFrameBuffer;
sl@4
    86
	iFrameBuffer=NULL;
sl@4
    87
	}
sl@4
    88
sl@4
    89
/**
sl@4
    90
*/
sl@4
    91
int GP1212A01A::Open()
sl@4
    92
	{
sl@4
    93
	int success = HidDevice::Open(KFutabaVendorId,KFutabaProductIdGP1212A01A,NULL);
sl@4
    94
	if (success)
sl@4
    95
		{
sl@4
    96
		delete iFrameBuffer;
sl@4
    97
		iFrameBuffer = NULL;
sl@4
    98
		iFrameBuffer=new BitArray(KGP12xFrameBufferPixelCount);
sl@4
    99
		SetNonBlocking(1);
sl@11
   100
        //Since we can't get our display position we force it to our default
sl@4
   101
		//This makes sure frames are in sync from the start
sl@11
   102
        //Clever clients will have taken care of putting back frame (0,0) before closing
sl@4
   103
		SetDisplayPosition(iDisplayPositionX,iDisplayPositionY);
sl@4
   104
		}
sl@4
   105
	return success;
sl@4
   106
	}
sl@4
   107
sl@4
   108
/**
sl@4
   109
*/
sl@4
   110
void GP1212A01A::SetPixel(unsigned char aX, unsigned char aY, bool aOn)
sl@4
   111
	{
sl@4
   112
	//Just specify a one pixel block
sl@4
   113
	//SetPixelBlock(aX,aY,0x00,0x01,aOn);
sl@4
   114
	//
sl@4
   115
	//int byteOffset=(aX*HeightInPixels()+aY)/8;
sl@4
   116
	//int bitOffset=(aX*HeightInPixels()+aY)%8;
sl@4
   117
	//iFrameBuffer[byteOffset] |= ( (aOn?0x01:0x00) << bitOffset );
sl@4
   118
	if (aOn)
sl@4
   119
		{
sl@4
   120
		iFrameBuffer->SetBit(aX*HeightInPixels()+aY);
sl@4
   121
		}
sl@4
   122
	else
sl@4
   123
		{
sl@4
   124
		iFrameBuffer->ClearBit(aX*HeightInPixels()+aY);
sl@4
   125
		}
sl@4
   126
	}
sl@4
   127
sl@4
   128
/**
sl@4
   129
*/
sl@6
   130
void GP1212A01A::BitBlit(const BitArray& aBitmap, int aSrcWidth, int aSrcHeight, int aTargetX, int aTargetY) const
sl@4
   131
	{
sl@4
   132
	//TODO: amend loop values so that we don't keep on looping past our frame buffer dimensions.
sl@4
   133
	for (int i=0;i<aSrcWidth;i++)
sl@4
   134
		{
sl@4
   135
		for (int j=0;j<aSrcHeight;j++)
sl@4
   136
			{
sl@4
   137
			iFrameBuffer->SetBitValue((aTargetX+i)*HeightInPixels()+aTargetY+j,aBitmap[+i*aSrcHeight+j]);
sl@4
   138
			}
sl@4
   139
		}
sl@4
   140
	}
sl@4
   141
sl@4
   142
/**
sl@4
   143
Set all pixels on our screen to the desired value.
sl@4
   144
This operation is performed off screen to avoid tearing.
sl@4
   145
@param 8 pixels pattern
sl@4
   146
*/
sl@4
   147
void GP1212A01A::SetAllPixels(unsigned char aPattern)
sl@4
   148
	{
sl@4
   149
	//With a single buffer
sl@4
   150
	//unsigned char screen[2048]; //One screen worth of pixels
sl@4
   151
	//memset(screen,0xFF,sizeof(screen));
sl@4
   152
	//SetPixelBlock(0,0,63,sizeof(screen),screen);
sl@4
   153
sl@4
   154
	//Using pattern SetPixelBlock variant.
sl@4
   155
	memset(iFrameBuffer->Ptr(),aPattern,FrameBufferSizeInBytes());
sl@4
   156
	//
sl@4
   157
sl@4
   158
sl@4
   159
	}
sl@4
   160
sl@4
   161
/**
sl@4
   162
Set our screen brightness.
sl@4
   163
@param The desired brightness level. Must be between MinBrightness and MaxBrightness.
sl@4
   164
*/
sl@4
   165
void GP1212A01A::SetBrightness(int aBrightness)
sl@4
   166
    {
sl@4
   167
	if (aBrightness<MinBrightness()||aBrightness>MaxBrightness())
sl@4
   168
        {
sl@4
   169
        //Brightness out of range.
sl@4
   170
        //Just ignore that request.
sl@4
   171
        return;
sl@4
   172
        }
sl@4
   173
sl@4
   174
    FutabaVfdReport report;
sl@4
   175
    report[0]=0x00; //Report ID
sl@4
   176
    report[1]=0x06; //Report size
sl@4
   177
    report[2]=0x1B; //Command ID
sl@4
   178
    report[3]=0x5C; //Command ID
sl@4
   179
    report[4]=0x3F; //Command ID
sl@4
   180
    report[5]=0x4C; //Command ID
sl@4
   181
    report[6]=0x44; //Command ID
sl@4
   182
    report[7]=0x30+aBrightness; //Brightness level
sl@4
   183
    Write(report);
sl@4
   184
sl@4
   185
	}
sl@4
   186
sl@4
   187
/**
sl@4
   188
Set the defined pixel block to the given value.
sl@4
   189
@param X coordinate of our pixel block starting point.
sl@4
   190
@param Y coordinate of our pixel block starting point.
sl@4
   191
@param The height of our pixel block.
sl@4
   192
@param The size of our pixel data. Number of pixels divided by 8.
sl@4
   193
@param The value set to 8 pixels used as a pattern.
sl@4
   194
*/
sl@4
   195
void GP1212A01A::SetPixelBlock(unsigned char aX, unsigned char aY, int aHeight, int aSize, unsigned char aValue)
sl@4
   196
	{
sl@4
   197
	OffScreenTranslation(aX,aY);
sl@4
   198
    FutabaVfdReport report;
sl@4
   199
    report[0]=0x00; //Report ID
sl@4
   200
    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
   201
    report[2]=0x1B; //Command ID
sl@4
   202
    report[3]=0x5B; //Command ID
sl@4
   203
    report[4]=0xF0; //Command ID
sl@4
   204
    report[5]=aX;   //X
sl@4
   205
    report[6]=aY;   //Y
sl@4
   206
    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
   207
	report[8]=aSize>>8; //Size of pixel data in bytes (MSB)
sl@4
   208
	report[9]=aSize;	//Size of pixel data in bytes (LSB)
sl@4
   209
    int sizeWritten=MIN(aSize,report.Size()-10);
sl@4
   210
    memset(report.Buffer()+10, aValue, sizeWritten);
sl@4
   211
    Write(report);
sl@4
   212
sl@4
   213
    int remainingSize=aSize;
sl@4
   214
    //We need to keep on sending our pixel data until we are done
sl@4
   215
    while (report[1]==64)
sl@4
   216
        {
sl@4
   217
        report.Reset();
sl@4
   218
        remainingSize-=sizeWritten;
sl@4
   219
        report[0]=0x00; //Report ID
sl@4
   220
        report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size
sl@4
   221
        sizeWritten=(report[1]==64?63:report[1]);
sl@4
   222
        memset(report.Buffer()+2, aValue, sizeWritten);
sl@4
   223
        Write(report);
sl@4
   224
        }
sl@4
   225
	}
sl@4
   226
sl@4
   227
/**
sl@4
   228
Set the defined pixel block to the given value.
sl@4
   229
@param X coordinate of our pixel block starting point.
sl@4
   230
@param Y coordinate of our pixel block starting point.
sl@4
   231
@param The height of our pixel block.
sl@4
   232
@param The size of our pixel data. Number of pixels divided by 8.
sl@4
   233
@param Pointer to our pixel data.
sl@4
   234
*/
sl@4
   235
void GP1212A01A::SetPixelBlock(unsigned char aX, unsigned char aY, int aHeight, int aSize, unsigned char* aPixels)
sl@4
   236
    {
sl@4
   237
	OffScreenTranslation(aX,aY);
sl@4
   238
    FutabaVfdReport report;
sl@4
   239
    report[0]=0x00; //Report ID
sl@4
   240
    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
   241
    report[2]=0x1B; //Command ID
sl@4
   242
    report[3]=0x5B; //Command ID
sl@4
   243
    report[4]=0xF0; //Command ID
sl@4
   244
    report[5]=aX;   //X
sl@4
   245
    report[6]=aY;   //Y
sl@4
   246
    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
   247
	report[8]=aSize>>8; //Size of pixel data in bytes (MSB)
sl@4
   248
	report[9]=aSize;	//Size of pixel data in bytes (LSB)
sl@4
   249
    int sizeWritten=MIN(aSize,report.Size()-10);
sl@4
   250
    memcpy(report.Buffer()+10, aPixels, sizeWritten);
sl@4
   251
    Write(report);
sl@4
   252
sl@4
   253
    int remainingSize=aSize;
sl@4
   254
    //We need to keep on sending our pixel data until we are done
sl@4
   255
    while (report[1]==64)
sl@4
   256
        {
sl@4
   257
        report.Reset();
sl@4
   258
        remainingSize-=sizeWritten;
sl@4
   259
        report[0]=0x00; //Report ID
sl@4
   260
        report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size
sl@4
   261
        sizeWritten=(report[1]==64?63:report[1]);
sl@4
   262
        memcpy(report.Buffer()+2, aPixels+(aSize-remainingSize), sizeWritten);
sl@4
   263
        Write(report);
sl@4
   264
        }
sl@4
   265
    }
sl@4
   266
sl@4
   267
sl@4
   268
/**
sl@4
   269
Clear our client side back buffer.
sl@4
   270
Call to SwapBuffers must follow to actually clear the display.
sl@4
   271
*/
sl@4
   272
void GP1212A01A::Clear()
sl@4
   273
	{
sl@4
   274
	//memset(iFrameBuffer->Ptr(),0x00,FrameBufferSizeInBytes());
sl@4
   275
	iFrameBuffer->ClearAll();
sl@4
   276
	}
sl@4
   277
sl@4
   278
/**
sl@4
   279
Using this function is advised against as is causes tearing.
sl@4
   280
Use Clear instead.
sl@4
   281
*/
sl@4
   282
void GP1212A01A::SendClearCommand()
sl@4
   283
	{
sl@4
   284
    //1BH,5BH,32H,4AH
sl@4
   285
    //Send Clear Display Command
sl@4
   286
	FutabaVfdReport report;
sl@4
   287
	report[0]=0x00; //Report ID
sl@4
   288
	report[1]=0x04; //Report length
sl@4
   289
	report[2]=0x1B; //Command ID
sl@4
   290
	report[3]=0x5B; //Command ID
sl@4
   291
	report[4]=0x32; //Command ID
sl@4
   292
	report[5]=0x4A; //Command ID
sl@4
   293
	Write(report);
sl@4
   294
	}
sl@4
   295
sl@4
   296
/**
sl@4
   297
Change our display position within our buffer.
sl@4
   298
*/
sl@4
   299
void GP1212A01A::SetDisplayPosition(DW aDw,unsigned char aX, unsigned char aY)
sl@4
   300
    {
sl@4
   301
    //1BH,5BH,Dw,Px,Py
sl@4
   302
    //Send Display Position Settings Command
sl@4
   303
    FutabaVfdReport report;
sl@4
   304
    report[0]=0x00; //Report ID
sl@4
   305
    report[1]=0x05; //Report length
sl@4
   306
    report[2]=0x1B; //Command ID
sl@4
   307
    report[3]=0x5B; //Command ID
sl@4
   308
    report[4]=aDw;  //Specify our DW
sl@4
   309
    report[5]=aX;   //X coordinate of our DW top-left corner
sl@4
   310
    report[6]=aY;   //Y coordinate of our DW top-left corner
sl@4
   311
    Write(report);
sl@4
   312
    }
sl@4
   313
sl@4
   314
/**
sl@4
   315
Change our display position within our buffer.
sl@4
   316
*/
sl@4
   317
void GP1212A01A::SetDisplayPosition(unsigned char aX, unsigned char aY)
sl@4
   318
	{
sl@4
   319
	//Specs apparently says both DW should remain the same
sl@4
   320
	//Just don't ask
sl@4
   321
    SetDisplayPosition(GP1212A01A::DW1,aX,aY);
sl@4
   322
    SetDisplayPosition(GP1212A01A::DW2,aX,aY);
sl@4
   323
	iDisplayPositionX=aX;
sl@4
   324
	iDisplayPositionY=aY;
sl@4
   325
	}
sl@4
   326
sl@4
   327
/**
sl@4
   328
Provide Y coordinate of our off screen buffer.
sl@4
   329
*/
sl@4
   330
unsigned char GP1212A01A::OffScreenY() const
sl@4
   331
	{
sl@4
   332
	//Overflowing is fine this is just what we want
sl@4
   333
	return iDisplayPositionY+HeightInPixels();
sl@4
   334
	}
sl@4
   335
sl@4
   336
/**
sl@4
   337
Put our off screen buffer on screen.
sl@4
   338
On screen buffer goes off screen.
sl@4
   339
*/
sl@4
   340
void GP1212A01A::SwapBuffers()
sl@4
   341
	{
sl@4
   342
	//Only perform buffer swapping if off screen mode is enabled
sl@4
   343
	if (OffScreenMode())
sl@4
   344
		{
sl@4
   345
		//Send host back buffer to device back buffer
sl@4
   346
		SetPixelBlock(0,0,63,FrameBufferSizeInBytes(),iFrameBuffer->Ptr());
sl@4
   347
		//Swap device front and back buffer
sl@4
   348
		SetDisplayPosition(iDisplayPositionX,OffScreenY());
sl@4
   349
		//Swap host buffers
sl@4
   350
		//unsigned char* backBuffer=iBackBuffer;
sl@4
   351
		//iBackBuffer = iFrontBuffer;
sl@4
   352
		//iFrontBuffer = backBuffer;
sl@4
   353
		}
sl@4
   354
	}
sl@4
   355
sl@4
   356
/**
sl@4
   357
Translate the given pixel coordinate according to our off screen mode.
sl@4
   358
*/
sl@4
   359
void GP1212A01A::OffScreenTranslation(unsigned char& aX, unsigned char& aY)
sl@4
   360
	{
sl@4
   361
	if (OffScreenMode())
sl@4
   362
		{
sl@4
   363
		aX+=WidthInPixels()-iDisplayPositionX;
sl@4
   364
		aY+=HeightInPixels()-iDisplayPositionY;
sl@4
   365
		}
sl@4
   366
	}
sl@4
   367
sl@4
   368
sl@4
   369
/**
sl@4
   370
*/
sl@4
   371
void GP1212A01A::ResetBuffers()
sl@4
   372
	{
sl@4
   373
	//iFrameBuffer->ClearAll();
sl@4
   374
	//memset(iFrameBuffer,0x00,sizeof(iFrameBuffer));
sl@4
   375
	//memset(iFrameBeta,0x00,sizeof(iFrameBeta));
sl@4
   376
	}
sl@4
   377
sl@4
   378
/**
sl@4
   379
*/
sl@9
   380
void GP1212A01A::RequestDeviceId()
sl@4
   381
    {
sl@9
   382
    if (RequestPending())
sl@9
   383
        {
sl@9
   384
        //Abort silently for now
sl@9
   385
        return;
sl@9
   386
        }
sl@9
   387
sl@4
   388
    //1BH,5BH,63H,49H,44H
sl@4
   389
    //Send Read ID command
sl@4
   390
    FutabaVfdReport report;
sl@4
   391
    report[0]=0x00; //Report ID
sl@4
   392
    report[1]=0x05; //Report length
sl@4
   393
    report[2]=0x1B; //Command ID
sl@4
   394
    report[3]=0x5B; //Command ID
sl@4
   395
    report[4]=0x63; //Command ID
sl@4
   396
    report[5]=0x49; //Command ID
sl@4
   397
    report[6]=0x44; //Command ID
sl@9
   398
    if (Write(report)==report.Size())
sl@9
   399
        {
sl@9
   400
        iRequest=ERequestDeviceId;
sl@9
   401
        }
sl@4
   402
    }
sl@4
   403
sl@4
   404
/**
sl@4
   405
*/
sl@4
   406
void GP1212A01A::RequestFirmwareRevision()
sl@4
   407
    {
sl@9
   408
    if (RequestPending())
sl@9
   409
        {
sl@9
   410
        //Abort silently for now
sl@9
   411
        return;
sl@9
   412
        }
sl@9
   413
sl@4
   414
    //1BH,5BH,63H,46H,52H
sl@4
   415
    //Send Software Revision Read Command
sl@4
   416
    FutabaVfdReport report;
sl@4
   417
    report[0]=0x00; //Report ID
sl@4
   418
    report[1]=0x05; //Report length
sl@4
   419
    report[2]=0x1B; //Command ID
sl@4
   420
    report[3]=0x5B; //Command ID
sl@4
   421
    report[4]=0x63; //Command ID
sl@4
   422
    report[5]=0x46; //Command ID
sl@4
   423
    report[6]=0x52; //Command ID
sl@9
   424
    if (Write(report)==report.Size())
sl@9
   425
        {
sl@9
   426
        iRequest=ERequestFirmwareRevision;
sl@9
   427
        }
sl@4
   428
    }
sl@4
   429
sl@4
   430
/**
sl@4
   431
*/
sl@4
   432
void GP1212A01A::RequestPowerSupplyStatus()
sl@4
   433
    {
sl@9
   434
    if (RequestPending())
sl@9
   435
        {
sl@9
   436
        //Abort silently for now
sl@9
   437
        return;
sl@9
   438
        }
sl@4
   439
    //1BH,5BH,63H,50H,4DH
sl@4
   440
    //Send Power Suppply Monitor Command
sl@4
   441
    FutabaVfdReport report;
sl@4
   442
    report[0]=0x00; //Report ID
sl@4
   443
    report[1]=0x05; //Report length
sl@4
   444
    report[2]=0x1B; //Command ID
sl@4
   445
    report[3]=0x5B; //Command ID
sl@4
   446
    report[4]=0x63; //Command ID
sl@4
   447
    report[5]=0x50; //Command ID
sl@4
   448
    report[6]=0x4D; //Command ID
sl@9
   449
    if (Write(report)==report.Size())
sl@9
   450
        {
sl@9
   451
        iRequest=ERequestPowerSupplyStatus;
sl@9
   452
        }
sl@4
   453
    }
sl@4
   454
sl@4
   455
sl@4
   456
/**
sl@4
   457
This is for development purposes only.
sl@4
   458
Production application should stick to off-screen mode to avoid tearing.
sl@4
   459
*/
sl@4
   460
void GP1212A01A::ToggleOffScreenMode()
sl@4
   461
	{
sl@4
   462
	iOffScreenMode=!iOffScreenMode;
sl@4
   463
	//Clean up our buffers upon switching modes	
sl@4
   464
	SetDisplayPosition(0,0);
sl@4
   465
	Clear();
sl@4
   466
	SwapBuffers();
sl@4
   467
	Clear();
sl@6
   468
	}
sl@9
   469
sl@9
   470
/**
sl@9
   471
 */
sl@9
   472
GP1212A01A::Request GP1212A01A::AttemptRequestCompletion()
sl@9
   473
    {
sl@9
   474
    if (!RequestPending())
sl@9
   475
        {
sl@9
   476
        return ERequestNone;
sl@9
   477
        }
sl@9
   478
sl@9
   479
    int res=Read(iInputReport);
sl@9
   480
sl@9
   481
    if (!res)
sl@9
   482
        {
sl@9
   483
        return ERequestNone;
sl@9
   484
        }
sl@9
   485
sl@9
   486
    //Process our request
sl@9
   487
    if (CurrentRequest()==GP1212A01A::ERequestPowerSupplyStatus)
sl@9
   488
        {
sl@9
   489
        if (iInputReport[1]==0x4F && iInputReport[2]==0x4E)
sl@9
   490
            {
sl@9
   491
            iPowerOn = true;
sl@9
   492
            }
sl@9
   493
        else if (iInputReport[1]==0x4F && iInputReport[2]==0x46 && iInputReport[3]==0x46)
sl@9
   494
            {
sl@9
   495
            iPowerOn = false;
sl@9
   496
            }
sl@9
   497
        }
sl@9
   498
sl@9
   499
    Request completed=iRequest;
sl@9
   500
    //Our request was completed
sl@9
   501
    iRequest=ERequestNone;
sl@9
   502
sl@9
   503
    return completed;
sl@9
   504
    }