上次在我的树莓派上接入了达特的甲醛传感器,可以把测的甲醛数据上传到服务器上。但是数据还是不够直观,想看数据还得依赖网络、依赖web页面。所以后来买了一块0.9英寸的OLED屏幕,接到了树莓派上,分辨率是128x64,也用了一段时间了,基本满足需求,能够实时显示浓度、时间、网络状态。所以这次分享下用.NET CORE如何驱动一块OLED 屏幕。

准备

  1. 杜邦线若干
  2. 一块OLED屏幕,驱动芯片是SSD1306
  3. 一个开启了GPIO的树莓派,需要支持I2C。如何开启,可参考之前博文。

接线

树莓派的引脚图可以参考之前的博文或者网上自行搜索。

接线比较简单:

  1. oled的vcc接树莓派的3.3v
  2. GND 接树莓派的GND
  3. SCL接树莓派的SCL
  4. SDA接树莓派的SDA

调试

输入sudo i2cdetect -y 1检查树莓派能否识别到I2C设备,正常的话,会看到如下的结果:

【.NET IoT】树莓派Raspberry Pi 3B+ 连接oled屏幕,SSD1306驱动_ssd1306
3c就是I2C设备的地址。

然后新建控制台项目,添加nuget包System.Device.Gpio
【.NET IoT】树莓派Raspberry Pi 3B+ 连接oled屏幕,SSD1306驱动_c#_02

写代码来创建I2C设备对象:

_i2c = I2cDevice.Create(new I2cConnectionSettings(1, 0x3c));//创建i2c设备,第一个参数1是busId,
if (_i2c == null)
{
   ConsoleHelper.Error("i2c创建失败");
   return;
}

I2cConnectionSettings的第一个参数是busId,树莓派3B+就是1。第二个参数是设备的地址,在上面的图中我们看到地址是0x3C

接下来就向I2C设备写数据了,直接调用_i2c.Write(data)方法即可。

SSD1306驱动芯片

简单来说这个芯片是来驱动屏幕,让屏幕显示出各种文字和图案。我们的程序只需要和这个芯片打交道即可,芯片定义了调用_i2c.Write(data)方法时入参data的格式。SSD1306支持的点阵是128x64,即128列,64行。64行有被分成了8份,叫做8个page。每个page在每一列上有8个像素点。所以向芯片写数据时,数据的基本单位就是这8个像素点的数据,xi将这8个像素点的数据绘制到屏幕上。因为SSD1306不带字库,理解芯片的绘制方式对于以后自定义显示文字和图像很重要。

