Windows 10 IoT, C# and ILI9325

Last experience with W10 was terrible. But it was an edge case, Python in hostile environment πŸ™‚
Today is time to see how W10 works in its natural language, C#.

Our test will measure how fast a screen can be filled with the colour. I really hope it is better that 3 minutes πŸ˜€

Planning

I didn’t write a single line in C# this year and only a few lines in previous year πŸ™‚ so it is gonna be fun πŸ˜€
Our task is to execute the fillRect function and measure its time. To achieve this we need to write a driver for ILI9325 LCD display.
If porting from Python goes well and performance would be a good one, we will add all drawing functions just for fun πŸ™‚

The application in Windows 10 IoT is a background one, it does not have an UI. This can be good and bad πŸ™‚
To begin with the app we need to create a new project called Background Application(IoT). With this, we can add our code to project.
Let’s create a new directory, lcd and there a new classes Lcd and Driver. We will, as always, split communication layer from functional layer.

I just want to mark, that I’m not a C# developer so my code will probably violate many standards and may hurt your eyes, badly. You are warned.

Driver

Driver code, almost 1:1 port:

class Driver
    {
        private Dictionary<string, int> pinNo = new Dictionary<string, int>() {
            {"RS", 27 },
            {"W", 17 },
            {"DB8", 22 },
            {"DB9", 23 },
            {"DB10", 24 },
            {"DB11", 5 },
            {"DB12", 12 },
            {"DB13", 16 },
            {"DB14", 20 },
            {"DB15", 21 },
            {"RST", 25 },
            {"LED", 6 },
            {"CS", 18 }
        };
        private Dictionary<string, GpioPin> pin = new Dictionary<string, GpioPin>();

        public Driver()
        {
            var gpio = GpioController.GetDefault();

            foreach (var pair in this.pinNo)
            {
                var pin = gpio.OpenPin(pair.Value);
                this.pin.Add(pair.Key, pin);
                pin.Write(GpioPinValue.Low);
                pin.SetDriveMode(GpioPinDriveMode.Output);

            }

            this.pin["LED"].Write(GpioPinValue.High);
        }

Here we have hard coded pins. Next, in the constructor, we create GpioPin instances for each pin we use. And we toggle a backlight.
We have two arrays, one for pins and second for objects. I’m sure that we could make it better.

        public void reset()
        {
            this.pin["RST"].Write(GpioPinValue.High);
            Task.Delay(5);
            this.pin["RST"].Write(GpioPinValue.Low);
            Task.Delay(5);
            this.pin["RST"].Write(GpioPinValue.High);
        }

        public void cmd(int data)
        {
            this.pin["RS"].Write(GpioPinValue.Low);
            this.send(data);
        }

        public void data(int data)
        {
            this.pin["RS"].Write(GpioPinValue.High);
            this.send(data);
        }

        private void send(int data)
        {
            this.pin["CS"].Write(GpioPinValue.Low);
            this.setPins(data >> 8);
            this.pin["W"].Write(GpioPinValue.Low);
            this.pin["W"].Write(GpioPinValue.High);
            this.setPins(data);
            this.pin["W"].Write(GpioPinValue.Low);
            this.pin["W"].Write(GpioPinValue.High);
            this.pin["CS"].Write(GpioPinValue.High);
        }

        private void setPins(int data)
        {
            string[] pins = {"DB8", "DB9", "DB10", "DB11", "DB12", "DB13", "DB14", "DB15" };
            foreach(string pin in pins)
            {
                if ((data & 0x01) == 1)
                {
                    this.pin[pin].Write(GpioPinValue.High);
                } else
                {
                    this.pin[pin].Write(GpioPinValue.Low);
                }
                data >>= 1;
            }
        }
    }

This part is almost 1:1 from Python. We have cmd and data public functions and send and setPins private functions. All this is enough to communicate with the display.

Display class

