MDM166AA: Improved icon APIs.
5 #include "FutabaMDM166AA.h"
12 typedef void (MDM166AA::*TSetIconStatus) (int aIndex, int aStatus);
14 const TSetIconStatus KFunctionPerIcon[]=
16 &MDM166AA::SetIconNetwork, //EMiniDisplayIconNetwork,
17 &MDM166AA::SetIconEmail, //EMiniDisplayIconEmail,
18 &MDM166AA::SetIconMute, //EMiniDisplayIconMute,
19 &MDM166AA::SetIconVolume, //EMiniDisplayIconVolume,
20 &MDM166AA::SetIconVolumeLabel, //EMiniDisplayIconVolumeLabel,
21 &MDM166AA::SetIconPlay, //EMiniDisplayIconPlay,
22 &MDM166AA::SetIconPause, //EMiniDisplayIconPause,
23 &MDM166AA::SetIconRecording //EMiniDisplayIconRecording
26 const int KMaxIconType = sizeof(KFunctionPerIcon)/sizeof(TSetIconStatus);
29 Define how segments each of our icons have.
32 const int KSegmentsPerIcon[]=
34 4, //EMiniDisplayIconNetwork,
35 2, //EMiniDisplayIconEmail,
36 1, //EMiniDisplayIconMute,
37 14, //EMiniDisplayIconVolume,
38 1, //EMiniDisplayIconVolumeLabel,
39 1, //EMiniDisplayIconPlay,
40 1, //EMiniDisplayIconPause,
41 1 //EMiniDisplayIconRecording
45 Define how status each of our icon can assume.
46 Its typically two for On and Off status.
48 const int KStatusPerIcon[]=
50 2, //EMiniDisplayIconNetwork,
51 2, //EMiniDisplayIconEmail,
52 2, //EMiniDisplayIconMute,
53 3, //EMiniDisplayIconVolume,
54 2, //EMiniDisplayIconVolumeLabel, 2, //EMiniDisplayIconPlay,
55 2, //EMiniDisplayIconPause,
56 2 //EMiniDisplayIconRecording
61 static void sleep(unsigned int mseconds)
63 clock_t goal = mseconds + clock();
64 while (goal > clock());
73 iNeedAccurateClockData(false),
82 iFirmwareRevision[0]=0;
108 int success = HidDevice::Open(KTargaVendorId,KFutabaProductIdMDM166AA,NULL);
111 //Allocate both frames
114 iFrameAlpha=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
118 iFrameBeta=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
122 iFrameGamma=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
124 iFrameNext=iFrameAlpha;
125 iFrameCurrent=iFrameBeta;
126 iFramePrevious=iFrameGamma;
132 //We will need accurate clock data
133 iNeedAccurateClockData=true;
134 //Until we get it just use rough time instead
135 //We don't set clock data here as it turns on clock display too and cause an unpleasant clock flash
136 //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.
140 //SetIconNetwork(0,EIconOn);
142 //SendCommandSymbolControl(EIconVolumeLabel,EIconOn);
144 //SetAllIcons(EIconOn);
151 void MDM166AA::SetPixel(unsigned char aX, unsigned char aY, unsigned int aPixel)
154 //int byteOffset=(aX*HeightInPixels()+aY)/8;
155 //int bitOffset=(aX*HeightInPixels()+aY)%8;
156 //iNextFrame[byteOffset] |= ( (aOn?0x01:0x00) << bitOffset );
158 //Pixel is on if any of the non-alpha component is not null
159 bool on = (aPixel&0x00FFFFFF)!=0x00000000;
165 iFrameNext->SetBit(aX*HeightInPixels()+aY);
169 iFrameNext->ClearBit(aX*HeightInPixels()+aY);
174 //Just specify a one pixel block
180 Clear our client side back buffer.
181 Call to SwapBuffers must follow to actually clear the display.
183 void MDM166AA::Clear()
185 //That one also clear the symbols
187 SendCommandClear(); //Clear icons too
192 Must be followed by a SwapBuffers call.
194 void MDM166AA::Fill()
200 Set all pixels on our screen to the desired value.
201 This operation is performed off screen to avoid tearing.
202 @param 8 pixels pattern
204 void MDM166AA::SetAllPixels(unsigned char aPattern)
206 //With a single buffer
207 //unsigned char screen[2048]; //One screen worth of pixels
208 //memset(screen,0xFF,sizeof(screen));
209 //SetPixelBlock(0,0,63,sizeof(screen),screen);
214 memset(iFrameNext->Ptr(),aPattern,FrameBufferSizeInBytes());
218 //Using pattern SetPixelBlock variant.
230 Whole display RAM areas including invisible area are filled with 00H data.
232 SL: Though there is no invisible area with that device.
234 void MDM166AA::SendCommandClear()
236 //Send Clear Display Command
237 FutabaVfdReport report;
238 report[0]=0x00; //Report ID
239 report[1]=0x02; //Report length
240 report[2]=0x1B; //Command ID
241 report[3]=0x50; //Command ID
246 Check if accurate clock data is needed and update display clock if system clock seconds are zero.
247 This is intended to be called every frame from our SwapBuffers function.
249 void MDM166AA::AttemptClockSynchronization()
251 //Check if accurate clock data is needed
252 if (!iNeedAccurateClockData)
259 struct tm * timeinfo;
261 timeinfo = localtime ( &rawtime );
263 //If our seconds are zero we synchronize our display clock
264 if (timeinfo->tm_sec==0)
266 SendCommandSetClockData(timeinfo->tm_hour,timeinfo->tm_min);
267 //Our clock is as accurate as it can for the time being
268 iNeedAccurateClockData=false;
273 Put our off screen buffer on screen.
274 On screen buffer goes off screen.
276 void MDM166AA::SwapBuffers()
278 //We need to synchronize our clock seconds
279 AttemptClockSynchronization();
281 //Only perform buffer swapping if off screen mode is enabled
284 //Send next frame to our display RAM
285 //We could attempt to implement a frame differencing algorithm much like we did for GP1212A01.
286 //However we see little point doing that since we already run at above 20 FPS.
287 SendCommandWriteGraphicData(FrameBufferSizeInBytes(),iFrameNext->Ptr());
289 //Cycle through our frame buffers
290 //We keep track of previous frame which is in fact our device back buffer.
291 //We can then compare previous and next frame and send only the differences to our device.
292 //This mechanism allows us to reduce traffic over our USB bus thus improving our frame rate from 14 FPS to 30 FPS.
293 //Keep our previous frame pointer
294 BitArrayLow* previousFrame=iFramePrevious;
295 //Current frame becomes the previous one
296 iFramePrevious = iFrameCurrent;
297 //Next frame becomes the current one
298 iFrameCurrent = iFrameNext;
299 //Next frame is now our former previous
300 iFrameNext = previousFrame;
305 //Define the edge of our pixel block
306 //Pixel blocks of 32x32 seems to run almost as fast as full screen update in worse case scenarii.
307 //Though I wonder if in some situations 16 could be better. Make this an attribute at some point if need be.
308 const int KPixelBlockEdge = 32;
309 const int KPixelBlockSizeInBits = KPixelBlockEdge*KPixelBlockEdge;
310 const int KPixelBlockSizeInBytes = KPixelBlockSizeInBits/8;
315 void MDM166AA::Request(TMiniDisplayRequest aRequest)
319 case EMiniDisplayRequestDeviceId:
322 case EMiniDisplayRequestFirmwareRevision:
323 RequestFirmwareRevision();
325 case EMiniDisplayRequestPowerSupplyStatus:
326 RequestPowerSupplyStatus();
337 void MDM166AA::ResetBuffers()
339 //iNextFrame->ClearAll();
340 //memset(iFrameAlpha,0x00,sizeof(iFrameAlpha));
341 //memset(iFrameBeta,0x00,sizeof(iFrameBeta));
346 void MDM166AA::RequestDeviceId()
353 void MDM166AA::RequestFirmwareRevision()
360 void MDM166AA::RequestPowerSupplyStatus()
367 This is for development purposes only.
368 Production application should stick to off-screen mode to avoid tearing.
370 void MDM166AA::ToggleOffScreenMode()
372 SetOffScreenMode(!iOffScreenMode);
376 * @brief MDM166AA::SetOffScreenMode
380 void MDM166AA::SetOffScreenMode(bool aOn)
382 if (aOn==iOffScreenMode)
390 //Clean up our buffers upon switching modes
397 Set our screen brightness.
398 @param The desired brightness level. Must be between MinBrightness and MaxBrightness.
400 void MDM166AA::SetBrightness(int aBrightness)
402 if (aBrightness<MinBrightness()||aBrightness>MaxBrightness())
404 //Brightness out of range.
405 //Just ignore that request.
409 FutabaVfdReport report;
410 report[0]=0x00; //Report ID
411 report[1]=0x03; //Report size
412 report[2]=0x1B; //Command ID
413 report[3]=0x40; //Command ID
414 report[4]=aBrightness; //Brightness level
421 void MDM166AA::ShowClock()
423 //Assuming display clock is at least roughly set since we do it when opening our display connection.
424 //We will need accurate clock data next we get a chance.
425 //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.
426 iNeedAccurateClockData=true;
427 //Show clock using specified styles
428 SendCommandClockDisplay(EClockLarge,EClock24);
433 void MDM166AA::HideClock()
441 int MDM166AA::IconCount(TMiniDisplayIconType aIcon)
443 return KSegmentsPerIcon[aIcon];
446 int MDM166AA::IconStatusCount(TMiniDisplayIconType aIcon)
448 return KStatusPerIcon[aIcon];
451 void MDM166AA::SetIconStatus(TMiniDisplayIconType aIcon, int aIndex, int aStatus)
453 if (aIcon<0||aIcon>=KMaxIconType||(KFunctionPerIcon[aIcon]==NULL))
455 //Out of range or no function pointer for that icon
459 (this->*KFunctionPerIcon[aIcon])(aIndex,aStatus);
464 void MDM166AA::SetIconNetwork(int aIndex, int aStatus)
466 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconNetwork])
472 SendCommandSymbolControl((TIconId)(aIndex+EIconNetworkMast),(aStatus==0?EIconOff:EIconOn));
477 void MDM166AA::SetIconEmail(int aIndex, int aStatus)
479 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconEmail])
485 SendCommandSymbolControl((TIconId)(aIndex+EIconEnvelop),(aStatus==0?EIconOff:EIconOn));
490 void MDM166AA::SetIconMute(int aIndex, int aStatus)
492 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconMute])
498 SendCommandSymbolControl((TIconId)(aIndex+EIconMute),(aStatus==0?EIconOff:EIconOn));
503 void MDM166AA::SetIconVolume(int aIndex, int aStatus)
505 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconVolume])
511 if (aStatus<EIconOff)
513 //Assuming we just want to turn it off then
517 //Make sure we cap at our highest status value
518 aStatus = MIN(EIconOn,aStatus);
520 SendCommandSymbolControl((TIconId)(aIndex+EIconVolumeLevel01),(TIconStatus)aStatus);
526 void MDM166AA::SetIconVolumeLabel(int aIndex, int aStatus)
528 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconMute])
534 SendCommandSymbolControl((TIconId)(aIndex+EIconVolumeLabel),(aStatus==0?EIconOff:EIconOn));
540 void MDM166AA::SetIconPlay(int aIndex, int aStatus)
542 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconPlay])
548 SendCommandSymbolControl((TIconId)(aIndex+EIconPlay),(aStatus==0?EIconOff:EIconOn));
554 void MDM166AA::SetIconPause(int aIndex, int aStatus)
556 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconPause])
562 SendCommandSymbolControl((TIconId)(aIndex+EIconPause),(aStatus==0?EIconOff:EIconOn));
568 void MDM166AA::SetIconRecording(int aIndex, int aStatus)
570 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconRecording])
576 SendCommandSymbolControl((TIconId)(aIndex+EIconRecording),(aStatus==0?EIconOff:EIconOn));
580 Set all our icons to the corresponding status.
582 void MDM166AA::SetAllIcons(TIconStatus aStatus)
584 for (int i=EIconFirst;i<=EIconLast;i++)
586 SendCommandSymbolControl((TIconId)i,aStatus);
592 Segment On/Off and Grayscale/Brightness
595 void MDM166AA::SendCommandSymbolControl(TIconId aIconId, TIconStatus aStatus)
597 FutabaVfdReport report;
598 report[0]=0x00; //Report ID
599 report[1]=0x04; //Report size
600 report[2]=0x1B; //Command ID
601 report[3]=0x30; //Command ID
612 [Function]Setting the clock data. The setting data is cleared, if the Reset command is input or power is turned off.
616 void MDM166AA::SendCommandSetClockData(unsigned char aHour, unsigned char aMinute)
618 FutabaVfdReport report;
619 report[0]=0x00; //Report ID
620 report[1]=0x04; //Report size
621 report[2]=0x1B; //Command ID
622 report[3]=0x00; //Command ID
624 //Minutes and Hours needs to be in hexadecimal view
625 //To get 21:59 you need to pass in 0x21:0x59
626 //Weirdest format ever, I know
627 report[4]=(aMinute/10*16)+aMinute%10;
628 report[5]=(aHour/10*16)+aHour%10;
634 Set display clock data according to local system time.
635 This will only provide 30s accuracy.
636 In fact display clock seconds are set to zero whenever clock data is set.
637 So you would only get second accuracy if this function was called when system time is at zero second.
638 It's the responsibility of AttemptClockSynchronization function to obtain second accuracy.
639 The present function is intended to provide only rough clock synchronization.
641 @note Unfortunately this command also turns on clock display.
643 void MDM166AA::SetClockData()
646 struct tm * timeinfo;
649 timeinfo = localtime ( &rawtime );
650 //Adjust minute as best as we can so that we have a 30 seconds offset at most rather a than a full minute.
651 if (timeinfo->tm_sec>30)
653 //Use the next minute then
655 if (timeinfo->tm_min==60)
657 //Use the next hour then
660 if (timeinfo->tm_hour==24)
662 //Move to the next day then
668 //Send hours and minutes to our display
669 SendCommandSetClockData(timeinfo->tm_hour,timeinfo->tm_min);
675 [Code] 1BH,Ps,aL,aH,Pf
676 [Function] Clock is displayed small or big.
678 void MDM166AA::SendCommandClockDisplay(TClockSize aClockSize, TClockFormat aClockFormat)
680 FutabaVfdReport report;
681 report[0]=0x00; //Report ID
682 report[1]=0x03; //Report size
683 report[2]=0x1B; //Command ID
684 report[3]=aClockSize; //
685 report[4]=aClockFormat; //
692 Display RAM filled with 00H.
693 Address Counter is set by 00H.
694 Dimming is set to 50%.
695 Turn off all icons segments.
697 void MDM166AA::SendCommandReset()
699 FutabaVfdReport report;
700 report[0]=0x00; //Report ID
701 report[1]=0x01; //Report length.
702 report[2]=0x1F; //Command ID
708 Set Address Counter (AC) values: 1BH + 60H + xxH
710 AC value represents the start address for graphic data.
711 There are 192 bytes as display RAM. It can be set on anywhere even if AC value is not visible area.
712 The default value is 00H.
714 When clock is displayed, AC value is set 00H.
716 void MDM166AA::SendCommandSetAddressCounter(unsigned char aAddressCounter)
718 FutabaVfdReport report;
719 report[0]=0x00; //Report ID
720 report[1]=0x03; //Report length.
721 report[2]=0x1B; //Command ID
722 report[3]=0x60; //Command ID
723 report[4]=aAddressCounter;
729 Set the defined pixel block to the given value.
731 @param The size of our pixel data. Number of pixels divided by 8.
732 @param Pointer to our pixel data.
734 void MDM166AA::SendCommandWriteGraphicData(int aSize, unsigned char* aPixels)
736 //TODO: Remove that at some point
737 SendCommandSetAddressCounter(0);
739 const int KMaxPixelBytes=48;
740 const int KHeaderSize=3;
742 int remainingSize=aSize;
745 while (remainingSize>0)
747 //Only send a maximum of 48 bytes worth of pixels per report
748 const int KPixelDataSize=(remainingSize<=KMaxPixelBytes?remainingSize:KMaxPixelBytes);
750 FutabaVfdReport report;
751 report[0]=0x00; //Report ID
752 report[1]=KPixelDataSize+KHeaderSize; //Report length. +3 is for our header first 3 bytes.
753 report[2]=0x1B; //Command ID
754 report[3]=0x70; //Command ID
755 report[4]=KPixelDataSize; //Size of pixel data in bytes
756 memcpy(report.Buffer()+5, aPixels+sizeWritten, KPixelDataSize);
759 sizeWritten+=KPixelDataSize;
760 remainingSize-=KPixelDataSize;