FutabaMDM166AA.cpp
author StephaneLenclud
Sat, 07 Feb 2015 13:50:11 +0100
changeset 32 2c844ef1ff4b
parent 31 0a2b658e0d56
child 33 fc42477ae80b
permissions -rw-r--r--
MDM166AA: Improved icon APIs.
StephaneLenclud@25
     1
//
StephaneLenclud@25
     2
//
StephaneLenclud@25
     3
//
StephaneLenclud@25
     4
StephaneLenclud@25
     5
#include "FutabaMDM166AA.h"
StephaneLenclud@25
     6
StephaneLenclud@25
     7
#include <stdio.h>
StephaneLenclud@25
     8
#include <time.h>
StephaneLenclud@25
     9
StephaneLenclud@25
    10
StephaneLenclud@25
    11
StephaneLenclud@32
    12
typedef void (MDM166AA::*TSetIconStatus) (int aIndex, int aStatus);
StephaneLenclud@32
    13
StephaneLenclud@32
    14
const TSetIconStatus KFunctionPerIcon[]=
StephaneLenclud@32
    15
	{
StephaneLenclud@32
    16
	&MDM166AA::SetIconNetwork,	//EMiniDisplayIconNetwork,
StephaneLenclud@32
    17
    &MDM166AA::SetIconEmail,	//EMiniDisplayIconEmail,
StephaneLenclud@32
    18
    &MDM166AA::SetIconMute,	//EMiniDisplayIconMute,
StephaneLenclud@32
    19
    &MDM166AA::SetIconVolume, //EMiniDisplayIconVolume,
StephaneLenclud@32
    20
	&MDM166AA::SetIconVolumeLabel,	//EMiniDisplayIconVolumeLabel,
StephaneLenclud@32
    21
	&MDM166AA::SetIconPlay,	//EMiniDisplayIconPlay,
StephaneLenclud@32
    22
	&MDM166AA::SetIconPause,	//EMiniDisplayIconPause,
StephaneLenclud@32
    23
	&MDM166AA::SetIconRecording	//EMiniDisplayIconRecording
StephaneLenclud@32
    24
	};
StephaneLenclud@32
    25
StephaneLenclud@32
    26
const int KMaxIconType = sizeof(KFunctionPerIcon)/sizeof(TSetIconStatus);
StephaneLenclud@32
    27
StephaneLenclud@32
    28
/**
StephaneLenclud@32
    29
Define how segments each of our icons have.
StephaneLenclud@32
    30
Order matters.
StephaneLenclud@32
    31
*/
StephaneLenclud@32
    32
const int KSegmentsPerIcon[]=
StephaneLenclud@32
    33
	{
StephaneLenclud@32
    34
	4,	//EMiniDisplayIconNetwork,
StephaneLenclud@32
    35
    2,	//EMiniDisplayIconEmail,
StephaneLenclud@32
    36
    1,	//EMiniDisplayIconMute,
StephaneLenclud@32
    37
    14, //EMiniDisplayIconVolume,
StephaneLenclud@32
    38
	1,	//EMiniDisplayIconVolumeLabel,
StephaneLenclud@32
    39
	1,	//EMiniDisplayIconPlay,
StephaneLenclud@32
    40
	1,	//EMiniDisplayIconPause,
StephaneLenclud@32
    41
	1	//EMiniDisplayIconRecording
StephaneLenclud@32
    42
	};
StephaneLenclud@32
    43
StephaneLenclud@32
    44
/**
StephaneLenclud@32
    45
Define how status each of our icon can assume.
StephaneLenclud@32
    46
Its typically two for On and Off status.
StephaneLenclud@32
    47
*/
StephaneLenclud@32
    48
const int KStatusPerIcon[]=
StephaneLenclud@32
    49
	{
StephaneLenclud@32
    50
	2,	//EMiniDisplayIconNetwork,
StephaneLenclud@32
    51
    2,	//EMiniDisplayIconEmail,
StephaneLenclud@32
    52
    2,	//EMiniDisplayIconMute,
StephaneLenclud@32
    53
    3, //EMiniDisplayIconVolume,
StephaneLenclud@32
    54
	2,	//EMiniDisplayIconVolumeLabel,	2,	//EMiniDisplayIconPlay,
StephaneLenclud@32
    55
	2,	//EMiniDisplayIconPause,
StephaneLenclud@32
    56
	2	//EMiniDisplayIconRecording
StephaneLenclud@32
    57
	};
StephaneLenclud@32
    58
StephaneLenclud@32
    59
StephaneLenclud@32
    60
StephaneLenclud@25
    61
static void sleep(unsigned int mseconds)
StephaneLenclud@25
    62
	{
StephaneLenclud@25
    63
    clock_t goal = mseconds + clock();
StephaneLenclud@25
    64
    while (goal > clock());
StephaneLenclud@25
    65
	}
StephaneLenclud@25
    66
StephaneLenclud@25
    67
//
StephaneLenclud@25
    68
// class MDM166AA
StephaneLenclud@25
    69
//
StephaneLenclud@25
    70
StephaneLenclud@25
    71
MDM166AA::MDM166AA():
StephaneLenclud@25
    72
    iOffScreenMode(true),
StephaneLenclud@30
    73
	iNeedAccurateClockData(false),
StephaneLenclud@25
    74
    iFrameNext(NULL),
StephaneLenclud@25
    75
    iFrameCurrent(NULL),
StephaneLenclud@25
    76
    iFramePrevious(NULL),
StephaneLenclud@25
    77
    iFrameAlpha(NULL),
StephaneLenclud@25
    78
    iFrameBeta(NULL),
StephaneLenclud@28
    79
    iFrameGamma(NULL)
StephaneLenclud@25
    80
	{
StephaneLenclud@25
    81
	iDeviceId[0]=0;
StephaneLenclud@25
    82
	iFirmwareRevision[0]=0;
StephaneLenclud@25
    83
	//ResetBuffers();
StephaneLenclud@25
    84
	}
StephaneLenclud@25
    85
