I have purchased a Feather OLED 128x32 display which uses the SSD1306 chip. Here is a “simplified” simplified driver for using this:
public class SSD1306OLEDDriver : BufferDrawTargetVerticalByteStrip1Bpp
{
private readonly byte[] _data;
private readonly byte[] _command = new byte[2];
private readonly Timer _refreshBurnoutTimer;
private readonly I2cDevice i2c;
public IntPtr Hdc { get; }
public DateTime _lastUpdated { get; private set; }
/// <summary>
/// Constructor for SSD1306 based display (Supports 128x64 and 128x32 displays).
/// </summary>
/// <param name="controller">I2cController</param>
/// <param name="width">Width of the display</param>
/// <param name="height">Height of device</param>
public SSD1306OLEDDriver(I2cController controller, int width = 128, int height = 32) : base(width, height)
{
_data = new byte[((width * height) / 8) + 1];
_data[0] = 0x40;
Hdc = GraphicsManager.RegisterDrawTarget(this);
i2c = controller.GetDevice(new I2cConnectionSettings(0x3C)
{
AddressFormat = I2cAddressFormat.SevenBit,
BusSpeed = I2cBusSpeed.FastMode,
});
Initialize();
_lastUpdated = DateTime.Now;
//This timer toggles the screen after a period of time to prevent pixel burnout
_refreshBurnoutTimer = new Timer((c) =>
{
if (DateTime.Now > _lastUpdated.AddMinutes(15))
{
SendCommand(0xAE);
Thread.Sleep(100);
SendCommand(0xAF);
_lastUpdated = DateTime.Now;
}
}, null, 60 * 1000, 5000);
}
private void Initialize()
{
Thread.Sleep(1500); //Allow display to power up
SendCommand(0xAE); //turn off OLED panel
SendCommand(0xD5); //set display clock divide ratio/oscillator frequency
SendCommand(0x80);
SendCommand(0xA6);
SendCommand((byte)(Height - 1));
SendCommand(0xD3);
SendCommand(0x00);
SendCommand(0x40 | 0x00);
SendCommand(0x8D);
SendCommand(0x14);
SendCommand(0x20);
SendCommand(0x00);
SendCommand(0xA0 | 0x1);
SendCommand(0xC8);
SendCommand(0xDA);
SendCommand(0x02);
SendCommand(0x81);
SendCommand(0x8F);
SendCommand(0xD9);
SendCommand(0xF1);
SendCommand(0xDB);
SendCommand(0x40);
SendCommand(0xA4);
SendCommand(0xA6);
SendCommand(0x2E);
SendCommand(0xAF);
SendCommand(0x20);
SendCommand(0x00);
SendCommand(0x21);
SendCommand(0);
SendCommand(128 - 1);
SendCommand(0x22);
SendCommand(0);
SendCommand(7);
}
private void SendCommand(byte cmd)
{
_command[1] = cmd;
i2c.Write(_command);
}
public override void Dispose()
{
base.Dispose();
i2c.Dispose();
_refreshBurnoutTimer?.Dispose();
}
/// <summary>
/// Writes the data to the OLED display
/// </summary>
public override void Flush()
{
base.Flush();
//Clears the data buffer used to write to the display
Array.Clear(_data, 1, _data.Length - 1);
//Writes the cleared buffer to the display
i2c.Write(_data);
//Copy the underlying buffer used by Graphics frame
Array.Copy(buffer, 0, _data, 1, _data.Length - 1);
//Writes the buffer to the display
i2c.Write(_data);
//Clearing both buffers
Array.Clear(_data, 1, _data.Length - 1);
Array.Clear(buffer, 0, buffer.Length);
_lastUpdated = DateTime.Now;
}
public void Clear()
{
Array.Clear(buffer, 0, buffer.Length);
Flush();
}
}
Here is a sample code for using such:
class Program
{
static void Main()
{
var adcController = GHIElectronics.TinyCLR.Devices.Adc.AdcController.FromName(STM32F4.AdcChannel.Id);
var battery = adcController.OpenChannel(STM32F4.AdcChannel.Channel3);
var i2cController = I2cController.FromName(STM32F4.I2cBus.I2c1);
SSD1306OLEDDriver screen = new SSD1306OLEDDriver(i2cController);
var graphics = Graphics.FromHdc(screen.Hdc);
var brush = new SolidBrush(Color.White);
var font = new Font("GHIMono8x5", 8);
StringBuilder sb = new StringBuilder();
while (true)
{
graphics.Clear(Color.Black);
graphics.Flush();
var b = (battery.ReadValue() * 2 * 3.3) / 4096;
var pow = b.ToString("F2");
sb.AppendLine("LYNK BOT");
sb.AppendLine($"DATE: {DateTime.Now.ToString("hh:mm:ss")}");
sb.AppendLine($"BATTERY: {pow}v");
graphics.DrawString(sb.ToString(), font, brush, 28, screen.Height / 2 - 16);
graphics.DrawEllipse(new Pen(Color.White), 0, screen.Height / 2 - 16, 24, 24);
graphics.DrawEllipse(new Pen(Color.White), 0, screen.Height / 2 - 16, 20, 20);
graphics.Flush();
Thread.Sleep(5000);
sb.Clear();
}
}
}
Add the following Nuget packages
- GHIElectronics.TinyCLR.Devices.I2c
- GHIElectronics.TinyCLR.Drawing
- GHIElectronics.TinyCLR.Devices.Adc