/* Based on Arduino SSD1306Ascii Library, Copyright (C) 2015 by William Greiman * Modifications (C) 2021 Neil McKechnie * * This file is part of CommandStation-EX * * This Library is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This Library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with the Arduino SSD1306Ascii Library. If not, see * . */ #include "SSD1306Ascii.h" #include "I2CManager.h" #include "FSH.h" //============================================================================== // SSD1306/SSD1106 I2C command bytes //------------------------------------------------------------------------------ /** Set Lower Column Start Address for Page Addressing Mode. */ static const uint8_t SSD1306_SETLOWCOLUMN = 0x00; /** Set Higher Column Start Address for Page Addressing Mode. */ static const uint8_t SSD1306_SETHIGHCOLUMN = 0x10; /** Set Memory Addressing Mode. */ static const uint8_t SSD1306_MEMORYMODE = 0x20; /** Set display RAM display start line register from 0 - 63. */ static const uint8_t SSD1306_SETSTARTLINE = 0x40; /** Set Display Contrast to one of 256 steps. */ static const uint8_t SSD1306_SETCONTRAST = 0x81; /** Enable or disable charge pump. Follow with 0X14 enable, 0X10 disable. */ static const uint8_t SSD1306_CHARGEPUMP = 0x8D; /** Set Segment Re-map between data column and the segment driver. */ static const uint8_t SSD1306_SEGREMAP = 0xA0; /** Resume display from GRAM content. */ static const uint8_t SSD1306_DISPLAYALLON_RESUME = 0xA4; /** Force display on regardless of GRAM content. */ static const uint8_t SSD1306_DISPLAYALLON = 0xA5; /** Set Normal Display. */ static const uint8_t SSD1306_NORMALDISPLAY = 0xA6; /** Set Inverse Display. */ static const uint8_t SSD1306_INVERTDISPLAY = 0xA7; /** Set Multiplex Ratio from 16 to 63. */ static const uint8_t SSD1306_SETMULTIPLEX = 0xA8; /** Set Display off. */ static const uint8_t SSD1306_DISPLAYOFF = 0xAE; /** Set Display on. */ static const uint8_t SSD1306_DISPLAYON = 0xAF; /**Set GDDRAM Page Start Address. */ static const uint8_t SSD1306_SETSTARTPAGE = 0xB0; /** Set COM output scan direction normal. */ static const uint8_t SSD1306_COMSCANINC = 0xC0; /** Set COM output scan direction reversed. */ static const uint8_t SSD1306_COMSCANDEC = 0xC8; /** Set Display Offset. */ static const uint8_t SSD1306_SETDISPLAYOFFSET = 0xD3; /** Sets COM signals pin configuration to match the OLED panel layout. */ static const uint8_t SSD1306_SETCOMPINS = 0xDA; /** This command adjusts the VCOMH regulator output. */ static const uint8_t SSD1306_SETVCOMDETECT = 0xDB; /** Set Display Clock Divide Ratio/ Oscillator Frequency. */ static const uint8_t SSD1306_SETDISPLAYCLOCKDIV = 0xD5; /** Set Pre-charge Period */ static const uint8_t SSD1306_SETPRECHARGE = 0xD9; /** Deactivate scroll */ static const uint8_t SSD1306_DEACTIVATE_SCROLL = 0x2E; /** No Operation Command. */ static const uint8_t SSD1306_NOP = 0xE3; //------------------------------------------------------------------------------ /** Set Pump voltage value: (30H~33H) 6.4, 7.4, 8.0 (POR), 9.0. */ static const uint8_t SH1106_SET_PUMP_VOLTAGE = 0x30; /** First byte of set charge pump mode */ static const uint8_t SH1106_SET_PUMP_MODE = 0xAD; /** Second byte charge pump on. */ static const uint8_t SH1106_PUMP_ON = 0x8B; /** Second byte charge pump off. */ static const uint8_t SH1106_PUMP_OFF = 0x8A; //------------------------------------------------------------------------------ // Sequence of blank pixels, to optimise clearing screen. // Send a maximum of 30 pixels per transmission. const uint8_t FLASH SSD1306AsciiWire::blankPixels[30] = {0x40, // First byte specifies data mode 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //============================================================================== // this section is based on https://github.com/adafruit/Adafruit_SSD1306 /** Initialization commands for a 128x32 or 128x64 SSD1306 oled display. */ const uint8_t FLASH SSD1306AsciiWire::Adafruit128xXXinit[] = { // Init sequence for Adafruit 128x32/64 OLED module 0x00, // Set to command mode SSD1306_DISPLAYOFF, SSD1306_SETDISPLAYCLOCKDIV, 0x80, // the suggested ratio 0x80 SSD1306_SETMULTIPLEX, 0x3F, // ratio 64 (initially) SSD1306_SETDISPLAYOFFSET, 0x0, // no offset SSD1306_SETSTARTLINE | 0x0, // line #0 SSD1306_CHARGEPUMP, 0x14, // internal vcc SSD1306_MEMORYMODE, 0x02, // page mode SSD1306_SEGREMAP | 0x1, // column 127 mapped to SEG0 SSD1306_COMSCANDEC, // column scan direction reversed SSD1306_SETCOMPINS, 0X12, // set COM pins SSD1306_SETCONTRAST, 0x7F, // contrast level 127 SSD1306_SETPRECHARGE, 0xF1, // pre-charge period (1, 15) SSD1306_SETVCOMDETECT, 0x40, // vcomh regulator level SSD1306_DISPLAYALLON_RESUME, SSD1306_NORMALDISPLAY, SSD1306_DISPLAYON }; //------------------------------------------------------------------------------ // This section is based on https://github.com/stanleyhuangyc/MultiLCD /** Initialization commands for a 128x64 SH1106 oled display. */ const uint8_t FLASH SSD1306AsciiWire::SH1106_132x64init[] = { 0x00, // Set to command mode SSD1306_DISPLAYOFF, SSD1306_SETDISPLAYCLOCKDIV, 0X80, // set osc division SSD1306_SETMULTIPLEX, 0x3F, // ratio 64 SSD1306_SETDISPLAYOFFSET, 0X00, // set display offset SSD1306_SETSTARTPAGE | 0X0, // set page address SSD1306_SETSTARTLINE | 0x0, // set start line SH1106_SET_PUMP_MODE, SH1106_PUMP_ON, // set charge pump enable SSD1306_SEGREMAP | 0X1, // set segment remap SSD1306_COMSCANDEC, // Com scan direction SSD1306_SETCOMPINS, 0X12, // set COM pins SSD1306_SETCONTRAST, 0x80, // 128 SSD1306_SETPRECHARGE, 0X1F, // set pre-charge period SSD1306_SETVCOMDETECT, 0x40, // set vcomh SH1106_SET_PUMP_VOLTAGE | 0X2, // 8.0 volts SSD1306_NORMALDISPLAY, // normal / reverse SSD1306_DISPLAYON }; //============================================================================== // SSD1306AsciiWire Method Definitions //------------------------------------------------------------------------------ // Auto-detect address SSD1306AsciiWire::SSD1306AsciiWire(int width, int height) : SSD1306AsciiWire(0, width, height) { } // Constructor with explicit address SSD1306AsciiWire::SSD1306AsciiWire(I2CAddress address, int width, int height) { m_i2cAddr = address; m_displayWidth = width; m_displayHeight = height; // Set size in characters m_charsPerColumn = m_displayHeight / fontHeight; m_charsPerRow = (m_displayWidth+fontWidth-1) / fontWidth; // Round up } bool SSD1306AsciiWire::begin() { I2CManager.begin(); I2CManager.setClock(400000L); // Set max supported I2C speede if (m_i2cAddr == 0) { // Probe for I2C device on 0x3c and 0x3d. for (uint8_t address = 0x3c; address <= 0x3d; address++) { if (I2CManager.exists(address)) { m_i2cAddr = address; break; } } if (m_i2cAddr == 0) DIAG(F("OLED display not found")); } m_col = 0; m_row = 0; m_colOffset = 0; if (m_displayWidth==132 && m_displayHeight==64) { // SH1106 display. This uses 128x64 centered within a 132x64 OLED. m_colOffset = 2; I2CManager.write_P(m_i2cAddr, SH1106_132x64init, sizeof(SH1106_132x64init)); } else if (m_displayWidth==128 && (m_displayHeight==64 || m_displayHeight==32)) { // SSD1306 or SSD1309 128x64 or 128x32 I2CManager.write_P(m_i2cAddr, Adafruit128xXXinit, sizeof(Adafruit128xXXinit)); if (m_displayHeight == 32) I2CManager.write(m_i2cAddr, 5, 0, // Set command mode SSD1306_SETMULTIPLEX, 0x1F, // ratio 32 SSD1306_SETCOMPINS, 0x02); // sequential COM pins, disable remap } else { DIAG(F("OLED configuration option not recognised")); return false; } // Device found DIAG(F("%dx%d OLED display configured on I2C:%s"), m_displayWidth, m_displayHeight, m_i2cAddr.toString()); return true; } /* Clear screen by writing blank pixels. */ void SSD1306AsciiWire::clearNative() { const int maxBytes = sizeof(blankPixels) - 1; // max number of pixel columns (bytes) per transmission for (uint8_t r = 0; r <= m_displayHeight/8 - 1; r++) { setRowNative(r); // Position at start of row to be erased for (uint8_t c = 0; c < m_displayWidth; c += maxBytes) { uint8_t len = m_displayWidth-c; // Number of pixel columns remaining if (len > maxBytes) len = maxBytes; I2CManager.write_P(m_i2cAddr, blankPixels, len+1); // Write command + 'len' blank columns } } } //------------------------------------------------------------------------------ // Set cursor position (by text line) void SSD1306AsciiWire::setRowNative(uint8_t line) { // Calculate pixel position from line number uint8_t row = line*8; if (row < m_displayHeight) { m_row = row; m_col = m_colOffset; // Before using buffer, wait for last request to complete requestBlock.wait(); // Build output buffer for I2C uint8_t len = 0; outputBuffer[len++] = 0x00; // Set to command mode outputBuffer[len++] = SSD1306_SETLOWCOLUMN | (m_col & 0XF); outputBuffer[len++] = SSD1306_SETHIGHCOLUMN | (m_col >> 4); outputBuffer[len++] = SSD1306_SETSTARTPAGE | (m_row/8); I2CManager.write(m_i2cAddr, outputBuffer, len, &requestBlock); } } //------------------------------------------------------------------------------ // Write a character to the OLED size_t SSD1306AsciiWire::writeNative(uint8_t ch) { const uint8_t* base = m_font; if (ch < m_fontFirstChar || ch >= (m_fontFirstChar + m_fontCharCount)) return 0; // Check if character would be partly or wholly off the display if (m_col + fontWidth > m_displayWidth) return 0; #if defined(NOLOWERCASE) // Adjust if lowercase is missing if (ch >= 'a') { if (ch <= 'z') ch = ch - 'a' + 'A'; // Capitalise else ch -= 26; // Allow for missing lowercase letters } #endif ch -= m_fontFirstChar; base += fontWidth * ch; // Before using buffer, wait for last request to complete requestBlock.wait(); // Build output buffer for I2C outputBuffer[0] = 0x40; // set SSD1306 controller to data mode uint8_t bufferPos = 1; // Copy character pixel columns for (uint8_t i = 0; i < fontWidth; i++) { if (m_col++ < m_displayWidth) outputBuffer[bufferPos++] = GETFLASH(base++); } // Write the data to I2C display I2CManager.write(m_i2cAddr, outputBuffer, bufferPos, &requestBlock); return 1; } //------------------------------------------------------------------------------ // Font characters, 6x8 pixels, starting at 0x20. // Lower case characters optionally omitted. const uint8_t FLASH SSD1306AsciiWire::System6x8[] = { // Fixed width; char width table not used !!!! // font data 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (space) (20) 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, // ! (21) 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, // " 0x14, 0x7F, 0x14, 0x7F, 0x14, 0x00, // # 0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x00, // $ 0x23, 0x13, 0x08, 0x64, 0x62, 0x00, // % 0x36, 0x49, 0x55, 0x22, 0x50, 0x00, // & 0x00, 0x05, 0x03, 0x00, 0x00, 0x00, // ' 0x00, 0x1C, 0x22, 0x41, 0x00, 0x00, // ( 0x00, 0x41, 0x22, 0x1C, 0x00, 0x00, // ) 0x08, 0x2A, 0x1C, 0x2A, 0x08, 0x00, // * 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, // + 0x00, 0x50, 0x30, 0x00, 0x00, 0x00, // , 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, // - 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, // . 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, // / (47) 0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, // 0 (48) 0x00, 0x42, 0x7F, 0x40, 0x00, 0x00, // 1 0x42, 0x61, 0x51, 0x49, 0x46, 0x00, // 2 0x21, 0x41, 0x45, 0x4B, 0x31, 0x00, // 3 0x18, 0x14, 0x12, 0x7F, 0x10, 0x00, // 4 0x27, 0x45, 0x45, 0x45, 0x39, 0x00, // 5 0x3C, 0x4A, 0x49, 0x49, 0x30, 0x00, // 6 0x01, 0x71, 0x09, 0x05, 0x03, 0x00, // 7 0x36, 0x49, 0x49, 0x49, 0x36, 0x00, // 8 0x06, 0x49, 0x49, 0x29, 0x1E, 0x00, // 9 (57) 0x00, 0x36, 0x36, 0x00, 0x00, 0x00, // : 0x00, 0x56, 0x36, 0x00, 0x00, 0x00, // ; 0x00, 0x08, 0x14, 0x22, 0x41, 0x00, // < 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, // = 0x41, 0x22, 0x14, 0x08, 0x00, 0x00, // > 0x02, 0x01, 0x51, 0x09, 0x06, 0x00, // ? 0x32, 0x49, 0x79, 0x41, 0x3E, 0x00, // @ (64) 0x7E, 0x11, 0x11, 0x11, 0x7E, 0x00, // A (65) 0x7F, 0x49, 0x49, 0x49, 0x36, 0x00, // B 0x3E, 0x41, 0x41, 0x41, 0x22, 0x00, // C 0x7F, 0x41, 0x41, 0x22, 0x1C, 0x00, // D 0x7F, 0x49, 0x49, 0x49, 0x41, 0x00, // E 0x7F, 0x09, 0x09, 0x01, 0x01, 0x00, // F 0x3E, 0x41, 0x41, 0x51, 0x32, 0x00, // G 0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00, // H 0x00, 0x41, 0x7F, 0x41, 0x00, 0x00, // I 0x20, 0x40, 0x41, 0x3F, 0x01, 0x00, // J 0x7F, 0x08, 0x14, 0x22, 0x41, 0x00, // K 0x7F, 0x40, 0x40, 0x40, 0x40, 0x00, // L 0x7F, 0x02, 0x04, 0x02, 0x7F, 0x00, // M 0x7F, 0x04, 0x08, 0x10, 0x7F, 0x00, // N 0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00, // O 0x7F, 0x09, 0x09, 0x09, 0x06, 0x00, // P 0x3E, 0x41, 0x51, 0x21, 0x5E, 0x00, // Q 0x7F, 0x09, 0x19, 0x29, 0x46, 0x00, // R 0x46, 0x49, 0x49, 0x49, 0x31, 0x00, // S 0x01, 0x01, 0x7F, 0x01, 0x01, 0x00, // T 0x3F, 0x40, 0x40, 0x40, 0x3F, 0x00, // U 0x1F, 0x20, 0x40, 0x20, 0x1F, 0x00, // V 0x7F, 0x20, 0x18, 0x20, 0x7F, 0x00, // W 0x63, 0x14, 0x08, 0x14, 0x63, 0x00, // X 0x03, 0x04, 0x78, 0x04, 0x03, 0x00, // Y 0x61, 0x51, 0x49, 0x45, 0x43, 0x00, // Z (90) 0x00, 0x00, 0x7F, 0x41, 0x41, 0x00, // [ 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, // "\" 0x41, 0x41, 0x7F, 0x00, 0x00, 0x00, // ] 0x04, 0x02, 0x01, 0x02, 0x04, 0x00, // ^ 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, // _ 0x00, 0x01, 0x02, 0x04, 0x00, 0x00, // ' (96) #ifndef NOLOWERCASE 0x20, 0x54, 0x54, 0x54, 0x78, 0x00, // a (97) 0x7F, 0x48, 0x44, 0x44, 0x38, 0x00, // b 0x38, 0x44, 0x44, 0x44, 0x20, 0x00, // c 0x38, 0x44, 0x44, 0x48, 0x7F, 0x00, // d 0x38, 0x54, 0x54, 0x54, 0x18, 0x00, // e 0x08, 0x7E, 0x09, 0x01, 0x02, 0x00, // f 0x08, 0x14, 0x54, 0x54, 0x3C, 0x00, // g 0x7F, 0x08, 0x04, 0x04, 0x78, 0x00, // h 0x00, 0x44, 0x7D, 0x40, 0x00, 0x00, // i 0x20, 0x40, 0x44, 0x3D, 0x00, 0x00, // j 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, // k 0x00, 0x41, 0x7F, 0x40, 0x00, 0x00, // l 0x7C, 0x04, 0x18, 0x04, 0x78, 0x00, // m 0x7C, 0x08, 0x04, 0x04, 0x78, 0x00, // n 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, // o 0x7C, 0x14, 0x14, 0x14, 0x08, 0x00, // p 0x08, 0x14, 0x14, 0x18, 0x7C, 0x00, // q 0x7C, 0x08, 0x04, 0x04, 0x08, 0x00, // r 0x48, 0x54, 0x54, 0x54, 0x20, 0x00, // s 0x04, 0x3F, 0x44, 0x40, 0x20, 0x00, // t 0x3C, 0x40, 0x40, 0x20, 0x7C, 0x00, // u 0x1C, 0x20, 0x40, 0x20, 0x1C, 0x00, // v 0x3C, 0x40, 0x30, 0x40, 0x3C, 0x00, // w 0x44, 0x28, 0x10, 0x28, 0x44, 0x00, // x 0x0C, 0x50, 0x50, 0x50, 0x3C, 0x00, // y 0x44, 0x64, 0x54, 0x4C, 0x44, 0x00, // z (122) #endif 0x00, 0x08, 0x36, 0x41, 0x00, 0x00, // { (123) 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, // | 0x00, 0x41, 0x36, 0x08, 0x00, 0x00, // } 0x08, 0x08, 0x2A, 0x1C, 0x08, 0x00, // -> 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x00, // <- (127) #ifndef NO_EXTENDED_CHARACTERS // Extended characters - based on "DOS Western Europe" characters // International characters not yet implemented. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x80 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x90 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0xa0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented // Extended characters 176-180 0x92, 0x00, 0x49, 0x00, 0x24, 0x00, // Light grey 0xb0 0xcc, 0x55, 0xcc, 0x55, 0xcc, 0x55, // Mid grey 0xb1 0x6a, 0xff, 0xb6, 0xff, 0xdb, 0xff, // Dark grey 0xb2 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, // Vertical line 0xb3 0x08, 0x08, 0x08, 0xff, 0x00, 0x00, // Vertical line with left spur 0xb4 0x14, 0x14, 0xfe, 0x00, 0xff, 0x00, // Vertical line with double left spur 0xb9 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented Double vertical line with single left spur 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented // Extended characters 185-190 0x28, 0x28, 0xef, 0x00, 0xff, 0x00, // Double vertical line with double left spur 0xb9 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, // Double vertical line 0xba 0x14, 0x14, 0xf4, 0x04, 0xfc, 0x00, // Double top right corner 0xbb 0x14, 0x14, 0x17, 0x10, 0x1f, 0x00, // Double bottom right corner 0xbc 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0xbd 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0xbe // Extended characters 191-199 0x08, 0x08, 0x08, 0xf8, 0x00, 0x00, // Top right corner 0xbf 0x00, 0x00, 0x00, 0x0f, 0x08, 0x08, // Bottom left corner 0xc0 0x08, 0x08, 0x08, 0x0f, 0x08, 0x08, // Horizontal line with upward spur 0xc1 0x08, 0x08, 0x08, 0xf8, 0x08, 0x08, // Horizontal line with downward spur 0xc2 0x00, 0x00, 0x00, 0xff, 0x08, 0x08, // Vertical line with right spur 0xc3 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, // Horizontal line 0xc4 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, // Cross 0xc5 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented // Extended characters 200-206 0x00, 0x00, 0x1f, 0x10, 0x17, 0x14, // Double bottom left corner 0xc8 0x00, 0x00, 0xfc, 0x04, 0xf4, 0x14, // Double top left corner 0xc9 0x14, 0x14, 0x17, 0x10, 0x17, 0x14, // Double horizontal with double upward spur 0xca 0x14, 0x14, 0xf4, 0x04, 0xf4, 0x14, // Double horizontal with double downward spur 0xcb 0x00, 0x00, 0xff, 0x00, 0xf7, 0x14, // Double vertical line with double right spur 0xcc 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, // Double horizontal line 0xcd 0x14, 0x14, 0xf7, 0x00, 0xf7, 0x14, // Double cross 0xce 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0xd0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented // Extended characters 217-223 0x08, 0x08, 0x08, 0x0f, 0x00, 0x00, // Bottom right corner 0xd9 0x00, 0x00, 0x00, 0xf8, 0x08, 0x08, // Top left corner 0xda 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // Solid block 0xdb 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // Bottom half block 0xdc 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, // Left half block 0xdd 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, // Right half block 0xde 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, // Top half block 0xdf 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0xe0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0xf0 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Not implemented // Extended character 248 0x00, 0x06, 0x09, 0x09, 0x06, 0x00, // degree symbol 0xf8 #endif 0x00 }; const uint8_t SSD1306AsciiWire::m_fontCharCount = sizeof(System6x8) / 6;