StephaneLenclud@25
    86
/**
StephaneLenclud@25
    87
*/
StephaneLenclud@25
    88
MDM166AA::~MDM166AA()
StephaneLenclud@25
    89
	{
StephaneLenclud@25
    90
    delete iFrameAlpha;
StephaneLenclud@25
    91
    iFrameAlpha=NULL;
StephaneLenclud@25
    92
    //
StephaneLenclud@25
    93
    delete iFrameBeta;
StephaneLenclud@25
    94
    iFrameBeta=NULL;
StephaneLenclud@25
    95
    //
StephaneLenclud@25
    96
    delete iFrameGamma;
StephaneLenclud@25
    97
    iFrameGamma=NULL;
StephaneLenclud@25
    98
    //
StephaneLenclud@25
    99
    iFrameNext=NULL;
StephaneLenclud@25
   100
    iFrameCurrent=NULL;
StephaneLenclud@25
   101
    iFramePrevious=NULL;
StephaneLenclud@25
   102
	}
StephaneLenclud@25
   103
StephaneLenclud@25
   104
/**
StephaneLenclud@25
   105
*/
StephaneLenclud@25
   106
int MDM166AA::Open()
StephaneLenclud@25
   107
	{
StephaneLenclud@25
   108
	int success = HidDevice::Open(KTargaVendorId,KFutabaProductIdMDM166AA,NULL);
StephaneLenclud@25
   109
	if (success)
StephaneLenclud@25
   110
		{
StephaneLenclud@25
   111
        //Allocate both frames
StephaneLenclud@25
   112
        delete iFrameAlpha;
StephaneLenclud@25
   113
        iFrameAlpha=NULL;
StephaneLenclud@25
   114
        iFrameAlpha=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
StephaneLenclud@25
   115
        //
StephaneLenclud@25
   116
        delete iFrameBeta;
StephaneLenclud@25
   117
        iFrameBeta=NULL;
StephaneLenclud@25
   118
        iFrameBeta=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
StephaneLenclud@25
   119
        //
StephaneLenclud@25
   120
        delete iFrameGamma;
StephaneLenclud@25
   121
        iFrameGamma=NULL;
StephaneLenclud@25
   122
        iFrameGamma=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
StephaneLenclud@25
   123
        //
StephaneLenclud@25
   124
        iFrameNext=iFrameAlpha;
StephaneLenclud@25
   125
        iFrameCurrent=iFrameBeta;
StephaneLenclud@25
   126
        iFramePrevious=iFrameGamma;
StephaneLenclud@25
   127
        //
StephaneLenclud@25
   128
		SetNonBlocking(1);
StephaneLenclud@25
   129
		//
StephaneLenclud@28
   130
		SendCommandReset();
StephaneLenclud@30
   131
		
StephaneLenclud@30
   132
		//We will need accurate clock data
StephaneLenclud@30
   133
		iNeedAccurateClockData=true;
StephaneLenclud@30
   134
		//Until we get it just use rough time instead
StephaneLenclud@30
   135
		//We don't set clock data here as it turns on clock display too and cause an unpleasant clock flash
StephaneLenclud@30
   136
		//Only side effect from not doing this here is that for at most one minute the first time you cold boot your display the time should be wrong.
StephaneLenclud@30
   137
		//SetClockData();
StephaneLenclud@31
   138
StephaneLenclud@31
   139
		//Turns mast ON
StephaneLenclud@31
   140
		//SetIconNetwork(0,EIconOn);
StephaneLenclud@31
   141
		//Show volume label
StephaneLenclud@31
   142
		//SendCommandSymbolControl(EIconVolumeLabel,EIconOn);
StephaneLenclud@31
   143
		//Icon checks
StephaneLenclud@31
   144
		//SetAllIcons(EIconOn);
StephaneLenclud@25
   145
		}
StephaneLenclud@25
   146
	return success;
StephaneLenclud@25
   147
	}
StephaneLenclud@25
   148
StephaneLenclud@25
   149
/**
StephaneLenclud@25
   150
*/
StephaneLenclud@25
   151
void MDM166AA::SetPixel(unsigned char aX, unsigned char aY, unsigned int aPixel)
StephaneLenclud@25
   152
	{
StephaneLenclud@25
   153
	//
StephaneLenclud@25
   154
	//int byteOffset=(aX*HeightInPixels()+aY)/8;
StephaneLenclud@25
   155
	//int bitOffset=(aX*HeightInPixels()+aY)%8;
StephaneLenclud@25
   156
    //iNextFrame[byteOffset] |= ( (aOn?0x01:0x00) << bitOffset );
StephaneLenclud@25
   157
StephaneLenclud@25
   158
	//Pixel is on if any of the non-alpha component is not null
StephaneLenclud@25
   159
	bool on = (aPixel&0x00FFFFFF)!=0x00000000;
StephaneLenclud@25
   160
StephaneLenclud@25
   161
    if (iOffScreenMode)
StephaneLenclud@25
   162
        {
StephaneLenclud@25
   163
        if (on)
StephaneLenclud@25
   164
            {
StephaneLenclud@25
   165
            iFrameNext->SetBit(aX*HeightInPixels()+aY);
StephaneLenclud@25
   166
            }
StephaneLenclud@25
   167
        else
StephaneLenclud@25
   168
            {
StephaneLenclud@25
   169
            iFrameNext->ClearBit(aX*HeightInPixels()+aY);
StephaneLenclud@25
   170
            }
StephaneLenclud@25
   171
        }
StephaneLenclud@25
   172
    else
StephaneLenclud@25
   173
        {
StephaneLenclud@25
   174
        //Just specify a one pixel block
StephaneLenclud@25
   175
        //TODO
StephaneLenclud@25
   176
        }
StephaneLenclud@25
   177
	}
StephaneLenclud@25
   178
StephaneLenclud@25
   179
/**
StephaneLenclud@25
   180
Clear our client side back buffer.
StephaneLenclud@25
   181
Call to SwapBuffers must follow to actually clear the display.
StephaneLenclud@25
   182
*/
StephaneLenclud@25
   183
