FutabaMDM166AA.cpp
author StephaneLenclud
Wed, 04 Feb 2015 21:47:17 +0100
changeset 25 3fa4007c0b19
child 27 949be5444c57
permissions -rw-r--r--
First draft implementation of Futaba MDM166AA.
Only clock and brightness working for now.
     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 	iDisplayPositionX(0),iDisplayPositionY(0),
    24     iOffScreenMode(true),
    25     iUseFrameDifferencing(true),
    26     iFrameNext(NULL),
    27     iFrameCurrent(NULL),
    28     iFramePrevious(NULL),
    29     iFrameAlpha(NULL),
    30     iFrameBeta(NULL),
    31     iFrameGamma(NULL),
    32     iNeedFullFrameUpdate(0),
    33     iPowerOn(false)
    34 	{
    35 	iDeviceId[0]=0;
    36 	iFirmwareRevision[0]=0;
    37 	//ResetBuffers();
    38 	}
    39 
    40 /**
    41 */
    42 MDM166AA::~MDM166AA()
    43 	{
    44     delete iFrameAlpha;
    45     iFrameAlpha=NULL;
    46     //
    47     delete iFrameBeta;
    48     iFrameBeta=NULL;
    49     //
    50     delete iFrameGamma;
    51     iFrameGamma=NULL;
    52     //
    53     iFrameNext=NULL;
    54     iFrameCurrent=NULL;
    55     iFramePrevious=NULL;
    56     //
    57     iNeedFullFrameUpdate=0;
    58 	}
    59 
    60 /**
    61 */
    62 int MDM166AA::Open()
    63 	{
    64 	int success = HidDevice::Open(KTargaVendorId,KFutabaProductIdMDM166AA,NULL);
    65 	if (success)
    66 		{
    67         //Allocate both frames
    68         delete iFrameAlpha;
    69         iFrameAlpha=NULL;
    70         iFrameAlpha=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
    71         //
    72         delete iFrameBeta;
    73         iFrameBeta=NULL;
    74         iFrameBeta=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
    75         //
    76         delete iFrameGamma;
    77         iFrameGamma=NULL;
    78         iFrameGamma=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
    79         //
    80         iFrameNext=iFrameAlpha;
    81         iFrameCurrent=iFrameBeta;
    82         iFramePrevious=iFrameGamma;
    83 
    84 
    85         //To make sure it is synced properly
    86         iNeedFullFrameUpdate=0;
    87         //
    88 		SetNonBlocking(1);
    89 		//
    90 		SendCommandClear();
    91 		//
    92 		SetClockSetting();
    93 
    94 		}
    95 	return success;
    96 	}
    97 
    98 
    99 /**
   100 Display RAM filled with 00H.
   101 Address Counter is set by 00H.
   102 Dimming is set to 50%.
   103 */
   104 void MDM166AA::SendCommandReset()
   105 	{
   106 	FutabaVfdReport report;
   107 	report[0]=0x00; //Report ID
   108 	report[1]=0x01; //Report length.
   109 	report[2]=0x1F; //Command ID
   110 	Write(report);
   111 	//Wait until reset is done. Is that needed?
   112 	//sleep(2000);
   113 	}
   114 
   115 
   116 /**
   117 Set Address Counter (AC) values: 1BH + 60H + xxH
   118 xxH: 00 ~ BFH
   119 AC value represents the start address for graphic data.
   120 There are 192 bytes as display RAM. It can be set on anywhere even if AC value is not visible area.
   121 The default value is 00H.
   122 Default: 00H
   123 When clock is displayed, AC value is set 00H.
   124 */
   125 void MDM166AA::SendCommandSetAddressCounter(unsigned char aAddressCounter)
   126 	{
   127 	FutabaVfdReport report;
   128 	report[0]=0x00; //Report ID
   129 	report[1]=0x03; //Report length.
   130 	report[2]=0x1B; //Command ID
   131 	report[3]=0x60; //Command ID
   132 	report[4]=aAddressCounter;
   133 	Write(report);
   134 	}
   135 
   136 
   137 /**
   138 */
   139 void MDM166AA::SetPixel(unsigned char aX, unsigned char aY, unsigned int aPixel)
   140 	{
   141 	//
   142 	//int byteOffset=(aX*HeightInPixels()+aY)/8;
   143 	//int bitOffset=(aX*HeightInPixels()+aY)%8;
   144     //iNextFrame[byteOffset] |= ( (aOn?0x01:0x00) << bitOffset );
   145 
   146 	//Pixel is on if any of the non-alpha component is not null
   147 	bool on = (aPixel&0x00FFFFFF)!=0x00000000;
   148 
   149     if (iOffScreenMode)
   150         {
   151         if (on)
   152             {
   153             iFrameNext->SetBit(aX*HeightInPixels()+aY);
   154             }
   155         else
   156             {
   157             iFrameNext->ClearBit(aX*HeightInPixels()+aY);
   158             }
   159         }
   160     else
   161         {
   162         //Just specify a one pixel block
   163         //TODO
   164         }
   165 	}
   166 
   167 /**
   168 */
   169 /*
   170 void MDM166AA::BitBlit(const BitArray& aBitmap, int aSrcWidth, int aSrcHeight, int aTargetX, int aTargetY) const
   171 	{
   172 	//TODO: amend loop values so that we don't keep on looping past our frame buffer dimensions.
   173 	for (int i=0;i<aSrcWidth;i++)
   174 		{
   175 		for (int j=0;j<aSrcHeight;j++)
   176 			{
   177             iFrameNext->SetBitValue((aTargetX+i)*HeightInPixels()+aTargetY+j,aBitmap[+i*aSrcHeight+j]);
   178 			}
   179 		}
   180 	}
   181 */
   182 
   183 /**
   184 Clear our client side back buffer.
   185 Call to SwapBuffers must follow to actually clear the display.
   186 */
   187 void MDM166AA::Clear()
   188     {
   189 	//That one also clear the symbols
   190     SendCommandClear();
   191 	//TODO: Consider just clearing the pixels instead
   192     }
   193 
   194 /**
   195 Turn on all pixels.
   196 Must be followed by a SwapBuffers call.
   197 */
   198 void MDM166AA::Fill()
   199 	{
   200 	SetAllPixels(0xFF);
   201 	}
   202 
   203 /**
   204 Set all pixels on our screen to the desired value.
   205 This operation is performed off screen to avoid tearing.
   206 @param 8 pixels pattern
   207 */
   208 void MDM166AA::SetAllPixels(unsigned char aPattern)
   209 	{
   210 	//With a single buffer
   211 	//unsigned char screen[2048]; //One screen worth of pixels
   212 	//memset(screen,0xFF,sizeof(screen));
   213 	//SetPixelBlock(0,0,63,sizeof(screen),screen);
   214 
   215 
   216     if (iOffScreenMode)
   217         {
   218         memset(iFrameNext->Ptr(),aPattern,FrameBufferSizeInBytes());
   219         }
   220     else
   221         {
   222         //Using pattern SetPixelBlock variant.
   223         //TODO
   224         }
   225 	//
   226 	}
   227 
   228 
   229 
   230 
   231 
   232 
   233 /**
   234 Whole display RAM areas including invisible area are filled with 00H data.
   235 (Include the symbol)
   236 SL: Though there is no invisible area with that device.
   237 */
   238 void MDM166AA::SendCommandClear()
   239 	{
   240     //Send Clear Display Command
   241 	FutabaVfdReport report;
   242 	report[0]=0x00; //Report ID
   243 	report[1]=0x02; //Report length
   244 	report[2]=0x1B; //Command ID
   245 	report[3]=0x50; //Command ID
   246 	Write(report);
   247 	}
   248 
   249 
   250 /**
   251 Provide Y coordinate of our off screen buffer.
   252 */
   253 unsigned char MDM166AA::OffScreenY() const
   254 	{
   255 	//Overflowing is fine this is just what we want
   256 	return iDisplayPositionY+HeightInPixels();
   257 	}
   258 
   259 /**
   260 Put our off screen buffer on screen.
   261 On screen buffer goes off screen.
   262 */
   263 void MDM166AA::SwapBuffers()
   264 	{
   265 	//Only perform buffer swapping if off screen mode is enabled
   266 	if (OffScreenMode())
   267 		{
   268 		//Send pixel directly into BMP box
   269 		//BmpBoxDataInput(FrameBufferSizeInBytes(),iFrameNext->Ptr());
   270 		//Send pixel data directly into the display window
   271 		//BmpDataInput(ETargetDisplayWindow,0x0000,EDirectionY, FrameBufferSizeInBytes(),iFrameNext->Ptr());
   272 		//Send pixel data first to Data Memory then copy into the selected BMP box	
   273 		//BmpDataInput(ETargetDataMemory,0x0000,EDirectionY, FrameBufferSizeInBytes(),iFrameNext->Ptr());
   274 		//BmpBoxDataMemoryTransfer(0x0000);
   275 		//Send pixel data first to Data Memory then copy into the selected BMP box, cycling through our Data Memory frmae
   276 		//BmpDataInput(ETargetDataMemory,iNextFrameAddress,EDirectionY, FrameBufferSizeInBytes(),iFrameNext->Ptr());
   277 		//BmpBoxDataMemoryTransfer(iNextFrameAddress);
   278 
   279 
   280         //Cycle through our frame buffers
   281         //We keep track of previous frame which is in fact our device back buffer.
   282         //We can then compare previous and next frame and send only the differences to our device.
   283         //This mechanism allows us to reduce traffic over our USB bus thus improving our frame rate from 14 FPS to 30 FPS.
   284         //Keep our previous frame pointer
   285         BitArrayLow* previousFrame=iFramePrevious;
   286         //Current frame becomes the previous one
   287         iFramePrevious = iFrameCurrent;
   288         //Next frame becomes the current one
   289         iFrameCurrent = iFrameNext;
   290         //Next frame is now our former previous
   291         iFrameNext = previousFrame;
   292 		}
   293 	}
   294 
   295 
   296 /**
   297 Set the defined pixel block to the given value.
   298 @param X coordinate of our pixel block starting point.
   299 @param Y coordinate of our pixel block starting point.
   300 @param The height of our pixel block.
   301 @param The size of our pixel data. Number of pixels divided by 8.
   302 @param Pointer to our pixel data.
   303 */
   304 void MDM166AA::SetPixelBlock(unsigned char aX, unsigned char aY, int aHeight, int aSize, unsigned char* aPixels)
   305     {
   306 	//TODO: Assuming 0,0 for now, do the math later
   307 	SendCommandSetAddressCounter(0);
   308 
   309 	const int KMaxPixelBytes=48;
   310 
   311     FutabaVfdReport report;
   312     report[0]=0x00; //Report ID
   313     report[1]=(aSize<=report.Size()-10?aSize+0x08:64); //Report length. -10 is for our header first 10 bytes. +8 is for our Futaba header size
   314     report[2]=0x1B; //Command ID
   315     report[3]=0x70; //Command ID
   316 	//TODO: All rubbish
   317 	report[4]=MAX(KMaxPixelBytes,aSize); //Size of pixel data in bytes
   318     int sizeWritten=MIN(aSize,report.Size()-10);
   319     memcpy(report.Buffer()+10, aPixels, sizeWritten);
   320     Write(report);
   321 
   322     int remainingSize=aSize;
   323     //We need to keep on sending our pixel data until we are done
   324     while (report[1]==64)
   325         {
   326         report.Reset();
   327         remainingSize-=sizeWritten;
   328         report[0]=0x00; //Report ID
   329         report[1]=(remainingSize<=report.Size()-2?remainingSize:64); //Report length, should be 64 or the remaining size
   330         sizeWritten=(report[1]==64?63:report[1]);
   331         memcpy(report.Buffer()+2, aPixels+(aSize-remainingSize), sizeWritten);
   332         Write(report);
   333         }
   334     }
   335 
   336 
   337 
   338 //Define the edge of our pixel block
   339 //Pixel blocks of 32x32 seems to run almost as fast as full screen update in worse case scenarii.
   340 //Though I wonder if in some situations 16 could be better. Make this an attribute at some point if need be.
   341 const int KPixelBlockEdge = 32;
   342 const int KPixelBlockSizeInBits = KPixelBlockEdge*KPixelBlockEdge;
   343 const int KPixelBlockSizeInBytes = KPixelBlockSizeInBits/8;
   344 
   345 
   346 /**
   347 Translate the given pixel coordinate according to our off screen mode.
   348 */
   349 void MDM166AA::OffScreenTranslation(unsigned char& aX, unsigned char& aY)
   350 	{
   351 	if (OffScreenMode())
   352 		{
   353 		aX+=WidthInPixels()-iDisplayPositionX;
   354 		aY+=HeightInPixels()-iDisplayPositionY;
   355 		}
   356 	}
   357 
   358 
   359 /**
   360 */
   361 void MDM166AA::Request(TMiniDisplayRequest aRequest)
   362 	{
   363 	switch (aRequest)
   364 		{
   365 	case EMiniDisplayRequestDeviceId:
   366 		RequestDeviceId();
   367 		break;
   368 	case EMiniDisplayRequestFirmwareRevision:
   369 		RequestFirmwareRevision();
   370 		break;
   371 	case EMiniDisplayRequestPowerSupplyStatus:
   372 		RequestPowerSupplyStatus();
   373 		break;
   374 	default:
   375 		//Not supported
   376 		break;
   377 		};
   378 	}
   379 
   380 
   381 /**
   382 */
   383 void MDM166AA::ResetBuffers()
   384 	{
   385     //iNextFrame->ClearAll();
   386     //memset(iFrameAlpha,0x00,sizeof(iFrameAlpha));
   387 	//memset(iFrameBeta,0x00,sizeof(iFrameBeta));
   388 	}
   389 
   390 /**
   391 */
   392 void MDM166AA::RequestDeviceId()
   393     {
   394 	//Not supported
   395     }
   396 
   397 /**
   398 ID code 
   399 [Code] 1BH,6AH,49H,44H
   400 [Function] Send the ID code to the Host system. ID code is software version.
   401 */
   402 void MDM166AA::RequestFirmwareRevision()
   403     {
   404     if (RequestPending())
   405         {
   406         //Abort silently for now
   407         return;
   408         }
   409 
   410     //1BH,6AH,49H,44H
   411     //Send Software Revision Read Command
   412     FutabaVfdReport report;
   413     report[0]=0x00; //Report ID
   414     report[1]=0x04; //Report length
   415     report[2]=0x1B; //Command ID
   416     report[3]=0x6A; //Command ID
   417     report[4]=0x49; //Command ID
   418     report[5]=0x44; //Command ID
   419     if (Write(report)==report.Size())
   420         {
   421         SetRequest(EMiniDisplayRequestFirmwareRevision);
   422         }
   423 
   424     }
   425 
   426 /**
   427 */
   428 void MDM166AA::RequestPowerSupplyStatus()
   429     {
   430 	//Not supported
   431     }
   432 
   433 
   434 /**
   435 This is for development purposes only.
   436 Production application should stick to off-screen mode to avoid tearing.
   437 */
   438 void MDM166AA::ToggleOffScreenMode()
   439 	{
   440     SetOffScreenMode(!iOffScreenMode);
   441 	}
   442 
   443 /**
   444  * @brief MDM166AA::SetOffScreenMode
   445  * @param aOn
   446  * @return
   447  */
   448 void MDM166AA::SetOffScreenMode(bool aOn)
   449     {
   450     if (aOn==iOffScreenMode)
   451     {
   452         //Nothing to do here
   453         return;
   454     }
   455 
   456     iOffScreenMode=aOn;
   457 
   458     //Clean up our buffers upon switching modes
   459     Clear();
   460     SwapBuffers();
   461     Clear();
   462     }
   463 
   464 /**
   465 Tries to complete our current request if we have one pending.
   466  */
   467 TMiniDisplayRequest MDM166AA::AttemptRequestCompletion()
   468     {
   469     if (!RequestPending())
   470         {
   471         return EMiniDisplayRequestNone;
   472         }
   473 
   474     int res=Read(iInputReport);
   475 
   476     if (!res)
   477         {
   478         return EMiniDisplayRequestNone;
   479         }
   480 
   481     //Process our request
   482 	if (CurrentRequest()==EMiniDisplayRequestFirmwareRevision)
   483 		{
   484 			unsigned char* ptr=&iInputReport[2];
   485 			iInputReport[7]=0x00;
   486 			strcpy(iFirmwareRevision,(const char*)ptr);
   487 		}
   488 
   489     TMiniDisplayRequest completed=CurrentRequest();
   490     //Our request was completed
   491     SetRequest(EMiniDisplayRequestNone);
   492 
   493     return completed;
   494 	}
   495 
   496 
   497 /**
   498 Set our screen brightness.
   499 @param The desired brightness level. Must be between MinBrightness and MaxBrightness.
   500 */
   501 void MDM166AA::SetBrightness(int aBrightness)
   502     {
   503     if (aBrightness<MinBrightness()||aBrightness>MaxBrightness())
   504         {
   505         //Brightness out of range.
   506         //Just ignore that request.
   507         return;
   508         }
   509 
   510     FutabaVfdReport report;
   511     report[0]=0x00; //Report ID
   512     report[1]=0x03; //Report size
   513     report[2]=0x1B; //Command ID
   514     report[3]=0x40; //Command ID
   515     report[4]=aBrightness; //Brightness level
   516     Write(report);
   517     }
   518 
   519 /**
   520 */
   521 bool MDM166AA::IsPowerOn()
   522 	{
   523 	return iPowerOn;
   524 	}
   525 
   526 /**
   527 */
   528 char* MDM166AA::DeviceId()
   529 	{
   530 	return iDeviceId;
   531 	}
   532 
   533 /**
   534 */
   535 char* MDM166AA::FirmwareRevision()
   536 	{
   537 	return iFirmwareRevision;
   538 	}
   539 
   540 /**
   541 */
   542 void MDM166AA::ShowClock()
   543 	{
   544 	SendCommandClockDisplay(EClockLarge,EClock24);
   545 	SetClockSetting();
   546 	}
   547 
   548 /**
   549 */
   550 void MDM166AA::HideClock()
   551 	{
   552 	//TODO: or reset
   553 	Clear();
   554 	}
   555 
   556 
   557 /**
   558 Clock setting 
   559 [Code]1BH,00H,Pm,Ph 
   560 [Function]Setting the clock data. The setting data is cleared, if the Reset command is input or power is turned off.
   561 Ph = hour 
   562 Pm = minute 
   563 */
   564 void MDM166AA::SendCommandClockSetting(unsigned char aHour, unsigned char aMinute)
   565 	{
   566 	FutabaVfdReport report;
   567     report[0]=0x00; //Report ID
   568     report[1]=0x04; //Report size
   569     report[2]=0x1B; //Command ID
   570     report[3]=0x00; //Command ID
   571 
   572 	//Minutes and Hours needs to be in hexadecimal view
   573 	//To get 21:59 you need to pass in 0x21:0x59
   574 	//Weirdest format ever, I know 
   575 	report[4]=(aMinute/10*16)+aMinute%10;
   576 	report[5]=(aHour/10*16)+aHour%10;
   577 
   578     Write(report);
   579 	}
   580 
   581 
   582 /**
   583 Set display clock settings according to local system time.
   584 This needs to be redone whenever we open or turn on our display.
   585 */
   586 void MDM166AA::SetClockSetting()
   587 	{
   588 	time_t rawtime;
   589 	struct tm * timeinfo;
   590 
   591 	time ( &rawtime );
   592 	timeinfo = localtime ( &rawtime );
   593 	//
   594 	SendCommandClockSetting(timeinfo->tm_hour,timeinfo->tm_min);
   595 	}
   596 
   597 
   598 /**
   599 Clock display
   600 [Code] 1BH,Ps,aL,aH,Pf
   601 [Function] Clock is displayed small or big.
   602 */
   603 void MDM166AA::SendCommandClockDisplay(TClockSize aClockSize, TClockFormat aClockFormat)
   604 	{
   605 	FutabaVfdReport report;
   606     report[0]=0x00; //Report ID
   607     report[1]=0x03; //Report size
   608     report[2]=0x1B; //Command ID
   609     report[3]=aClockSize; //
   610     report[4]=aClockFormat; //
   611 
   612     Write(report);
   613 	}
   614