Trying to sort out our interop nicely.
2 // Copyright (C) 2014-2015 Stéphane Lenclud.
4 // This file is part of MiniDisplay.
6 // MiniDisplay is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
11 // MiniDisplay is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with MiniDisplay. If not, see <http://www.gnu.org/licenses/>.
20 #include "FutabaMDM166AA.h"
27 typedef void (MDM166AA::*TSetIconStatus) (int aIndex, int aStatus);
29 const TSetIconStatus KFunctionPerIcon[]=
31 &MDM166AA::SetIconNetworkSignal, //EMiniDisplayIconNetworkSignal,
32 &MDM166AA::SetIconInternet, //EMiniDisplayInternet,
33 &MDM166AA::SetIconEmail, //EMiniDisplayIconEmail,
34 &MDM166AA::SetIconMute, //EMiniDisplayIconMute,
35 &MDM166AA::SetIconVolume, //EMiniDisplayIconVolume,
36 &MDM166AA::SetIconVolumeLabel, //EMiniDisplayIconVolumeLabel,
37 &MDM166AA::SetIconPlay, //EMiniDisplayIconPlay,
38 &MDM166AA::SetIconPause, //EMiniDisplayIconPause,
39 &MDM166AA::SetIconRecording //EMiniDisplayIconRecording
42 const int KMaxIconType = sizeof(KFunctionPerIcon)/sizeof(TSetIconStatus);
45 Define how segments each of our icons have.
48 const int KSegmentsPerIcon[]=
50 3, //EMiniDisplayIconNetworkSignal,
51 1, //EMiniDisplayIconInternet,
52 2, //EMiniDisplayIconEmail,
53 1, //EMiniDisplayIconMute,
54 14, //EMiniDisplayIconVolume,
55 1, //EMiniDisplayIconVolumeLabel,
56 1, //EMiniDisplayIconPlay,
57 1, //EMiniDisplayIconPause,
58 1 //EMiniDisplayIconRecording
62 Define how status each of our icon can assume.
63 Its typically two for On and Off status.
65 const int KStatusPerIcon[]=
67 2, //EMiniDisplayIconNetworkSignal,
68 2, //EMiniDisplayIconInternet,
69 2, //EMiniDisplayIconEmail,
70 2, //EMiniDisplayIconMute,
71 3, //EMiniDisplayIconVolume,
72 2, //EMiniDisplayIconVolumeLabel,
73 2, //EMiniDisplayIconPlay,
74 2, //EMiniDisplayIconPause,
75 2 //EMiniDisplayIconRecording
80 static void sleep(unsigned int mseconds)
82 clock_t goal = mseconds + clock();
83 while (goal > clock());
92 iNeedAccurateClockData(false),
101 iFirmwareRevision[0]=0;
107 MDM166AA::~MDM166AA()
127 int success = HidDevice::Open(KTargaVendorId,KFutabaProductIdMDM166AA,NULL);
130 //Allocate both frames
133 iFrameAlpha=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
137 iFrameBeta=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
141 iFrameGamma=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
143 iFrameNext=iFrameAlpha;
144 iFrameCurrent=iFrameBeta;
145 iFramePrevious=iFrameGamma;
151 //We will need accurate clock data
152 //iNeedAccurateClockData=true;
153 //Until we get it just use rough time instead
154 //This is needed otherwise the clock won't work for the first minute.
155 //It flashes the clock when opening the display but that's no big deal.
160 //SetIconNetwork(0,EIconOn);
162 //SendCommandSymbolControl(EIconVolumeLabel,EIconOn);
164 //SetAllIcons(EIconOn);
171 void MDM166AA::SetPixel(unsigned char aX, unsigned char aY, unsigned int aPixel)
174 //int byteOffset=(aX*HeightInPixels()+aY)/8;
175 //int bitOffset=(aX*HeightInPixels()+aY)%8;
176 //iNextFrame[byteOffset] |= ( (aOn?0x01:0x00) << bitOffset );
178 //Pixel is on if any of the non-alpha component is not null
179 bool on = (aPixel&0x00FFFFFF)!=0x00000000;
185 iFrameNext->SetBit(aX*HeightInPixels()+aY);
189 iFrameNext->ClearBit(aX*HeightInPixels()+aY);
194 //Just specify a one pixel block
200 Clear our client side back buffer.
201 Call to SwapBuffers must follow to actually clear the display.
203 void MDM166AA::Clear()
205 //That one also clear the symbols
207 //SendCommandClear(); //Clear icons too
212 Must be followed by a SwapBuffers call.
214 void MDM166AA::Fill()
220 Set all pixels on our screen to the desired value.
221 This operation is performed off screen to avoid tearing.
222 @param 8 pixels pattern
224 void MDM166AA::SetAllPixels(unsigned char aPattern)
226 //With a single buffer
227 //unsigned char screen[2048]; //One screen worth of pixels
228 //memset(screen,0xFF,sizeof(screen));
229 //SetPixelBlock(0,0,63,sizeof(screen),screen);
234 memset(iFrameNext->Ptr(),aPattern,FrameBufferSizeInBytes());
238 //Using pattern SetPixelBlock variant.
250 Whole display RAM areas including invisible area are filled with 00H data.
252 SL: Though there is no invisible area with that device.
254 void MDM166AA::SendCommandClear()
256 //Send Clear Display Command
257 FutabaVfdReport report;
258 report[0]=0x00; //Report ID
259 report[1]=0x02; //Report length
260 report[2]=0x1B; //Command ID
261 report[3]=0x50; //Command ID
266 Check if accurate clock data is needed and update display clock if system clock seconds are zero.
267 This is intended to be called every frame from our SwapBuffers function.
269 void MDM166AA::AttemptClockSynchronization()
271 //Check if accurate clock data is needed
272 if (!iNeedAccurateClockData)
279 struct tm * timeinfo;
281 timeinfo = localtime ( &rawtime );
283 //If our seconds are zero we synchronize our display clock
284 if (timeinfo->tm_sec==0)
286 SendCommandSetClockData(timeinfo->tm_hour,timeinfo->tm_min);
287 //Our clock is as accurate as it can for the time being
288 iNeedAccurateClockData=false;
293 Put our off screen buffer on screen.
294 On screen buffer goes off screen.
296 void MDM166AA::SwapBuffers()
298 //We need to synchronize our clock seconds
299 AttemptClockSynchronization();
301 //Only perform buffer swapping if off screen mode is enabled
304 //Send next frame to our display RAM
305 //We could attempt to implement a frame differencing algorithm much like we did for GP1212A01.
306 //However we see little point doing that since we already run at above 20 FPS.
307 SendCommandWriteGraphicData(FrameBufferSizeInBytes(),iFrameNext->Ptr());
309 //Cycle through our frame buffers
310 //We keep track of previous frame which is in fact our device back buffer.
311 //We can then compare previous and next frame and send only the differences to our device.
312 //This mechanism allows us to reduce traffic over our USB bus thus improving our frame rate from 14 FPS to 30 FPS.
313 //Keep our previous frame pointer
314 BitArrayLow* previousFrame=iFramePrevious;
315 //Current frame becomes the previous one
316 iFramePrevious = iFrameCurrent;
317 //Next frame becomes the current one
318 iFrameCurrent = iFrameNext;
319 //Next frame is now our former previous
320 iFrameNext = previousFrame;
325 //Define the edge of our pixel block
326 //Pixel blocks of 32x32 seems to run almost as fast as full screen update in worse case scenarii.
327 //Though I wonder if in some situations 16 could be better. Make this an attribute at some point if need be.
328 const int KPixelBlockEdge = 32;
329 const int KPixelBlockSizeInBits = KPixelBlockEdge*KPixelBlockEdge;
330 const int KPixelBlockSizeInBytes = KPixelBlockSizeInBits/8;
335 void MDM166AA::Request(TMiniDisplayRequest aRequest)
339 case EMiniDisplayRequestDeviceId:
342 case EMiniDisplayRequestFirmwareRevision:
343 RequestFirmwareRevision();
345 case EMiniDisplayRequestPowerSupplyStatus:
346 RequestPowerSupplyStatus();
357 void MDM166AA::ResetBuffers()
359 //iNextFrame->ClearAll();
360 //memset(iFrameAlpha,0x00,sizeof(iFrameAlpha));
361 //memset(iFrameBeta,0x00,sizeof(iFrameBeta));
366 void MDM166AA::RequestDeviceId()
373 void MDM166AA::RequestFirmwareRevision()
380 void MDM166AA::RequestPowerSupplyStatus()
387 This is for development purposes only.
388 Production application should stick to off-screen mode to avoid tearing.
390 void MDM166AA::ToggleOffScreenMode()
392 SetOffScreenMode(!iOffScreenMode);
396 * @brief MDM166AA::SetOffScreenMode
400 void MDM166AA::SetOffScreenMode(bool aOn)
402 if (aOn==iOffScreenMode)
410 //Clean up our buffers upon switching modes
417 Set our screen brightness.
418 @param The desired brightness level. Must be between MinBrightness and MaxBrightness.
420 void MDM166AA::SetBrightness(int aBrightness)
422 if (aBrightness<MinBrightness()||aBrightness>MaxBrightness())
424 //Brightness out of range.
425 //Just ignore that request.
429 FutabaVfdReport report;
430 report[0]=0x00; //Report ID
431 report[1]=0x03; //Report size
432 report[2]=0x1B; //Command ID
433 report[3]=0x40; //Command ID
434 report[4]=aBrightness; //Brightness level
441 void MDM166AA::ShowClock()
443 //Assuming display clock is at least roughly set since we do it when opening our display connection.
444 //We will need accurate clock data next we get a chance.
445 //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.
446 iNeedAccurateClockData=true;
447 //Show clock using specified styles
448 SendCommandClockDisplay(EClockLarge,EClock24);
453 void MDM166AA::HideClock()
461 int MDM166AA::IconCount(TMiniDisplayIconType aIcon)
463 return KSegmentsPerIcon[aIcon];
466 int MDM166AA::IconStatusCount(TMiniDisplayIconType aIcon)
468 return KStatusPerIcon[aIcon];
471 void MDM166AA::SetIconStatus(TMiniDisplayIconType aIcon, int aIndex, int aStatus)
473 if (aIcon<0||aIcon>=KMaxIconType||(KFunctionPerIcon[aIcon]==NULL))
475 //Out of range or no function pointer for that icon
479 (this->*KFunctionPerIcon[aIcon])(aIndex,aStatus);
484 void MDM166AA::SetIconNetworkSignal(int aIndex, int aStatus)
486 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconNetworkSignal])
492 SendCommandSymbolControl((TIconId)(aIndex+EIconNetworkSignalLow),(aStatus==0?EIconOff:EIconOn));
497 void MDM166AA::SetIconInternet(int aIndex, int aStatus)
499 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconInternet])
505 SendCommandSymbolControl((TIconId)(aIndex+EIconNetworkMast),(aStatus==0?EIconOff:EIconOn));
510 void MDM166AA::SetIconEmail(int aIndex, int aStatus)
512 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconEmail])
518 SendCommandSymbolControl((TIconId)(aIndex+EIconEnvelop),(aStatus==0?EIconOff:EIconOn));
523 void MDM166AA::SetIconMute(int aIndex, int aStatus)
525 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconMute])
531 SendCommandSymbolControl((TIconId)(aIndex+EIconMute),(aStatus==0?EIconOff:EIconOn));
536 void MDM166AA::SetIconVolume(int aIndex, int aStatus)
538 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconVolume])
544 if (aStatus<EIconOff)
546 //Assuming we just want to turn it off then
550 //Make sure we cap at our highest status value
551 aStatus = MIN(EIconOn,aStatus);
553 SendCommandSymbolControl((TIconId)(aIndex+EIconVolumeLevel01),(TIconStatus)aStatus);
559 void MDM166AA::SetIconVolumeLabel(int aIndex, int aStatus)
561 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconMute])
567 SendCommandSymbolControl((TIconId)(aIndex+EIconVolumeLabel),(aStatus==0?EIconOff:EIconOn));
573 void MDM166AA::SetIconPlay(int aIndex, int aStatus)
575 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconPlay])
581 SendCommandSymbolControl((TIconId)(aIndex+EIconPlay),(aStatus==0?EIconOff:EIconOn));
587 void MDM166AA::SetIconPause(int aIndex, int aStatus)
589 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconPause])
595 SendCommandSymbolControl((TIconId)(aIndex+EIconPause),(aStatus==0?EIconOff:EIconOn));
601 void MDM166AA::SetIconRecording(int aIndex, int aStatus)
603 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconRecording])
609 SendCommandSymbolControl((TIconId)(aIndex+EIconRecording),(aStatus==0?EIconOff:EIconOn));
613 Set all our icons to the corresponding status.
615 void MDM166AA::SetAllIcons(TIconStatus aStatus)
617 for (int i=EIconFirst;i<=EIconLast;i++)
619 SendCommandSymbolControl((TIconId)i,aStatus);
625 Segment On/Off and Grayscale/Brightness
628 void MDM166AA::SendCommandSymbolControl(TIconId aIconId, TIconStatus aStatus)
630 FutabaVfdReport report;
631 report[0]=0x00; //Report ID
632 report[1]=0x04; //Report size
633 report[2]=0x1B; //Command ID
634 report[3]=0x30; //Command ID
645 [Function]Setting the clock data. The setting data is cleared, if the Reset command is input or power is turned off.
649 void MDM166AA::SendCommandSetClockData(unsigned char aHour, unsigned char aMinute)
651 FutabaVfdReport report;
652 report[0]=0x00; //Report ID
653 report[1]=0x04; //Report size
654 report[2]=0x1B; //Command ID
655 report[3]=0x00; //Command ID
657 //Minutes and Hours needs to be in hexadecimal view
658 //To get 21:59 you need to pass in 0x21:0x59
659 //Weirdest format ever, I know
660 report[4]=(aMinute/10*16)+aMinute%10;
661 report[5]=(aHour/10*16)+aHour%10;
667 Set display clock data according to local system time.
668 This will only provide 30s accuracy.
669 In fact display clock seconds are set to zero whenever clock data is set.
670 So you would only get second accuracy if this function was called when system time is at zero second.
671 It's the responsibility of AttemptClockSynchronization function to obtain second accuracy.
672 The present function is intended to provide only rough clock synchronization.
674 @note Unfortunately this command also turns on clock display.
676 void MDM166AA::SetClockData()
679 struct tm * timeinfo;
682 timeinfo = localtime ( &rawtime );
683 //Adjust minute as best as we can so that we have a 30 seconds offset at most rather a than a full minute.
684 if (timeinfo->tm_sec>30)
686 //Use the next minute then
688 if (timeinfo->tm_min==60)
690 //Use the next hour then
693 if (timeinfo->tm_hour==24)
695 //Move to the next day then
701 //Send hours and minutes to our display
702 SendCommandSetClockData(timeinfo->tm_hour,timeinfo->tm_min);
708 [Code] 1BH,Ps,aL,aH,Pf
709 [Function] Clock is displayed small or big.
711 void MDM166AA::SendCommandClockDisplay(TClockSize aClockSize, TClockFormat aClockFormat)
713 FutabaVfdReport report;
714 report[0]=0x00; //Report ID
715 report[1]=0x03; //Report size
716 report[2]=0x1B; //Command ID
717 report[3]=aClockSize; //
718 report[4]=aClockFormat; //
725 Display RAM filled with 00H.
726 Address Counter is set by 00H.
727 Dimming is set to 50%.
728 Turn off all icons segments.
730 This does not reset our clock settings.
732 void MDM166AA::SendCommandReset()
734 FutabaVfdReport report;
735 report[0]=0x00; //Report ID
736 report[1]=0x01; //Report length.
737 report[2]=0x1F; //Command ID
743 Set Address Counter (AC) values: 1BH + 60H + xxH
745 AC value represents the start address for graphic data.
746 There are 192 bytes as display RAM. It can be set on anywhere even if AC value is not visible area.
747 The default value is 00H.
749 When clock is displayed, AC value is set 00H.
751 void MDM166AA::SendCommandSetAddressCounter(unsigned char aAddressCounter)
753 FutabaVfdReport report;
754 report[0]=0x00; //Report ID
755 report[1]=0x03; //Report length.
756 report[2]=0x1B; //Command ID
757 report[3]=0x60; //Command ID
758 report[4]=aAddressCounter;
764 Set the defined pixel block to the given value.
766 @param The size of our pixel data. Number of pixels divided by 8.
767 @param Pointer to our pixel data.
769 void MDM166AA::SendCommandWriteGraphicData(int aSize, unsigned char* aPixels)
771 //TODO: Remove that at some point
772 SendCommandSetAddressCounter(0);
774 const int KMaxPixelBytes=48;
775 const int KHeaderSize=3;
777 int remainingSize=aSize;
780 while (remainingSize>0)
782 //Only send a maximum of 48 bytes worth of pixels per report
783 const int KPixelDataSize=(remainingSize<=KMaxPixelBytes?remainingSize:KMaxPixelBytes);
785 FutabaVfdReport report;
786 report[0]=0x00; //Report ID
787 report[1]=KPixelDataSize+KHeaderSize; //Report length. +3 is for our header first 3 bytes.
788 report[2]=0x1B; //Command ID
789 report[3]=0x70; //Command ID
790 report[4]=KPixelDataSize; //Size of pixel data in bytes
791 memcpy(report.Buffer()+5, aPixels+sizeWritten, KPixelDataSize);
794 sizeWritten+=KPixelDataSize;
795 remainingSize-=KPixelDataSize;