void MDM166AA::Clear()
StephaneLenclud@25
   184
    {
StephaneLenclud@25
   185
	//That one also clear the symbols
StephaneLenclud@28
   186
    SetAllPixels(0x00);
StephaneLenclud@31
   187
	SendCommandClear(); //Clear icons too
StephaneLenclud@25
   188
    }
StephaneLenclud@25
   189
StephaneLenclud@25
   190
/**
StephaneLenclud@25
   191
Turn on all pixels.
StephaneLenclud@25
   192
Must be followed by a SwapBuffers call.
StephaneLenclud@25
   193
*/
StephaneLenclud@25
   194
void MDM166AA::Fill()
StephaneLenclud@25
   195
	{
StephaneLenclud@25
   196
	SetAllPixels(0xFF);
StephaneLenclud@25
   197
	}
StephaneLenclud@25
   198
StephaneLenclud@25
   199
/**
StephaneLenclud@25
   200
Set all pixels on our screen to the desired value.
StephaneLenclud@25
   201
This operation is performed off screen to avoid tearing.
StephaneLenclud@25
   202
@param 8 pixels pattern
StephaneLenclud@25
   203
*/
StephaneLenclud@25
   204
void MDM166AA::SetAllPixels(unsigned char aPattern)
StephaneLenclud@25
   205
	{
StephaneLenclud@25
   206
	//With a single buffer
StephaneLenclud@25
   207
	//unsigned char screen[2048]; //One screen worth of pixels
StephaneLenclud@25
   208
	//memset(screen,0xFF,sizeof(screen));
StephaneLenclud@25
   209
	//SetPixelBlock(0,0,63,sizeof(screen),screen);
StephaneLenclud@25
   210
StephaneLenclud@25
   211
StephaneLenclud@25
   212
    if (iOffScreenMode)
StephaneLenclud@25
   213
        {
StephaneLenclud@25
   214
        memset(iFrameNext->Ptr(),aPattern,FrameBufferSizeInBytes());
StephaneLenclud@25
   215
        }
StephaneLenclud@25
   216
    else
StephaneLenclud@25
   217
        {
StephaneLenclud@25
   218
        //Using pattern SetPixelBlock variant.
StephaneLenclud@25
   219
        //TODO
StephaneLenclud@25
   220
        }
StephaneLenclud@25
   221
	//
StephaneLenclud@25
   222
	}
StephaneLenclud@25
   223
StephaneLenclud@25
   224
StephaneLenclud@25
   225
StephaneLenclud@25
   226
StephaneLenclud@25
   227
StephaneLenclud@25
   228
StephaneLenclud@25
   229
/**
StephaneLenclud@25
   230
Whole display RAM areas including invisible area are filled with 00H data.
StephaneLenclud@25
   231
(Include the symbol)
StephaneLenclud@25
   232
SL: Though there is no invisible area with that device.
StephaneLenclud@25
   233
*/
StephaneLenclud@25
   234
void MDM166AA::SendCommandClear()
StephaneLenclud@25
   235
	{
StephaneLenclud@25
   236
    //Send Clear Display Command
StephaneLenclud@25
   237
	FutabaVfdReport report;
StephaneLenclud@25
   238
	report[0]=0x00; //Report ID
StephaneLenclud@25
   239
	report[1]=0x02; //Report length
StephaneLenclud@25
   240
	report[2]=0x1B; //Command ID
StephaneLenclud@25
   241
	report[3]=0x50; //Command ID
StephaneLenclud@25
   242
	Write(report);
StephaneLenclud@25
   243
	}
StephaneLenclud@25
   244
StephaneLenclud@25
   245
/**
StephaneLenclud@30
   246
Check if accurate clock data is needed and update display clock if system clock seconds are zero.
StephaneLenclud@30
   247
This is intended to be called every frame from our SwapBuffers function.
StephaneLenclud@30
   248
*/
StephaneLenclud@30
   249
void MDM166AA::AttemptClockSynchronization()
StephaneLenclud@30
   250
	{
StephaneLenclud@30
   251
	//Check if accurate clock data is needed
StephaneLenclud@30
   252
	if (!iNeedAccurateClockData)
StephaneLenclud@30
   253
		{
StephaneLenclud@30
   254
		return;
StephaneLenclud@30
   255
		}
StephaneLenclud@30
   256
StephaneLenclud@30
   257
	//Fetch local time
StephaneLenclud@30
   258
	time_t rawtime;
StephaneLenclud@30
   259
	struct tm * timeinfo;
StephaneLenclud@30
   260
	time ( &rawtime );
StephaneLenclud@30
   261
	timeinfo = localtime ( &rawtime );
StephaneLenclud@30
   262
StephaneLenclud@30
   263
	//If our seconds are zero we synchronize our display clock
StephaneLenclud@30
   264
	if (timeinfo->tm_sec==0)
StephaneLenclud@30
   265
		{
StephaneLenclud@30
   266
		SendCommandSetClockData(timeinfo->tm_hour,timeinfo->tm_min);
StephaneLenclud@30
   267
		//Our clock is as accurate as it can for the time being
StephaneLenclud@30
   268
		iNeedAccurateClockData=false;
StephaneLenclud@30
   269
		}
StephaneLenclud@30
   270
	}
StephaneLenclud@30
   271
StephaneLenclud@30
   272
/**
StephaneLenclud@25
   273
Put our off screen buffer on screen.
StephaneLenclud@25
   274
On screen buffer goes off screen.
StephaneLenclud@25
   275
*/
StephaneLenclud@25
   276
