Windows IoT and N18Display

But as they don’t appear anywhere in code, I think they are useless. Aren’t they ?

Thnaks a lot to make me look again !!! It was in code ! :wall: :wall: :wall: I think a pause is required …

After a pause, driver is rewritten, but now I just have a blank and white screen. Drawpixel seems do Nothing, and even clear function seems to not clear screen. Perhaps others eyes can see some stupid mistakes I wrote.


using System;
using System.Threading.Tasks;
using Windows.Devices.Spi;
using GHIElectronics.UWP.GadgeteerCore;
using GHIElectronics.UWP.GadgeteerCore.SocketInterfaces;
using SpiDevice = GHIElectronics.UWP.GadgeteerCore.SocketInterfaces.SpiDevice;

namespace N18Display
{
    public class N18Display : Module
    {
        public override string Name => "N18Display";
        public override string Manufacturer => "GHI";

        private SpiDevice _spiDevice;
        private DigitalIO _resetIo; // Hardware reset
        private DigitalIO _backlightIo; // backlight control
        private DigitalIO _cdIo; // command: false, data: true
        private byte[] _data;
        private ushort[] _ushortData;

        #region Command for ST7735
        private byte CASET = 0x2A;
        private byte RASET = 0x2B;
        private byte RAMWR = 0x2C;
        private byte DISPON = 0x29;
        private byte SLPOUT = 0x11;
        private byte FRMCTR1 = 0xB1;
        private byte FRMCTR2 = 0xB2;
        private byte FRMCTR3 = 0xB3;
        private byte INVCTR = 0xB4;
        private byte PWCTR1 = 0xC0;
        private byte PWCTR2=0xC1;
        private byte PWCTR3=0xC2;
        private byte PWCTR4=0xC3;
        private byte PWCTR5=0xC4;
        private byte VMCTR1=0xC5;
        private byte GMCTRP1=0xE0;
        private byte GMCTRN1=0xE1;
        private byte COLMOD=0x3A;
        private byte MADCTL=0x36;

        #endregion

        protected override async Task Initialize(ISocket parentSocket)
        {
            _data = null;
            _spiDevice =
                await
                    parentSocket.CreateSpiDeviceAsync(new SpiConnectionSettings(0)
                    {
                        Mode = SpiMode.Mode0,
                        ClockFrequency = 12000000,
                    });
            _resetIo = await parentSocket.CreateDigitalIOAsync(SocketPinNumber.Three, false);
            _backlightIo = await parentSocket.CreateDigitalIOAsync(SocketPinNumber.Four, true);
            _cdIo = await parentSocket.CreateDigitalIOAsync(SocketPinNumber.Five, false);
            _data = new byte[1];
            _ushortData = new ushort[2];
            Reset(); // Hard reset
            ConfigureDisplay();
            Clear();
        }

