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