void MDM166AA::SwapBuffers()
StephaneLenclud@25
   277
	{
StephaneLenclud@30
   278
	//We need to synchronize our clock seconds
StephaneLenclud@30
   279
	AttemptClockSynchronization();
StephaneLenclud@30
   280
StephaneLenclud@25
   281
	//Only perform buffer swapping if off screen mode is enabled
StephaneLenclud@25
   282
	if (OffScreenMode())
StephaneLenclud@25
   283
		{
StephaneLenclud@28
   284
		//Send next frame to our display RAM
StephaneLenclud@28
   285
		//We could attempt to implement a frame differencing algorithm much like we did for GP1212A01.
StephaneLenclud@28
   286
		//However we see little point doing that since we already run at above 20 FPS.
StephaneLenclud@27
   287
		SendCommandWriteGraphicData(FrameBufferSizeInBytes(),iFrameNext->Ptr());
StephaneLenclud@25
   288
StephaneLenclud@25
   289
        //Cycle through our frame buffers
StephaneLenclud@25
   290
        //We keep track of previous frame which is in fact our device back buffer.
StephaneLenclud@25
   291
        //We can then compare previous and next frame and send only the differences to our device.
StephaneLenclud@25
   292
        //This mechanism allows us to reduce traffic over our USB bus thus improving our frame rate from 14 FPS to 30 FPS.
StephaneLenclud@25
   293
        //Keep our previous frame pointer
StephaneLenclud@25
   294
        BitArrayLow* previousFrame=iFramePrevious;
StephaneLenclud@25
   295
        //Current frame becomes the previous one
StephaneLenclud@25
   296
        iFramePrevious = iFrameCurrent;
StephaneLenclud@25
   297
        //Next frame becomes the current one
StephaneLenclud@25
   298
        iFrameCurrent = iFrameNext;
StephaneLenclud@25
   299
        //Next frame is now our former previous
StephaneLenclud@25
   300
        iFrameNext = previousFrame;
StephaneLenclud@25
   301
		}
StephaneLenclud@25
   302
	}
StephaneLenclud@25
   303
StephaneLenclud@25
   304
StephaneLenclud@25
   305
//Define the edge of our pixel block
StephaneLenclud@25
   306
//Pixel blocks of 32x32 seems to run almost as fast as full screen update in worse case scenarii.
StephaneLenclud@25
   307
//Though I wonder if in some situations 16 could be better. Make this an attribute at some point if need be.
StephaneLenclud@25
   308
const int KPixelBlockEdge = 32;
StephaneLenclud@25
   309
const int KPixelBlockSizeInBits = KPixelBlockEdge*KPixelBlockEdge;
StephaneLenclud@25
   310
const int KPixelBlockSizeInBytes = KPixelBlockSizeInBits/8;
StephaneLenclud@25
   311
StephaneLenclud@25
   312
StephaneLenclud@25
   313
/**
StephaneLenclud@25
   314
*/
StephaneLenclud@25
   315
void MDM166AA::Request(TMiniDisplayRequest aRequest)
StephaneLenclud@25
   316
	{
StephaneLenclud@25
   317
	switch (aRequest)
StephaneLenclud@25
   318
		{
StephaneLenclud@25
   319
	case EMiniDisplayRequestDeviceId:
StephaneLenclud@25
   320
		RequestDeviceId();
StephaneLenclud@25
   321
		break;
StephaneLenclud@25
   322
	case EMiniDisplayRequestFirmwareRevision:
StephaneLenclud@25
   323
		RequestFirmwareRevision();
StephaneLenclud@25
   324
		break;
StephaneLenclud@25
   325
	case EMiniDisplayRequestPowerSupplyStatus:
StephaneLenclud@25
   326
		RequestPowerSupplyStatus();
StephaneLenclud@25
   327
		break;
StephaneLenclud@25
   328
	default:
StephaneLenclud@25
   329
		//Not supported
StephaneLenclud@25
   330
		break;
StephaneLenclud@25
   331
		};
StephaneLenclud@25
   332
	}
StephaneLenclud@25
   333
StephaneLenclud@25
   334
StephaneLenclud@25
   335
/**
StephaneLenclud@25
   336
*/
StephaneLenclud@25
   337
void MDM166AA::ResetBuffers()
StephaneLenclud@25
   338
	{
StephaneLenclud@25
   339
    //iNextFrame->ClearAll();
StephaneLenclud@25
   340
    //memset(iFrameAlpha,0x00,sizeof(iFrameAlpha));
StephaneLenclud@25
   341
	//memset(iFrameBeta,0x00,sizeof(iFrameBeta));
StephaneLenclud@25
   342
	}
StephaneLenclud@25
   343
StephaneLenclud@25
   344
/**
StephaneLenclud@25
   345
*/
StephaneLenclud@25
   346
void MDM166AA::RequestDeviceId()
StephaneLenclud@25
   347
    {
StephaneLenclud@25
   348
	//Not supported
StephaneLenclud@25
   349
    }
StephaneLenclud@25
   350
StephaneLenclud@25
   351
/**
StephaneLenclud@25
   352
*/
StephaneLenclud@25
   353
void MDM166AA::RequestFirmwareRevision()
StephaneLenclud@25
   354
    {
StephaneLenclud@28
   355
	//Not supported
StephaneLenclud@25
   356
    }
StephaneLenclud@25
   357
StephaneLenclud@25
   358
/**
StephaneLenclud@25
   359
*/
StephaneLenclud@25
   360
void MDM166AA::RequestPowerSupplyStatus()
StephaneLenclud@25
   361
    {
StephaneLenclud@25
   362
	//Not supported
StephaneLenclud@25
   363
    }
StephaneLenclud@25
   364
StephaneLenclud@25
   365
StephaneLenclud@25
   366
/**
StephaneLenclud@25
   367
This is for development purposes only.
StephaneLenclud@25
   368
Production application should stick to off-screen mode to avoid tearing.
StephaneLenclud@25
   369
*/
StephaneLenclud@25
   370
void MDM166AA::ToggleOffScreenMode()
StephaneLenclud@25
   371
	{
StephaneLenclud@25
   372
    SetOffScreenMode(!iOffScreenMode);
StephaneLenclud@25
   373
	}
StephaneLenclud@25
   374
StephaneLenclud@25
   375
