FutabaMDM166AA.cpp
author StephaneLenclud
Thu, 05 Feb 2015 15:09:48 +0100
changeset 30 7f649078cb52
parent 29 9b44c6e1651c
child 31 0a2b658e0d56
permissions -rw-r--r--
MDM166AA: Clock is now synchronized at the second.
     1 //
     2 //
     3 //
     4 
     5 #include "FutabaMDM166AA.h"
     6 
     7 #include <stdio.h>
     8 #include <time.h>
     9 
    10 
    11 
    12 static void sleep(unsigned int mseconds)
    13 	{
    14     clock_t goal = mseconds + clock();
    15     while (goal > clock());
    16 	}
    17 
    18 //
    19 // class MDM166AA
    20 //
    21 
    22 MDM166AA::MDM166AA():
    23     iOffScreenMode(true),
    24 	iNeedAccurateClockData(false),
    25     iFrameNext(NULL),
    26     iFrameCurrent(NULL),
    27     iFramePrevious(NULL),
    28     iFrameAlpha(NULL),
    29     iFrameBeta(NULL),
    30     iFrameGamma(NULL)
    31 	{
    32 	iDeviceId[0]=0;
    33 	iFirmwareRevision[0]=0;
    34 	//ResetBuffers();
    35 	}
    36 
    37 /**
    38 */
    39 MDM166AA::~MDM166AA()
    40 	{
    41     delete iFrameAlpha;
    42     iFrameAlpha=NULL;
    43     //
    44     delete iFrameBeta;
    45     iFrameBeta=NULL;
    46     //
    47     delete iFrameGamma;
    48     iFrameGamma=NULL;
    49     //
    50     iFrameNext=NULL;
    51     iFrameCurrent=NULL;
    52     iFramePrevious=NULL;
    53 	}
    54 
    55 /**
    56 */
    57 int MDM166AA::Open()
    58 	{
    59 	int success = HidDevice::Open(KTargaVendorId,KFutabaProductIdMDM166AA,NULL);
    60 	if (success)
    61 		{
    62         //Allocate both frames
    63         delete iFrameAlpha;
    64         iFrameAlpha=NULL;
    65         iFrameAlpha=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
    66         //
    67         delete iFrameBeta;
    68         iFrameBeta=NULL;
    69         iFrameBeta=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
    70         //
    71         delete iFrameGamma;
    72         iFrameGamma=NULL;
    73         iFrameGamma=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
    74         //
    75         iFrameNext=iFrameAlpha;
    76         iFrameCurrent=iFrameBeta;
    77         iFramePrevious=iFrameGamma;
    78         //
    79 		SetNonBlocking(1);
    80 		//
    81 		SendCommandReset();
    82 		
    83 		//We will need accurate clock data
    84 		iNeedAccurateClockData=true;
    85 		//Until we get it just use rough time instead
    86 		//We don't set clock data here as it turns on clock display too and cause an unpleasant clock flash
    87 		//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.
    88 		//SetClockData();
    89 		}
    90 	return success;
    91 	}
    92 
    93 /**
    94 */
    95 void MDM166AA::SetPixel(unsigned char aX, unsigned char aY, unsigned int aPixel)
    96 	{
    97 	//
    98 	//int byteOffset=(aX*HeightInPixels()+aY)/8;
    99 	//int bitOffset=(aX*HeightInPixels()+aY)%8;
   100     //iNextFrame[byteOffset] |= ( (aOn?0x01:0x00) << bitOffset );
   101 
   102 	//Pixel is on if any of the non-alpha component is not null
   103 	bool on = (aPixel&0x00FFFFFF)!=0x00000000;
   104 
   105     if (iOffScreenMode)
   106         {
   107         if (on)
   108             {
   109             iFrameNext->SetBit(aX*HeightInPixels()+aY);
   110             }
   111         else
   112             {
   113             iFrameNext->ClearBit(aX*HeightInPixels()+aY);
   114             }
   115         }
   116     else
   117         {
   118         //Just specify a one pixel block
   119         //TODO
   120         }
   121 	}
   122 
   123 /**
   124 Clear our client side back buffer.
   125 Call to SwapBuffers must follow to actually clear the display.
   126 */
   127 void MDM166AA::Clear()
   128     {
   129 	//That one also clear the symbols
   130     SetAllPixels(0x00);
   131     }
   132 
   133 /**
   134 Turn on all pixels.
   135 Must be followed by a SwapBuffers call.
   136 */
   137 void MDM166AA::Fill()
   138 	{
   139 	SetAllPixels(0xFF);
   140 	}
   141 
   142 /**
   143 Set all pixels on our screen to the desired value.
   144 This operation is performed off screen to avoid tearing.
   145 @param 8 pixels pattern
   146 */
   147 void MDM166AA::SetAllPixels(unsigned char aPattern)
   148 	{
   149 	//With a single buffer
   150 	//unsigned char screen[2048]; //One screen worth of pixels
   151 	//memset(screen,0xFF,sizeof(screen));
   152 	//SetPixelBlock(0,0,63,sizeof(screen),screen);
   153 
   154 
   155     if (iOffScreenMode)
   156         {
   157         memset(iFrameNext->Ptr(),aPattern,FrameBufferSizeInBytes());
   158         }
   159     else
   160         {
   161         //Using pattern SetPixelBlock variant.
   162         //TODO
   163         }
   164 	//
   165 	}
   166 
   167 
   168 
   169 
   170 
   171 
   172 /**
   173 Whole display RAM areas including invisible area are filled with 00H data.
   174 (Include the symbol)
   175 SL: Though there is no invisible area with that device.
   176 */
   177 void MDM166AA::SendCommandClear()
   178 	{
   179     //Send Clear Display Command
   180 	FutabaVfdReport report;
   181 	report[0]=0x00; //Report ID
   182 	report[1]=0x02; //Report length
   183 	report[2]=0x1B; //Command ID
   184 	report[3]=0x50; //Command ID
   185 	Write(report);
   186 	}
   187 
   188 /**
   189 Check if accurate clock data is needed and update display clock if system clock seconds are zero.
   190 This is intended to be called every frame from our SwapBuffers function.
   191 */
   192 void MDM166AA::AttemptClockSynchronization()
   193 	{
   194 	//Check if accurate clock data is needed
   195 	if (!iNeedAccurateClockData)
   196 		{
   197 		return;
   198 		}
   199 
   200 	//Fetch local time
   201 	time_t rawtime;
   202 	struct tm * timeinfo;
   203 	time ( &rawtime );
   204 	timeinfo = localtime ( &rawtime );
   205 
   206 	//If our seconds are zero we synchronize our display clock
   207 	if (timeinfo->tm_sec==0)
   208 		{
   209 		SendCommandSetClockData(timeinfo->tm_hour,timeinfo->tm_min);
   210 		//Our clock is as accurate as it can for the time being
   211 		iNeedAccurateClockData=false;
   212 		}
   213 	}
   214 
   215 /**
   216 Put our off screen buffer on screen.
   217 On screen buffer goes off screen.
   218 */
   219 void MDM166AA::SwapBuffers()
   220 	{
   221 	//We need to synchronize our clock seconds
   222 	AttemptClockSynchronization();
   223 
   224 	//Only perform buffer swapping if off screen mode is enabled
   225 	if (OffScreenMode())
   226 		{
   227 		//Send next frame to our display RAM
   228 		//We could attempt to implement a frame differencing algorithm much like we did for GP1212A01.
   229 		//However we see little point doing that since we already run at above 20 FPS.
   230 		SendCommandWriteGraphicData(FrameBufferSizeInBytes(),iFrameNext->Ptr());
   231 
   232         //Cycle through our frame buffers
   233         //We keep track of previous frame which is in fact our device back buffer.
   234         //We can then compare previous and next frame and send only the differences to our device.
   235         //This mechanism allows us to reduce traffic over our USB bus thus improving our frame rate from 14 FPS to 30 FPS.
   236         //Keep our previous frame pointer
   237         BitArrayLow* previousFrame=iFramePrevious;
   238         //Current frame becomes the previous one
   239         iFramePrevious = iFrameCurrent;
   240         //Next frame becomes the current one
   241         iFrameCurrent = iFrameNext;
   242         //Next frame is now our former previous
   243         iFrameNext = previousFrame;
   244 		}
   245 	}
   246 
   247 
   248 //Define the edge of our pixel block
   249 //Pixel blocks of 32x32 seems to run almost as fast as full screen update in worse case scenarii.
   250 //Though I wonder if in some situations 16 could be better. Make this an attribute at some point if need be.
   251 const int KPixelBlockEdge = 32;
   252 const int KPixelBlockSizeInBits = KPixelBlockEdge*KPixelBlockEdge;
   253 const int KPixelBlockSizeInBytes = KPixelBlockSizeInBits/8;
   254 
   255 
   256 /**
   257 */
   258 void MDM166AA::Request(TMiniDisplayRequest aRequest)
   259 	{
   260 	switch (aRequest)
   261 		{
   262 	case EMiniDisplayRequestDeviceId:
   263 		RequestDeviceId();
   264 		break;
   265 	case EMiniDisplayRequestFirmwareRevision:
   266 		RequestFirmwareRevision();
   267 		break;
   268 	case EMiniDisplayRequestPowerSupplyStatus:
   269 		RequestPowerSupplyStatus();
   270 		break;
   271 	default:
   272 		//Not supported
   273 		break;
   274 		};
   275 	}
   276 
   277 
   278 /**
   279 */
   280 void MDM166AA::ResetBuffers()
   281 	{
   282     //iNextFrame->ClearAll();
   283     //memset(iFrameAlpha,0x00,sizeof(iFrameAlpha));
   284 	//memset(iFrameBeta,0x00,sizeof(iFrameBeta));
   285 	}
   286 
   287 /**
   288 */
   289 void MDM166AA::RequestDeviceId()
   290     {
   291 	//Not supported
   292     }
   293 
   294 /**
   295 */
   296 void MDM166AA::RequestFirmwareRevision()
   297     {
   298 	//Not supported
   299     }
   300 
   301 /**
   302 */
   303 void MDM166AA::RequestPowerSupplyStatus()
   304     {
   305 	//Not supported
   306     }
   307 
   308 
   309 /**
   310 This is for development purposes only.
   311 Production application should stick to off-screen mode to avoid tearing.
   312 */
   313 void MDM166AA::ToggleOffScreenMode()
   314 	{
   315     SetOffScreenMode(!iOffScreenMode);
   316 	}
   317 
   318 /**
   319  * @brief MDM166AA::SetOffScreenMode
   320  * @param aOn
   321  * @return
   322  */
   323 void MDM166AA::SetOffScreenMode(bool aOn)
   324     {
   325     if (aOn==iOffScreenMode)
   326     {
   327         //Nothing to do here
   328         return;
   329     }
   330 
   331     iOffScreenMode=aOn;
   332 
   333     //Clean up our buffers upon switching modes
   334     Clear();
   335     SwapBuffers();
   336     Clear();
   337     }
   338 
   339 /**
   340 Set our screen brightness.
   341 @param The desired brightness level. Must be between MinBrightness and MaxBrightness.
   342 */
   343 void MDM166AA::SetBrightness(int aBrightness)
   344     {
   345     if (aBrightness<MinBrightness()||aBrightness>MaxBrightness())
   346         {
   347         //Brightness out of range.
   348         //Just ignore that request.
   349         return;
   350         }
   351 
   352     FutabaVfdReport report;
   353     report[0]=0x00; //Report ID
   354     report[1]=0x03; //Report size
   355     report[2]=0x1B; //Command ID
   356     report[3]=0x40; //Command ID
   357     report[4]=aBrightness; //Brightness level
   358     Write(report);
   359     }
   360 
   361 
   362 /**
   363 */
   364 void MDM166AA::ShowClock()
   365 	{
   366 	//Assuming display clock is at least roughly set since we do it when opening our display connection.
   367 	//We will need accurate clock data next we get a chance.
   368 	//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.
   369 	iNeedAccurateClockData=true;
   370 	//Show clock using specified styles
   371 	SendCommandClockDisplay(EClockLarge,EClock24);
   372 	}
   373 
   374 /**
   375 */
   376 void MDM166AA::HideClock()
   377 	{
   378 	//TODO: or reset
   379 	Clear();
   380 	}
   381 
   382 
   383 /**
   384 Clock setting 
   385 [Code]1BH,00H,Pm,Ph 
   386 [Function]Setting the clock data. The setting data is cleared, if the Reset command is input or power is turned off.
   387 Ph = hour 
   388 Pm = minute 
   389 */
   390 void MDM166AA::SendCommandSetClockData(unsigned char aHour, unsigned char aMinute)
   391 	{
   392 	FutabaVfdReport report;
   393     report[0]=0x00; //Report ID
   394     report[1]=0x04; //Report size
   395     report[2]=0x1B; //Command ID
   396     report[3]=0x00; //Command ID
   397 
   398 	//Minutes and Hours needs to be in hexadecimal view
   399 	//To get 21:59 you need to pass in 0x21:0x59
   400 	//Weirdest format ever, I know 
   401 	report[4]=(aMinute/10*16)+aMinute%10;
   402 	report[5]=(aHour/10*16)+aHour%10;
   403 
   404     Write(report);
   405 	}
   406 
   407 
   408 /**
   409 Set display clock data according to local system time.
   410 This will only provide 30s accuracy.
   411 In fact display clock seconds are set to zero whenever clock data is set.
   412 So you would only get second accuracy if this function was called when system time is at zero second.
   413 It's the responsibility of AttemptClockSynchronization function to obtain second accuracy.
   414 The present function is intended to provide only rough clock synchronization.
   415 
   416 @note Unfortunately this command also turns on clock display.
   417 */
   418 void MDM166AA::SetClockData()
   419 	{
   420 	time_t rawtime;
   421 	struct tm * timeinfo;
   422 
   423 	time ( &rawtime );
   424 	timeinfo = localtime ( &rawtime );
   425 	//Adjust minute as best as we can so that we have a 30 seconds offset at most rather a than a full minute.
   426 	if (timeinfo->tm_sec>30)
   427 		{
   428 		//Use the next minute then
   429 		timeinfo->tm_min++;
   430 		if (timeinfo->tm_min==60)
   431 			{
   432 			//Use the next hour then
   433 			timeinfo->tm_hour++;
   434 			timeinfo->tm_min=0;
   435 			if (timeinfo->tm_hour==24)
   436 				{
   437 				//Move to the next day then
   438 				timeinfo->tm_hour=0;
   439 				}
   440 			}
   441 		}
   442 
   443 	//Send hours and minutes to our display
   444 	SendCommandSetClockData(timeinfo->tm_hour,timeinfo->tm_min);
   445 	}
   446 
   447 
   448 /**
   449 Clock display
   450 [Code] 1BH,Ps,aL,aH,Pf
   451 [Function] Clock is displayed small or big.
   452 */
   453 void MDM166AA::SendCommandClockDisplay(TClockSize aClockSize, TClockFormat aClockFormat)
   454 	{
   455 	FutabaVfdReport report;
   456     report[0]=0x00; //Report ID
   457     report[1]=0x03; //Report size
   458     report[2]=0x1B; //Command ID
   459     report[3]=aClockSize; //
   460     report[4]=aClockFormat; //
   461 
   462     Write(report);
   463 	}
   464 
   465 
   466 /**
   467 Display RAM filled with 00H.
   468 Address Counter is set by 00H.
   469 Dimming is set to 50%.
   470 */
   471 void MDM166AA::SendCommandReset()
   472 	{
   473 	FutabaVfdReport report;
   474 	report[0]=0x00; //Report ID
   475 	report[1]=0x01; //Report length.
   476 	report[2]=0x1F; //Command ID
   477 	Write(report);
   478 	//Wait until reset is done. Is that needed?
   479 	//sleep(2000);
   480 	}
   481 
   482 
   483 /**
   484 Set Address Counter (AC) values: 1BH + 60H + xxH
   485 xxH: 00 ~ BFH
   486 AC value represents the start address for graphic data.
   487 There are 192 bytes as display RAM. It can be set on anywhere even if AC value is not visible area.
   488 The default value is 00H.
   489 Default: 00H
   490 When clock is displayed, AC value is set 00H.
   491 */
   492 void MDM166AA::SendCommandSetAddressCounter(unsigned char aAddressCounter)
   493 	{
   494 	FutabaVfdReport report;
   495 	report[0]=0x00; //Report ID
   496 	report[1]=0x03; //Report length.
   497 	report[2]=0x1B; //Command ID
   498 	report[3]=0x60; //Command ID
   499 	report[4]=aAddressCounter;
   500 	Write(report);
   501 	}
   502 
   503 
   504 /**
   505 Set the defined pixel block to the given value.
   506 
   507 @param The size of our pixel data. Number of pixels divided by 8.
   508 @param Pointer to our pixel data.
   509 */
   510 void MDM166AA::SendCommandWriteGraphicData(int aSize, unsigned char* aPixels)
   511     {
   512 	//TODO: Remove that at some point
   513 	SendCommandSetAddressCounter(0);
   514 
   515 	const int KMaxPixelBytes=48;
   516 	const int KHeaderSize=3;
   517 	
   518 	int remainingSize=aSize;
   519 	int sizeWritten=0;
   520 
   521 	while (remainingSize>0)
   522 		{
   523 		//Only send a maximum of 48 bytes worth of pixels per report
   524 		const int KPixelDataSize=(remainingSize<=KMaxPixelBytes?remainingSize:KMaxPixelBytes);
   525 
   526 		FutabaVfdReport report;
   527 		report[0]=0x00; //Report ID
   528 		report[1]=KPixelDataSize+KHeaderSize; //Report length. +3 is for our header first 3 bytes.
   529 		report[2]=0x1B; //Command ID
   530 		report[3]=0x70; //Command ID
   531 		report[4]=KPixelDataSize; //Size of pixel data in bytes		
   532 		memcpy(report.Buffer()+5, aPixels+sizeWritten, KPixelDataSize);
   533 		Write(report);
   534 		//Advance
   535 		sizeWritten+=KPixelDataSize;
   536 		remainingSize-=KPixelDataSize;
   537 		}
   538     }