SPI display provider in TinyCLR 0.9

From 0.9 release notes

All devices now also have a native SPI display provider that is used just like the existing parallel displays. On a DisplayController created for the SPI native API, call ApplySettings with an instance of SpiDisplayControllerSettings. Then just create an instance of Graphics like previously. Some displays, like the N18, require certain configuration before flushing data, so make sure to do that yourself before calling Flush. It’s also expected that you initialize the display itself as well.

Is there any example code available using this controller for the N18? I am experimenting with the G80 Development Board.

Reworking TinyCLR 0.9 code for G400 Development Board:

using System;
using System.Collections;
using System.Text;
using System.Threading;
using GHIElectronics.TinyCLR.Devices.Display;
using System.Drawing;
using GHIElectronics.TinyCLR.Pins;

namespace TinyCLRG80_SPIDisplay
{
    class Program
    {
        static void Main()
        {
            DisplayController displayController = DisplayController.GetDefault();
            // Enter the proper display configurations
            displayController.ApplySettings(new SpiDisplayControllerSettings
            {
                Width = 128,
                Height = 160,
                SpiSelector = G80.SpiBus.Spi2
            });
            Font font = Resource1.GetFont(Resource1.FontResources.small);
            // Some needed objects
            Graphics screen = Graphics.FromHdc(displayController.Hdc);
            Pen GreenPen = new Pen(Color.Green);
            // Start Drawing (to memroy)
            screen.Clear(Color.Black);
            screen.DrawEllipse(GreenPen, 40, 30, 20, 40);
            // Flush the memory to the display. This is a very fast operation.
            screen.DrawString("Text", font, new SolidBrush(Color.White), 50, 50);
            // screen.FillRectangle(new SolidBrush(Color.Red), 10, 136, 460, 100);
            // screen.DrawString("Overlay", font, new SolidBrush(Color.Black), 50, 150);
            screen.Flush();
            displayController.WriteString("Test");
        }
    }
}

The following exception is thrown at runtime:

An unhandled exception of type ‘System.ArgumentException’ occurred in mscorlib.dll occurred

Also, what string is required for the “SpiSelector” setting?
Thanks.

I think you need to know the SPI port attached to the display. You are using SPI2 port. Anyway the driver doesn’t initialize the spi display. You need to send via direct SPI connection all the init commands BEFORE using the spi display driver itself.
I can’t help you about N18, but I think you can find info on the internet (arduino, netmf and so on…).

I believe @John_Brochue already has the code you need.

1 Like

For display N18 with TinyCLR, the code is here :

http://docs.ghielectronics.com/tinyclr/accessories/adafruit_display_shield.html

Thank you for the suggestions.
I have the N18 display working on the G80 Development Board using the N18 driver from

A big thank you to @Bauland.

@Gus_Issa, I am still keen to see @John_Brochue’s code to be able to use the TinyCLR 0.9 additions.

Looking forward to the V1.0 previews.

Thanks.

That’s just the sense of term “Community” :wink:

2 Likes

Below is a complete example that works on the G80 Development Board:

using GHIElectronics.TinyCLR.Devices.Display;
using GHIElectronics.TinyCLR.Devices.Display.Provider;
using GHIElectronics.TinyCLR.Devices.Gpio;
using GHIElectronics.TinyCLR.Devices.Spi;
using GHIElectronics.TinyCLR.Pins;
using System.Drawing;
using System.Threading;

