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