/**
StephaneLenclud@25
   376
 * @brief MDM166AA::SetOffScreenMode
StephaneLenclud@25
   377
 * @param aOn
StephaneLenclud@25
   378
 * @return
StephaneLenclud@25
   379
 */
StephaneLenclud@25
   380
void MDM166AA::SetOffScreenMode(bool aOn)
StephaneLenclud@25
   381
    {
StephaneLenclud@25
   382
    if (aOn==iOffScreenMode)
StephaneLenclud@25
   383
    {
StephaneLenclud@25
   384
        //Nothing to do here
StephaneLenclud@25
   385
        return;
StephaneLenclud@25
   386
    }
StephaneLenclud@25
   387
StephaneLenclud@25
   388
    iOffScreenMode=aOn;
StephaneLenclud@25
   389
StephaneLenclud@25
   390
    //Clean up our buffers upon switching modes
StephaneLenclud@25
   391
    Clear();
StephaneLenclud@25
   392
    SwapBuffers();
StephaneLenclud@25
   393
    Clear();
StephaneLenclud@25
   394
    }
StephaneLenclud@25
   395
StephaneLenclud@25
   396
/**
StephaneLenclud@25
   397
Set our screen brightness.
StephaneLenclud@25
   398
@param The desired brightness level. Must be between MinBrightness and MaxBrightness.
StephaneLenclud@25
   399
*/
StephaneLenclud@25
   400
void MDM166AA::SetBrightness(int aBrightness)
StephaneLenclud@25
   401
    {
StephaneLenclud@25
   402
    if (aBrightness<MinBrightness()||aBrightness>MaxBrightness())
StephaneLenclud@25
   403
        {
StephaneLenclud@25
   404
        //Brightness out of range.
StephaneLenclud@25
   405
        //Just ignore that request.
StephaneLenclud@25
   406
        return;
StephaneLenclud@25
   407
        }
StephaneLenclud@25
   408
StephaneLenclud@25
   409
    FutabaVfdReport report;
StephaneLenclud@25
   410
    report[0]=0x00; //Report ID
StephaneLenclud@25
   411
    report[1]=0x03; //Report size
StephaneLenclud@25
   412
    report[2]=0x1B; //Command ID
StephaneLenclud@25
   413
    report[3]=0x40; //Command ID
StephaneLenclud@25
   414
    report[4]=aBrightness; //Brightness level
StephaneLenclud@25
   415
    Write(report);
StephaneLenclud@25
   416
    }
StephaneLenclud@25
   417
StephaneLenclud@25
   418
StephaneLenclud@25
   419
/**
StephaneLenclud@25
   420
*/
StephaneLenclud@25
   421
void MDM166AA::ShowClock()
StephaneLenclud@25
   422
	{
StephaneLenclud@30
   423
	//Assuming display clock is at least roughly set since we do it when opening our display connection.
StephaneLenclud@30
   424
	//We will need accurate clock data next we get a chance.
StephaneLenclud@30
   425
	//This should guarantee that if our display remain open for weeks our clock will be synchronized whenever we switch back from clock mode to render mode.
StephaneLenclud@30
   426
	iNeedAccurateClockData=true;
StephaneLenclud@30
   427
	//Show clock using specified styles
StephaneLenclud@25
   428
	SendCommandClockDisplay(EClockLarge,EClock24);
StephaneLenclud@25
   429
	}
StephaneLenclud@25
   430
StephaneLenclud@25
   431
/**
StephaneLenclud@25
   432
*/
StephaneLenclud@25
   433
void MDM166AA::HideClock()
StephaneLenclud@25
   434
	{
StephaneLenclud@25
   435
	//TODO: or reset
StephaneLenclud@25
   436
	Clear();
StephaneLenclud@25
   437
	}
StephaneLenclud@25
   438
StephaneLenclud@32
   439
/**
StephaneLenclud@32
   440
*/
StephaneLenclud@32
   441
int MDM166AA::IconCount(TMiniDisplayIconType aIcon)
StephaneLenclud@32
   442
	{
StephaneLenclud@32
   443
	return KSegmentsPerIcon[aIcon];
StephaneLenclud@32
   444
	}
StephaneLenclud@32
   445
StephaneLenclud@32
   446
int MDM166AA::IconStatusCount(TMiniDisplayIconType aIcon)
StephaneLenclud@32
   447
	{
StephaneLenclud@32
   448
	return KStatusPerIcon[aIcon];
StephaneLenclud@32
   449
	}
StephaneLenclud@32
   450
StephaneLenclud@32
   451
void MDM166AA::SetIconStatus(TMiniDisplayIconType aIcon, int aIndex, int aStatus)
StephaneLenclud@32
   452
	{
StephaneLenclud@32
   453
	if (aIcon<0||aIcon>=KMaxIconType||(KFunctionPerIcon[aIcon]==NULL))
StephaneLenclud@32
   454
		{
StephaneLenclud@32
   455
		//Out of range or no function pointer for that icon
StephaneLenclud@32
   456
		return;
StephaneLenclud@32
   457
		}
StephaneLenclud@32
   458
StephaneLenclud@32
   459
	(this->*KFunctionPerIcon[aIcon])(aIndex,aStatus);
StephaneLenclud@32
   460
	}
StephaneLenclud@25
   461
StephaneLenclud@25
   462
/**
StephaneLenclud@31
   463
*/
StephaneLenclud@31
   464
void MDM166AA::SetIconNetwork(int aIndex, int aStatus)
StephaneLenclud@31
   465
	{
StephaneLenclud@32
   466
	if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconNetwork])
StephaneLenclud@31
   467
		{
StephaneLenclud@31
   468
		//Out of range
StephaneLenclud@31
   469
		return;
StephaneLenclud@31
   470
		}
StephaneLenclud@31
   471
StephaneLenclud@31
   472
	SendCommandSymbolControl((TIconId)(aIndex+EIconNetworkMast),(aStatus==0?EIconOff:EIconOn));
StephaneLenclud@31
   473
	}
StephaneLenclud@31
   474
StephaneLenclud@31
   475