        private void ConfigureDisplay()
        {
            // ST7735R

            WriteCommand(SLPOUT); // Exit sleep mode
            Task.Delay(120);
            WriteCommand(FRMCTR1);
            WriteData(0x01); WriteData(0x2C); WriteData(0x2D);
            WriteCommand(FRMCTR2);
            WriteData(0x01); WriteData(0x2C); WriteData(0x2D);
            WriteCommand(FRMCTR3);
            WriteData(0x01); WriteData(0x2C); WriteData(0x2D);
            WriteData(0x01); WriteData(0x2C); WriteData(0x2D);
            WriteCommand(INVCTR); // Column inversion
            WriteData(0x07);

            // Power Sequence
            WriteCommand(PWCTR1);
            WriteData(0xA2); WriteData(0x02); WriteData(0x84);
            WriteCommand(PWCTR2);
            WriteData(0xc5);
            WriteCommand(PWCTR3);
            WriteData(0x0a); WriteData(0x00);
            WriteCommand(PWCTR4);
            WriteData(0x8a); WriteData(0x2a);
            WriteCommand(PWCTR5);
            WriteData(0x8a); WriteData(0xee);

            // VCOM
            WriteCommand(VMCTR1);
            WriteData(0x0e);

            // Mode address
            WriteCommand(MADCTL);
            WriteData(0xc8);
            //

            // Gamma Sequence
            WriteCommand(GMCTRP1);
            WriteData(0x0f); WriteData(0x1a); WriteData(0x0f); WriteData(0x18);
            WriteData(0x2f); WriteData(0x28); WriteData(0x20); WriteData(0x22);
            WriteData(0x1f); WriteData(0x1b); WriteData(0x23); WriteData(0x37);
            WriteData(0x00); WriteData(0x07); WriteData(0x02); WriteData(0x10);
            WriteCommand(GMCTRN1);
            WriteData(0x0f); WriteData(0x1b); WriteData(0x0f); WriteData(0x17);
            WriteData(0x33); WriteData(0x2c); WriteData(0x29); WriteData(0x2e);
            WriteData(0x30); WriteData(0x30); WriteData(0x39); WriteData(0x3f);
            WriteData(0x00); WriteData(0x07); WriteData(0x03); WriteData(0x10);

            WriteCommand(CASET);
            WriteData(0x00); WriteData(0x00); WriteData(0x00); WriteData(0x7f);
            WriteCommand(RASET);
            WriteData(0x00); WriteData(0x00); WriteData(0x00); WriteData(0x9f);

            WriteCommand(0xf0); // Enable test command
            WriteData(0x01);
            WriteCommand(0xf6); // Disable ram power save mode
            WriteData(0x00);
            WriteCommand(COLMOD); // 65k mode
            WriteData(0x05);
            WriteCommand(DISPON); // Display on
        }

        public bool Backlight
        {
            get { return _backlightIo.Read(); }
            set { _backlightIo.Write(value); }
        }

        public async void Reset()
        {
            _resetIo.Write(false);
            await Task.Delay(150);
            _resetIo.Write(true);
        }

        private void WriteCommand(byte command)
        {
            _data[0] = command;
            _cdIo.Write(false);
            _spiDevice.Write(_data);
        }

        private void WriteData(byte[] data)
        {
            _cdIo.Write(true);
            _spiDevice.Write(data);
        }

        private void WriteData(byte data)
        {
            _data[0] = data;
            WriteData(_data);
        }

        private void WriteData(ushort[] data)
        {
            _cdIo.Write(true);
            foreach (var us in data)
            {
            _spiDevice.Write(BitConverter.GetBytes(us));
                
            }
        }

        public void Clear()
        {
            var data = new byte[64 * 80 * 2];
            DrawRaw(data, 0, 0, 64, 80);
            DrawRaw(data, 64, 0, 64, 80);
            DrawRaw(data, 0, 80, 64, 80);
            DrawRaw(data, 64, 80, 64, 80);
        }

        public void DrawRaw(byte[] data, int x, int y, int width, int height)
        {
            SetClippingArea(x,y,width-1,height-1);
            WriteCommand(RAMWR);
            WriteData(data);
        }

        private void SetClippingArea(int x, int y, int width, int height)
        {
            _ushortData[0] = (ushort)x;
            _ushortData[1] = (ushort)(x + width);
            WriteCommand(CASET);
            WriteData(_ushortData);
            _ushortData[0] = (ushort)y;
            _ushortData[1] = (ushort)(y + height);
            WriteCommand(RASET);
            WriteData(_ushortData);
        }

        public void DrawPixel(int x, int y, byte r, byte g, byte b)
        {
            SetClippingArea(x,y,0,0);
            WriteCommand(RAMWR);
            WriteData(r);
            WriteData(g);
            WriteData(b);
        }
    }
}


Hi,
did you see this turorial?
https://developer.microsoft.com/en-us/windows/iot/samples/spidisplay

Do you have an oscilloscope or logicanalyser to see what happens at the pins?

I try to do simplest thing first: I only use Normal format. Format is in ConfigureDisplay after mode address comment.

@ RoSchmi - No I don’t have. I can be very easier.

I test communication with:


        public void ReadDisplayStatus()
        {
            _data[0] = RDDST;
            var readBuffer = new byte[4];
            _cdIo.Write(false);
            _spiDevice.Write(_data);
            _cdIo.Write(true);
            _spiDevice.Read(readBuffer);
        }

readbuffer is always containing 0. So I suspect a wrong communication with spi … But without analyzer it’s difficult to understand what’s happened !

