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