驱动代码如下:

    public class SSD1306
    {
        private I2cDevice _i2c;

        #region 参数

        private const int SCREEN_WIDTH_PX = 128;
        private const int SCREEN_HEIGHT_PX = 64;
        private const int SCREEN_HEIGHT_PAGES = SCREEN_HEIGHT_PX / 8;//每页是8个像素,计算一共有几页
        private byte[,] _displayBuffer = new byte[SCREEN_WIDTH_PX, SCREEN_HEIGHT_PAGES];

        /// <summary>
        /// 关闭屏幕
        /// </summary>
        private static readonly byte[] CMD_DISPLAY_OFF = { 0xAE };

        /// <summary>
        /// 开启屏幕
        /// </summary>
        private static readonly byte[] CMD_DISPLAY_ON = { 0xAF };

        /// <summary>
        /// 供电
        /// </summary>
        private static readonly byte[] CMD_CHARGEPUMP_ON = { 0x8D, 0x14 };

        /// <summary>
        /// 内存地址模式:见上述引用3
        /// 1.页模式:从左到右,不会自动换行
        /// 2.水平模式:从左到右,会自动换行
        /// 3.垂直模式:从上到下
        /// </summary>
        private static readonly byte[] CMD_MEMADDRMODE = { 0x20, 0x00 };

        /// <summary>
        /// Remaps the segments, which has the effect of mirroring the display horizontally
        /// </summary>
        private static readonly byte[] CMD_SEGREMAP = { 0xA1 };

        /// <summary>
        /// Set the COM scan direction to inverse, which flips the screen vertically
        /// </summary>
        private static readonly byte[] CMD_COMSCANDIR = { 0xC8 };

        /// <summary>
        /// Setup column start and end address,0x00:第一列,0x7F最后一列
        /// </summary>
        private static readonly byte[] CMD_RESETCOLADDR = { 0x21, 0x00, 0x7F };

        /// <summary>
        /// Setup page start and end address,0x00:第一页,0x07最后一页
        /// </summary>
        private static readonly byte[] CMD_RESETPAGEADDR = { 0x22, 0x00, 0x07 };

        #endregion 参数

        public SSD1306()
        {
            _i2c = I2cDevice.Create(new I2cConnectionSettings(1, 0x3c));//创建i2c设备

            if (_i2c == null)
            {
                ConsoleHelper.Error("i2c创建失败");
                return;
            }

            //初始化
            WriteCommand(CMD_CHARGEPUMP_ON);//供电
            WriteCommand(CMD_MEMADDRMODE);//内存地址;水平模式
            //Flip the display horizontally, so it's easier to read on the breadboard
            WriteCommand(CMD_SEGREMAP);
            //Flip the display vertically, so it's easier to read on the breadboard
            WriteCommand(CMD_COMSCANDIR);
            WriteCommand(CMD_DISPLAY_ON);//开启屏幕
            ConsoleHelper.Success("i2c设备init完成");
        }

        /// <summary>
        /// 输出字符串(英文)
        /// </summary>
        /// <param name="Line"></param>
        /// <param name="col"></param>
        /// <param name="row"></param>
        public void WriteLine(int row, int col, string msg)
        {
            int charWidth;
            ClearRow(row);

            foreach (char c in msg)
            {
                charWidth = WriteToDisplayBuffer(c, col, row);
                if (charWidth == 0)
                {
                    ConsoleHelper.Error($"字符:{c},超出宽高,无法显示");
                    return;
                }
                col += charWidth;
            }
            Display();
        }
        public void WriteImage(int row, int col, ImageType img)
        {
            ClearRow(row);
            WriteToDisplayBuffer(' ', col, row, img);
            Display();
        }
        /// <summary>
        /// 清空整行内容
        /// </summary>
        /// <param name="row"></param>
        private void ClearRow(int row)
        {
            int clearPage = row * DisplayFontTable.FontHeightPage;
            for (int i = 0; i < 128; i++)
            {
                _displayBuffer[i, clearPage] = 0x00;
                _displayBuffer[i, clearPage + 1] = 0x00;
            }
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="Data"></param>
        private void WriteData(byte[] Data)
        {
            byte[] commandBuffer = new byte[Data.Length + 1];
            Data.CopyTo(commandBuffer, 1);
            commandBuffer[0] = 0x40;//数据模式40开头

            _i2c.Write(commandBuffer);
        }

        /// <summary>
        /// 发送命令
        /// </summary>
        /// <param name="Command"></param>
        private void WriteCommand(byte[] Command)
        {
            byte[] commandBuffer = new byte[Command.Length + 1];
            Command.CopyTo(commandBuffer, 1);
            commandBuffer[0] = 0x00; //命令模式00开头

            _i2c.Write(commandBuffer);
        }

        /// <summary>
        /// 将buffer里的数据绘制到屏幕
        /// </summary>
        private void Display()
        {
            int index = 0;
            byte[] _serializedDisplayBuffer = new byte[SCREEN_WIDTH_PX * SCREEN_HEIGHT_PAGES];
            for (int pageY = 0; pageY < SCREEN_HEIGHT_PAGES; pageY++)
            {
                for (int pixelX = 0; pixelX < SCREEN_WIDTH_PX; pixelX++)
                {
                    _serializedDisplayBuffer[index] = _displayBuffer[pixelX, pageY];
                    index++;
                }
            }

            WriteCommand(CMD_RESETCOLADDR);
            WriteCommand(CMD_RESETPAGEADDR);
            WriteData(_serializedDisplayBuffer);
        }

        /// <summary>
        /// 把一个char写入到buffer里,并返回这个char需要占用的列数
        /// </summary>
        /// <param name="chr">字符</param>
        /// <param name="col">从第几列开始</param>
        /// <param name="row">从第几行开始</param>
        /// <returns></returns>
        private int WriteToDisplayBuffer(char chr, int col, int row, ImageType imgType = ImageType.None)
        {
            var descriptor = DisplayFontTable.GetCharacterDescriptor(chr);
            if (imgType != ImageType.None)
            {
                descriptor = DisplayFontTable.GetImageDescriptor(imgType);
            }
            int maxRowValue = (SCREEN_HEIGHT_PAGES / DisplayFontTable.FontHeightPage) - 1;
            int maxColValue = SCREEN_WIDTH_PX;
            if (row > maxRowValue)
            {
                return 0;
            }
            if ((col + descriptor.CellWidthPx + DisplayFontTable.FontCharSpacingCol) > maxColValue)
            {
                return 0;
            }

            int charDataIndex = 0;

            int startPage = row * 2;
            int endPage = startPage + descriptor.CellHeightPage;

            int startCol = col;
            int endCol = startCol + descriptor.CellWidthPx;

            int currentPage;
            int currentCol = 0;

            /* Copy the character image into the display buffer */
            for (currentPage = startPage; currentPage < endPage; currentPage++)
            {
                for (currentCol = startCol; currentCol < endCol; currentCol++)
                {
                    _displayBuffer[currentCol, currentPage] = descriptor.CellData[charDataIndex];
                    charDataIndex++;
                }
            }

            /* Pad blank spaces to the right of the character so there exists space between adjacent characters */
            for (currentPage = startPage; currentPage < endPage; currentPage++)
            {
                for (; currentCol < endCol + DisplayFontTable.FontCharSpacingCol; currentCol++)
                {
                    _displayBuffer[currentCol, currentPage] = 0x00;
                }
            }

            /* Return the number of horizontal pixels used by the character */
            return currentCol - startCol;
        }
    }

每一个符号其实都有对应的二进制数据,这些东西就是字库(字模、字母表):

namespace HomeCenter.Iot.EnvironmentSensor.Sensors
{
    internal class BaseDescriptor
    {
        public int CellWidthPx => CellData.Length / CellHeightPage;
        public int CellHeightPage { get; set; }
        public byte[] CellData { get; set; }

        public BaseDescriptor(int cellHeightPage, byte[] cellData)
        {
            CellHeightPage = cellHeightPage;
            CellData = cellData;
        }
    }

    internal class FontCharacterDescriptor : BaseDescriptor
    {
        public char Character { get; }

        public FontCharacterDescriptor(char chr, int charHeightPage, byte[] charData) : base(charHeightPage, charData)
        {
            Character = chr;
        }
    }

    internal class ImageDescriptor : BaseDescriptor
    {
        public ImageType ImgType { get; set; }

        public ImageDescriptor(ImageType imgType, int charHeightPage, byte[] charData) : base(charHeightPage, charData)
        {
            ImgType = imgType;
        }
    }

    internal static class DisplayFontTable
    {
        /// <summary>
        /// 表示一个字符占多高,2表示占两页,即2*8=16个像素
        /// </summary>
        public static readonly int FontHeightPage = 2;

        /// <summary>
        /// 字符间隔:1表示占一列
        /// </summary>
        public static readonly int FontCharSpacingCol = 1;

        /// <summary>
        /// 显示一个裂开的方框
        /// </summary>
        private static byte[] _errorImg = new byte[] 
        {
            0x00, 0x00, 0xfc, 0x04, 0x04, 0x04, 0x04, 0x04, 0x84, 0x44, 0x24, 0x14, 0x0c, 0xfc, 0x00, 0x00,
            0x00, 0x00, 0x3f, 0x30, 0x28, 0x24, 0x22, 0x21, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3f, 0x00, 0x00
        };

        public static BaseDescriptor GetCharacterDescriptor(char Chr)
        {
            foreach (FontCharacterDescriptor CharDescriptor in FontTable)
            {
                if (CharDescriptor.Character == Chr)
                {
                    return CharDescriptor;
                }
            }
            return new BaseDescriptor(FontHeightPage, _errorImg);
        }

        public static BaseDescriptor GetImageDescriptor(ImageType type)
        {
            foreach (var d in ImageTable)
            {
                if (d.ImgType == type)
                    return d;
            }
            return new BaseDescriptor(FontHeightPage, _errorImg);
        }

        /* Table with all the character data */

        private static readonly FontCharacterDescriptor[] FontTable =
        {
            new FontCharacterDescriptor(' ' ,FontHeightPage,new byte[]{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}),
            new FontCharacterDescriptor('!' ,FontHeightPage,new byte[]{0xFE,0x05}),
            new FontCharacterDescriptor('"' ,FontHeightPage,new byte[]{0x1E,0x00,0x1E,0x00,0x00,0x00}),
            new FontCharacterDescriptor('#' ,FontHeightPage,new byte[]{0x80,0x90,0xF0,0x9E,0xF0,0x9E,0x10,0x00,0x07,0x00,0x07,0x00,0x00,0x00}),
            new FontCharacterDescriptor('$' ,FontHeightPage,new byte[]{0x38,0x44,0xFE,0x44,0x98,0x02,0x04,0x0F,0x04,0x03}),
            new FontCharacterDescriptor('%' ,FontHeightPage,new byte[]{0x0C,0x12,0x12,0x8C,0x40,0x20,0x10,0x88,0x84,0x00,0x00,0x02,0x01,0x00,0x00,0x00,0x03,0x04,0x04,0x03}),
            new FontCharacterDescriptor('&' ,FontHeightPage,new byte[]{0x80,0x5C,0x22,0x62,0x9C,0x00,0x00,0x03,0x04,0x04,0x04,0x05,0x02,0x05}),
            new FontCharacterDescriptor('\'',FontHeightPage,new byte[]{0x1E,0x00}),
            new FontCharacterDescriptor('(' ,FontHeightPage,new byte[]{0xF0,0x0C,0x02,0x07,0x18,0x20}),
            new FontCharacterDescriptor(')' ,FontHeightPage,new byte[]{0x02,0x0C,0xF0,0x20,0x18,0x07}),
            new FontCharacterDescriptor('*' ,FontHeightPage,new byte[]{0x14,0x18,0x0E,0x18,0x14,0x00,0x00,0x00,0x00,0x00}),
            new FontCharacterDescriptor('+' ,FontHeightPage,new byte[]{0x40,0x40,0xF0,0x40,0x40,0x00,0x00,0x01,0x00,0x00}),
            new FontCharacterDescriptor(',' ,FontHeightPage,new byte[]{0x00,0x00,0x08,0x04}),
            new FontCharacterDescriptor('-' ,FontHeightPage,new byte[]{0x40,0x40,0x40,0x40,0x00,0x00,0x00,0x00}),
            new FontCharacterDescriptor('.' ,FontHeightPage,new byte[]{0x00,0x04}),
            new FontCharacterDescriptor('/' ,FontHeightPage,new byte[]{0x00,0x80,0x70,0x0E,0x1C,0x03,0x00,0x00}),
            new FontCharacterDescriptor('0' ,FontHeightPage,new byte[]{0xFC,0x02,0x02,0x02,0xFC,0x03,0x04,0x04,0x04,0x03}),
            new FontCharacterDescriptor('1' ,FontHeightPage,new byte[]{0x04,0x04,0xFE,0x00,0x00,0x07}),
            new FontCharacterDescriptor('2' ,FontHeightPage,new byte[]{0x0C,0x82,0x42,0x22,0x1C,0x07,0x04,0x04,0x04,0x04}),
            new FontCharacterDescriptor('3' ,FontHeightPage,new byte[]{0x04,0x02,0x22,0x22,0xDC,0x02,0x04,0x04,0x04,0x03}),
            new FontCharacterDescriptor('4' ,FontHeightPage,new byte[]{0xC0,0xA0,0x98,0x84,0xFE,0x00,0x00,0x00,0x00,0x07}),
            new FontCharacterDescriptor('5' ,FontHeightPage,new byte[]{0x7E,0x22,0x22,0x22,0xC2,0x02,0x04,0x04,0x04,0x03}),
            new FontCharacterDescriptor('6' ,FontHeightPage,new byte[]{0xFC,0x42,0x22,0x22,0xC4,0x03,0x04,0x04,0x04,0x03}),
            new FontCharacterDescriptor('7' ,FontHeightPage,new byte[]{0x02,0x02,0xC2,0x32,0x0E,0x00,0x07,0x00,0x00,0x00}),
            new FontCharacterDescriptor('8' ,FontHeightPage,new byte[]{0xDC,0x22,0x22,0x22,0xDC,0x03,0x04,0x04,0x04,0x03}),
            new FontCharacterDescriptor('9' ,FontHeightPage,new byte[]{0x3C,0x42,0x42,0x22,0xFC,0x02,0x04,0x04,0x04,0x03}),
            new FontCharacterDescriptor(':' ,FontHeightPage,new byte[]{0x10,0x04}),
            new FontCharacterDescriptor(';' ,FontHeightPage,new byte[]{0x00,0x10,0x08,0x04}),
            new FontCharacterDescriptor('<' ,FontHeightPage,new byte[]{0x40,0xE0,0xB0,0x18,0x08,0x00,0x00,0x01,0x03,0x02}),
            new FontCharacterDescriptor('=' ,FontHeightPage,new byte[]{0xA0,0xA0,0xA0,0xA0,0xA0,0x00,0x00,0x00,0x00,0x00}),
            new FontCharacterDescriptor('>' ,FontHeightPage,new byte[]{0x08,0x18,0xB0,0xE0,0x40,0x02,0x03,0x01,0x00,0x00}),
            new FontCharacterDescriptor('?' ,FontHeightPage,new byte[]{0x0C,0x02,0xC2,0x22,0x1C,0x00,0x00,0x05,0x00,0x00}),
            new FontCharacterDescriptor('@' ,FontHeightPage,new byte[]{0xF0,0x0C,0x02,0x02,0xE1,0x11,0x11,0x91,0x72,0x02,0x0C,0xF0,0x00,0x03,0x04,0x04,0x08,0x09,0x09,0x08,0x09,0x05,0x05,0x00}),
            new FontCharacterDescriptor('A' ,FontHeightPage,new byte[]{0x00,0x80,0xE0,0x98,0x86,0x98,0xE0,0x80,0x00,0x06,0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x06}),
            new FontCharacterDescriptor('B' ,FontHeightPage,new byte[]{0xFE,0x22,0x22,0x22,0x22,0x22,0xDC,0x07,0x04,0x04,0x04,0x04,0x04,0x03}),
            new FontCharacterDescriptor('C' ,FontHeightPage,new byte[]{0xF8,0x04,0x02,0x02,0x02,0x02,0x04,0x08,0x01,0x02,0x04,0x04,0x04,0x04,0x02,0x01}),
            new FontCharacterDescriptor('D' ,FontHeightPage,new byte[]{0xFE,0x02,0x02,0x02,0x02,0x02,0x04,0xF8,0x07,0x04,0x04,0x04,0x04,0x04,0x02,0x01}),
            new FontCharacterDescriptor('E' ,FontHeightPage,new byte[]{0xFE,0x22,0x22,0x22,0x22,0x22,0x02,0x07,0x04,0x04,0x04,0x04,0x04,0x04}),
            new FontCharacterDescriptor('F' ,FontHeightPage,new byte[]{0xFE,0x22,0x22,0x22,0x22,0x22,0x02,0x07,0x00,0x00,0x00,0x00,0x00,0x00}),
            new FontCharacterDescriptor('G' ,FontHeightPage,new byte[]{0xF8,0x04,0x02,0x02,0x02,0x42,0x44,0xC8,0x01,0x02,0x04,0x04,0x04,0x04,0x02,0x07}),
            new FontCharacterDescriptor('H' ,FontHeightPage,new byte[]{0xFE,0x20,0x20,0x20,0x20,0x20,0x20,0xFE,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x07}),
            new FontCharacterDescriptor('I' ,FontHeightPage,new byte[]{0xFE,0x07}),
            new FontCharacterDescriptor('J' ,FontHeightPage,new byte[]{0x00,0x00,0x00,0x00,0xFE,0x03,0x04,0x04,0x04,0x03}),
            new FontCharacterDescriptor('K' ,FontHeightPage,new byte[]{0xFE,0x20,0x50,0x88,0x04,0x02,0x00,0x07,0x00,0x00,0x00,0x01,0x02,0x04}),
            new FontCharacterDescriptor('L' ,FontHeightPage,new byte[]{0xFE,0x00,0x00,0x00,0x00,0x00,0x07,0x04,0x04,0x04,0x04,0x04}),
            new FontCharacterDescriptor('M' ,FontHeightPage,new byte[]{0xFE,0x18,0x60,0x80,0x00,0x80,0x60,0x18,0xFE,0x07,0x00,0x00,0x01,0x06,0x01,0x00,0x00,0x07}),
            new FontCharacterDescriptor('N' ,FontHeightPage,new byte[]{0xFE,0x04,0x18,0x20,0x40,0x80,0x00,0xFE,0x07,0x00,0x00,0x00,0x00,0x01,0x02,0x07}),
            new FontCharacterDescriptor('O' ,FontHeightPage,new byte[]{0xF8,0x04,0x02,0x02,0x02,0x02,0x04,0xF8,0x01,0x02,0x04,0x04,0x04,0x04,0x02,0x01}),
            new FontCharacterDescriptor('P' ,FontHeightPage,new byte[]{0xFE,0x42,0x42,0x42,0x42,0x42,0x24,0x18,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00}),
            new FontCharacterDescriptor('Q' ,FontHeightPage,new byte[]{0xF8,0x04,0x02,0x02,0x02,0x02,0x04,0xF8,0x01,0x02,0x04,0x04,0x04,0x05,0x02,0x05}),
            new FontCharacterDescriptor('R' ,FontHeightPage,new byte[]{0xFE,0x42,0x42,0x42,0x42,0x42,0x64,0x98,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x04}),
            new FontCharacterDescriptor('S' ,FontHeightPage,new byte[]{0x1C,0x22,0x22,0x22,0x42,0x42,0x8C,0x03,0x04,0x04,0x04,0x04,0x04,0x03}),
            new FontCharacterDescriptor('T' ,FontHeightPage,new byte[]{0x02,0x02,0x02,0x02,0xFE,0x02,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00}),
            new FontCharacterDescriptor('U' ,FontHeightPage,new byte[]{0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x01,0x02,0x04,0x04,0x04,0x04,0x02,0x01}),
            new FontCharacterDescriptor('V' ,FontHeightPage,new byte[]{0x06,0x18,0x60,0x80,0x00,0x80,0x60,0x18,0x06,0x00,0x00,0x00,0x01,0x06,0x01,0x00,0x00,0x00}),
            new FontCharacterDescriptor('W' ,FontHeightPage,new byte[]{0x0E,0x30,0xC0,0x00,0xC0,0x30,0x0E,0x30,0xC0,0x00,0xC0,0x30,0x0E,0x00,0x00,0x01,0x06,0x01,0x00,0x00,0x00,0x01,0x06,0x01,0x00,0x00}),
            new FontCharacterDescriptor('X' ,FontHeightPage,new byte[]{0x06,0x08,0x90,0x60,0x60,0x90,0x08,0x06,0x06,0x01,0x00,0x00,0x00,0x00,0x01,0x06}),
            new FontCharacterDescriptor('Y' ,FontHeightPage,new byte[]{0x06,0x08,0x10,0x20,0xC0,0x20,0x10,0x08,0x06,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00}),
            new FontCharacterDescriptor('Z' ,FontHeightPage,new byte[]{0x02,0x82,0x42,0x22,0x1A,0x06,0x06,0x05,0x04,0x04,0x04,0x04}),
            new FontCharacterDescriptor('[' ,FontHeightPage,new byte[]{0xFE,0x02,0x02,0x3F,0x20,0x20}),
            new FontCharacterDescriptor('\\',FontHeightPage,new byte[]{0x0E,0x70,0x80,0x00,0x00,0x00,0x03,0x1C}),
            new FontCharacterDescriptor('^' ,FontHeightPage,new byte[]{0x02,0x02,0xFE,0x20,0x20,0x3F}),
            new FontCharacterDescriptor('_' ,FontHeightPage,new byte[]{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x10}),
            new FontCharacterDescriptor('`' ,FontHeightPage,new byte[]{0x02,0x04,0x00,0x00}),
            new FontCharacterDescriptor('a' ,FontHeightPage,new byte[]{0xA0,0x50,0x50,0x50,0x50,0xE0,0x00,0x03,0x04,0x04,0x04,0x04,0x03,0x04}),
            new FontCharacterDescriptor('b' ,FontHeightPage,new byte[]{0xFE,0x20,0x10,0x10,0x10,0xE0,0x07,0x02,0x04,0x04,0x04,0x03}),
            new FontCharacterDescriptor('c' ,FontHeightPage,new byte[]{0xE0,0x10,0x10,0x10,0x10,0x20,0x03,0x04,0x04,0x04,0x04,0x02}),
            new FontCharacterDescriptor('d' ,FontHeightPage,new byte[]{0xE0,0x10,0x10,0x10,0x20,0xFE,0x03,0x04,0x04,0x04,0x02,0x07}),
            new FontCharacterDescriptor('e' ,FontHeightPage,new byte[]{0xE0,0x90,0x90,0x90,0x90,0xE0,0x03,0x04,0x04,0x04,0x04,0x02}),
            new FontCharacterDescriptor('f' ,FontHeightPage,new byte[]{0x10,0xFC,0x12,0x00,0x07,0x00}),
            new FontCharacterDescriptor('g' ,FontHeightPage,new byte[]{0xE0,0x10,0x10,0x10,0x20,0xF0,0x03,0x24,0x24,0x24,0x22,0x1F}),
            new FontCharacterDescriptor('h' ,FontHeightPage,new byte[]{0xFE,0x20,0x10,0x10,0xE0,0x07,0x00,0x00,0x00,0x07}),
            new FontCharacterDescriptor('i' ,FontHeightPage,new byte[]{0xF2,0x07}),
            new FontCharacterDescriptor('j' ,FontHeightPage,new byte[]{0x00,0xF2,0x20,0x1F}),
            new FontCharacterDescriptor('k' ,FontHeightPage,new byte[]{0xFE,0x80,0xC0,0x20,0x10,0x00,0x07,0x00,0x00,0x01,0x02,0x04}),
            new FontCharacterDescriptor('l' ,FontHeightPage,new byte[]{0xFE,0x07}),
            new FontCharacterDescriptor('m' ,FontHeightPage,new byte[]{0xF0,0x20,0x10,0x10,0xE0,0x20,0x10,0x10,0xE0,0x07,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07}),
            new FontCharacterDescriptor('n' ,FontHeightPage,new byte[]{0xF0,0x20,0x10,0x10,0xE0,0x07,0x00,0x00,0x00,0x07}),
            new FontCharacterDescriptor('o' ,FontHeightPage,new byte[]{0xE0,0x10,0x10,0x10,0x10,0xE0,0x03,0x04,0x04,0x04,0x04,0x03}),
            new FontCharacterDescriptor('p' ,FontHeightPage,new byte[]{0xF0,0x20,0x10,0x10,0x10,0xE0,0x3F,0x02,0x04,0x04,0x04,0x03}),
            new FontCharacterDescriptor('q' ,FontHeightPage,new byte[]{0xE0,0x10,0x10,0x10,0x20,0xF0,0x03,0x04,0x04,0x04,0x02,0x3F}),
            new FontCharacterDescriptor('r' ,FontHeightPage,new byte[]{0xF0,0x20,0x10,0x07,0x00,0x00}),
            new FontCharacterDescriptor('s' ,FontHeightPage,new byte[]{0x60,0x90,0x90,0x90,0x20,0x02,0x04,0x04,0x04,0x03}),
            new FontCharacterDescriptor('t' ,FontHeightPage,new byte[]{0x10,0xFC,0x10,0x00,0x03,0x04}),
            new FontCharacterDescriptor('u' ,FontHeightPage,new byte[]{0xF0,0x00,0x00,0x00,0xF0,0x03,0x04,0x04,0x02,0x07}),
            new FontCharacterDescriptor('v' ,FontHeightPage,new byte[]{0x30,0xC0,0x00,0x00,0x00,0xC0,0x30,0x00,0x00,0x03,0x04,0x03,0x00,0x00}),
            new FontCharacterDescriptor('w' ,FontHeightPage,new byte[]{0x30,0xC0,0x00,0xC0,0x30,0xC0,0x00,0xC0,0x30,0x00,0x01,0x06,0x01,0x00,0x01,0x06,0x01,0x00}),
            new FontCharacterDescriptor('x' ,FontHeightPage,new byte[]{0x10,0x20,0xC0,0xC0,0x20,0x10,0x04,0x02,0x01,0x01,0x02,0x04}),
            new FontCharacterDescriptor('y' ,FontHeightPage,new byte[]{0x30,0xC0,0x00,0x00,0x00,0xC0,0x30,0x20,0x20,0x13,0x0C,0x03,0x00,0x00}),
            new FontCharacterDescriptor('z' ,FontHeightPage,new byte[]{0x10,0x90,0x50,0x30,0x06,0x05,0x04,0x04}),
            new FontCharacterDescriptor('{' ,FontHeightPage,new byte[]{0x80,0x80,0x7C,0x02,0x02,0x00,0x00,0x1F,0x20,0x20}),
            new FontCharacterDescriptor('|' ,FontHeightPage,new byte[]{0xFE,0x3F}),
            new FontCharacterDescriptor('}' ,FontHeightPage,new byte[]{0x02,0x02,0x7C,0x80,0x80,0x20,0x20,0x1F,0x00,0x00}),
            new FontCharacterDescriptor('~' ,FontHeightPage,new byte[]{0x0C,0x02,0x02,0x04,0x08,0x08,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00}),
        };

        private static readonly ImageDescriptor[] ImageTable = 
        {
            new ImageDescriptor(ImageType.NetworkConnected,FontHeightPage, new byte[]
            {
                0x00, 0x00, 0x3c, 0xe8, 0x3c, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xf8, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x0f, 0x08, 0x28, 0x28, 0x38, 0x38, 0x28, 0x28, 0x08, 0x0f, 0x00, 0x00, 0x00
            }),
            new ImageDescriptor(ImageType.NetworkDisconnected,FontHeightPage,new byte[]
            {
                0x00, 0x00, 0x3c, 0xe8, 0x3c, 0x08, 0x08, 0x08, 0x88, 0x48, 0x28, 0x18, 0xf8, 0x04, 0x02, 0x00,
                0x00, 0x20, 0x10, 0x0f, 0x0c, 0x2a, 0x29, 0x38, 0x38, 0x28, 0x28, 0x08, 0x0f, 0x00, 0x00, 0x00
            })
        };
    }
}

最后

【.NET IoT】树莓派Raspberry Pi 3B+ 连接oled屏幕,SSD1306驱动_嵌入式_03

参考:

  1. Windows 10 IoT: How to Drive an SSD1306 I2C Display
  2. 一文彻底了解SSD1306驱动0.96寸OLED
  3. 字模在线工具