MDM166AA: Clock is now synchronized at the second.
5 #include "FutabaMDM166AA.h"
12 static void sleep(unsigned int mseconds)
14 clock_t goal = mseconds + clock();
15 while (goal > clock());
24 iNeedAccurateClockData(false),
33 iFirmwareRevision[0]=0;
59 int success = HidDevice::Open(KTargaVendorId,KFutabaProductIdMDM166AA,NULL);
62 //Allocate both frames
65 iFrameAlpha=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
69 iFrameBeta=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
73 iFrameGamma=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
75 iFrameNext=iFrameAlpha;
76 iFrameCurrent=iFrameBeta;
77 iFramePrevious=iFrameGamma;
83 //We will need accurate clock data
84 iNeedAccurateClockData=true;
85 //Until we get it just use rough time instead
86 //We don't set clock data here as it turns on clock display too and cause an unpleasant clock flash
87 //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.
95 void MDM166AA::SetPixel(unsigned char aX, unsigned char aY, unsigned int aPixel)
98 //int byteOffset=(aX*HeightInPixels()+aY)/8;
99 //int bitOffset=(aX*HeightInPixels()+aY)%8;
100 //iNextFrame[byteOffset] |= ( (aOn?0x01:0x00) << bitOffset );
102 //Pixel is on if any of the non-alpha component is not null
103 bool on = (aPixel&0x00FFFFFF)!=0x00000000;
109 iFrameNext->SetBit(aX*HeightInPixels()+aY);
113 iFrameNext->ClearBit(aX*HeightInPixels()+aY);
118 //Just specify a one pixel block
124 Clear our client side back buffer.
125 Call to SwapBuffers must follow to actually clear the display.
127 void MDM166AA::Clear()
129 //That one also clear the symbols
135 Must be followed by a SwapBuffers call.
137 void MDM166AA::Fill()
143 Set all pixels on our screen to the desired value.
144 This operation is performed off screen to avoid tearing.
145 @param 8 pixels pattern
147 void MDM166AA::SetAllPixels(unsigned char aPattern)
149 //With a single buffer
150 //unsigned char screen[2048]; //One screen worth of pixels
151 //memset(screen,0xFF,sizeof(screen));
152 //SetPixelBlock(0,0,63,sizeof(screen),screen);
157 memset(iFrameNext->Ptr(),aPattern,FrameBufferSizeInBytes());
161 //Using pattern SetPixelBlock variant.
173 Whole display RAM areas including invisible area are filled with 00H data.
175 SL: Though there is no invisible area with that device.
177 void MDM166AA::SendCommandClear()
179 //Send Clear Display Command
180 FutabaVfdReport report;
181 report[0]=0x00; //Report ID
182 report[1]=0x02; //Report length
183 report[2]=0x1B; //Command ID
184 report[3]=0x50; //Command ID
189 Check if accurate clock data is needed and update display clock if system clock seconds are zero.
190 This is intended to be called every frame from our SwapBuffers function.
192 void MDM166AA::AttemptClockSynchronization()
194 //Check if accurate clock data is needed
195 if (!iNeedAccurateClockData)
202 struct tm * timeinfo;
204 timeinfo = localtime ( &rawtime );
206 //If our seconds are zero we synchronize our display clock
207 if (timeinfo->tm_sec==0)
209 SendCommandSetClockData(timeinfo->tm_hour,timeinfo->tm_min);
210 //Our clock is as accurate as it can for the time being
211 iNeedAccurateClockData=false;
216 Put our off screen buffer on screen.
217 On screen buffer goes off screen.
219 void MDM166AA::SwapBuffers()
221 //We need to synchronize our clock seconds
222 AttemptClockSynchronization();
224 //Only perform buffer swapping if off screen mode is enabled
227 //Send next frame to our display RAM
228 //We could attempt to implement a frame differencing algorithm much like we did for GP1212A01.
229 //However we see little point doing that since we already run at above 20 FPS.
230 SendCommandWriteGraphicData(FrameBufferSizeInBytes(),iFrameNext->Ptr());
232 //Cycle through our frame buffers
233 //We keep track of previous frame which is in fact our device back buffer.
234 //We can then compare previous and next frame and send only the differences to our device.
235 //This mechanism allows us to reduce traffic over our USB bus thus improving our frame rate from 14 FPS to 30 FPS.
236 //Keep our previous frame pointer
237 BitArrayLow* previousFrame=iFramePrevious;
238 //Current frame becomes the previous one
239 iFramePrevious = iFrameCurrent;
240 //Next frame becomes the current one
241 iFrameCurrent = iFrameNext;
242 //Next frame is now our former previous
243 iFrameNext = previousFrame;
248 //Define the edge of our pixel block
249 //Pixel blocks of 32x32 seems to run almost as fast as full screen update in worse case scenarii.
250 //Though I wonder if in some situations 16 could be better. Make this an attribute at some point if need be.
251 const int KPixelBlockEdge = 32;
252 const int KPixelBlockSizeInBits = KPixelBlockEdge*KPixelBlockEdge;
253 const int KPixelBlockSizeInBytes = KPixelBlockSizeInBits/8;
258 void MDM166AA::Request(TMiniDisplayRequest aRequest)
262 case EMiniDisplayRequestDeviceId:
265 case EMiniDisplayRequestFirmwareRevision:
266 RequestFirmwareRevision();
268 case EMiniDisplayRequestPowerSupplyStatus:
269 RequestPowerSupplyStatus();
280 void MDM166AA::ResetBuffers()
282 //iNextFrame->ClearAll();
283 //memset(iFrameAlpha,0x00,sizeof(iFrameAlpha));
284 //memset(iFrameBeta,0x00,sizeof(iFrameBeta));
289 void MDM166AA::RequestDeviceId()
296 void MDM166AA::RequestFirmwareRevision()
303 void MDM166AA::RequestPowerSupplyStatus()
310 This is for development purposes only.
311 Production application should stick to off-screen mode to avoid tearing.
313 void MDM166AA::ToggleOffScreenMode()
315 SetOffScreenMode(!iOffScreenMode);
319 * @brief MDM166AA::SetOffScreenMode
323 void MDM166AA::SetOffScreenMode(bool aOn)
325 if (aOn==iOffScreenMode)
333 //Clean up our buffers upon switching modes
340 Set our screen brightness.
341 @param The desired brightness level. Must be between MinBrightness and MaxBrightness.
343 void MDM166AA::SetBrightness(int aBrightness)
345 if (aBrightness<MinBrightness()||aBrightness>MaxBrightness())
347 //Brightness out of range.
348 //Just ignore that request.
352 FutabaVfdReport report;
353 report[0]=0x00; //Report ID
354 report[1]=0x03; //Report size
355 report[2]=0x1B; //Command ID
356 report[3]=0x40; //Command ID
357 report[4]=aBrightness; //Brightness level
364 void MDM166AA::ShowClock()
366 //Assuming display clock is at least roughly set since we do it when opening our display connection.
367 //We will need accurate clock data next we get a chance.
368 //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.
369 iNeedAccurateClockData=true;
370 //Show clock using specified styles
371 SendCommandClockDisplay(EClockLarge,EClock24);
376 void MDM166AA::HideClock()
386 [Function]Setting the clock data. The setting data is cleared, if the Reset command is input or power is turned off.
390 void MDM166AA::SendCommandSetClockData(unsigned char aHour, unsigned char aMinute)
392 FutabaVfdReport report;
393 report[0]=0x00; //Report ID
394 report[1]=0x04; //Report size
395 report[2]=0x1B; //Command ID
396 report[3]=0x00; //Command ID
398 //Minutes and Hours needs to be in hexadecimal view
399 //To get 21:59 you need to pass in 0x21:0x59
400 //Weirdest format ever, I know
401 report[4]=(aMinute/10*16)+aMinute%10;
402 report[5]=(aHour/10*16)+aHour%10;
409 Set display clock data according to local system time.
410 This will only provide 30s accuracy.
411 In fact display clock seconds are set to zero whenever clock data is set.
412 So you would only get second accuracy if this function was called when system time is at zero second.
413 It's the responsibility of AttemptClockSynchronization function to obtain second accuracy.
414 The present function is intended to provide only rough clock synchronization.
416 @note Unfortunately this command also turns on clock display.
418 void MDM166AA::SetClockData()
421 struct tm * timeinfo;
424 timeinfo = localtime ( &rawtime );
425 //Adjust minute as best as we can so that we have a 30 seconds offset at most rather a than a full minute.
426 if (timeinfo->tm_sec>30)
428 //Use the next minute then
430 if (timeinfo->tm_min==60)
432 //Use the next hour then
435 if (timeinfo->tm_hour==24)
437 //Move to the next day then
443 //Send hours and minutes to our display
444 SendCommandSetClockData(timeinfo->tm_hour,timeinfo->tm_min);
450 [Code] 1BH,Ps,aL,aH,Pf
451 [Function] Clock is displayed small or big.
453 void MDM166AA::SendCommandClockDisplay(TClockSize aClockSize, TClockFormat aClockFormat)
455 FutabaVfdReport report;
456 report[0]=0x00; //Report ID
457 report[1]=0x03; //Report size
458 report[2]=0x1B; //Command ID
459 report[3]=aClockSize; //
460 report[4]=aClockFormat; //
467 Display RAM filled with 00H.
468 Address Counter is set by 00H.
469 Dimming is set to 50%.
471 void MDM166AA::SendCommandReset()
473 FutabaVfdReport report;
474 report[0]=0x00; //Report ID
475 report[1]=0x01; //Report length.
476 report[2]=0x1F; //Command ID
478 //Wait until reset is done. Is that needed?
484 Set Address Counter (AC) values: 1BH + 60H + xxH
486 AC value represents the start address for graphic data.
487 There are 192 bytes as display RAM. It can be set on anywhere even if AC value is not visible area.
488 The default value is 00H.
490 When clock is displayed, AC value is set 00H.
492 void MDM166AA::SendCommandSetAddressCounter(unsigned char aAddressCounter)
494 FutabaVfdReport report;
495 report[0]=0x00; //Report ID
496 report[1]=0x03; //Report length.
497 report[2]=0x1B; //Command ID
498 report[3]=0x60; //Command ID
499 report[4]=aAddressCounter;
505 Set the defined pixel block to the given value.
507 @param The size of our pixel data. Number of pixels divided by 8.
508 @param Pointer to our pixel data.
510 void MDM166AA::SendCommandWriteGraphicData(int aSize, unsigned char* aPixels)
512 //TODO: Remove that at some point
513 SendCommandSetAddressCounter(0);
515 const int KMaxPixelBytes=48;
516 const int KHeaderSize=3;
518 int remainingSize=aSize;
521 while (remainingSize>0)
523 //Only send a maximum of 48 bytes worth of pixels per report
524 const int KPixelDataSize=(remainingSize<=KMaxPixelBytes?remainingSize:KMaxPixelBytes);
526 FutabaVfdReport report;
527 report[0]=0x00; //Report ID
528 report[1]=KPixelDataSize+KHeaderSize; //Report length. +3 is for our header first 3 bytes.
529 report[2]=0x1B; //Command ID
530 report[3]=0x70; //Command ID
531 report[4]=KPixelDataSize; //Size of pixel data in bytes
532 memcpy(report.Buffer()+5, aPixels+sizeWritten, KPixelDataSize);
535 sizeWritten+=KPixelDataSize;
536 remainingSize-=KPixelDataSize;