Using the SSD1306 I2C Display

To use an SSD1306 I2C Display with Endpoint I have ported the TinyCLR driver (below).

As this display is 1Bpp you will need the TinyCLR BasicGraphics driver, and port it to

namespace GHIElectronics.Endpoint.Drivers.BasicGraphics;

BasicGraphics does not need any modification other than updating the namespace.

SSD1306.cs

using GHIElectronics.Endpoint.Core;
using System.Device.I2c;

namespace GHIElectronics.Endpoint.Drivers.SolomonSystech.SSD1306
{
    public class SSD1306Controller
    {
        private readonly byte[] vram = new byte[128 * 64 / 8 + 1];
        private readonly byte[] buffer2 = new byte[2];
        private readonly I2cDevice i2cDev;

        public int Width => 128;
        public int Height => 64;

        public SSD1306Controller(int i2cBus)
        {
            this.vram[0] = 0x40;

            EPM815.I2c.Initialize(i2cBus)
            I2cConnectionSettings i2cConnectionSetting = new I2cConnectionSettings(i2cBus, 0x3C);
            i2cDev = I2cDevice.Create(i2cConnectionSetting);

            this.Initialize();
        }

        private void Initialize()
        {
            this.SendCommand(0xAE); //turn off oled panel
            this.SendCommand(0x00); //set low column address
            this.SendCommand(0x10); //set high column address
            this.SendCommand(0x40); //set start line address
            this.SendCommand(0x81); //set contrast control register
            this.SendCommand(0xCF);
            this.SendCommand(0xA1); //set segment re-map 95 to 0
            this.SendCommand(0xA6); //set normal display
            this.SendCommand(0xA8); //set multiplex ratio(1 to 64)
            this.SendCommand(0x3F); //1/64 duty
            this.SendCommand(0xD3); //set display offset
            this.SendCommand(0x00); //not offset
            this.SendCommand(0xD5); //set display clock divide ratio/oscillator frequency
            this.SendCommand(0x80); //set divide ratio
            this.SendCommand(0xD9); //set pre-charge period
            this.SendCommand(0xF1);
            this.SendCommand(0xDA); //set com pins hardware configuration
            this.SendCommand(0x12);
            this.SendCommand(0xDB); //set vcomh
            this.SendCommand(0x40); //set startline 0x0
            this.SendCommand(0x8D); //set Charge Pump enable/disable
            this.SendCommand(0x14); //set(0x10) disable
            this.SendCommand(0xAF); //turn on oled panel
            this.SendCommand(0xC8); //mirror the screen

            // Mapping
            this.SendCommand(0x20);
            this.SendCommand(0x00);
            this.SendCommand(0x21);
            this.SendCommand(0);
            this.SendCommand(128 - 1);
            this.SendCommand(0x22);
            this.SendCommand(0);
            this.SendCommand(7);
        }

        public void Dispose() => this.i2cDev.Dispose();

        private void SendCommand(byte cmd)
        {
            this.buffer2[1] = cmd;
            this.i2cDev.Write(this.buffer2);
        }

        public void SetColorFormat(bool invert) => this.SendCommand((byte)(invert ? 0xA7 : 0xA6));

        //public void SetPixel(int x, int y, bool color) {
        //    if (x < 0 || y < 0 || x >= this.Width || y >= this.Height) return;

        //    var index = (y / 8) * this.Width + x;

        //    if (color) {
        //        this.vram[1 + index] |= (byte)(1 << (y % 8));
        //    }
        //    else {
        //        this.vram[1 + index] &= (byte)(~(1 << (y % 8)));
        //    }
        //}

        //public void DrawBuffer(byte[] buffer) {
        //    var x = 0;
        //    var y = 0;

        //    for (var i = 0; i < buffer.Length; i += 2) {
        //        var color = (buffer[i + 1] << 8) | (buffer[i]);

        //        if (color == 0) {
        //            this.SetPixel(x++, y, false);
        //        }
        //        else {
        //            this.SetPixel(x++, y, true);
        //        }

        //        if (x == this.Width) {
        //            x = 0;
        //            y++;
        //        }
        //    }



        //    this.i2c.Write(this.vram);
        //}

        public void DrawBufferNative(byte[] buffer) => this.DrawBufferNative(buffer, 0, buffer.Length);

        public void DrawBufferNative(byte[] buffer, int offset, int count)
        {
            Array.Copy(buffer, offset, this.vram, 1, count);

            this.i2cDev.Write(this.vram);
        }
    }
}

Example:

using GHIElectronics.Endpoint.Core;
using GHIElectronics.Endpoint.Drivers.BasicGraphics;
using GHIElectronics.Endpoint.Drivers.SolomonSystech.SSD1306;

namespace TinyCLROLED
{
    internal class Program
    {
        private static SSD1306Controller OLED;
        private const int SCREEN_WIDTH = 128;
        private const int SCREEN_HEIGHT = 64;
        static void Main()
        {

            OLED = new SSD1306Controller(EPM815.I2c.I2c6);

            var basicGfx = new BasicGraphics(SCREEN_WIDTH, SCREEN_HEIGHT, ColorFormat.OneBpp);
            var colorWhite = BasicGraphics.ColorFromRgb(255, 255, 255);

            basicGfx.Clear();

            basicGfx.DrawString("Hello world!", colorWhite, 5, 5);
            basicGfx.DrawString("2nd line.", colorWhite, 5, 20);
            basicGfx.DrawTinyString("3rd line.", colorWhite, 5, 35);

            OLED.DrawBufferNative(basicGfx.Buffer);
            Thread.Sleep(1);
        }

    }
}
7 Likes