namespace TinyCLRN18SPI {
    public static class Program {
        public static void Main() {
            var bus = G80.SpiBus.Spi2;
            var driver = new N18Driver(G80.GpioPin.PE10, G80.GpioPin.PE12, G80.GpioPin.PC7, G80.GpioPin.PD10, bus);
            var displayController = DisplayController.GetControllers(DisplayProvider.FromId("GHIElectronics.TinyCLR.NativeApis.Drivers.SPIDisplay\\0"))[0];

            displayController.ApplySettings(new SpiDisplayControllerSettings {
                Width = driver.Width,
                Height = driver.Height,
                DataFormat = DisplayDataFormat.Rgb565,
                SpiSelector = bus,
            });

            using (var screen = Graphics.FromHdc(displayController.Hdc)) {
                screen.FillRectangle(new SolidBrush(Color.FromArgb(100, 0xFF, 0, 0xFF)), 25, 25, 25, 25);

                driver.PrepareToShow();

                screen.Flush();
            }
        }
    }

    public sealed class N18Driver {
        private readonly byte[] buffer1 = new byte[1];
        private readonly SpiDevice spi;
        private readonly GpioPin controlPin;
        private readonly GpioPin resetPin;
        private readonly GpioPin backlightPin;

        public uint Width => 128;
        public uint Height => 160;

        public N18Driver(int controlPin, int resetPin, int backlightPin, int chipSelectPin, string spiId) {
            this.spi = SpiDevice.FromId(spiId, new SpiConnectionSettings(chipSelectPin) {
                Mode = SpiMode.Mode3,
                ClockFrequency = 12000000,
                DataBitLength = 8,
                SharingMode = SpiSharingMode.Shared
            });

            var gpio = GpioController.GetDefault();
            this.controlPin = gpio.OpenPin(controlPin);
            this.resetPin = gpio.OpenPin(resetPin);
            this.backlightPin = gpio.OpenPin(backlightPin);

            this.controlPin.SetDriveMode(GpioPinDriveMode.Output);
            this.resetPin.SetDriveMode(GpioPinDriveMode.Output);
            this.backlightPin.SetDriveMode(GpioPinDriveMode.Output);

            this.backlightPin.Write(GpioPinValue.High);

            this.resetPin.Write(GpioPinValue.Low);
            Thread.Sleep(30);

            this.resetPin.Write(GpioPinValue.High);
            Thread.Sleep(100);

            this.Initialize();
        }

        public void PrepareToShow() {
            this.SetClip(0, 0, this.Width, this.Height);

            this.WriteCommand(0x2C);
            this.controlPin.Write(GpioPinValue.High);
        }

        private void WriteCommand(byte command) {
            this.buffer1[0] = command;
            this.controlPin.Write(GpioPinValue.Low);
            this.spi.Write(this.buffer1);
        }

        private void WriteData(byte data) {
            this.buffer1[0] = data;
            this.controlPin.Write(GpioPinValue.High);
            this.spi.Write(this.buffer1);
        }

        private void SendData(params byte[] data) {
            this.controlPin.Write(GpioPinValue.High);
            this.spi.Write(data);
        }

        private void SetClip(uint x, uint y, uint width, uint height) {
            var left = x;
            var top = y;
            var right = x + width - 1;
            var bottom = y + height - 1;

            this.WriteCommand(0x2A);
            this.SendData((byte)((left >> 8) & 0xFF),
                     (byte)(left & 0xFF),
                     (byte)((right >> 8) & 0xFF),
                     (byte)(right & 0xFF));

            this.WriteCommand(0x2B);
            this.SendData((byte)((top >> 8) & 0xFF),
                     (byte)(top & 0xFF),
                     (byte)((bottom >> 8) & 0xFF),
                     (byte)(bottom & 0xFF));
        }

