StephaneLenclud@25: //
StephaneLenclud@35: // Copyright (C) 2014-2015 Stéphane Lenclud.
StephaneLenclud@25: //
StephaneLenclud@35: // This file is part of MiniDisplay.
StephaneLenclud@35: //
StephaneLenclud@35: // MiniDisplay is free software: you can redistribute it and/or modify
StephaneLenclud@35: // it under the terms of the GNU General Public License as published by
StephaneLenclud@35: // the Free Software Foundation, either version 3 of the License, or
StephaneLenclud@35: // (at your option) any later version.
StephaneLenclud@35: //
StephaneLenclud@35: // MiniDisplay is distributed in the hope that it will be useful,
StephaneLenclud@35: // but WITHOUT ANY WARRANTY; without even the implied warranty of
StephaneLenclud@35: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
StephaneLenclud@35: // GNU General Public License for more details.
StephaneLenclud@35: //
StephaneLenclud@35: // You should have received a copy of the GNU General Public License
StephaneLenclud@35: // along with MiniDisplay. If not, see .
StephaneLenclud@25: //
StephaneLenclud@25:
StephaneLenclud@25: #include "FutabaMDM166AA.h"
StephaneLenclud@25:
StephaneLenclud@25: #include
StephaneLenclud@25: #include
StephaneLenclud@25:
StephaneLenclud@25:
StephaneLenclud@25:
StephaneLenclud@32: typedef void (MDM166AA::*TSetIconStatus) (int aIndex, int aStatus);
StephaneLenclud@32:
StephaneLenclud@32: const TSetIconStatus KFunctionPerIcon[]=
StephaneLenclud@32: {
StephaneLenclud@33: &MDM166AA::SetIconNetworkSignal, //EMiniDisplayIconNetworkSignal,
StephaneLenclud@33: &MDM166AA::SetIconInternet, //EMiniDisplayInternet,
StephaneLenclud@33: &MDM166AA::SetIconEmail, //EMiniDisplayIconEmail,
StephaneLenclud@32: &MDM166AA::SetIconMute, //EMiniDisplayIconMute,
StephaneLenclud@32: &MDM166AA::SetIconVolume, //EMiniDisplayIconVolume,
StephaneLenclud@32: &MDM166AA::SetIconVolumeLabel, //EMiniDisplayIconVolumeLabel,
StephaneLenclud@32: &MDM166AA::SetIconPlay, //EMiniDisplayIconPlay,
StephaneLenclud@32: &MDM166AA::SetIconPause, //EMiniDisplayIconPause,
StephaneLenclud@32: &MDM166AA::SetIconRecording //EMiniDisplayIconRecording
StephaneLenclud@32: };
StephaneLenclud@32:
StephaneLenclud@32: const int KMaxIconType = sizeof(KFunctionPerIcon)/sizeof(TSetIconStatus);
StephaneLenclud@32:
StephaneLenclud@32: /**
StephaneLenclud@32: Define how segments each of our icons have.
StephaneLenclud@32: Order matters.
StephaneLenclud@32: */
StephaneLenclud@32: const int KSegmentsPerIcon[]=
StephaneLenclud@32: {
StephaneLenclud@33: 3, //EMiniDisplayIconNetworkSignal,
StephaneLenclud@33: 1, //EMiniDisplayIconInternet,
StephaneLenclud@32: 2, //EMiniDisplayIconEmail,
StephaneLenclud@32: 1, //EMiniDisplayIconMute,
StephaneLenclud@32: 14, //EMiniDisplayIconVolume,
StephaneLenclud@32: 1, //EMiniDisplayIconVolumeLabel,
StephaneLenclud@32: 1, //EMiniDisplayIconPlay,
StephaneLenclud@32: 1, //EMiniDisplayIconPause,
StephaneLenclud@32: 1 //EMiniDisplayIconRecording
StephaneLenclud@32: };
StephaneLenclud@32:
StephaneLenclud@32: /**
StephaneLenclud@32: Define how status each of our icon can assume.
StephaneLenclud@32: Its typically two for On and Off status.
StephaneLenclud@32: */
StephaneLenclud@32: const int KStatusPerIcon[]=
StephaneLenclud@32: {
StephaneLenclud@33: 2, //EMiniDisplayIconNetworkSignal,
StephaneLenclud@33: 2, //EMiniDisplayIconInternet,
StephaneLenclud@32: 2, //EMiniDisplayIconEmail,
StephaneLenclud@32: 2, //EMiniDisplayIconMute,
StephaneLenclud@33: 3, //EMiniDisplayIconVolume,
StephaneLenclud@33: 2, //EMiniDisplayIconVolumeLabel,
StephaneLenclud@33: 2, //EMiniDisplayIconPlay,
StephaneLenclud@32: 2, //EMiniDisplayIconPause,
StephaneLenclud@32: 2 //EMiniDisplayIconRecording
StephaneLenclud@32: };
StephaneLenclud@32:
StephaneLenclud@32:
StephaneLenclud@32:
StephaneLenclud@25: static void sleep(unsigned int mseconds)
StephaneLenclud@25: {
StephaneLenclud@25: clock_t goal = mseconds + clock();
StephaneLenclud@25: while (goal > clock());
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@25: //
StephaneLenclud@25: // class MDM166AA
StephaneLenclud@25: //
StephaneLenclud@25:
StephaneLenclud@25: MDM166AA::MDM166AA():
StephaneLenclud@25: iOffScreenMode(true),
StephaneLenclud@30: iNeedAccurateClockData(false),
StephaneLenclud@25: iFrameNext(NULL),
StephaneLenclud@25: iFrameCurrent(NULL),
StephaneLenclud@25: iFramePrevious(NULL),
StephaneLenclud@25: iFrameAlpha(NULL),
StephaneLenclud@25: iFrameBeta(NULL),
StephaneLenclud@28: iFrameGamma(NULL)
StephaneLenclud@25: {
StephaneLenclud@25: iDeviceId[0]=0;
StephaneLenclud@25: iFirmwareRevision[0]=0;
StephaneLenclud@25: //ResetBuffers();
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@25: /**
StephaneLenclud@25: */
StephaneLenclud@25: MDM166AA::~MDM166AA()
StephaneLenclud@25: {
StephaneLenclud@25: delete iFrameAlpha;
StephaneLenclud@25: iFrameAlpha=NULL;
StephaneLenclud@25: //
StephaneLenclud@25: delete iFrameBeta;
StephaneLenclud@25: iFrameBeta=NULL;
StephaneLenclud@25: //
StephaneLenclud@25: delete iFrameGamma;
StephaneLenclud@25: iFrameGamma=NULL;
StephaneLenclud@25: //
StephaneLenclud@25: iFrameNext=NULL;
StephaneLenclud@25: iFrameCurrent=NULL;
StephaneLenclud@25: iFramePrevious=NULL;
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@25: /**
StephaneLenclud@25: */
StephaneLenclud@25: int MDM166AA::Open()
StephaneLenclud@25: {
StephaneLenclud@25: int success = HidDevice::Open(KTargaVendorId,KFutabaProductIdMDM166AA,NULL);
StephaneLenclud@25: if (success)
StephaneLenclud@25: {
StephaneLenclud@25: //Allocate both frames
StephaneLenclud@25: delete iFrameAlpha;
StephaneLenclud@25: iFrameAlpha=NULL;
StephaneLenclud@25: iFrameAlpha=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
StephaneLenclud@25: //
StephaneLenclud@25: delete iFrameBeta;
StephaneLenclud@25: iFrameBeta=NULL;
StephaneLenclud@25: iFrameBeta=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
StephaneLenclud@25: //
StephaneLenclud@25: delete iFrameGamma;
StephaneLenclud@25: iFrameGamma=NULL;
StephaneLenclud@25: iFrameGamma=new BitArrayLow(KMDM166AAFrameBufferPixelCount);
StephaneLenclud@25: //
StephaneLenclud@25: iFrameNext=iFrameAlpha;
StephaneLenclud@25: iFrameCurrent=iFrameBeta;
StephaneLenclud@25: iFramePrevious=iFrameGamma;
StephaneLenclud@25: //
StephaneLenclud@25: SetNonBlocking(1);
StephaneLenclud@25: //
StephaneLenclud@28: SendCommandReset();
StephaneLenclud@30:
StephaneLenclud@30: //We will need accurate clock data
StephaneLenclud@34: //iNeedAccurateClockData=true;
StephaneLenclud@30: //Until we get it just use rough time instead
StephaneLenclud@34: //This is needed otherwise the clock won't work for the first minute.
StephaneLenclud@34: //It flashes the clock when opening the display but that's no big deal.
StephaneLenclud@34: ShowClock();
StephaneLenclud@34: SetClockData();
StephaneLenclud@31:
StephaneLenclud@31: //Turns mast ON
StephaneLenclud@31: //SetIconNetwork(0,EIconOn);
StephaneLenclud@31: //Show volume label
StephaneLenclud@31: //SendCommandSymbolControl(EIconVolumeLabel,EIconOn);
StephaneLenclud@31: //Icon checks
StephaneLenclud@31: //SetAllIcons(EIconOn);
StephaneLenclud@25: }
StephaneLenclud@25: return success;
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@25: /**
StephaneLenclud@25: */
StephaneLenclud@25: void MDM166AA::SetPixel(unsigned char aX, unsigned char aY, unsigned int aPixel)
StephaneLenclud@25: {
StephaneLenclud@25: //
StephaneLenclud@25: //int byteOffset=(aX*HeightInPixels()+aY)/8;
StephaneLenclud@25: //int bitOffset=(aX*HeightInPixels()+aY)%8;
StephaneLenclud@25: //iNextFrame[byteOffset] |= ( (aOn?0x01:0x00) << bitOffset );
StephaneLenclud@25:
StephaneLenclud@25: //Pixel is on if any of the non-alpha component is not null
StephaneLenclud@25: bool on = (aPixel&0x00FFFFFF)!=0x00000000;
StephaneLenclud@25:
StephaneLenclud@25: if (iOffScreenMode)
StephaneLenclud@25: {
StephaneLenclud@25: if (on)
StephaneLenclud@25: {
StephaneLenclud@25: iFrameNext->SetBit(aX*HeightInPixels()+aY);
StephaneLenclud@25: }
StephaneLenclud@25: else
StephaneLenclud@25: {
StephaneLenclud@25: iFrameNext->ClearBit(aX*HeightInPixels()+aY);
StephaneLenclud@25: }
StephaneLenclud@25: }
StephaneLenclud@25: else
StephaneLenclud@25: {
StephaneLenclud@25: //Just specify a one pixel block
StephaneLenclud@25: //TODO
StephaneLenclud@25: }
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@25: /**
StephaneLenclud@25: Clear our client side back buffer.
StephaneLenclud@25: Call to SwapBuffers must follow to actually clear the display.
StephaneLenclud@25: */
StephaneLenclud@25: void MDM166AA::Clear()
StephaneLenclud@25: {
StephaneLenclud@25: //That one also clear the symbols
StephaneLenclud@28: SetAllPixels(0x00);
StephaneLenclud@34: //SendCommandClear(); //Clear icons too
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@25: /**
StephaneLenclud@25: Turn on all pixels.
StephaneLenclud@25: Must be followed by a SwapBuffers call.
StephaneLenclud@25: */
StephaneLenclud@25: void MDM166AA::Fill()
StephaneLenclud@25: {
StephaneLenclud@25: SetAllPixels(0xFF);
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@25: /**
StephaneLenclud@25: Set all pixels on our screen to the desired value.
StephaneLenclud@25: This operation is performed off screen to avoid tearing.
StephaneLenclud@25: @param 8 pixels pattern
StephaneLenclud@25: */
StephaneLenclud@25: void MDM166AA::SetAllPixels(unsigned char aPattern)
StephaneLenclud@25: {
StephaneLenclud@25: //With a single buffer
StephaneLenclud@25: //unsigned char screen[2048]; //One screen worth of pixels
StephaneLenclud@25: //memset(screen,0xFF,sizeof(screen));
StephaneLenclud@25: //SetPixelBlock(0,0,63,sizeof(screen),screen);
StephaneLenclud@25:
StephaneLenclud@25:
StephaneLenclud@25: if (iOffScreenMode)
StephaneLenclud@25: {
StephaneLenclud@25: memset(iFrameNext->Ptr(),aPattern,FrameBufferSizeInBytes());
StephaneLenclud@25: }
StephaneLenclud@25: else
StephaneLenclud@25: {
StephaneLenclud@25: //Using pattern SetPixelBlock variant.
StephaneLenclud@25: //TODO
StephaneLenclud@25: }
StephaneLenclud@25: //
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@25:
StephaneLenclud@25:
StephaneLenclud@25:
StephaneLenclud@25:
StephaneLenclud@25:
StephaneLenclud@25: /**
StephaneLenclud@25: Whole display RAM areas including invisible area are filled with 00H data.
StephaneLenclud@25: (Include the symbol)
StephaneLenclud@25: SL: Though there is no invisible area with that device.
StephaneLenclud@25: */
StephaneLenclud@25: void MDM166AA::SendCommandClear()
StephaneLenclud@25: {
StephaneLenclud@25: //Send Clear Display Command
StephaneLenclud@25: FutabaVfdReport report;
StephaneLenclud@25: report[0]=0x00; //Report ID
StephaneLenclud@25: report[1]=0x02; //Report length
StephaneLenclud@25: report[2]=0x1B; //Command ID
StephaneLenclud@25: report[3]=0x50; //Command ID
StephaneLenclud@25: Write(report);
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@25: /**
StephaneLenclud@30: Check if accurate clock data is needed and update display clock if system clock seconds are zero.
StephaneLenclud@30: This is intended to be called every frame from our SwapBuffers function.
StephaneLenclud@30: */
StephaneLenclud@30: void MDM166AA::AttemptClockSynchronization()
StephaneLenclud@30: {
StephaneLenclud@30: //Check if accurate clock data is needed
StephaneLenclud@30: if (!iNeedAccurateClockData)
StephaneLenclud@30: {
StephaneLenclud@30: return;
StephaneLenclud@30: }
StephaneLenclud@30:
StephaneLenclud@30: //Fetch local time
StephaneLenclud@30: time_t rawtime;
StephaneLenclud@30: struct tm * timeinfo;
StephaneLenclud@30: time ( &rawtime );
StephaneLenclud@30: timeinfo = localtime ( &rawtime );
StephaneLenclud@30:
StephaneLenclud@30: //If our seconds are zero we synchronize our display clock
StephaneLenclud@30: if (timeinfo->tm_sec==0)
StephaneLenclud@30: {
StephaneLenclud@30: SendCommandSetClockData(timeinfo->tm_hour,timeinfo->tm_min);
StephaneLenclud@30: //Our clock is as accurate as it can for the time being
StephaneLenclud@30: iNeedAccurateClockData=false;
StephaneLenclud@30: }
StephaneLenclud@30: }
StephaneLenclud@30:
StephaneLenclud@30: /**
StephaneLenclud@25: Put our off screen buffer on screen.
StephaneLenclud@25: On screen buffer goes off screen.
StephaneLenclud@25: */
StephaneLenclud@25: void MDM166AA::SwapBuffers()
StephaneLenclud@25: {
StephaneLenclud@30: //We need to synchronize our clock seconds
StephaneLenclud@30: AttemptClockSynchronization();
StephaneLenclud@30:
StephaneLenclud@25: //Only perform buffer swapping if off screen mode is enabled
StephaneLenclud@25: if (OffScreenMode())
StephaneLenclud@25: {
StephaneLenclud@28: //Send next frame to our display RAM
StephaneLenclud@28: //We could attempt to implement a frame differencing algorithm much like we did for GP1212A01.
StephaneLenclud@28: //However we see little point doing that since we already run at above 20 FPS.
StephaneLenclud@27: SendCommandWriteGraphicData(FrameBufferSizeInBytes(),iFrameNext->Ptr());
StephaneLenclud@25:
StephaneLenclud@25: //Cycle through our frame buffers
StephaneLenclud@25: //We keep track of previous frame which is in fact our device back buffer.
StephaneLenclud@25: //We can then compare previous and next frame and send only the differences to our device.
StephaneLenclud@25: //This mechanism allows us to reduce traffic over our USB bus thus improving our frame rate from 14 FPS to 30 FPS.
StephaneLenclud@25: //Keep our previous frame pointer
StephaneLenclud@25: BitArrayLow* previousFrame=iFramePrevious;
StephaneLenclud@25: //Current frame becomes the previous one
StephaneLenclud@25: iFramePrevious = iFrameCurrent;
StephaneLenclud@25: //Next frame becomes the current one
StephaneLenclud@25: iFrameCurrent = iFrameNext;
StephaneLenclud@25: //Next frame is now our former previous
StephaneLenclud@25: iFrameNext = previousFrame;
StephaneLenclud@25: }
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@25:
StephaneLenclud@25: //Define the edge of our pixel block
StephaneLenclud@25: //Pixel blocks of 32x32 seems to run almost as fast as full screen update in worse case scenarii.
StephaneLenclud@25: //Though I wonder if in some situations 16 could be better. Make this an attribute at some point if need be.
StephaneLenclud@25: const int KPixelBlockEdge = 32;
StephaneLenclud@25: const int KPixelBlockSizeInBits = KPixelBlockEdge*KPixelBlockEdge;
StephaneLenclud@25: const int KPixelBlockSizeInBytes = KPixelBlockSizeInBits/8;
StephaneLenclud@25:
StephaneLenclud@25:
StephaneLenclud@25: /**
StephaneLenclud@25: */
StephaneLenclud@25: void MDM166AA::Request(TMiniDisplayRequest aRequest)
StephaneLenclud@25: {
StephaneLenclud@25: switch (aRequest)
StephaneLenclud@25: {
StephaneLenclud@25: case EMiniDisplayRequestDeviceId:
StephaneLenclud@25: RequestDeviceId();
StephaneLenclud@25: break;
StephaneLenclud@25: case EMiniDisplayRequestFirmwareRevision:
StephaneLenclud@25: RequestFirmwareRevision();
StephaneLenclud@25: break;
StephaneLenclud@25: case EMiniDisplayRequestPowerSupplyStatus:
StephaneLenclud@25: RequestPowerSupplyStatus();
StephaneLenclud@25: break;
StephaneLenclud@25: default:
StephaneLenclud@25: //Not supported
StephaneLenclud@25: break;
StephaneLenclud@25: };
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@25:
StephaneLenclud@25: /**
StephaneLenclud@25: */
StephaneLenclud@25: void MDM166AA::ResetBuffers()
StephaneLenclud@25: {
StephaneLenclud@25: //iNextFrame->ClearAll();
StephaneLenclud@25: //memset(iFrameAlpha,0x00,sizeof(iFrameAlpha));
StephaneLenclud@25: //memset(iFrameBeta,0x00,sizeof(iFrameBeta));
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@25: /**
StephaneLenclud@25: */
StephaneLenclud@25: void MDM166AA::RequestDeviceId()
StephaneLenclud@25: {
StephaneLenclud@25: //Not supported
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@25: /**
StephaneLenclud@25: */
StephaneLenclud@25: void MDM166AA::RequestFirmwareRevision()
StephaneLenclud@25: {
StephaneLenclud@28: //Not supported
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@25: /**
StephaneLenclud@25: */
StephaneLenclud@25: void MDM166AA::RequestPowerSupplyStatus()
StephaneLenclud@25: {
StephaneLenclud@25: //Not supported
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@25:
StephaneLenclud@25: /**
StephaneLenclud@25: This is for development purposes only.
StephaneLenclud@25: Production application should stick to off-screen mode to avoid tearing.
StephaneLenclud@25: */
StephaneLenclud@25: void MDM166AA::ToggleOffScreenMode()
StephaneLenclud@25: {
StephaneLenclud@25: SetOffScreenMode(!iOffScreenMode);
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@25: /**
StephaneLenclud@25: * @brief MDM166AA::SetOffScreenMode
StephaneLenclud@25: * @param aOn
StephaneLenclud@25: * @return
StephaneLenclud@25: */
StephaneLenclud@25: void MDM166AA::SetOffScreenMode(bool aOn)
StephaneLenclud@25: {
StephaneLenclud@25: if (aOn==iOffScreenMode)
StephaneLenclud@25: {
StephaneLenclud@25: //Nothing to do here
StephaneLenclud@25: return;
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@25: iOffScreenMode=aOn;
StephaneLenclud@25:
StephaneLenclud@25: //Clean up our buffers upon switching modes
StephaneLenclud@25: Clear();
StephaneLenclud@25: SwapBuffers();
StephaneLenclud@25: Clear();
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@25: /**
StephaneLenclud@25: Set our screen brightness.
StephaneLenclud@25: @param The desired brightness level. Must be between MinBrightness and MaxBrightness.
StephaneLenclud@25: */
StephaneLenclud@25: void MDM166AA::SetBrightness(int aBrightness)
StephaneLenclud@25: {
StephaneLenclud@25: if (aBrightnessMaxBrightness())
StephaneLenclud@25: {
StephaneLenclud@25: //Brightness out of range.
StephaneLenclud@25: //Just ignore that request.
StephaneLenclud@25: return;
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@25: FutabaVfdReport report;
StephaneLenclud@25: report[0]=0x00; //Report ID
StephaneLenclud@25: report[1]=0x03; //Report size
StephaneLenclud@25: report[2]=0x1B; //Command ID
StephaneLenclud@25: report[3]=0x40; //Command ID
StephaneLenclud@25: report[4]=aBrightness; //Brightness level
StephaneLenclud@25: Write(report);
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@25:
StephaneLenclud@25: /**
StephaneLenclud@25: */
StephaneLenclud@25: void MDM166AA::ShowClock()
StephaneLenclud@25: {
StephaneLenclud@30: //Assuming display clock is at least roughly set since we do it when opening our display connection.
StephaneLenclud@30: //We will need accurate clock data next we get a chance.
StephaneLenclud@30: //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.
StephaneLenclud@30: iNeedAccurateClockData=true;
StephaneLenclud@30: //Show clock using specified styles
StephaneLenclud@25: SendCommandClockDisplay(EClockLarge,EClock24);
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@25: /**
StephaneLenclud@25: */
StephaneLenclud@25: void MDM166AA::HideClock()
StephaneLenclud@25: {
StephaneLenclud@34: SetAllPixels(0x00);
StephaneLenclud@34: SwapBuffers();
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@32: /**
StephaneLenclud@32: */
StephaneLenclud@32: int MDM166AA::IconCount(TMiniDisplayIconType aIcon)
StephaneLenclud@32: {
StephaneLenclud@32: return KSegmentsPerIcon[aIcon];
StephaneLenclud@32: }
StephaneLenclud@32:
StephaneLenclud@32: int MDM166AA::IconStatusCount(TMiniDisplayIconType aIcon)
StephaneLenclud@32: {
StephaneLenclud@32: return KStatusPerIcon[aIcon];
StephaneLenclud@32: }
StephaneLenclud@32:
StephaneLenclud@32: void MDM166AA::SetIconStatus(TMiniDisplayIconType aIcon, int aIndex, int aStatus)
StephaneLenclud@32: {
StephaneLenclud@32: if (aIcon<0||aIcon>=KMaxIconType||(KFunctionPerIcon[aIcon]==NULL))
StephaneLenclud@32: {
StephaneLenclud@32: //Out of range or no function pointer for that icon
StephaneLenclud@32: return;
StephaneLenclud@32: }
StephaneLenclud@32:
StephaneLenclud@32: (this->*KFunctionPerIcon[aIcon])(aIndex,aStatus);
StephaneLenclud@32: }
StephaneLenclud@25:
StephaneLenclud@25: /**
StephaneLenclud@31: */
StephaneLenclud@33: void MDM166AA::SetIconNetworkSignal(int aIndex, int aStatus)
StephaneLenclud@31: {
StephaneLenclud@33: if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconNetworkSignal])
StephaneLenclud@33: {
StephaneLenclud@33: //Out of range
StephaneLenclud@33: return;
StephaneLenclud@33: }
StephaneLenclud@33:
StephaneLenclud@33: SendCommandSymbolControl((TIconId)(aIndex+EIconNetworkSignalLow),(aStatus==0?EIconOff:EIconOn));
StephaneLenclud@33: }
StephaneLenclud@33:
StephaneLenclud@33: /**
StephaneLenclud@33: */
StephaneLenclud@33: void MDM166AA::SetIconInternet(int aIndex, int aStatus)
StephaneLenclud@33: {
StephaneLenclud@33: if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconInternet])
StephaneLenclud@31: {
StephaneLenclud@31: //Out of range
StephaneLenclud@31: return;
StephaneLenclud@31: }
StephaneLenclud@31:
StephaneLenclud@31: SendCommandSymbolControl((TIconId)(aIndex+EIconNetworkMast),(aStatus==0?EIconOff:EIconOn));
StephaneLenclud@31: }
StephaneLenclud@31:
StephaneLenclud@31: /**
StephaneLenclud@31: */
StephaneLenclud@31: void MDM166AA::SetIconEmail(int aIndex, int aStatus)
StephaneLenclud@31: {
StephaneLenclud@32: if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconEmail])
StephaneLenclud@31: {
StephaneLenclud@31: //Out of range
StephaneLenclud@31: return;
StephaneLenclud@31: }
StephaneLenclud@31:
StephaneLenclud@31: SendCommandSymbolControl((TIconId)(aIndex+EIconEnvelop),(aStatus==0?EIconOff:EIconOn));
StephaneLenclud@31: }
StephaneLenclud@31:
StephaneLenclud@31: /**
StephaneLenclud@31: */
StephaneLenclud@31: void MDM166AA::SetIconMute(int aIndex, int aStatus)
StephaneLenclud@31: {
StephaneLenclud@32: if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconMute])
StephaneLenclud@31: {
StephaneLenclud@31: //Out of range
StephaneLenclud@31: return;
StephaneLenclud@31: }
StephaneLenclud@31:
StephaneLenclud@31: SendCommandSymbolControl((TIconId)(aIndex+EIconMute),(aStatus==0?EIconOff:EIconOn));
StephaneLenclud@31: }
StephaneLenclud@31:
StephaneLenclud@31: /**
StephaneLenclud@31: */
StephaneLenclud@31: void MDM166AA::SetIconVolume(int aIndex, int aStatus)
StephaneLenclud@31: {
StephaneLenclud@32: if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconVolume])
StephaneLenclud@31: {
StephaneLenclud@31: //Out of range
StephaneLenclud@31: return;
StephaneLenclud@31: }
StephaneLenclud@31:
StephaneLenclud@32: if (aStatus=KSegmentsPerIcon[EMiniDisplayIconMute])
StephaneLenclud@31: {
StephaneLenclud@31: //Out of range
StephaneLenclud@31: return;
StephaneLenclud@31: }
StephaneLenclud@31:
StephaneLenclud@32: SendCommandSymbolControl((TIconId)(aIndex+EIconVolumeLabel),(aStatus==0?EIconOff:EIconOn));
StephaneLenclud@31: }
StephaneLenclud@31:
StephaneLenclud@31:
StephaneLenclud@31: /**
StephaneLenclud@31: */
StephaneLenclud@31: void MDM166AA::SetIconPlay(int aIndex, int aStatus)
StephaneLenclud@31: {
StephaneLenclud@32: if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconPlay])
StephaneLenclud@31: {
StephaneLenclud@31: //Out of range
StephaneLenclud@31: return;
StephaneLenclud@31: }
StephaneLenclud@31:
StephaneLenclud@31: SendCommandSymbolControl((TIconId)(aIndex+EIconPlay),(aStatus==0?EIconOff:EIconOn));
StephaneLenclud@31: }
StephaneLenclud@31:
StephaneLenclud@31:
StephaneLenclud@31: /**
StephaneLenclud@31: */
StephaneLenclud@31: void MDM166AA::SetIconPause(int aIndex, int aStatus)
StephaneLenclud@31: {
StephaneLenclud@32: if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconPause])
StephaneLenclud@31: {
StephaneLenclud@31: //Out of range
StephaneLenclud@31: return;
StephaneLenclud@31: }
StephaneLenclud@31:
StephaneLenclud@31: SendCommandSymbolControl((TIconId)(aIndex+EIconPause),(aStatus==0?EIconOff:EIconOn));
StephaneLenclud@31: }
StephaneLenclud@31:
StephaneLenclud@31:
StephaneLenclud@31: /**
StephaneLenclud@31: */
StephaneLenclud@31: void MDM166AA::SetIconRecording(int aIndex, int aStatus)
StephaneLenclud@31: {
StephaneLenclud@32: if (aIndex<0||aIndex>=KSegmentsPerIcon[EMiniDisplayIconRecording])
StephaneLenclud@31: {
StephaneLenclud@31: //Out of range
StephaneLenclud@31: return;
StephaneLenclud@31: }
StephaneLenclud@31:
StephaneLenclud@31: SendCommandSymbolControl((TIconId)(aIndex+EIconRecording),(aStatus==0?EIconOff:EIconOn));
StephaneLenclud@31: }
StephaneLenclud@31:
StephaneLenclud@31: /**
StephaneLenclud@31: Set all our icons to the corresponding status.
StephaneLenclud@31: */
StephaneLenclud@31: void MDM166AA::SetAllIcons(TIconStatus aStatus)
StephaneLenclud@31: {
StephaneLenclud@31: for (int i=EIconFirst;i<=EIconLast;i++)
StephaneLenclud@31: {
StephaneLenclud@31: SendCommandSymbolControl((TIconId)i,aStatus);
StephaneLenclud@31: }
StephaneLenclud@31: }
StephaneLenclud@31:
StephaneLenclud@31: /**
StephaneLenclud@31: Symbols control
StephaneLenclud@31: Segment On/Off and Grayscale/Brightness
StephaneLenclud@31: [Code]1BH,30H,Ps,Pb
StephaneLenclud@31: */
StephaneLenclud@31: void MDM166AA::SendCommandSymbolControl(TIconId aIconId, TIconStatus aStatus)
StephaneLenclud@31: {
StephaneLenclud@31: FutabaVfdReport report;
StephaneLenclud@31: report[0]=0x00; //Report ID
StephaneLenclud@31: report[1]=0x04; //Report size
StephaneLenclud@31: report[2]=0x1B; //Command ID
StephaneLenclud@31: report[3]=0x30; //Command ID
StephaneLenclud@31: report[4]=aIconId;
StephaneLenclud@31: report[5]=aStatus;
StephaneLenclud@31:
StephaneLenclud@31: Write(report);
StephaneLenclud@31: }
StephaneLenclud@31:
StephaneLenclud@31:
StephaneLenclud@31: /**
StephaneLenclud@25: Clock setting
StephaneLenclud@25: [Code]1BH,00H,Pm,Ph
StephaneLenclud@25: [Function]Setting the clock data. The setting data is cleared, if the Reset command is input or power is turned off.
StephaneLenclud@25: Ph = hour
StephaneLenclud@25: Pm = minute
StephaneLenclud@25: */
StephaneLenclud@28: void MDM166AA::SendCommandSetClockData(unsigned char aHour, unsigned char aMinute)
StephaneLenclud@25: {
StephaneLenclud@25: FutabaVfdReport report;
StephaneLenclud@25: report[0]=0x00; //Report ID
StephaneLenclud@25: report[1]=0x04; //Report size
StephaneLenclud@25: report[2]=0x1B; //Command ID
StephaneLenclud@25: report[3]=0x00; //Command ID
StephaneLenclud@25:
StephaneLenclud@25: //Minutes and Hours needs to be in hexadecimal view
StephaneLenclud@25: //To get 21:59 you need to pass in 0x21:0x59
StephaneLenclud@25: //Weirdest format ever, I know
StephaneLenclud@25: report[4]=(aMinute/10*16)+aMinute%10;
StephaneLenclud@25: report[5]=(aHour/10*16)+aHour%10;
StephaneLenclud@25:
StephaneLenclud@25: Write(report);
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@25: /**
StephaneLenclud@28: Set display clock data according to local system time.
StephaneLenclud@30: This will only provide 30s accuracy.
StephaneLenclud@30: In fact display clock seconds are set to zero whenever clock data is set.
StephaneLenclud@30: So you would only get second accuracy if this function was called when system time is at zero second.
StephaneLenclud@30: It's the responsibility of AttemptClockSynchronization function to obtain second accuracy.
StephaneLenclud@30: The present function is intended to provide only rough clock synchronization.
StephaneLenclud@30:
StephaneLenclud@30: @note Unfortunately this command also turns on clock display.
StephaneLenclud@25: */
StephaneLenclud@28: void MDM166AA::SetClockData()
StephaneLenclud@25: {
StephaneLenclud@25: time_t rawtime;
StephaneLenclud@25: struct tm * timeinfo;
StephaneLenclud@25:
StephaneLenclud@25: time ( &rawtime );
StephaneLenclud@25: timeinfo = localtime ( &rawtime );
StephaneLenclud@28: //Adjust minute as best as we can so that we have a 30 seconds offset at most rather a than a full minute.
StephaneLenclud@28: if (timeinfo->tm_sec>30)
StephaneLenclud@28: {
StephaneLenclud@28: //Use the next minute then
StephaneLenclud@28: timeinfo->tm_min++;
StephaneLenclud@28: if (timeinfo->tm_min==60)
StephaneLenclud@28: {
StephaneLenclud@28: //Use the next hour then
StephaneLenclud@28: timeinfo->tm_hour++;
StephaneLenclud@28: timeinfo->tm_min=0;
StephaneLenclud@28: if (timeinfo->tm_hour==24)
StephaneLenclud@28: {
StephaneLenclud@28: //Move to the next day then
StephaneLenclud@28: timeinfo->tm_hour=0;
StephaneLenclud@28: }
StephaneLenclud@28: }
StephaneLenclud@28: }
StephaneLenclud@28:
StephaneLenclud@28: //Send hours and minutes to our display
StephaneLenclud@28: SendCommandSetClockData(timeinfo->tm_hour,timeinfo->tm_min);
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@25:
StephaneLenclud@25: /**
StephaneLenclud@25: Clock display
StephaneLenclud@25: [Code] 1BH,Ps,aL,aH,Pf
StephaneLenclud@25: [Function] Clock is displayed small or big.
StephaneLenclud@25: */
StephaneLenclud@25: void MDM166AA::SendCommandClockDisplay(TClockSize aClockSize, TClockFormat aClockFormat)
StephaneLenclud@25: {
StephaneLenclud@25: FutabaVfdReport report;
StephaneLenclud@25: report[0]=0x00; //Report ID
StephaneLenclud@25: report[1]=0x03; //Report size
StephaneLenclud@25: report[2]=0x1B; //Command ID
StephaneLenclud@25: report[3]=aClockSize; //
StephaneLenclud@25: report[4]=aClockFormat; //
StephaneLenclud@25:
StephaneLenclud@25: Write(report);
StephaneLenclud@25: }
StephaneLenclud@25:
StephaneLenclud@27:
StephaneLenclud@27: /**
StephaneLenclud@27: Display RAM filled with 00H.
StephaneLenclud@27: Address Counter is set by 00H.
StephaneLenclud@27: Dimming is set to 50%.
StephaneLenclud@31: Turn off all icons segments.
StephaneLenclud@34:
StephaneLenclud@34: This does not reset our clock settings.
StephaneLenclud@27: */
StephaneLenclud@27: void MDM166AA::SendCommandReset()
StephaneLenclud@27: {
StephaneLenclud@27: FutabaVfdReport report;
StephaneLenclud@27: report[0]=0x00; //Report ID
StephaneLenclud@27: report[1]=0x01; //Report length.
StephaneLenclud@27: report[2]=0x1F; //Command ID
StephaneLenclud@27: Write(report);
StephaneLenclud@27: }
StephaneLenclud@27:
StephaneLenclud@27:
StephaneLenclud@27: /**
StephaneLenclud@27: Set Address Counter (AC) values: 1BH + 60H + xxH
StephaneLenclud@27: xxH: 00 ~ BFH
StephaneLenclud@27: AC value represents the start address for graphic data.
StephaneLenclud@27: There are 192 bytes as display RAM. It can be set on anywhere even if AC value is not visible area.
StephaneLenclud@27: The default value is 00H.
StephaneLenclud@27: Default: 00H
StephaneLenclud@27: When clock is displayed, AC value is set 00H.
StephaneLenclud@27: */
StephaneLenclud@27: void MDM166AA::SendCommandSetAddressCounter(unsigned char aAddressCounter)
StephaneLenclud@27: {
StephaneLenclud@27: FutabaVfdReport report;
StephaneLenclud@27: report[0]=0x00; //Report ID
StephaneLenclud@27: report[1]=0x03; //Report length.
StephaneLenclud@27: report[2]=0x1B; //Command ID
StephaneLenclud@27: report[3]=0x60; //Command ID
StephaneLenclud@27: report[4]=aAddressCounter;
StephaneLenclud@27: Write(report);
StephaneLenclud@27: }
StephaneLenclud@27:
StephaneLenclud@27:
StephaneLenclud@27: /**
StephaneLenclud@27: Set the defined pixel block to the given value.
StephaneLenclud@27:
StephaneLenclud@27: @param The size of our pixel data. Number of pixels divided by 8.
StephaneLenclud@27: @param Pointer to our pixel data.
StephaneLenclud@27: */
StephaneLenclud@27: void MDM166AA::SendCommandWriteGraphicData(int aSize, unsigned char* aPixels)
StephaneLenclud@27: {
StephaneLenclud@27: //TODO: Remove that at some point
StephaneLenclud@27: SendCommandSetAddressCounter(0);
StephaneLenclud@27:
StephaneLenclud@27: const int KMaxPixelBytes=48;
StephaneLenclud@27: const int KHeaderSize=3;
StephaneLenclud@27:
StephaneLenclud@27: int remainingSize=aSize;
StephaneLenclud@27: int sizeWritten=0;
StephaneLenclud@27:
StephaneLenclud@27: while (remainingSize>0)
StephaneLenclud@27: {
StephaneLenclud@27: //Only send a maximum of 48 bytes worth of pixels per report
StephaneLenclud@27: const int KPixelDataSize=(remainingSize<=KMaxPixelBytes?remainingSize:KMaxPixelBytes);
StephaneLenclud@27:
StephaneLenclud@27: FutabaVfdReport report;
StephaneLenclud@27: report[0]=0x00; //Report ID
StephaneLenclud@27: report[1]=KPixelDataSize+KHeaderSize; //Report length. +3 is for our header first 3 bytes.
StephaneLenclud@27: report[2]=0x1B; //Command ID
StephaneLenclud@27: report[3]=0x70; //Command ID
StephaneLenclud@27: report[4]=KPixelDataSize; //Size of pixel data in bytes
StephaneLenclud@27: memcpy(report.Buffer()+5, aPixels+sizeWritten, KPixelDataSize);
StephaneLenclud@27: Write(report);
StephaneLenclud@27: //Advance
StephaneLenclud@27: sizeWritten+=KPixelDataSize;
StephaneLenclud@27: remainingSize-=KPixelDataSize;
StephaneLenclud@27: }
StephaneLenclud@27: }