We have a connection driver and we may focus on higher abstraction. This class will use the driver to perform actions. It needs to do same as in Python, initialize display and fill the screen.

   class Lcd
    {

        Driver driver;

        public Lcd(Driver driver)
        {
            this.driver = driver;
        }

        public void init()
        {
            this.driver.reset();
            this.driver.cmd(0x0001);
            this.driver.data(0x0100);
            this.driver.cmd(0x0002);
            this.driver.data(0x0200);
            // set GRAM write direction and BGR=1
            this.driver.cmd(0x0003);
            this.driver.data(0x1030);
            // Resize register
            this.driver.cmd(0x0004);
            this.driver.data(0x0000);
            // set the back porch and front porch
            this.driver.cmd(0x0008);
            this.driver.data(0x0207);
            //# set non-display area refresh cycle ISC[3:0]
            this.driver.cmd(0x0009);
            this.driver.data(0x0000);
            //# FMARK function
            this.driver.cmd(0x000A);
            this.driver.data(0x0000);
            //# RGB interface setting
            this.driver.cmd(0x000C);
            this.driver.data(0x0000);
            //# Frame marker Position
            this.driver.cmd(0x000D);
            this.driver.data(0x0000);
            //# RGB interface polarity
            this.driver.cmd(0x000F);
            this.driver.data(0x0000);

            //# ************* Power On sequence ****************
            //# SAP, BT[3:0], AP, DSTB, SLP, STB
            this.driver.cmd(0x0010);
            this.driver.data(0x0000);
            //# DC1[2:0], DC0[2:0], VC[2:0]
            this.driver.cmd(0x0011);
            this.driver.data(0x0007);
            // # VREG1OUT voltage
            this.driver.cmd(0x0012);
            this.driver.data(0x0000);
            //  # VDV[4:0] for VCOM amplitude
            this.driver.cmd(0x0013);
            this.driver.data(0x0000);
            this.driver.cmd(0x0007);
            this.driver.data(0x0001);
            Task.Delay(5);  //# Dis-charge capacitor power voltage
                            // # SAP, BT[3:0], AP, DSTB, SLP, STB
            this.driver.cmd(0x0010);
            this.driver.data(0x1690);
            // # Set DC1[2:0], DC0[2:0], VC[2:0]
            this.driver.cmd(0x0011);
            this.driver.data(0x0227);
            Task.Delay(5);
            this.driver.cmd(0x0012);
            this.driver.data(0x000D);
            Task.Delay(5);
            // # VDV[4:0] for VCOM amplitude
            this.driver.cmd(0x0013);
            this.driver.data(0x1200);
            // # 04  VCM[5:0] for VCOMH
            this.driver.cmd(0x0029);
            this.driver.data(0x000A);
            // # Set Frame Rate
            this.driver.cmd(0x002B);
            this.driver.data(0x000D);

            Task.Delay(5);
            // # GRAM horizontal Address
            this.driver.cmd(0x0020);
            this.driver.data(0x0000);
            // # GRAM Vertical Address
            this.driver.cmd(0x0021);
            this.driver.data(0x0000);

            //# ************* Adjust the Gamma Curve *************
            this.driver.cmd(0x0030);
            this.driver.data(0x0000);
            this.driver.cmd(0x0031);
            this.driver.data(0x0404);
            this.driver.cmd(0x0032);
            this.driver.data(0x0003);
            this.driver.cmd(0x0035);
            this.driver.data(0x0405);
            this.driver.cmd(0x0036);
            this.driver.data(0x0808);
            this.driver.cmd(0x0037);
            this.driver.data(0x0407);
            this.driver.cmd(0x0038);
            this.driver.data(0x0303);
            this.driver.cmd(0x0039);
            this.driver.data(0x0707);
            this.driver.cmd(0x003C);
            this.driver.data(0x0504);
            this.driver.cmd(0x003D);
            this.driver.data(0x0808);

            //# ************* Set GRAM area *************
            //# Horizontal GRAM Start Address
            this.driver.cmd(0x0050);
            this.driver.data(0x0000);
            //# Horizontal GRAM End Address
            this.driver.cmd(0x0051);
            this.driver.data(0x00EF);
            //# Vertical GRAM Start Address
            this.driver.cmd(0x0052);
            this.driver.data(0x0000);
            //# Vertical GRAM Start Address
            this.driver.cmd(0x0053);
            this.driver.data(0x013F);
            //# Gate Scan Line
            this.driver.cmd(0x0060);
            this.driver.data(0xA700);
            //# NDL, VLE, REV
            this.driver.cmd(0x0061);
            this.driver.data(0x0001);
            //# set scrolling line
            this.driver.cmd(0x006A);
            this.driver.data(0x0000);

            // # ************* Partial Display Control *************
            this.driver.cmd(0x0080);
            this.driver.data(0x0000);
            this.driver.cmd(0x0081);
            this.driver.data(0x0000);
            this.driver.cmd(0x0082);
            this.driver.data(0x0000);
            this.driver.cmd(0x0083);
            this.driver.data(0x0000);
            this.driver.cmd(0x0084);
            this.driver.data(0x0000);
            this.driver.cmd(0x0085);
            this.driver.data(0x0000);

            //# ************* Panel Control *************
            this.driver.cmd(0x0090);
            this.driver.data(0x0010);
            this.driver.cmd(0x0092);
            this.driver.data(0x0000);
            // # 262K color and display ON
            this.driver.cmd(0x0007);
            this.driver.data(0x0133);
        }

Init function, as in Python is quite long. But does it’s job.

public void drawPixel(int x, int y)
        {
            this.setArea(x, y, x, y);
            this.setPixel(255, 255, 0);

        }

        private void setPixel(int r, int g, int b)
        {
            int ch = ((r & 248) | g >> 5);
            int cl = ((g & 28) << 3 | b >> 3);
            this.driver.data(ch<<8 | cl);

        }

        private void setArea(int pos_x1, int pos_y1, int pos_x2, int pos_y2)
        {
            this.driver.cmd(0x0020);
            this.driver.data(pos_x1);
            this.driver.cmd(0x0021);
            this.driver.data(pos_y1);
            this.driver.cmd(0x0050);
            this.driver.data(pos_x1);
            this.driver.cmd(0x0052);
            this.driver.data(pos_y1);
            this.driver.cmd(0x0051);
            this.driver.data(pos_x2);
            this.driver.cmd(0x0053);
            this.driver.data(pos_y2);
            this.driver.cmd(0x0022);
        }

        public void fillRect(int pos_x1, int pos_y1, int pos_x2, int pos_y2)
        {
            int size = (Math.Abs(pos_x2 - pos_x1) + 1) * (Math.Abs(pos_y2 - pos_y1) + 1);
            this.setArea(
                Math.Min(pos_x1, pos_x2),
                Math.Min(pos_y1, pos_y2),
                Math.Max(pos_x1, pos_x2),
                Math.Max(pos_y1, pos_y2)
            );
            for (int i = 0; i < size; i++) {
                this.setPixel(255, 0, 255);
            }
        }
    }