/**
StephaneLenclud@31
   476
*/
StephaneLenclud@31
   477
void MDM166AA::SetIconEmail(int aIndex, int aStatus)
StephaneLenclud@31
   478
	{
StephaneLenclud@32
   479
	if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconEmail])
StephaneLenclud@31
   480
		{
StephaneLenclud@31
   481
		//Out of range
StephaneLenclud@31
   482
		return;
StephaneLenclud@31
   483
		}
StephaneLenclud@31
   484
StephaneLenclud@31
   485
	SendCommandSymbolControl((TIconId)(aIndex+EIconEnvelop),(aStatus==0?EIconOff:EIconOn));
StephaneLenclud@31
   486
	}
StephaneLenclud@31
   487
StephaneLenclud@31
   488
/**
StephaneLenclud@31
   489
*/
StephaneLenclud@31
   490
void MDM166AA::SetIconMute(int aIndex, int aStatus)
StephaneLenclud@31
   491
	{
StephaneLenclud@32
   492
	if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconMute])
StephaneLenclud@31
   493
		{
StephaneLenclud@31
   494
		//Out of range
StephaneLenclud@31
   495
		return;
StephaneLenclud@31
   496
		}
StephaneLenclud@31
   497
StephaneLenclud@31
   498
	SendCommandSymbolControl((TIconId)(aIndex+EIconMute),(aStatus==0?EIconOff:EIconOn));
StephaneLenclud@31
   499
	}
StephaneLenclud@31
   500
StephaneLenclud@31
   501
/**
StephaneLenclud@31
   502
*/
StephaneLenclud@31
   503
void MDM166AA::SetIconVolume(int aIndex, int aStatus)
StephaneLenclud@31
   504
	{
StephaneLenclud@32
   505
	if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconVolume])
StephaneLenclud@31
   506
		{
StephaneLenclud@31
   507
		//Out of range
StephaneLenclud@31
   508
		return;
StephaneLenclud@31
   509
		}
StephaneLenclud@31
   510
StephaneLenclud@32
   511
	if (aStatus<EIconOff)
StephaneLenclud@32
   512
		{
StephaneLenclud@32
   513
		//Assuming we just want to turn it off then
StephaneLenclud@32
   514
		aStatus=EIconOff;
StephaneLenclud@32
   515
		}
StephaneLenclud@32
   516
StephaneLenclud@32
   517
	//Make sure we cap at our highest status value
StephaneLenclud@32
   518
	aStatus = MIN(EIconOn,aStatus);
StephaneLenclud@32
   519
StephaneLenclud@32
   520
	SendCommandSymbolControl((TIconId)(aIndex+EIconVolumeLevel01),(TIconStatus)aStatus);
StephaneLenclud@32
   521
	}
StephaneLenclud@32
   522
StephaneLenclud@32
   523
StephaneLenclud@32
   524
/**
StephaneLenclud@32
   525
*/
StephaneLenclud@32
   526
void MDM166AA::SetIconVolumeLabel(int aIndex, int aStatus)
StephaneLenclud@32
   527
	{
StephaneLenclud@32
   528
	if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconMute])
StephaneLenclud@31
   529
		{
StephaneLenclud@31
   530
		//Out of range
StephaneLenclud@31
   531
		return;
StephaneLenclud@31
   532
		}
StephaneLenclud@31
   533
StephaneLenclud@32
   534
	SendCommandSymbolControl((TIconId)(aIndex+EIconVolumeLabel),(aStatus==0?EIconOff:EIconOn));
StephaneLenclud@31
   535
	}
StephaneLenclud@31
   536
StephaneLenclud@31
   537
StephaneLenclud@31
   538
/**
StephaneLenclud@31
   539
*/
StephaneLenclud@31
   540
void MDM166AA::SetIconPlay(int aIndex, int aStatus)
StephaneLenclud@31
   541
	{
StephaneLenclud@32
   542
	if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconPlay])
StephaneLenclud@31
   543
		{
StephaneLenclud@31
   544
		//Out of range
StephaneLenclud@31
   545
		return;
StephaneLenclud@31
   546
		}
StephaneLenclud@31
   547
StephaneLenclud@31
   548
	SendCommandSymbolControl((TIconId)(aIndex+EIconPlay),(aStatus==0?EIconOff:EIconOn));
StephaneLenclud@31
   549
	}
StephaneLenclud@31
   550
StephaneLenclud@31
   551
StephaneLenclud@31
   552
/**
StephaneLenclud@31
   553
*/
StephaneLenclud@31
   554
void MDM166AA::SetIconPause(int aIndex, int aStatus)
StephaneLenclud@31
   555
	{
StephaneLenclud@32
   556
	if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconPause])
StephaneLenclud@31
   557
		{
StephaneLenclud@31
   558
		//Out of range
StephaneLenclud@31
   559
		return;
StephaneLenclud@31
   560
		}
StephaneLenclud@31
   561
StephaneLenclud@31
   562
	SendCommandSymbolControl((TIconId)(aIndex+EIconPause),(aStatus==0?EIconOff:EIconOn));
StephaneLenclud@31
   563
	}
StephaneLenclud@31
   564
StephaneLenclud@31
   565
StephaneLenclud@31
   566
/**
StephaneLenclud@31
   567
*/
StephaneLenclud@31
   568
void MDM166AA::SetIconRecording(int aIndex, int aStatus)
StephaneLenclud@31
   569
	{
StephaneLenclud@32
   570
	if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconRecording])
StephaneLenclud@31
   571
		{
StephaneLenclud@31
   572
		//Out of range
StephaneLenclud@31
   573
		return;
StephaneLenclud@31
   574
		}
StephaneLenclud@31
   575
StephaneLenclud@31
   576
	SendCommandSymbolControl((TIconId)(aIndex+EIconRecording),(aStatus==0?EIconOff:EIconOn));
StephaneLenclud@31
   577
	}
StephaneLenclud@31
   578
StephaneLenclud@31
   579