        private void Initialize() {
            this.WriteCommand(0x11);

            Thread.Sleep(200);

            this.WriteCommand(0xB1);
            this.WriteData(0x01);
            this.WriteData(0x2C);
            this.WriteData(0x2D);

            this.WriteCommand(0xB2);
            this.WriteData(0x01);
            this.WriteData(0x2C);
            this.WriteData(0x2D);

            this.WriteCommand(0xB3);
            this.WriteData(0x01);
            this.WriteData(0x2C);
            this.WriteData(0x2D);
            this.WriteData(0x01);
            this.WriteData(0x2C);
            this.WriteData(0x2D);

            this.WriteCommand(0xB4);
            this.WriteData(0x07);

            this.WriteCommand(0xC0);
            this.WriteData(0xA2);
            this.WriteData(0x02);
            this.WriteData(0x84);

            this.WriteCommand(0xC1);
            this.WriteData(0xC5);

            this.WriteCommand(0xC2);
            this.WriteData(0x0A);
            this.WriteData(0x00);

            this.WriteCommand(0xC3);
            this.WriteData(0x8A);
            this.WriteData(0x2A);

            this.WriteCommand(0xC4);
            this.WriteData(0x8A);
            this.WriteData(0xEE);

            this.WriteCommand(0xC5);
            this.WriteData(0x0E);

            this.WriteCommand(0x36);
            this.WriteData(0xC0);

            this.WriteCommand(0xE0);
            this.WriteData(0x0F);
            this.WriteData(0x1A);
            this.WriteData(0x0F);
            this.WriteData(0x18);
            this.WriteData(0x2F);
            this.WriteData(0x28);
            this.WriteData(0x20);
            this.WriteData(0x22);
            this.WriteData(0x1F);
            this.WriteData(0x1B);
            this.WriteData(0x23);
            this.WriteData(0x37);
            this.WriteData(0x00);
            this.WriteData(0x07);
            this.WriteData(0x02);
            this.WriteData(0x10);

            this.WriteCommand(0xE1);
            this.WriteData(0x0F);
            this.WriteData(0x1B);
            this.WriteData(0x0F);
            this.WriteData(0x17);
            this.WriteData(0x33);
            this.WriteData(0x2C);
            this.WriteData(0x29);
            this.WriteData(0x2E);
            this.WriteData(0x30);
            this.WriteData(0x30);
            this.WriteData(0x39);
            this.WriteData(0x3F);
            this.WriteData(0x00);
            this.WriteData(0x07);
            this.WriteData(0x03);
            this.WriteData(0x10);

            this.WriteCommand(0x2A);
            this.WriteData(0x00);
            this.WriteData(0x00);
            this.WriteData(0x00);
            this.WriteData(0x7F);

            this.WriteCommand(0x2B);
            this.WriteData(0x00);
            this.WriteData(0x00);
            this.WriteData(0x00);
            this.WriteData(0x9F);

            this.WriteCommand(0xF0);
            this.WriteData(0x01);

            this.WriteCommand(0xF6);
            this.WriteData(0x00);

            this.WriteCommand(0x3A);
            this.WriteData(0x05);

            this.WriteCommand(0x29);

            Thread.Sleep(50);

            this.WriteCommand(0x11);

            Thread.Sleep(50);
        }
    }
}

2 Likes

@John_Brochue, Thank you.

Using @John_Brochue’s provided code:

  • All works well for 0.9.
  • Updated firmware to 0.10.0 and updated libraries to 0.10.0 - compiles and deploys ok - display is all white.
  • Rolling back to 0.9 and it all works again.

Using @Bauland’s code:

  • All works well for 0.9.
  • Updated firmware to 0.10.0 and updated libraries to 0.10.0 - compiles and deploys ok - display is all white.
  • Rolling back to 0.9 and it all works again.

The Release Notes indicated a number of display fixes. It appears the update has changed something that invalidates this display code. Can others confirm?
Thanks.

You’re probably seeing Default GPIO pin state should be set during soft reset, not gpio acquire · Issue #256 · ghi-electronics/TinyCLR-Ports · GitHub looks like it wasn’t included in the known issues by mistake.

If you are, the fix is to move GpioController.GetDefault to the very start of Main.

@John_Brochue, thank you. That was it.
Moving GpioController.GetDefault did the trick - all working in 0.10.0.

@Bauland’s code: moved
_controlPin = GpioController.GetDefault().OpenPin(controlPin);
and also had to split into two for the Controller and Pin to resolve.