Slow Graphical LCD Driver FEZ Panda

Hi everybody,

I’ve been using the FEZ Panda for a little while now, and I really like it. Coding in c# is glorious compared to c, i feel more efficient and my code feels much more readable.

I am currently working on a simple alarm clock that uses a graphical lcd to display the current time, when the alarm is set for, etc. The glcd is 128x64 and is controlled by the ST7565 serial SPI controller. (Datasheet: [url]http://www.ladyada.net/learn/lcd/TG12864H3-03A%20EN_V1.1.pdf[/url]) Just for learning purposes and experimentation, I wrote my own driver for the ST7565. I used ladyada’s arduino library (here: [url]http://github.com/adafruit/ST7565-LCD[/url]) and GHI’s drivers (here: [url]http://www.tinyclr.com/downloads/Extension/FEZ_Extensions_Graphical128x64_UEXT_Display.cs[/url]) heavily as a base, however.

The driver works, but with a couple of hang-ups. At first, everything was displayed upside down… “test” would be displayed “ʇsǝʇ”. I figured it had to do with big/little-endianess so I put in a bit reversing algorithm, and sure enough, the images were displayed right-side-up. The larger problem, however, is the refresh speed of the screen. When I used the screen with an arduino, the refresh was almost instantaneous (oh, and the bits did not need to be reversed). Now, however, the screen takes what I can guess to be more than a quarter second to refresh, and I cannot use it with my clock because the time changes by the time the screen is done refreshing itself, and as a result the time readout jumps around… for example from 12:00:02 to 12:00:04. I attached my driver below.

Does anyone have any advice on how I can make my code faster, or why this code is so much slower than ladyada’s arduino library? Any help would be greatly appreciated. :slight_smile:


public class ST7565
        {
            public enum Commands
            {
                //really a very long list
                //i can attach them if needed
            }
            byte[] pagemap = new byte[8] { 3, 2, 1, 0, 7, 6, 5, 4 };
            SPI spi;
            OutputPort rst;
            OutputPort a0;

            public ST7565(Cpu.Pin ResetPin, Cpu.Pin CommandPin, Cpu.Pin SelectPin, SPI.SPI_module SPIModule)
            {
                // Set up spi
                SPI.Configuration spiconfig = new SPI.Configuration(SelectPin, false, 100, 100, false, true, 50000, SPIModule);
                spi = new SPI(spiconfig);

                // Set up reset and command pins
                rst = new OutputPort(ResetPin, true); // reset occurs when toggled low
                a0 = new OutputPort(CommandPin, true); // command occurs when toggled low

                // reset lcd
                rst.Write(false);
                Thread.Sleep(500);
                rst.Write(true);

                // send initialization commands
                SendCommand(Commands.SetBias7); // lcd bias
                SendCommand(Commands.SetAdcNormal); // adc select
                SendCommand(Commands.SetComNormal); //lhs select
                SendCommand(Commands.SetDispStartLin); //initial start line
                SendCommand(Commands.SetPowerControl, 0x4);
                Thread.Sleep(50);
                SendCommand(Commands.SetPowerControl, 0x6);
                Thread.Sleep(50);
                SendCommand(Commands.SetPowerControl, 0x7);
                Thread.Sleep(10);
                SendCommand(Commands.SetResistorRatio, 0x6);
            }

            public void SendCommand(Commands command)
            {
                // turn command mode on
                a0.Write(false);

                // send command over spi
                spi.Write(new byte[] { (byte) command});
            }

            public void SendCommand(Commands command, byte trail)
            {
                //turn command mode on
                a0.Write(false);

                //combine commands
                byte cmd = (byte)command;
                cmd |= trail;

                // send command over spi
                spi.Write(new byte[] { cmd });
            }

            private void SendData(byte data)
            {
                a0.Write(true);
                spi.Write(new byte[] { data });
            }

            public void SendDisp(byte[] DispBuffer)
            {
                Debug.Print("Starting Display Send");
                for (Int16 p = 0; p < 8; p++)
                {
                    SendCommand(Commands.SetPage, pagemap[p]);
                    SendCommand(Commands.SetColumnUpper, (0x0 & 0xF));
                    SendCommand(Commands.SetColumnLower, ((0x0 >> 4) & 0xF));
                    SendCommand(Commands.Rmw);
                    SendData(0xFF);

                    for (Int16 c = 0; c < 128; c++)
                    {
                        SendData(Reverse(DispBuffer[128 * p + c]));
                    }
                }
                Debug.Print("Finished");
            }

            private byte Reverse(byte inByte)
		    {
                long test = (inByte * 0x0202020202 & 0x010884422010) % 1023;
                return (byte)test;
		    }

            public void SetBrightness(int val)
            {
                SendCommand(Commands.SetVolumeFirst);
                SendCommand(Commands.SetVolumeSecond, (byte)(val & 0x3F));
            }

        }

Since you have only included the code for the driver, I assume you have done a detailed timing analysis, and have determined that the delay is in the driver, and not somewhere else in your program?

Hi Mike,

I have not done a detailed (as in down to the millisecond) analysis. I have used the lcd in multiple projects using different code than my alarm program (such as writing the word “test” at random places on the screen and measuring the amount of “tests” drawn in an amount of time), and the screen still has refreshed slowly. Also, in my code, the delay between

Debug.Print("Starting Display Send");

and

Debug.Print("Finished");

is clearly visible when executed.

imax, many times when someone complains to me that the Fez cannot keep up with some task, I ask them to run “free” outside the debugger. There is significant time taken up by the debug interface updating variables and such. In most cases, they come back and say, “Oh, outside the debugger, it runs great.”

Hi Blue Hair Bob (& Mike),

I set up a simple screen draw and stored a DateTime.Now before the draw and a DateTime.Now after the draw, then subtracted the two and redrew the screen with the resulting TimeSpan drawn on it as a string. Connected to the debugger, it took .7182 seconds from beginning to end. I then did a Deploy Solution and did not use the debugger, and the same process took .7090 seconds, so I don’t think the debugger is causing a problem.

To me, .7 seconds seems like an pretty large amount of time for a screen draw, and is much too long for my alarm project. Any ideas on how to make my screen drawing algorithm faster?

Thanks a lot,
Max ;D

OK, good test! .01 seconds difference is probably “in the noise”. Let’s see what others come up with.

My 2 cents:

  1. Look at datasheet chapter 11. There is command (9) Display normal/reverse. Maybe it will help, and you will win some time on processing.
  2. Don’t know if it’s an issue, but datasheet page 25 says SPI cycle is 0.25uS = 4Mhz in your code 50.
  3. This part of code is very nasty:
spi.Write(new byte[] { data });

Try to define array once in your code, maybe static, not every write.

Let me start by saying a good job on getting the display to work but then I want to say that your code is horribly written for speed :frowning: I bet the garbage collector is going crazy, right?

Please take a look at section "thinking small in the book microframeworkprojects.com

Here is why:

  1. You are sending data byte by byte instead of sending chunks…slow
  2. you should NOT allocate an object in your “inner loops”
spi.Write(new byte[] { data });
Should be 
//global data 
byte[] dddd = new byte[1];
//...
// in your send function
dddd = data; 
spi.Write({ dddd });
  1. you should use teh registers inside the display to rotate your data if possible instead of manually changing every bit and piece.

I suggest you take another look at the GHI driver. It runs pretty fast.

==============
Now, I have a question

How does this work, haven’t seen similar thing before!

 long test = (inByte * 0x0202020202 & 0x010884422010) % 1023;

Hi everybody

Thank you, I knew when I wrote the driver it would be rough, and thanks for showing me my errors. I did some re-writing, and the driver now sends data 1 page (128 bytes) at a time and does not allocate objects in inner loops. The time to draw a screen is now .199 seconds (which is so much better than .7!).

To answer your question, Gus, that piece of code is a very handy bit reversal algorithm that I found on the internet, it is very fast because it only takes three operations to complete.

As for the Display Normal/Reverse command, that inverts the color of the pixels, so on would be off and off would be on. I have done some experimentation with the COM select command, however. When I set the COM to reverse, the data is displayed correctly, but on the bottom of the screen. At first I thought it was unhelpful, but now that I think about it, there may be some promise to it (perhaps switching the order of the elements in the pagemap array). I’ll let you know.

I did a timing analysis of my alarm clock program (without lcd drawing) and found out that it is also pretty slow, so I’m going to work on that also haha. I’ll post any results or questions I get.

Thanks again for all your help guys!

Max

==========================
Edit: By sending the Reverse COM command and by reversing the order of the elements in the pagemap array, there is no longer a need for reversing the bits. (The Reverse() function). The draw time is now .1 seconds! Now on to fixing my alarm code…

I’m having a similar issue with the Panda II and Touch.

Just using the LCD Driver 2.2 (http://code.tinyclr.com/project/363/fez-touch-driver-ver-22/) with no modifications other than debug statements before and after the two DrawString operations, and it’s taking around 800ms to draw each line of text.

Running “off debug” doesn’t seem to make any difference. The text is displayed like an old teletype… one character at a time.

Is this to be expected? is there something I can optimise in the driver? or should I be looking outside the code for a cause?

Thanks in advance for any advice.

Aleks, I modified the original GHI FEZ Touch driver to support custom fonts. In the process I “cleaned up” and “reorganized” the original code to make it more structured, and in my opinion more readable. However, those changes probably made it less efficient. I’ve always intended to go back in and try to optomize it for speed, but so far haven’t had a chance to do that.

You pointed out correctly that drawing one character at a time is probably not the most efficient way to do things, but drawing more than one character at a time is a lot more complicated. Also, I did some speed tests, and it’s not just the text drawing that is slow, all my methods seem to be a bit slower than the original driver from GHI. :-[

I would very much welcome any suggestions from the community on how to improve this driver. Thanks.

First the display reversal:
The display has bot color reversal (showing white on black) and flip on Y axis reversal using command code 0xC8.

Next, the operations done here are pretty good for text… but not for graphics. I borrowed pieces from Arduino library too and put it together with your code. Most important are the two Refresh methods. Here the impact of doing something in chunk rather than byte by byte make an incredible difference in time of write. If the SPI only had a start and end index for the Write function… I could save memory allocation too.


using System;
using System.Threading;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

using GHIElectronics.NETMF.FEZ;

public class ST7565Display
{
    #region character data
    static byte[] m_characters = new byte[129 * 5]
        {
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,
         0x00,0x00,0x00,0x00,0x00,

         0x00,0x00,0x00,0x00,0x00, /* space    0x20 */
         0x00,0x00,0x4f,0x00,0x00, /* ! */
         0x00,0x07,0x00,0x07,0x00, /* " */
         0x14,0x7f,0x14,0x7f,0x14, /* # */
         0x24,0x2a,0x7f,0x2a,0x12, /* 0x */
         0x23,0x13,0x08,0x64,0x62, /* % */
         0x36,0x49,0x55,0x22,0x20, /* & */
         0x00,0x05,0x03,0x00,0x00, /* ' */
         0x00,0x1c,0x22,0x41,0x00, /* ( */
         0x00,0x41,0x22,0x1c,0x00, /* ) */
         0x14,0x08,0x3e,0x08,0x14, /* // */
         0x08,0x08,0x3e,0x08,0x08, /* + */
         0x50,0x30,0x00,0x00,0x00, /* , */
         0x08,0x08,0x08,0x08,0x08, /* - */ 
         0x00,0x60,0x60,0x00,0x00, /* . */
         0x20,0x10,0x08,0x04,0x02, /* / */
         0x3e,0x51,0x49,0x45,0x3e, /* 0        0x30 */
         0x00,0x42,0x7f,0x40,0x00, /* 1 */
         0x42,0x61,0x51,0x49,0x46, /* 2 */
         0x21,0x41,0x45,0x4b,0x31, /* 3 */
         0x18,0x14,0x12,0x7f,0x10, /* 4 */
         0x27,0x45,0x45,0x45,0x39, /* 5 */
         0x3c,0x4a,0x49,0x49,0x30, /* 6 */
         0x01,0x71,0x09,0x05,0x03, /* 7 */
         0x36,0x49,0x49,0x49,0x36, /* 8 */
         0x06,0x49,0x49,0x29,0x1e, /* 9 */
         0x00,0x36,0x36,0x00,0x00, /* : */
         0x00,0x56,0x36,0x00,0x00, /* ; */
         0x08,0x14,0x22,0x41,0x00, /* < */
         0x14,0x14,0x14,0x14,0x14, /* = */
         0x00,0x41,0x22,0x14,0x08, /* > */
         0x02,0x01,0x51,0x09,0x06, /* ? */
         0x3e,0x41,0x5d,0x55,0x1e, /* @        0x40 */
         0x7e,0x11,0x11,0x11,0x7e, /* A */
         0x7f,0x49,0x49,0x49,0x36, /* B */
         0x3e,0x41,0x41,0x41,0x22, /* C */
         0x7f,0x41,0x41,0x22,0x1c, /* D */
         0x7f,0x49,0x49,0x49,0x41, /* E */
         0x7f,0x09,0x09,0x09,0x01, /* F */
         0x3e,0x41,0x49,0x49,0x7a, /* G */
         0x7f,0x08,0x08,0x08,0x7f, /* H */
         0x00,0x41,0x7f,0x41,0x00, /* I */
         0x20,0x40,0x41,0x3f,0x01, /* J */
         0x7f,0x08,0x14,0x22,0x41, /* K */
         0x7f,0x40,0x40,0x40,0x40, /* L */
         0x7f,0x02,0x0c,0x02,0x7f, /* M */
         0x7f,0x04,0x08,0x10,0x7f, /* N */
         0x3e,0x41,0x41,0x41,0x3e, /* O */
         0x7f,0x09,0x09,0x09,0x06, /* P        0x50 */
         0x3e,0x41,0x51,0x21,0x5e, /* Q */
         0x7f,0x09,0x19,0x29,0x46, /* R */
         0x26,0x49,0x49,0x49,0x32, /* S */
         0x01,0x01,0x7f,0x01,0x01, /* T */
         0x3f,0x40,0x40,0x40,0x3f, /* U */
         0x1f,0x20,0x40,0x20,0x1f, /* V */
         0x3f,0x40,0x38,0x40,0x3f, /* W */
         0x63,0x14,0x08,0x14,0x63, /* X */
         0x07,0x08,0x70,0x08,0x07, /* Y */
         0x61,0x51,0x49,0x45,0x43, /* Z */
         0x00,0x7f,0x41,0x41,0x00, /* [ */
         0x02,0x04,0x08,0x10,0x20, /* \ */
         0x00,0x41,0x41,0x7f,0x00, /* ] */
         0x04,0x02,0x01,0x02,0x04, /* ^ */
         0x40,0x40,0x40,0x40,0x40, /* _ */
         0x00,0x00,0x03,0x05,0x00, /* `        0x60 */
         0x20,0x54,0x54,0x54,0x78, /* a */
         0x7F,0x44,0x44,0x44,0x38, /* b */
         0x38,0x44,0x44,0x44,0x44, /* c */
         0x38,0x44,0x44,0x44,0x7f, /* d */
         0x38,0x54,0x54,0x54,0x18, /* e */
         0x04,0x04,0x7e,0x05,0x05, /* f */
         0x08,0x54,0x54,0x54,0x3c, /* g */
         0x7f,0x08,0x04,0x04,0x78, /* h */
         0x00,0x44,0x7d,0x40,0x00, /* i */
         0x20,0x40,0x44,0x3d,0x00, /* j */
         0x7f,0x10,0x28,0x44,0x00, /* k */
         0x00,0x41,0x7f,0x40,0x00, /* l */
         0x7c,0x04,0x7c,0x04,0x78, /* m */
         0x7c,0x08,0x04,0x04,0x78, /* n */
         0x38,0x44,0x44,0x44,0x38, /* o */
         0x7c,0x14,0x14,0x14,0x08, /* p        0x70 */
         0x08,0x14,0x14,0x14,0x7c, /* q */
         0x7c,0x08,0x04,0x04,0x00, /* r */
         0x48,0x54,0x54,0x54,0x24, /* s */
         0x04,0x04,0x3f,0x44,0x44, /* t */
         0x3c,0x40,0x40,0x20,0x7c, /* u */
         0x1c,0x20,0x40,0x20,0x1c, /* v */
         0x3c,0x40,0x30,0x40,0x3c, /* w */
         0x44,0x28,0x10,0x28,0x44, /* x */
         0x0c,0x50,0x50,0x50,0x3c, /* y */
         0x44,0x64,0x54,0x4c,0x44, /* z */
         0x08,0x36,0x41,0x41,0x00, /* { */
         0x00,0x00,0x77,0x00,0x00, /* | */
         0x00,0x41,0x41,0x36,0x08, /* } */
         0x08,0x08,0x2a,0x1c,0x08, /* <- */
         0x08,0x1c,0x2a,0x08,0x08, /* -> */
         0xff,0xff,0xff,0xff,0xff, /*          0x80 */
     };
    #endregion

    public enum BWColor
    {
        Black = 1,
        White = 0
    }

    private enum Commands
    {
        SetColumnUpperAddr = 0x10,
        SetColumnLowerAddr = 0x00,
        SetPageAddress = 0xB0,
        StartReadModifyWriteMode = 0xE0,
        EndReadModifyWriteMode = 0xEE
    }


    private SPI        m_spi;
    private OutputPort m_reset;
    private OutputPort m_command;
    private byte[]     m_buf_byte;
    private byte[]     m_buf_line;
    private byte[]     m_buf_char;
    private byte[] m_buf_graphics;
    private bool       m_cmdmode;
    private int        m_curpage;

    public const int graphicsBufferSize = 1024;
    public const int DisplayWidth = 128;
    public const int DisplayHeight = 64;
    public bool AutoRefreshScreen = true;

    public ST7565Display(Cpu.Pin reset, Cpu.Pin command, Cpu.Pin select, SPI.SPI_module spiModule)
    {
        m_spi      = new SPI(new SPI.Configuration(select, false, 0, 0, false, true, 15000, spiModule)); // 8Mhz clock
        m_reset    = new OutputPort(reset, true);
        m_command  = new OutputPort(command, true);
        m_buf_byte = new byte[1];
        m_buf_line = new byte[128];
        m_buf_char = new byte[5];
        m_buf_graphics = new byte[graphicsBufferSize];

        m_cmdmode  = false;
        m_curpage  = -1;

        // bring reset low to reset the screen
        m_reset.Write(false);

        // and wait for reset...
        Thread.Sleep(200);

        // bring reset high to let it start up
        m_reset.Write(true);

        // and wait for startup...
        Thread.Sleep(200);

        WriteToDisplay(true, 0xA0); // ADC SELECT (NORMAL)
        WriteToDisplay(true, 0xC8); // COM OUTPUT DIRECTION (Flipped)
        WriteToDisplay(true, 0xA2); // LCD DRIVE BIAS (1/9)
        WriteToDisplay(true, 0x40); // Initial Display Line = 0
        WriteToDisplay(true, 0xAE); // DISPLAY ON/OFF (OFF)
        WriteToDisplay(true, 0xA6); // Reverse Display ON/OFF = OFF (Normal)
        WriteToDisplay(true, 0x2F); // POWER CONTROL (Internal voltage converter circuit is ON, Internal voltage regulator circuit is ON, Internal voltage follower circuit is ON)

        WriteToDisplay(true, 0x21); // REGULATOR RESISTOR SELECT (3.5)
        WriteToDisplay(true, 0x25); // REGULATOR RESISTOR SELECT (5.5)
        // Vf, Vc, Vr on in this order
        WriteToDisplay(true, 0x28 | 0x01); Thread.Sleep(50);
        WriteToDisplay(true, 0x28 | 0x05); Thread.Sleep(50);
        WriteToDisplay(true, 0x28 | 0x07); Thread.Sleep(50);
        WriteToDisplay(true, 0x81); // REFERENCE VOLTAGE MODE (set contrast mode)
        WriteToDisplay(true, 0x2F); // REFERENCE VOLTAGE SET (47)

        Clear();

        WriteToDisplay(true, 0xAF); // DISPLAY ON/OFF (ON)
    }

    public ST7565Display(FEZ_Pin.Digital reset, FEZ_Pin.Digital command, FEZ_Pin.Digital select, SPI.SPI_module spiModule) : this((Cpu.Pin)reset, (Cpu.Pin)command, (Cpu.Pin)select, spiModule) { }

    #region Display Geometry
    public int GetDisplayHeight() { return DisplayHeight; }

    public int GetDisplayWidth() { return DisplayWidth; }
    #endregion
    #region Text Functions
    public void Clear()
    {
        Array.Clear(m_buf_line, 0, m_buf_line.Length);
        
        // Clear the graphics buffer too
        for(int i=0; i<16; i++)
            m_buf_graphics[i] = 0;
        for (int i = 16; i < 1024; i <<= 1)
            Array.Copy(m_buf_graphics, 0, m_buf_graphics, i, i);

        // rewrite the entire screen with zeros
        this.Refresh();
    }

    /// <summary>
    /// Move cursot to X and page Y (not coordinate, actual page number)
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    private void Move(int x, int y)
    {
        x *= 6;

        if( m_curpage != y )
        {
            WriteToDisplay(true, (byte)(0xb0 | y));     // set page address
            m_curpage = y;
        }

        WriteToDisplay(true, (byte)(0x10 | (x >> 4)));  // set column address MSB
        WriteToDisplay(true, (byte)(0x0f & x));         // set column address LSB
    }

    /// <summary>
    /// Prints a character chr. Coordinates are lines and columns not graphical ones
    /// </summary>
    /// <param name="x">Column number [0..21] and not graphical coordinate</param>
    /// <param name="y">page number (line) [0..7] and not graphical coordinate</param>
    /// <param name="chr">Charcter to display</param>
    /// <param name="color">Either black or White, default to black</param>
    public void Print(int x, int y, char chr, BWColor color = BWColor.Black)
    {
        Move(x, y);
        Array.Copy(m_characters, 5 * chr, m_buf_char, 0, 5);
        if (color == BWColor.White)
            for (int i = 0; i < m_buf_char.Length; i++)
                m_buf_char[i] ^= 0xFF;

        // Write to the display
        WriteToDisplay(false, m_buf_char);
        // update the graphics buffer in host RAM
        int index = (x * 6) + y * 128;
        Array.Copy(m_buf_char, 0, m_buf_graphics, index, 5);
    }

    public void Print(int x, int y, string str, BWColor color = BWColor.Black)
    {
        for( var i = 0; i < str.Length; i++ )
            Print(x + i, y, str[i]);
    }
    #endregion



    #region GraphicFunctions
    protected void Swap(ref int a, ref int b)
    {
        var t = a; a = b; b = t;
    }
    /// <summary>
    /// Sets the column of the display to X.
    /// </summary>
    /// <param name="x"></param>
    private void gotoX(int x)
    {
        int uaddr, laddr;

        uaddr = (x & 0xF0) >> 4;
        laddr = x & 0x0F;

        WriteToDisplay(true, (byte)((int)Commands.SetColumnUpperAddr | uaddr));
        WriteToDisplay(true, (byte)((int)Commands.SetColumnLowerAddr | laddr));
    }

    /// <summary>
    /// Sets the row of the display to Y [0..63]
    /// </summary>
    /// <param name="y"></param>
    /// <returns>The bit representing the Y coordinate inside current page of the display</returns>
    private byte gotoY(int y)
    {
        byte mask;
        int m;
        byte page = (byte)(y >> 3); // Divide by 8

        page |= (byte)Commands.SetPageAddress;
        WriteToDisplay(true, page);
        m = 0x01 << (y % 8);
        mask = (byte)m;

        return mask;
    }

    /// <summary>
    /// Sets the display address to coordinates X and Y.
    /// For odd rows the address is set to 128+ previous page
    /// </summary>
    /// <param name="x">The X coordinate [0..127]</param>
    /// <param name="y">The Y coordinate [0..63]</param>
    /// <returns>The bit representing the Y coordinate inside current page of the display</returns>
    private byte gotoXY(int x, int y)
    {
        int actualX, page;
        int mask;

        page = (byte)(y >> 3);
        if ((page & 0x01) != 0)
        {
            page -= 1;
            actualX = x + 128;
        }
        else
        {
            actualX = x;
        }
        gotoX(actualX);
        m_curpage = page;
        return gotoY(page);
    }
    
    /// <summary>
    /// Set pixel in memory only. Call Refresh if you want it displayed
    /// </summary>
    /// <param name="x">X coordinate</param>
    /// <param name="y">Y coordinate</param>
    /// <param name="color">Black or White</param>
    public void SetPixel(int x, int y, BWColor color)
    {
        if ((x >= DisplayWidth) || (y >= DisplayHeight))
            return;

        if (color == BWColor.Black)
            m_buf_graphics[x + (y / 8) * 128] |= (byte)(1 << (y % 8)); // Because we have flliped display, Y=0 means D7
        else
            m_buf_graphics[x + (y / 8) * 128] &= (byte)~(1 << (y % 8));
    }

    /// <summary>
    /// Draw a line in memory and refreshes the display if AutoRefreshScreen is true
    /// </summary>
    /// <param name="x0"></param>
    /// <param name="y0"></param>
    /// <param name="x1"></param>
    /// <param name="y1"></param>
    /// <param name="color"></param>
    public void DrawLine(int x0, int y0, int x1, int y1, BWColor color)
    {
        int steep = (System.Math.Abs(y1 - y0) > System.Math.Abs(x1 - x0)) ? 1 : 0;

        if (steep != 0)
        {
            Swap(ref x0, ref y0);
            Swap(ref x1, ref y1);
        }

        if (x0 > x1)
        {
            Swap(ref x0, ref x1);
            Swap(ref y0, ref y1);
        }

        int dx, dy;
        dx = x1 - x0;
        dy = System.Math.Abs(y1 - y0);

        int err = dx / 2;
        int ystep;

        if (y0 < y1)
        {
            ystep = 1;
        }
        else
        {
            ystep = -1;
        }

        for (; x0 < x1; x0++)
        {
            if (steep != 0)
            {
                SetPixel(y0, x0, color);
            }
            else
            {
                SetPixel(x0, y0, color);
            }

            err -= dy;

            if (err < 0)
            {
                y0 += ystep;
                err += dx;
            }
        }
        if (AutoRefreshScreen)
            Refresh();
    }

    public void DrawRectangle(int x, int y, int w, int h, BWColor color)
    {
        int x2 = x + w;
        int y2 = y + h;

        for (int i = x; i < x2; i++)
        {
            SetPixel(i, y, color);
            SetPixel(i, y2 - 1, color);
        }
        for (int i = y; i < y2; i++)
        {
            SetPixel(x, i, color);
            SetPixel(x2 - 1, i, color);
        }
        if (AutoRefreshScreen)
            Refresh();
    }
        
    public void FillRectangle(int x, int y, int w, int h, BWColor color)
    {
        int x2, y2;

        x2 = x + w;
        y2 = y + h;
        for (int i = x; i < x2; i++)
            for (int j = y; j < y2; j++)
                if (color == BWColor.Black)
                    m_buf_graphics[i + (j / 8) * 128] |= (byte)(1 << (j % 8)); // Because we have flliped display, Y=0 means D7
                else
                    m_buf_graphics[i + (j / 8) * 128] &= (byte)~(1 << (j % 8));
        if (AutoRefreshScreen)
            Refresh();
    }

    public void DrawCircle(int x0, int y0, int r, BWColor color)
    {
        int f = 1 - r;
        int ddF_x = 1;
        int ddF_y = -2 * r;
        int x = 0;
        int y = r;

        SetPixel(x0, y0 + r, color);
        SetPixel(x0, y0 - r, color);
        SetPixel(x0 + r, y0, color);
        SetPixel(x0 - r, y0, color);

        while (x < y)
        {
            if (f >= 0)
            {
                y--;
                ddF_y += 2;
                f += ddF_y;
            }

            x++;
            ddF_x += 2;
            f += ddF_x;

            SetPixel(x0 + x, y0 + y, color);
            SetPixel(x0 - x, y0 + y, color);
            SetPixel(x0 + x, y0 - y, color);
            SetPixel(x0 - x, y0 - y, color);

            SetPixel(x0 + y, y0 + x, color);
            SetPixel(x0 - y, y0 + x, color);
            SetPixel(x0 + y, y0 - x, color);
            SetPixel(x0 - y, y0 - x, color);
        }
        if (AutoRefreshScreen)
            Refresh();
    }

    public void FillCircle(int x0, int y0, int r, BWColor color)
    {
        int f = 1 - r;
        int ddF_x = 1;
        int ddF_y = -2 * r;
        int x = 0;
        int y = r;

        for (int i = y0 - r; i <= y0 + r; i++)
        {
            SetPixel(x0, i, color);
        }

        while (x < y)
        {
            if (f >= 0)
            {
                y--;
                ddF_y += 2;
                f += ddF_y;
            }

            x++;
            ddF_x += 2;
            f += ddF_x;

            for (int i = y0 - y; i <= y0 + y; i++)
            {
                SetPixel(x0 + x, i, color);
                SetPixel(x0 - x, i, color);
            }

            for (int i = y0 - x; i <= y0 + x; i++)
            {
                SetPixel(x0 + y, i, color);
                SetPixel(x0 - y, i, color);
            }
        }
        if (AutoRefreshScreen)
            Refresh();
    }
    #endregion


    #region Display Control
    public void SetContrast(int contrast)
    {
        if( contrast < 0 || contrast > 63 )
            throw new ArgumentOutOfRangeException("contrast", "Contrast cannot be set less than 0 or greater than 63.");

        WriteToDisplay(true, 0x81);           // REFERENCE VOLTAGE MODE (set contrast mode)
        WriteToDisplay(true, (byte)contrast); // REFERENCE VOLTAGE SET
    }

    public void Off()
    {
        WriteToDisplay(true, 0xAE); // DISPLAY ON/OFF (OFF)
    }

    public void On()
    {
        WriteToDisplay(true, 0xAF); // DISPLAY ON/OFF (ON)
    }

    public void InverseDisplay()
    {
        WriteToDisplay(true, 0xA7);
    }
    #endregion



    #region Display HW access functions
    /// <summary>
    /// Refreshes the entire display with the internal representation of host memory buffer.
    /// Optimized for performance
    /// </summary>
    public virtual void Refresh()
    {
        int x, y;
        byte[] copy = new byte[128];

        for (y = 0; y < 8; y++)
        {
            WriteToDisplay(true, (byte)((int)Commands.SetPageAddress | y));
            WriteToDisplay(true, (byte)Commands.SetColumnUpperAddr);
            WriteToDisplay(true, (byte)Commands.SetColumnLowerAddr);
            m_cmdmode = false;
            m_command.Write(true);
            int rowOffset = y << 7;
            int rowLast = rowOffset + 128;
            Array.Copy(m_buf_graphics, rowOffset, copy, 0, 128);
            m_spi.Write(copy);
        }
        m_curpage = -1; // Invalidate current page
    }

    public virtual void Refresh(int x0, int y0, int width, int height)
    {
        int x, y;
        int mx, my;

        mx = x0 + width;
        if (mx > 127) mx = 127;
        my = (y0 + height) >> 3;
        if ( ((y0 + height) % 8) != 0)
            my += 1;
        if (my > 7)
            my = 7;

        byte[] copy = new byte[mx];
        for (y = (y0 >> 3); y <= my; y++)
        {
            WriteToDisplay(true, (byte)((int)Commands.SetPageAddress | y));
            gotoX(x0);
            m_cmdmode = false;
            m_command.Write(true);

            Array.Copy(m_buf_graphics, x0 + (y<<7), copy, 0, mx);
            m_spi.Write(copy);
        }
        m_curpage = -1; // Invalidate current page
    }

    private void WriteToDisplay(bool command, byte data)
    {
        m_buf_byte[0] = data;

        WriteToDisplay(command, m_buf_byte);
    }

    private void WriteToDisplay(bool command, byte[] data)
    {
        if( !m_cmdmode && command )
        {
            // switch to command mode
            m_cmdmode = true;
            m_command.Write(false);
        }
        else if( m_cmdmode && !command )
        {
            // switch to data mode
            m_cmdmode = false;
            m_command.Write(true);
        }

        m_spi.Write(data);
    }
    #endregion
}



@ Zakie Mashiah

Would you still have a copy of the complete code ?
(The post seems to have gotten truncated)

Hi Zakie Mashiah…Where is the rest of code…?

@ YuvaRaja -

Azkie has not logged on to the forum in over three months.

If you are looking for specific information, or have a question, please start a new thread.

Thanks Mike…
Am already Completed GLCD now working with that…

Regards YuvaRaja