/**
StephaneLenclud@31
   580
Set all our icons to the corresponding status.
StephaneLenclud@31
   581
*/
StephaneLenclud@31
   582
void MDM166AA::SetAllIcons(TIconStatus aStatus)
StephaneLenclud@31
   583
	{
StephaneLenclud@31
   584
	for (int i=EIconFirst;i<=EIconLast;i++)
StephaneLenclud@31
   585
		{
StephaneLenclud@31
   586
		SendCommandSymbolControl((TIconId)i,aStatus);
StephaneLenclud@31
   587
		}
StephaneLenclud@31
   588
	}
StephaneLenclud@31
   589
StephaneLenclud@31
   590
/**
StephaneLenclud@31
   591
Symbols control
StephaneLenclud@31
   592
Segment On/Off and Grayscale/Brightness
StephaneLenclud@31
   593
[Code]1BH,30H,Ps,Pb
StephaneLenclud@31
   594
*/
StephaneLenclud@31
   595
void MDM166AA::SendCommandSymbolControl(TIconId aIconId, TIconStatus aStatus)
StephaneLenclud@31
   596
	{
StephaneLenclud@31
   597
	FutabaVfdReport report;
StephaneLenclud@31
   598
    report[0]=0x00; //Report ID
StephaneLenclud@31
   599
    report[1]=0x04; //Report size
StephaneLenclud@31
   600
    report[2]=0x1B; //Command ID
StephaneLenclud@31
   601
    report[3]=0x30; //Command ID
StephaneLenclud@31
   602
	report[4]=aIconId;
StephaneLenclud@31
   603
	report[5]=aStatus;
StephaneLenclud@31
   604
StephaneLenclud@31
   605
    Write(report);
StephaneLenclud@31
   606
	}
StephaneLenclud@31
   607
StephaneLenclud@31
   608
StephaneLenclud@31
   609
/**
StephaneLenclud@25
   610
Clock setting 
StephaneLenclud@25
   611
[Code]1BH,00H,Pm,Ph 
StephaneLenclud@25
   612
[Function]Setting the clock data. The setting data is cleared, if the Reset command is input or power is turned off.
StephaneLenclud@25
   613
Ph = hour 
StephaneLenclud@25
   614
Pm = minute 
StephaneLenclud@25
   615
*/
StephaneLenclud@28
   616
void MDM166AA::SendCommandSetClockData(unsigned char aHour, unsigned char aMinute)
StephaneLenclud@25
   617
	{
StephaneLenclud@25
   618
	FutabaVfdReport report;
StephaneLenclud@25
   619
    report[0]=0x00; //Report ID
StephaneLenclud@25
   620
    report[1]=0x04; //Report size
StephaneLenclud@25
   621
    report[2]=0x1B; //Command ID
StephaneLenclud@25
   622
    report[3]=0x00; //Command ID
StephaneLenclud@25
   623
StephaneLenclud@25
   624
	//Minutes and Hours needs to be in hexadecimal view
StephaneLenclud@25
   625
	//To get 21:59 you need to pass in 0x21:0x59
StephaneLenclud@25
   626
	//Weirdest format ever, I know 
StephaneLenclud@25
   627
	report[4]=(aMinute/10*16)+aMinute%10;
StephaneLenclud@25
   628
	report[5]=(aHour/10*16)+aHour%10;
StephaneLenclud@25
   629
StephaneLenclud@25
   630
    Write(report);
StephaneLenclud@25
   631
	}
StephaneLenclud@25
   632
StephaneLenclud@25
   633
/**
StephaneLenclud@28
   634
Set display clock data according to local system time.
StephaneLenclud@30
   635
This will only provide 30s accuracy.
StephaneLenclud@30
   636
In fact display clock seconds are set to zero whenever clock data is set.
StephaneLenclud@30
   637
So you would only get second accuracy if this function was called when system time is at zero second.
StephaneLenclud@30
   638
It's the responsibility of AttemptClockSynchronization function to obtain second accuracy.
StephaneLenclud@30
   639
The present function is intended to provide only rough clock synchronization.
StephaneLenclud@30
   640
StephaneLenclud@30
   641
@note Unfortunately this command also turns on clock display.
StephaneLenclud@25
   642
*/
StephaneLenclud@28
   643
void MDM166AA::SetClockData()
StephaneLenclud@25
   644
	{
StephaneLenclud@25
   645
	time_t rawtime;
StephaneLenclud@25
   646
	struct tm * timeinfo;
StephaneLenclud@25
   647
StephaneLenclud@25
   648
	time ( &rawtime );
StephaneLenclud@25
   649
	timeinfo = localtime ( &rawtime );
StephaneLenclud@28
   650
	//Adjust minute as best as we can so that we have a 30 seconds offset at most rather a than a full minute.
StephaneLenclud@28
   651
	if (timeinfo->tm_sec>30)
StephaneLenclud@28
   652
		{
StephaneLenclud@28
   653
		//Use the next minute then
StephaneLenclud@28
   654
		timeinfo->tm_min++;
StephaneLenclud@28
   655
		if (timeinfo->tm_min==60)
StephaneLenclud@28
   656
			{
StephaneLenclud@28
   657
			//Use the next hour then
StephaneLenclud@28
   658
			timeinfo->tm_hour++;
StephaneLenclud@28
   659
			timeinfo->tm_min=0;
StephaneLenclud@28
   660
			if (timeinfo->tm_hour==24)
StephaneLenclud@28
   661
				{
StephaneLenclud@28
   662
				//Move to the next day then
StephaneLenclud@28
   663
				timeinfo->tm_hour=0;
StephaneLenclud@28
   664
				}
StephaneLenclud@28
   665
			}
StephaneLenclud@28
   666
		}
StephaneLenclud@28
   667
StephaneLenclud@28
   668
	//Send hours and minutes to our display
StephaneLenclud@28
   669
	SendCommandSetClockData(timeinfo->tm_hour,timeinfo->tm_min);
StephaneLenclud@25
   670
	}
StephaneLenclud@25
   671