@ Bauland - do you know if there are pull up resistors on CLCK, MOSI, MISO CS? If there are no pull-ups the signal might not be able to go high. I would try to apply a 10 KOhm resistor from 3.3 V power to each of the SPI datalines

Well, I finished to find an oscillo.
I try to send a command to read display power mode (0x0a).
Then I read and I should received 0x1c.

I obtain the image below on oscilloscope.
Three observations:
[ol] write command is correctly send,
read signal is too low to be read but it exists,
value is not what I expect[/ol]
Reason can be that display use MOSI line to send data. Indeed MISO is not connected if I correctly read n18display schematic.
So I suspect it’s not a real spi communication (or just to send command from mainboard to display).

@ Bauland - great :clap: :clap: I would now doublecheck with the oscilloscope that RS and CS have the right polarity. And doublecheck that the correct commands are sent in the configureDisplay() method.
Yes, it seems that only the spi write function is implemented.

Edit: Perhaps it can help to Debug.Print the sent spi commands and data to compare wheter they are the same on the NETMF and the Raspberry side.

What build are you running of Win10 IoT? I don’t know anything but was interested - and like I needed something else to distract me (oooh, shiny!) I might be able to squeeze in some time to run up a check if you needed more eyes looking.

Looking at the scope it shows that the data is being set on the rising edge of the clock. Is this the correct mode for the display?

If you look at the reply, it appears that the display is clocking out on the falling edge. It’s not 100% clear so maybe you can check this? This would be correct if the polarity is right as one of the modes is to clock in on rising and out on falling.

SPI has 2 modes for the clock. Try changing to the other one and see if the device responds correctly.

1 Like

