MDM166AA: Clock and Clear adjustments.
5 #include "FutabaMDM166AA.h"
12 typedef void (MDM166AA::*TSetIconStatus) (int aIndex, int aStatus);
14 const TSetIconStatus KFunctionPerIcon[]=
16 &MDM166AA::SetIconNetworkSignal, //EMiniDisplayIconNetworkSignal,
17 &MDM166AA::SetIconInternet, //EMiniDisplayInternet,
18 &MDM166AA::SetIconEmail, //EMiniDisplayIconEmail,
19 &MDM166AA::SetIconMute, //EMiniDisplayIconMute,
20 &MDM166AA::SetIconVolume, //EMiniDisplayIconVolume,
21 &MDM166AA::SetIconVolumeLabel, //EMiniDisplayIconVolumeLabel,
22 &MDM166AA::SetIconPlay, //EMiniDisplayIconPlay,
23 &MDM166AA::SetIconPause, //EMiniDisplayIconPause,
24 &MDM166AA::SetIconRecording //EMiniDisplayIconRecording
27 const int KMaxIconType = sizeof(KFunctionPerIcon)/sizeof(TSetIconStatus);
30 Define how segments each of our icons have.
33 const int KSegmentsPerIcon[]=
35 3, //EMiniDisplayIconNetworkSignal,
36 1, //EMiniDisplayIconInternet,
37 2, //EMiniDisplayIconEmail,
38 1, //EMiniDisplayIconMute,
39 14, //EMiniDisplayIconVolume,
40 1, //EMiniDisplayIconVolumeLabel,
41 1, //EMiniDisplayIconPlay,
42 1, //EMiniDisplayIconPause,
43 1 //EMiniDisplayIconRecording
47 Define how status each of our icon can assume.
48 Its typically two for On and Off status.
50 const int KStatusPerIcon[]=
52 2, //EMiniDisplayIconNetworkSignal,
53 2, //EMiniDisplayIconInternet,
54 2, //EMiniDisplayIconEmail,
55 2, //EMiniDisplayIconMute,
56 3, //EMiniDisplayIconVolume,
57 2, //EMiniDisplayIconVolumeLabel,
58 2, //EMiniDisplayIconPlay,
59 2, //EMiniDisplayIconPause,
60 2 //EMiniDisplayIconRecording
65 static void sleep(unsigned int mseconds)
67 clock_t goal = mseconds + clock();
68 while (goal > clock());
77 iNeedAccurateClockData(false),
86 iFirmwareRevision[0]=0;
112 int success = HidDevice::Open(KTargaVendorId,KFutabaProductIdMDM166AA,NULL);
115 //Allocate both frames
118 iFrameAlpha=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
122 iFrameBeta=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
126 iFrameGamma=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
128 iFrameNext=iFrameAlpha;
129 iFrameCurrent=iFrameBeta;
130 iFramePrevious=iFrameGamma;
136 //We will need accurate clock data
137 //iNeedAccurateClockData=true;
138 //Until we get it just use rough time instead
139 //This is needed otherwise the clock won't work for the first minute.
140 //It flashes the clock when opening the display but that's no big deal.
145 //SetIconNetwork(0,EIconOn);
147 //SendCommandSymbolControl(EIconVolumeLabel,EIconOn);
149 //SetAllIcons(EIconOn);
156 void MDM166AA::SetPixel(unsigned char aX, unsigned char aY, unsigned int aPixel)
159 //int byteOffset=(aX*HeightInPixels()+aY)/8;
160 //int bitOffset=(aX*HeightInPixels()+aY)%8;
161 //iNextFrame[byteOffset] |= ( (aOn?0x01:0x00) << bitOffset );
163 //Pixel is on if any of the non-alpha component is not null
164 bool on = (aPixel&0x00FFFFFF)!=0x00000000;
170 iFrameNext->SetBit(aX*HeightInPixels()+aY);
174 iFrameNext->ClearBit(aX*HeightInPixels()+aY);
179 //Just specify a one pixel block
185 Clear our client side back buffer.
186 Call to SwapBuffers must follow to actually clear the display.
188 void MDM166AA::Clear()
190 //That one also clear the symbols
192 //SendCommandClear(); //Clear icons too
197 Must be followed by a SwapBuffers call.
199 void MDM166AA::Fill()
205 Set all pixels on our screen to the desired value.
206 This operation is performed off screen to avoid tearing.
207 @param 8 pixels pattern
209 void MDM166AA::SetAllPixels(unsigned char aPattern)
211 //With a single buffer
212 //unsigned char screen[2048]; //One screen worth of pixels
213 //memset(screen,0xFF,sizeof(screen));
214 //SetPixelBlock(0,0,63,sizeof(screen),screen);
219 memset(iFrameNext->Ptr(),aPattern,FrameBufferSizeInBytes());
223 //Using pattern SetPixelBlock variant.
235 Whole display RAM areas including invisible area are filled with 00H data.
237 SL: Though there is no invisible area with that device.
239 void MDM166AA::SendCommandClear()
241 //Send Clear Display Command
242 FutabaVfdReport report;
243 report[0]=0x00; //Report ID
244 report[1]=0x02; //Report length
245 report[2]=0x1B; //Command ID
246 report[3]=0x50; //Command ID
251 Check if accurate clock data is needed and update display clock if system clock seconds are zero.
252 This is intended to be called every frame from our SwapBuffers function.
254 void MDM166AA::AttemptClockSynchronization()
256 //Check if accurate clock data is needed
257 if (!iNeedAccurateClockData)
264 struct tm * timeinfo;
266 timeinfo = localtime ( &rawtime );
268 //If our seconds are zero we synchronize our display clock
269 if (timeinfo->tm_sec==0)
271 SendCommandSetClockData(timeinfo->tm_hour,timeinfo->tm_min);
272 //Our clock is as accurate as it can for the time being
273 iNeedAccurateClockData=false;
278 Put our off screen buffer on screen.
279 On screen buffer goes off screen.
281 void MDM166AA::SwapBuffers()
283 //We need to synchronize our clock seconds
284 AttemptClockSynchronization();
286 //Only perform buffer swapping if off screen mode is enabled
289 //Send next frame to our display RAM
290 //We could attempt to implement a frame differencing algorithm much like we did for GP1212A01.
291 //However we see little point doing that since we already run at above 20 FPS.
292 SendCommandWriteGraphicData(FrameBufferSizeInBytes(),iFrameNext->Ptr());
294 //Cycle through our frame buffers
295 //We keep track of previous frame which is in fact our device back buffer.
296 //We can then compare previous and next frame and send only the differences to our device.
297 //This mechanism allows us to reduce traffic over our USB bus thus improving our frame rate from 14 FPS to 30 FPS.
298 //Keep our previous frame pointer
299 BitArrayLow* previousFrame=iFramePrevious;
300 //Current frame becomes the previous one
301 iFramePrevious = iFrameCurrent;
302 //Next frame becomes the current one
303 iFrameCurrent = iFrameNext;
304 //Next frame is now our former previous
305 iFrameNext = previousFrame;
310 //Define the edge of our pixel block
311 //Pixel blocks of 32x32 seems to run almost as fast as full screen update in worse case scenarii.
312 //Though I wonder if in some situations 16 could be better. Make this an attribute at some point if need be.
313 const int KPixelBlockEdge = 32;
314 const int KPixelBlockSizeInBits = KPixelBlockEdge*KPixelBlockEdge;
315 const int KPixelBlockSizeInBytes = KPixelBlockSizeInBits/8;
320 void MDM166AA::Request(TMiniDisplayRequest aRequest)
324 case EMiniDisplayRequestDeviceId:
327 case EMiniDisplayRequestFirmwareRevision:
328 RequestFirmwareRevision();
330 case EMiniDisplayRequestPowerSupplyStatus:
331 RequestPowerSupplyStatus();
342 void MDM166AA::ResetBuffers()
344 //iNextFrame->ClearAll();
345 //memset(iFrameAlpha,0x00,sizeof(iFrameAlpha));
346 //memset(iFrameBeta,0x00,sizeof(iFrameBeta));
351 void MDM166AA::RequestDeviceId()
358 void MDM166AA::RequestFirmwareRevision()
365 void MDM166AA::RequestPowerSupplyStatus()
372 This is for development purposes only.
373 Production application should stick to off-screen mode to avoid tearing.
375 void MDM166AA::ToggleOffScreenMode()
377 SetOffScreenMode(!iOffScreenMode);
381 * @brief MDM166AA::SetOffScreenMode
385 void MDM166AA::SetOffScreenMode(bool aOn)
387 if (aOn==iOffScreenMode)
395 //Clean up our buffers upon switching modes
402 Set our screen brightness.
403 @param The desired brightness level. Must be between MinBrightness and MaxBrightness.
405 void MDM166AA::SetBrightness(int aBrightness)
407 if (aBrightness<MinBrightness()||aBrightness>MaxBrightness())
409 //Brightness out of range.
410 //Just ignore that request.
414 FutabaVfdReport report;
415 report[0]=0x00; //Report ID
416 report[1]=0x03; //Report size
417 report[2]=0x1B; //Command ID
418 report[3]=0x40; //Command ID
419 report[4]=aBrightness; //Brightness level
426 void MDM166AA::ShowClock()
428 //Assuming display clock is at least roughly set since we do it when opening our display connection.
429 //We will need accurate clock data next we get a chance.
430 //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.
431 iNeedAccurateClockData=true;
432 //Show clock using specified styles
433 SendCommandClockDisplay(EClockLarge,EClock24);
438 void MDM166AA::HideClock()
446 int MDM166AA::IconCount(TMiniDisplayIconType aIcon)
448 return KSegmentsPerIcon[aIcon];
451 int MDM166AA::IconStatusCount(TMiniDisplayIconType aIcon)
453 return KStatusPerIcon[aIcon];
456 void MDM166AA::SetIconStatus(TMiniDisplayIconType aIcon, int aIndex, int aStatus)
458 if (aIcon<0||aIcon>=KMaxIconType||(KFunctionPerIcon[aIcon]==NULL))
460 //Out of range or no function pointer for that icon
464 (this->*KFunctionPerIcon[aIcon])(aIndex,aStatus);
469 void MDM166AA::SetIconNetworkSignal(int aIndex, int aStatus)
471 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconNetworkSignal])
477 SendCommandSymbolControl((TIconId)(aIndex+EIconNetworkSignalLow),(aStatus==0?EIconOff:EIconOn));
482 void MDM166AA::SetIconInternet(int aIndex, int aStatus)
484 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconInternet])
490 SendCommandSymbolControl((TIconId)(aIndex+EIconNetworkMast),(aStatus==0?EIconOff:EIconOn));
495 void MDM166AA::SetIconEmail(int aIndex, int aStatus)
497 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconEmail])
503 SendCommandSymbolControl((TIconId)(aIndex+EIconEnvelop),(aStatus==0?EIconOff:EIconOn));
508 void MDM166AA::SetIconMute(int aIndex, int aStatus)
510 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconMute])
516 SendCommandSymbolControl((TIconId)(aIndex+EIconMute),(aStatus==0?EIconOff:EIconOn));
521 void MDM166AA::SetIconVolume(int aIndex, int aStatus)
523 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconVolume])
529 if (aStatus<EIconOff)
531 //Assuming we just want to turn it off then
535 //Make sure we cap at our highest status value
536 aStatus = MIN(EIconOn,aStatus);
538 SendCommandSymbolControl((TIconId)(aIndex+EIconVolumeLevel01),(TIconStatus)aStatus);
544 void MDM166AA::SetIconVolumeLabel(int aIndex, int aStatus)
546 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconMute])
552 SendCommandSymbolControl((TIconId)(aIndex+EIconVolumeLabel),(aStatus==0?EIconOff:EIconOn));
558 void MDM166AA::SetIconPlay(int aIndex, int aStatus)
560 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconPlay])
566 SendCommandSymbolControl((TIconId)(aIndex+EIconPlay),(aStatus==0?EIconOff:EIconOn));
572 void MDM166AA::SetIconPause(int aIndex, int aStatus)
574 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconPause])
580 SendCommandSymbolControl((TIconId)(aIndex+EIconPause),(aStatus==0?EIconOff:EIconOn));
586 void MDM166AA::SetIconRecording(int aIndex, int aStatus)
588 if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconRecording])
594 SendCommandSymbolControl((TIconId)(aIndex+EIconRecording),(aStatus==0?EIconOff:EIconOn));
598 Set all our icons to the corresponding status.
600 void MDM166AA::SetAllIcons(TIconStatus aStatus)
602 for (int i=EIconFirst;i<=EIconLast;i++)
604 SendCommandSymbolControl((TIconId)i,aStatus);
610 Segment On/Off and Grayscale/Brightness
613 void MDM166AA::SendCommandSymbolControl(TIconId aIconId, TIconStatus aStatus)
615 FutabaVfdReport report;
616 report[0]=0x00; //Report ID
617 report[1]=0x04; //Report size
618 report[2]=0x1B; //Command ID
619 report[3]=0x30; //Command ID
630 [Function]Setting the clock data. The setting data is cleared, if the Reset command is input or power is turned off.
634 void MDM166AA::SendCommandSetClockData(unsigned char aHour, unsigned char aMinute)
636 FutabaVfdReport report;
637 report[0]=0x00; //Report ID
638 report[1]=0x04; //Report size
639 report[2]=0x1B; //Command ID
640 report[3]=0x00; //Command ID
642 //Minutes and Hours needs to be in hexadecimal view
643 //To get 21:59 you need to pass in 0x21:0x59
644 //Weirdest format ever, I know
645 report[4]=(aMinute/10*16)+aMinute%10;
646 report[5]=(aHour/10*16)+aHour%10;
652 Set display clock data according to local system time.
653 This will only provide 30s accuracy.
654 In fact display clock seconds are set to zero whenever clock data is set.
655 So you would only get second accuracy if this function was called when system time is at zero second.
656 It's the responsibility of AttemptClockSynchronization function to obtain second accuracy.
657 The present function is intended to provide only rough clock synchronization.
659 @note Unfortunately this command also turns on clock display.
661 void MDM166AA::SetClockData()
664 struct tm * timeinfo;
667 timeinfo = localtime ( &rawtime );
668 //Adjust minute as best as we can so that we have a 30 seconds offset at most rather a than a full minute.
669 if (timeinfo->tm_sec>30)
671 //Use the next minute then
673 if (timeinfo->tm_min==60)
675 //Use the next hour then
678 if (timeinfo->tm_hour==24)
680 //Move to the next day then
686 //Send hours and minutes to our display
687 SendCommandSetClockData(timeinfo->tm_hour,timeinfo->tm_min);
693 [Code] 1BH,Ps,aL,aH,Pf
694 [Function] Clock is displayed small or big.
696 void MDM166AA::SendCommandClockDisplay(TClockSize aClockSize, TClockFormat aClockFormat)
698 FutabaVfdReport report;
699 report[0]=0x00; //Report ID
700 report[1]=0x03; //Report size
701 report[2]=0x1B; //Command ID
702 report[3]=aClockSize; //
703 report[4]=aClockFormat; //
710 Display RAM filled with 00H.
711 Address Counter is set by 00H.
712 Dimming is set to 50%.
713 Turn off all icons segments.
715 This does not reset our clock settings.
717 void MDM166AA::SendCommandReset()
719 FutabaVfdReport report;
720 report[0]=0x00; //Report ID
721 report[1]=0x01; //Report length.
722 report[2]=0x1F; //Command ID
728 Set Address Counter (AC) values: 1BH + 60H + xxH
730 AC value represents the start address for graphic data.
731 There are 192 bytes as display RAM. It can be set on anywhere even if AC value is not visible area.
732 The default value is 00H.
734 When clock is displayed, AC value is set 00H.
736 void MDM166AA::SendCommandSetAddressCounter(unsigned char aAddressCounter)
738 FutabaVfdReport report;
739 report[0]=0x00; //Report ID
740 report[1]=0x03; //Report length.
741 report[2]=0x1B; //Command ID
742 report[3]=0x60; //Command ID
743 report[4]=aAddressCounter;
749 Set the defined pixel block to the given value.
751 @param The size of our pixel data. Number of pixels divided by 8.
752 @param Pointer to our pixel data.
754 void MDM166AA::SendCommandWriteGraphicData(int aSize, unsigned char* aPixels)
756 //TODO: Remove that at some point
757 SendCommandSetAddressCounter(0);
759 const int KMaxPixelBytes=48;
760 const int KHeaderSize=3;
762 int remainingSize=aSize;
765 while (remainingSize>0)
767 //Only send a maximum of 48 bytes worth of pixels per report
768 const int KPixelDataSize=(remainingSize<=KMaxPixelBytes?remainingSize:KMaxPixelBytes);
770 FutabaVfdReport report;
771 report[0]=0x00; //Report ID
772 report[1]=KPixelDataSize+KHeaderSize; //Report length. +3 is for our header first 3 bytes.
773 report[2]=0x1B; //Command ID
774 report[3]=0x70; //Command ID
775 report[4]=KPixelDataSize; //Size of pixel data in bytes
776 memcpy(report.Buffer()+5, aPixels+sizeWritten, KPixelDataSize);
779 sizeWritten+=KPixelDataSize;
780 remainingSize-=KPixelDataSize;