And this is the most important part πŸ™‚ especially a fillRect function. We will measure its performance.

We have all bricks at the place so we can take some measurements and see how it goes.

Performance

The entry point for background application is a StartupTask.cs file. There we have the main class and there we put our code:

            deferral = taskInstance.GetDeferral();
            lcd.Lcd tft = new lcd.Lcd(new lcd.Driver());
            tft.init();
            tft.drawPixel(100, 100);
            var watch = System.Diagnostics.Stopwatch.StartNew();
            tft.fillRect(0, 0, 239, 319);
            watch.Stop();
            var elapsedMs = watch.ElapsedMilliseconds;
            Debug.WriteLine(elapsedMs/1000);

To check how fast filling goes, we are using a Stopwatch.
It is time to run it and…not impressive 13 seconds but definitely better that 3 minutes πŸ˜€

Summary

My curiosity of Windows 10 IoT performance is fulfilled. It is not impressive and it is better to stick with “slow” Python and Linux. How slow? Filling the screen with size 240×320 takes:

Python @ Linux: 5s
Python @ Windows 10 IoT: 3m 35s
C# @ Windows 10 IoT: 13s

The conclusion is, we won’t port the Doton and GfxLCD to C# it is a waste of time. It is better to focus on new 5″ IPS display.

Advertisements

One comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s