Tests have been made with the lastest official version.
I’m trying now 14376 version from insider and try to enable Lightning driver (Perhaps it could increase speed).
Monday I should receive an analyzer (it would be more easier to read all 5 signal (clk, mosi, miso, cd, cs).

I’ve test all four modes and indeed, 2 of them seems close. From datasheet if I read correctly, it corresponds to mode 0. I will more test with analyzer it could be more accurate.

It’s indeed easier with analyzer.
I’ve take a spider motherboard, connect the N18Display module and see what happened (I have just modifier spi frequency to see more accuratly what happened).

Picture seems confirm that spi mode is mode0.

Now I must try to create same data on spi with Raspberry pi. I think I would begin without FEZ Cream.

1 Like

I have succeeded to clear screen !!! ;D
Even to draw a blue pixel … But I must dive in datasheet to understand color bytes.

Mistake was in that function:


        private void WriteData(ushort[] data)
        {
            _cdIo.Write(true);
            foreach (var us in data)
            {
                _spiDevice.Write(BitConverter.GetBytes(us).Reverse().ToArray());
            }
        }

In didn’t have reversed order of byte so coordinates was incorrect. But without analyzer is would be difficult to find it !!!

Please, can you explain a little bit more what you mean. Is there a difference between NETMF and Ras Pi how bytes are sent over SPI MOSI, LSB first or MSB first? Or what do you mean?

It concerns just sending ushort. If ushort contains 0x003f, Bitconverter.getbytes seems convert it to 0x3f, 0x00. So if I reverse order, all was ok: 0x00, 0x3f. I’m using FEZ Cream finally so it’s NetMF.

1 Like

Ah, that is the ENDIAN rule. Each micro and/or OS has it’s own way of storing the data, little endian or big endian. In your case, it was the BYTE order that was wrong. Well spotted and you are correct, a logic analyser is something software engineers working on embedded designs should not be without :slight_smile:

Progress have been done … See picture !


        private void _button_Pressed(Button sender, object args)
        {
            _bLed = !_bLed;
            _button.SetLed(_bLed);
            N18Font font = new N18Font();
            _display.DrawText("Hello World !", font, 10, 10, 0xff, 0xff, 0x00);
            _display.DrawRect(50, 50, 25, 25, 0xff, 0xff, 0x00);
            _display.DrawRect(100, 50, 25, 25, 0xff, 0x00, 0xff);
            _display.DrawRect(50, 100, 25, 25, 0x00, 0xff, 0xff);
            _display.DrawRect(100, 100, 25, 25, 0xff, 0xff, 0xff);
        }

3 Likes

Driver goes on ! What is missing:
[ul]display a bitmap,
display line,
display empty rectangle,
display circle.
[/ul]

In codeshare: [url]https://www.ghielectronics.com/community/codeshare/entry/1093[/url]
As codeshare has disappeared (hidden should I say), here is the code:


using System;
using System.Linq;
using System.Threading.Tasks;
using Windows.Devices.Spi;
using GHIElectronics.UWP.GadgeteerCore;
using GHIElectronics.UWP.GadgeteerCore.SocketInterfaces;
using SpiDevice = GHIElectronics.UWP.GadgeteerCore.SocketInterfaces.SpiDevice;

//using SpiDevice = GHIElectronics.UWP.GadgeteerCore.SocketInterfaces.SpiDevice;

namespace Bauland
{
    /// <summary>
    /// Class to manage N18display
    /// </summary>
    public class N18Display : Module
    {
        /// <summary>
        /// Module's name
        /// </summary>
        public override string Name => "N18Display";

        /// <summary>
        /// Manufacturer's module
        /// </summary>
        public override string Manufacturer => "GHI";

        private SpiDevice _spiDevice;
        private DigitalIO _resetIo; // Hardware reset
        private DigitalIO _backlightIo; // backlight control
        private DigitalIO _cdIo; // command: false, data: true
        private byte[] _data;
        private ushort[] _ushortData;
        private DisplayOrientation _orientation;
        private bool _isBgr;

        /// <summary>
        /// Get or set display orientation. Screen is not clear, so previous text remains on screen.
        /// </summary>
        public DisplayOrientation Orientation
        {
            get { return _orientation; }
            set
            {
                _orientation = value;
                SetFormat();
            }
        }

        /// <summary>
        /// If channel red and blue are swaped for this display, then call one.
        /// </summary>
        public void SwapRedBlueChannels()
        {
            _isBgr = !_isBgr;
            SetFormat();
        }

        private void SetFormat()
        {
            var bgr = (_isBgr ? MADCTL_BGR : 0);
            WriteCommand(MADCTL);
            switch (_orientation)
            {
                case DisplayOrientation.Normal:
                    WriteData((byte)(MADCTL_MX | MADCTL_MY | bgr));
                    break;
                case DisplayOrientation.Cw90:
                    WriteData((byte)(MADCTL_MV | MADCTL_MY | bgr));
                    break;
                case DisplayOrientation.UpsideDown:
                    WriteData((byte)(bgr));
                    break;
                case DisplayOrientation.Ccw90:
                    WriteData((byte)(MADCTL_MV | MADCTL_MX | bgr));
                    break;
            }
        }

        #region Command for ST7735

        private byte CASET = 0x2A;
        private byte RASET = 0x2B;
        private byte RAMWR = 0x2C;
        private byte DISPON = 0x29;
        private byte SLPOUT = 0x11;
        private byte FRMCTR1 = 0xB1;
        private byte FRMCTR2 = 0xB2;
        private byte FRMCTR3 = 0xB3;
        private byte INVCTR = 0xB4;
        private byte PWCTR1 = 0xC0;
        private byte PWCTR2 = 0xC1;
        private byte PWCTR3 = 0xC2;
        private byte PWCTR4 = 0xC3;
        private byte PWCTR5 = 0xC4;
        private byte VMCTR1 = 0xC5;
        private byte GMCTRP1 = 0xE0;
        private byte GMCTRN1 = 0xE1;
        private byte COLMOD = 0x3A;
        private byte MADCTL = 0x36;
        private byte MADCTL_BGR = 0x08;
        private byte MADCTL_MX = 0x40;
        private byte MADCTL_MY = 0x80;
        private byte MADCTL_MV = 0x20;

        public N18Display()
        {
            _isBgr = true;
        }

        #endregion

        /// <summary>
        /// Initialize module and connect to mainboard
        /// </summary>
        /// <param name="parentSocket">Socket of mainboard on which module is connected</param>
        /// <returns>Task</returns>
        protected override async Task Initialize(ISocket parentSocket)
        {
            parentSocket.EnsureTypeIsSupported(SocketType.S);
            _data = null;
            _spiDevice =
                await
                    parentSocket.CreateSpiDeviceAsync(new SpiConnectionSettings(0)
                    {
                        Mode = SpiMode.Mode0,
                        ClockFrequency = 10000000,
                    });
            _resetIo = await parentSocket.CreateDigitalIOAsync(SocketPinNumber.Three, false);
            _backlightIo = await parentSocket.CreateDigitalIOAsync(SocketPinNumber.Four, true);
            _cdIo = await parentSocket.CreateDigitalIOAsync(SocketPinNumber.Five, false);
            _data = new byte[1];
            _ushortData = new ushort[2];
            await HardReset(); // Hard reset
        }

        /// <summary>
        /// Hard reset display.
        /// </summary>
        /// <returns>Task object</returns>
        public async Task HardReset()
        {
            await Reset();
            await ConfigureDisplay();
            Clear();
        }

        private async Task ConfigureDisplay()
        {
            // ST7735R or ST7735 ???
            WriteCommand(SLPOUT); // Exit sleep mode
            await Task.Delay(120);
            WriteCommand(FRMCTR1);
            WriteData(0x01);
            WriteData(0x2C);
            WriteData(0x2D);
            WriteCommand(FRMCTR2);
            WriteData(0x01);
            WriteData(0x2C);
            WriteData(0x2D);
            WriteCommand(FRMCTR3);
            WriteData(0x01);
            WriteData(0x2C);
            WriteData(0x2D);
            WriteData(0x01);
            WriteData(0x2C);
            WriteData(0x2D);
            WriteCommand(INVCTR); // Column inversion
            WriteData(0x07);

            // Power Sequence
            WriteCommand(PWCTR1);
            WriteData(0xA2);
            WriteData(0x02);
            WriteData(0x84);
            WriteCommand(PWCTR2);
            WriteData(0xc5);
            WriteCommand(PWCTR3);
            WriteData(0x0a);
            WriteData(0x00);
            WriteCommand(PWCTR4);
            WriteData(0x8a);
            WriteData(0x2a);
            WriteCommand(PWCTR5);
            WriteData(0x8a);
            WriteData(0xee);

            // VCOM
            WriteCommand(VMCTR1);
            WriteData(0x0e);

            // Mode address
            SetFormat();

            // Gamma Sequence
            WriteCommand(GMCTRP1);
            WriteData(0x0f);
            WriteData(0x1a);
            WriteData(0x0f);
            WriteData(0x18);
            WriteData(0x2f);
            WriteData(0x28);
            WriteData(0x20);
            WriteData(0x22);
            WriteData(0x1f);
            WriteData(0x1b);
            WriteData(0x23);
            WriteData(0x37);
            WriteData(0x00);
            WriteData(0x07);
            WriteData(0x02);
            WriteData(0x10);
            WriteCommand(GMCTRN1);
            WriteData(0x0f);
            WriteData(0x1b);
            WriteData(0x0f);
            WriteData(0x17);
            WriteData(0x33);
            WriteData(0x2c);
            WriteData(0x29);
            WriteData(0x2e);
            WriteData(0x30);
            WriteData(0x30);
            WriteData(0x39);
            WriteData(0x3f);
            WriteData(0x00);
            WriteData(0x07);
            WriteData(0x03);
            WriteData(0x10);

            WriteCommand(CASET);
            WriteData(0x00);
            WriteData(0x00);
            WriteData(0x00);
            WriteData(0x7f);
            WriteCommand(RASET);
            WriteData(0x00);
            WriteData(0x00);
            WriteData(0x00);
            WriteData(0x9f);

            //WriteCommand(0xf0); // Enable test command
            //WriteData(0x01);
            //WriteCommand(0xf6); // Disable ram power save mode
            //WriteData(0x00);
            WriteCommand(COLMOD); // 65k mode
            WriteData(0x55);
            WriteCommand(DISPON); // Display on
        }

        /// <summary>
        /// Get or set backlight status. Must be true to see what is displayed.
        /// </summary>
        public bool Backlight
        {
            get { return _backlightIo.Read(); }
            set { _backlightIo.Write(value); }
        }

        /// <summary>
        /// Soft reset
        /// </summary>
        /// <returns>Task Object</returns>
        public async Task Reset()
        {
            _resetIo.Write(false);
            await Task.Delay(150);
            _resetIo.Write(true);
        }

        private void WriteCommand(byte command)
        {
            _data[0] = command;
            _cdIo.Write(false);
            _spiDevice.Write(_data);
        }

        private void WriteData(byte[] data)
        {
            _cdIo.Write(true);
            _spiDevice.Write(data);
        }

        private void WriteData(byte data)
        {
            _data[0] = data;
            WriteData(_data);
        }

        private void WriteData(ushort[] data)
        {
            _cdIo.Write(true);
            foreach (var us in data)
            {
                _spiDevice.Write(BitConverter.GetBytes(us).Reverse().ToArray());
            }
        }

        /// <summary>
        /// Clear Display
        /// </summary>
        public void Clear()
        {
            var data = new byte[64 * 80 * 2];
            DrawRaw(data, 0, 0, 64, 80);
            DrawRaw(data, 64, 0, 64, 80);
            DrawRaw(data, 0, 80, 64, 80);
            DrawRaw(data, 64, 80, 64, 80);
        }

        private void DrawRaw(byte[] data, int x, int y, int width, int height)
        {
            SetClippingArea(x, y, width - 1, height - 1);
            WriteCommand(RAMWR);
            WriteData(data);
        }

        private void SetClippingArea(int x, int y, int width, int height)
        {
            _ushortData[0] = (ushort)x;
            _ushortData[1] = (ushort)(x + width);
            WriteCommand(CASET);
            WriteData(_ushortData);
            _ushortData[0] = (ushort)y;
            _ushortData[1] = (ushort)(y + height);
            WriteCommand(RASET);
            WriteData(_ushortData);
        }

        /// <summary>
        /// Draw a pixel on display
        /// </summary>
        /// <param name="x">x coordinate of pixel</param>
        /// <param name="y">y coordinate of pixel</param>
        /// <param name="color">color of pixel</param>
        public void DrawPixel(int x, int y, Color color)
        {
            byte[] a = color.To565ByteArray();
            DrawRaw(a, x, y, 1, 1);
        }

        /// <summary>
        /// Draw a pixel on display
        /// </summary>
        /// <param name="x">x coordinate of pixel</param>
        /// <param name="y">y coordinate of pixel</param>
        /// <param name="r">red channel value</param>
        /// <param name="g">green channel value</param>
        /// <param name="b">blue channel value</param>
        public void DrawPixel(int x, int y, byte r, byte g, byte b)
        {
            byte[] a = ToRGB565(r, g, b);
            DrawRaw(a, x, y, 1, 1);
        }

        /// <summary>
        /// Draw a fill rectangle on display
        /// </summary>
        /// <param name="x">x coordinate of pixel</param>
        /// <param name="y">y coordinate of pixel</param>
        /// <param name="width">width of rectangle</param>
        /// <param name="height">height of rectangle</param>
        /// <param name="color">color of rectangle</param>
        public void DrawFillRect(int x, int y, int width, int height, Color color)
        {
            // Prepare data
            byte[] data = new byte[width * height * 2];
            byte[] a = color.To565ByteArray();
            for (int i = 0; i < width * height; i++)
            {
                data[i * 2] = a[0];
                data[i * 2 + 1] = a[1];
            }
            DrawRaw(data, x, y, width, height);
        }

        /// <summary>
        /// Draw a fill rectangle on display
        /// </summary>
        /// <param name="x">x coordinate of pixel</param>
        /// <param name="y">y coordinate of pixel</param>
        /// <param name="width">width of rectangle</param>
        /// <param name="height">height of rectangle</param>
        /// <param name="r">red channel value</param>
        /// <param name="g">green channel value</param>
        /// <param name="b">blue channel value</param>
        public void DrawFillRect(int x, int y, int width, int height, byte r, byte g, byte b)
        {
            // Prepare data
            byte[] data = new byte[width * height * 2];
            byte[] a = ToRGB565(r, g, b);
            for (int i = 0; i < width * height; i++)
            {
                data[i * 2] = a[0];
                data[i * 2 + 1] = a[1];
            }
            DrawRaw(data, x, y, width, height);
        }

        private byte[] ToRGB565(byte r, byte g, byte b)
        {
            byte[] arrayBytes = new byte[2];
            r = (byte)(r & 0x1f);
            g = (byte)(g & 0x3f);
            b = (byte)(b & 0x1f);

            arrayBytes[0] = (byte)((b << 3) + (g >> 3));
            arrayBytes[1] = (byte)((g << 5) + r);
            return arrayBytes;
        }

        /// <summary>
        /// Draw a single character on display
        /// </summary>
        /// <param name="c">Character to display</param>
        /// <param name="font">Font used to display character</param>
        /// <param name="x">x coordinate on screen</param>
        /// <param name="y">y coordinate on screen</param>
        /// <param name="color">Color of character</param>
        public void DrawChar(char c, Font font, int x, int y, Color color)
        {
            var letter = font.GetLetter(c);
            int height = 8;
            int width = letter.Length;
            byte[] abcolor = color.To565ByteArray();
            byte[] data = new byte[height * width * 2];
            byte mask = 0x01;
            bool bit;
            for (int j = 0; j < 8; j++)
            {
                for (int i = 0; i < letter.Length; i++)
                {
                    bit = (letter[i] & mask) == mask;
                    if (bit)
                    {
                        data[2 * (i + j * letter.Length)] = abcolor[0];
                        data[2 * (i + j * letter.Length) + 1] = abcolor[1];
                    }
                }
                mask = (byte)(mask << 1);
            }
            DrawRaw(data, x, y, width, height);
        }

        /// <summary>
        /// Draw a single character on display
        /// </summary>
        /// <param name="c">Character to display</param>
        /// <param name="font">Font used to display character</param>
        /// <param name="x">x coordinate on screen</param>
        /// <param name="y">y coordinate on screen</param>
        /// <param name="r">red channel value</param>
        /// <param name="g">green channel value</param>
        /// <param name="b">blue channel value</param>
        public void DrawChar(char c, Font font, int x, int y, byte r, byte g, byte b)
        {
            var letter = font.GetLetter(c);
            int height = 8;
            int width = letter.Length;
            byte[] color = ToRGB565(r, g, b);
            byte[] data = new byte[height * width * 2];
            byte mask = 0x01;
            bool bit;
            for (int j = 0; j < 8; j++)
            {
                for (int i = 0; i < letter.Length; i++)
                {
                    bit = (letter[i] & mask) == mask;
                    if (bit)
                    {
                        data[2 * (i + j * letter.Length)] = color[0];
                        data[2 * (i + j * letter.Length) + 1] = color[1];
                    }
                }
                mask = (byte)(mask << 1);
            }
            DrawRaw(data, x, y, width, height);
        }

        /// <summary>
        /// Draw a text on display
        /// </summary>
        /// <param name="text">Text to display</param>
        /// <param name="font">Font used to display text</param>
        /// <param name="x">x coordinate on screen</param>
        /// <param name="y">y coordinate on screen</param>
        /// <param name="color">Color of text to display</param>
        public void DrawText(string text, Font font, int x, int y, Color color)
        {
            int fontWidth = font.Width;
            int index = 0;
            foreach (char c in text)
            {
                DrawChar(c, font, x + index * (fontWidth + 1), y, color);
                index++;
            }
        }

        /// <summary>
        /// Draw a text on display
        /// </summary>
        /// <param name="text">Text to display</param>
        /// <param name="font">Font used to display text</param>
        /// <param name="x">x coordinate on screen</param>
        /// <param name="y">y coordinate on screen</param>
        /// <param name="r">red channel value</param>
        /// <param name="g">green channel value</param>
        /// <param name="b">blue channel value</param>
        public void DrawText(string text, Font font, int x, int y, byte r, byte g, byte b)
        {
            int fontWidth = font.Width;
            int index = 0;
            foreach (char c in text)
            {
                DrawChar(c, font, x + index * (fontWidth + 1), y, r, g, b);
                index++;
            }
        }

        /// <summary>
        /// Orientation value of screen
        /// </summary>
        public enum DisplayOrientation
        {
            Normal,
            Cw90,
            UpsideDown,
            Ccw90
        }
    }
}

3 Likes