StephaneLenclud@25
   672
StephaneLenclud@25
   673
/**
StephaneLenclud@25
   674
Clock display
StephaneLenclud@25
   675
[Code] 1BH,Ps,aL,aH,Pf
StephaneLenclud@25
   676
[Function] Clock is displayed small or big.
StephaneLenclud@25
   677
*/
StephaneLenclud@25
   678
void MDM166AA::SendCommandClockDisplay(TClockSize aClockSize, TClockFormat aClockFormat)
StephaneLenclud@25
   679
	{
StephaneLenclud@25
   680
	FutabaVfdReport report;
StephaneLenclud@25
   681
    report[0]=0x00; //Report ID
StephaneLenclud@25
   682
    report[1]=0x03; //Report size
StephaneLenclud@25
   683
    report[2]=0x1B; //Command ID
StephaneLenclud@25
   684
    report[3]=aClockSize; //
StephaneLenclud@25
   685
    report[4]=aClockFormat; //
StephaneLenclud@25
   686
StephaneLenclud@25
   687
    Write(report);
StephaneLenclud@25
   688
	}
StephaneLenclud@25
   689
StephaneLenclud@27
   690
StephaneLenclud@27
   691
/**
StephaneLenclud@27
   692
Display RAM filled with 00H.
StephaneLenclud@27
   693
Address Counter is set by 00H.
StephaneLenclud@27
   694
Dimming is set to 50%.
StephaneLenclud@31
   695
Turn off all icons segments.
StephaneLenclud@27
   696
*/
StephaneLenclud@27
   697
void MDM166AA::SendCommandReset()
StephaneLenclud@27
   698
	{
StephaneLenclud@27
   699
	FutabaVfdReport report;
StephaneLenclud@27
   700
	report[0]=0x00; //Report ID
StephaneLenclud@27
   701
	report[1]=0x01; //Report length.
StephaneLenclud@27
   702
	report[2]=0x1F; //Command ID
StephaneLenclud@27
   703
	Write(report);
StephaneLenclud@27
   704
	}
StephaneLenclud@27
   705
StephaneLenclud@27
   706
StephaneLenclud@27
   707
/**
StephaneLenclud@27
   708
Set Address Counter (AC) values: 1BH + 60H + xxH
StephaneLenclud@27
   709
xxH: 00 ~ BFH
StephaneLenclud@27
   710
AC value represents the start address for graphic data.
StephaneLenclud@27
   711
There are 192 bytes as display RAM. It can be set on anywhere even if AC value is not visible area.
StephaneLenclud@27
   712
The default value is 00H.
StephaneLenclud@27
   713
Default: 00H
StephaneLenclud@27
   714
When clock is displayed, AC value is set 00H.
StephaneLenclud@27
   715
*/
StephaneLenclud@27
   716
void MDM166AA::SendCommandSetAddressCounter(unsigned char aAddressCounter)
StephaneLenclud@27
   717
	{
StephaneLenclud@27
   718
	FutabaVfdReport report;
StephaneLenclud@27
   719
	report[0]=0x00; //Report ID
StephaneLenclud@27
   720
	report[1]=0x03; //Report length.
StephaneLenclud@27
   721
	report[2]=0x1B; //Command ID
StephaneLenclud@27
   722
	report[3]=0x60; //Command ID
StephaneLenclud@27
   723
	report[4]=aAddressCounter;
StephaneLenclud@27
   724
	Write(report);
StephaneLenclud@27
   725
	}
StephaneLenclud@27
   726
StephaneLenclud@27
   727
StephaneLenclud@27
   728
/**
StephaneLenclud@27
   729
Set the defined pixel block to the given value.
StephaneLenclud@27
   730
StephaneLenclud@27
   731
@param The size of our pixel data. Number of pixels divided by 8.
StephaneLenclud@27
   732
@param Pointer to our pixel data.
StephaneLenclud@27
   733
*/
StephaneLenclud@27
   734
void MDM166AA::SendCommandWriteGraphicData(int aSize, unsigned char* aPixels)
StephaneLenclud@27
   735
    {
StephaneLenclud@27
   736
	//TODO: Remove that at some point
StephaneLenclud@27
   737
	SendCommandSetAddressCounter(0);
StephaneLenclud@27
   738
StephaneLenclud@27
   739
	const int KMaxPixelBytes=48;
StephaneLenclud@27
   740
	const int KHeaderSize=3;
StephaneLenclud@27
   741
	
StephaneLenclud@27
   742
	int remainingSize=aSize;
StephaneLenclud@27
   743
	int sizeWritten=0;
StephaneLenclud@27
   744
StephaneLenclud@27
   745
	while (remainingSize>0)
StephaneLenclud@27
   746
		{
StephaneLenclud@27
   747
		//Only send a maximum of 48 bytes worth of pixels per report
StephaneLenclud@27
   748
		const int KPixelDataSize=(remainingSize<=KMaxPixelBytes?remainingSize:KMaxPixelBytes);
StephaneLenclud@27
   749
StephaneLenclud@27
   750
		FutabaVfdReport report;
StephaneLenclud@27
   751
		report[0]=0x00; //Report ID
StephaneLenclud@27
   752
		report[1]=KPixelDataSize+KHeaderSize; //Report length. +3 is for our header first 3 bytes.
StephaneLenclud@27
   753
		report[2]=0x1B; //Command ID
StephaneLenclud@27
   754
		report[3]=0x70; //Command ID
StephaneLenclud@27
   755
		report[4]=KPixelDataSize; //Size of pixel data in bytes		
StephaneLenclud@27
   756
		memcpy(report.Buffer()+5, aPixels+sizeWritten, KPixelDataSize);
StephaneLenclud@27
   757
		Write(report);
StephaneLenclud@27
   758
		//Advance
StephaneLenclud@27
   759
		sizeWritten+=KPixelDataSize;
StephaneLenclud@27
   760
		remainingSize-=KPixelDataSize;
StephaneLenclud@27
   761
		}
StephaneLenclud@27
   762
    }