/* * MIT License Copyright (c) 2017 DeeplyEmbedded Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * SSD1306_OLED.c * * Created on : Sep 26, 2017 * Author : Vinay Divakar * Description : SSD1306 OLED Driver, Graphics API's. * Website : www.deeplyembedded.org */ /* Lib Includes */ #include #include #include #include #include "ssd1306.hpp" /* MACROS */ #define PGM_READ_BYTE(addr) (*TO_CONST_UCHAR_STR(addr)) #define PGM_READ_WORD(addr) (*reinterpret_cast(addr)) #define PGM_READ_DWORD(addr) (*reinterpret_cast(addr)) #define PGM_READ_POINTER(addr) (reinterpret_castPGM_READ_WORD(addr)) const unsigned char ssd1306::font[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x5B, 0x4F, 0x5B, 0x3E, 0x3E, 0x6B, 0x4F, 0x6B, 0x3E, 0x1C, 0x3E, 0x7C, 0x3E, 0x1C, 0x18, 0x3C, 0x7E, 0x3C, 0x18, 0x1C, 0x57, 0x7D, 0x57, 0x1C, 0x1C, 0x5E, 0x7F, 0x5E, 0x1C, 0x00, 0x18, 0x3C, 0x18, 0x00, 0xFF, 0xE7, 0xC3, 0xE7, 0xFF, 0x00, 0x18, 0x24, 0x18, 0x00, 0xFF, 0xE7, 0xDB, 0xE7, 0xFF, 0x30, 0x48, 0x3A, 0x06, 0x0E, 0x26, 0x29, 0x79, 0x29, 0x26, 0x40, 0x7F, 0x05, 0x05, 0x07, 0x40, 0x7F, 0x05, 0x25, 0x3F, 0x5A, 0x3C, 0xE7, 0x3C, 0x5A, 0x7F, 0x3E, 0x1C, 0x1C, 0x08, 0x08, 0x1C, 0x1C, 0x3E, 0x7F, 0x14, 0x22, 0x7F, 0x22, 0x14, 0x5F, 0x5F, 0x00, 0x5F, 0x5F, 0x06, 0x09, 0x7F, 0x01, 0x7F, 0x00, 0x66, 0x89, 0x95, 0x6A, 0x60, 0x60, 0x60, 0x60, 0x60, 0x94, 0xA2, 0xFF, 0xA2, 0x94, 0x08, 0x04, 0x7E, 0x04, 0x08, 0x10, 0x20, 0x7E, 0x20, 0x10, 0x08, 0x08, 0x2A, 0x1C, 0x08, 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x1E, 0x10, 0x10, 0x10, 0x10, 0x0C, 0x1E, 0x0C, 0x1E, 0x0C, 0x30, 0x38, 0x3E, 0x38, 0x30, 0x06, 0x0E, 0x3E, 0x0E, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x14, 0x7F, 0x14, 0x7F, 0x14, 0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x23, 0x13, 0x08, 0x64, 0x62, 0x36, 0x49, 0x56, 0x20, 0x50, 0x00, 0x08, 0x07, 0x03, 0x00, 0x00, 0x1C, 0x22, 0x41, 0x00, 0x00, 0x41, 0x22, 0x1C, 0x00, 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x80, 0x70, 0x30, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x60, 0x60, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, 0x42, 0x7F, 0x40, 0x00, 0x72, 0x49, 0x49, 0x49, 0x46, 0x21, 0x41, 0x49, 0x4D, 0x33, 0x18, 0x14, 0x12, 0x7F, 0x10, 0x27, 0x45, 0x45, 0x45, 0x39, 0x3C, 0x4A, 0x49, 0x49, 0x31, 0x41, 0x21, 0x11, 0x09, 0x07, 0x36, 0x49, 0x49, 0x49, 0x36, 0x46, 0x49, 0x49, 0x29, 0x1E, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x40, 0x34, 0x00, 0x00, 0x00, 0x08, 0x14, 0x22, 0x41, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x41, 0x22, 0x14, 0x08, 0x02, 0x01, 0x59, 0x09, 0x06, 0x3E, 0x41, 0x5D, 0x59, 0x4E, 0x7C, 0x12, 0x11, 0x12, 0x7C, 0x7F, 0x49, 0x49, 0x49, 0x36, 0x3E, 0x41, 0x41, 0x41, 0x22, 0x7F, 0x41, 0x41, 0x41, 0x3E, 0x7F, 0x49, 0x49, 0x49, 0x41, 0x7F, 0x09, 0x09, 0x09, 0x01, 0x3E, 0x41, 0x41, 0x51, 0x73, 0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00, 0x41, 0x7F, 0x41, 0x00, 0x20, 0x40, 0x41, 0x3F, 0x01, 0x7F, 0x08, 0x14, 0x22, 0x41, 0x7F, 0x40, 0x40, 0x40, 0x40, 0x7F, 0x02, 0x1C, 0x02, 0x7F, 0x7F, 0x04, 0x08, 0x10, 0x7F, 0x3E, 0x41, 0x41, 0x41, 0x3E, 0x7F, 0x09, 0x09, 0x09, 0x06, 0x3E, 0x41, 0x51, 0x21, 0x5E, 0x7F, 0x09, 0x19, 0x29, 0x46, 0x26, 0x49, 0x49, 0x49, 0x32, 0x03, 0x01, 0x7F, 0x01, 0x03, 0x3F, 0x40, 0x40, 0x40, 0x3F, 0x1F, 0x20, 0x40, 0x20, 0x1F, 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x63, 0x14, 0x08, 0x14, 0x63, 0x03, 0x04, 0x78, 0x04, 0x03, 0x61, 0x59, 0x49, 0x4D, 0x43, 0x00, 0x7F, 0x41, 0x41, 0x41, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x41, 0x41, 0x41, 0x7F, 0x04, 0x02, 0x01, 0x02, 0x04, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x03, 0x07, 0x08, 0x00, 0x20, 0x54, 0x54, 0x78, 0x40, 0x7F, 0x28, 0x44, 0x44, 0x38, 0x38, 0x44, 0x44, 0x44, 0x28, 0x38, 0x44, 0x44, 0x28, 0x7F, 0x38, 0x54, 0x54, 0x54, 0x18, 0x00, 0x08, 0x7E, 0x09, 0x02, 0x18, 0xA4, 0xA4, 0x9C, 0x78, 0x7F, 0x08, 0x04, 0x04, 0x78, 0x00, 0x44, 0x7D, 0x40, 0x00, 0x20, 0x40, 0x40, 0x3D, 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, 0x00, 0x41, 0x7F, 0x40, 0x00, 0x7C, 0x04, 0x78, 0x04, 0x78, 0x7C, 0x08, 0x04, 0x04, 0x78, 0x38, 0x44, 0x44, 0x44, 0x38, 0xFC, 0x18, 0x24, 0x24, 0x18, 0x18, 0x24, 0x24, 0x18, 0xFC, 0x7C, 0x08, 0x04, 0x04, 0x08, 0x48, 0x54, 0x54, 0x54, 0x24, 0x04, 0x04, 0x3F, 0x44, 0x24, 0x3C, 0x40, 0x40, 0x20, 0x7C, 0x1C, 0x20, 0x40, 0x20, 0x1C, 0x3C, 0x40, 0x30, 0x40, 0x3C, 0x44, 0x28, 0x10, 0x28, 0x44, 0x4C, 0x90, 0x90, 0x90, 0x7C, 0x44, 0x64, 0x54, 0x4C, 0x44, 0x00, 0x08, 0x36, 0x41, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x41, 0x36, 0x08, 0x00, 0x02, 0x01, 0x02, 0x04, 0x02, 0x3C, 0x26, 0x23, 0x26, 0x3C, 0x1E, 0xA1, 0xA1, 0x61, 0x12, 0x3A, 0x40, 0x40, 0x20, 0x7A, 0x38, 0x54, 0x54, 0x55, 0x59, 0x21, 0x55, 0x55, 0x79, 0x41, 0x22, 0x54, 0x54, 0x78, 0x42, 0x21, 0x55, 0x54, 0x78, 0x40, 0x20, 0x54, 0x55, 0x79, 0x40, 0x0C, 0x1E, 0x52, 0x72, 0x12, 0x39, 0x55, 0x55, 0x55, 0x59, 0x39, 0x54, 0x54, 0x54, 0x59, 0x39, 0x55, 0x54, 0x54, 0x58, 0x00, 0x00, 0x45, 0x7C, 0x41, 0x00, 0x02, 0x45, 0x7D, 0x42, 0x00, 0x01, 0x45, 0x7C, 0x40, 0x7D, 0x12, 0x11, 0x12, 0x7D, 0xF0, 0x28, 0x25, 0x28, 0xF0, 0x7C, 0x54, 0x55, 0x45, 0x00, 0x20, 0x54, 0x54, 0x7C, 0x54, 0x7C, 0x0A, 0x09, 0x7F, 0x49, 0x32, 0x49, 0x49, 0x49, 0x32, 0x3A, 0x44, 0x44, 0x44, 0x3A, 0x32, 0x4A, 0x48, 0x48, 0x30, 0x3A, 0x41, 0x41, 0x21, 0x7A, 0x3A, 0x42, 0x40, 0x20, 0x78, 0x00, 0x9D, 0xA0, 0xA0, 0x7D, 0x3D, 0x42, 0x42, 0x42, 0x3D, 0x3D, 0x40, 0x40, 0x40, 0x3D, 0x3C, 0x24, 0xFF, 0x24, 0x24, 0x48, 0x7E, 0x49, 0x43, 0x66, 0x2B, 0x2F, 0xFC, 0x2F, 0x2B, 0xFF, 0x09, 0x29, 0xF6, 0x20, 0xC0, 0x88, 0x7E, 0x09, 0x03, 0x20, 0x54, 0x54, 0x79, 0x41, 0x00, 0x00, 0x44, 0x7D, 0x41, 0x30, 0x48, 0x48, 0x4A, 0x32, 0x38, 0x40, 0x40, 0x22, 0x7A, 0x00, 0x7A, 0x0A, 0x0A, 0x72, 0x7D, 0x0D, 0x19, 0x31, 0x7D, 0x26, 0x29, 0x29, 0x2F, 0x28, 0x26, 0x29, 0x29, 0x29, 0x26, 0x30, 0x48, 0x4D, 0x40, 0x20, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x38, 0x2F, 0x10, 0xC8, 0xAC, 0xBA, 0x2F, 0x10, 0x28, 0x34, 0xFA, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x08, 0x14, 0x2A, 0x14, 0x22, 0x22, 0x14, 0x2A, 0x14, 0x08, 0x55, 0x00, 0x55, 0x00, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0xFF, 0x55, 0xFF, 0x55, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x10, 0x10, 0x10, 0xFF, 0x00, 0x14, 0x14, 0x14, 0xFF, 0x00, 0x10, 0x10, 0xFF, 0x00, 0xFF, 0x10, 0x10, 0xF0, 0x10, 0xF0, 0x14, 0x14, 0x14, 0xFC, 0x00, 0x14, 0x14, 0xF7, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x14, 0x14, 0xF4, 0x04, 0xFC, 0x14, 0x14, 0x17, 0x10, 0x1F, 0x10, 0x10, 0x1F, 0x10, 0x1F, 0x14, 0x14, 0x14, 0x1F, 0x00, 0x10, 0x10, 0x10, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x10, 0x10, 0x10, 0x10, 0x1F, 0x10, 0x10, 0x10, 0x10, 0xF0, 0x10, 0x00, 0x00, 0x00, 0xFF, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0xFF, 0x10, 0x00, 0x00, 0x00, 0xFF, 0x14, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x1F, 0x10, 0x17, 0x00, 0x00, 0xFC, 0x04, 0xF4, 0x14, 0x14, 0x17, 0x10, 0x17, 0x14, 0x14, 0xF4, 0x04, 0xF4, 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xF7, 0x00, 0xF7, 0x14, 0x14, 0x14, 0x17, 0x14, 0x10, 0x10, 0x1F, 0x10, 0x1F, 0x14, 0x14, 0x14, 0xF4, 0x14, 0x10, 0x10, 0xF0, 0x10, 0xF0, 0x00, 0x00, 0x1F, 0x10, 0x1F, 0x00, 0x00, 0x00, 0x1F, 0x14, 0x00, 0x00, 0x00, 0xFC, 0x14, 0x00, 0x00, 0xF0, 0x10, 0xF0, 0x10, 0x10, 0xFF, 0x10, 0xFF, 0x14, 0x14, 0x14, 0xFF, 0x14, 0x10, 0x10, 0x10, 0x1F, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x38, 0x44, 0x44, 0x38, 0x44, 0xFC, 0x4A, 0x4A, 0x4A, 0x34, 0x7E, 0x02, 0x02, 0x06, 0x06, 0x02, 0x7E, 0x02, 0x7E, 0x02, 0x63, 0x55, 0x49, 0x41, 0x63, 0x38, 0x44, 0x44, 0x3C, 0x04, 0x40, 0x7E, 0x20, 0x1E, 0x20, 0x06, 0x02, 0x7E, 0x02, 0x02, 0x99, 0xA5, 0xE7, 0xA5, 0x99, 0x1C, 0x2A, 0x49, 0x2A, 0x1C, 0x4C, 0x72, 0x01, 0x72, 0x4C, 0x30, 0x4A, 0x4D, 0x4D, 0x30, 0x30, 0x48, 0x78, 0x48, 0x30, 0xBC, 0x62, 0x5A, 0x46, 0x3D, 0x3E, 0x49, 0x49, 0x49, 0x00, 0x7E, 0x01, 0x01, 0x01, 0x7E, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x44, 0x44, 0x5F, 0x44, 0x44, 0x40, 0x51, 0x4A, 0x44, 0x40, 0x40, 0x44, 0x4A, 0x51, 0x40, 0x00, 0x00, 0xFF, 0x01, 0x03, 0xE0, 0x80, 0xFF, 0x00, 0x00, 0x08, 0x08, 0x6B, 0x6B, 0x08, 0x36, 0x12, 0x36, 0x24, 0x36, 0x06, 0x0F, 0x09, 0x0F, 0x06, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x30, 0x40, 0xFF, 0x01, 0x01, 0x00, 0x1F, 0x01, 0x01, 0x1E, 0x00, 0x19, 0x1D, 0x17, 0x12, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00 }; /**************************************************************** * Function Name : clearDisplay * Description : Clear the display memory buffer * Returns : NONE. * Params : NONE. ****************************************************************/ void ssd1306::clear_display() { memset(screen_, 0x00, DISPLAY_BUFF_SIZE); } /**************************************************************** * Function Name : display_Init_seq * Description : Performs SSD1306 OLED Initialization Sequence * Returns : NONE. * Params : NONE. ****************************************************************/ void ssd1306::display_init_seq() { /* Add the reset code, If needed */ /* Send display OFF command */ cmd(SSD1306_DISPLAY_OFF); /* Set display clock frequency */ cmd(SSD1306_SET_DISP_CLK); /* Send display CLK command parameter */ cmd(SSD1306_DISPCLK_DIV); /* Set display multiplex */ cmd(SSD1306_SET_MULTIPLEX); /* Send display MULT command parameter */ cmd(SSD1306_MULT_DAT); /* Set display OFFSET */ cmd(SSD1306_SET_DISP_OFFSET); /* Send display OFFSET command parameter */ cmd(SSD1306_DISP_OFFSET_VAL); /* Set display START LINE - Check this command if something weird happens */ cmd(SSD1306_SET_DISP_START_LINE); /* Enable CHARGEPUMP*/ cmd(SSD1306_CONFIG_CHARGE_PUMP); /* Send display CHARGEPUMP command parameter */ cmd(SSD1306_CHARGE_PUMP_EN); /* Set display MEMORYMODE */ cmd(SSD1306_SET_MEM_ADDR_MODE); /* Send display HORIZONTAL MEMORY ADDR MODE command parameter */ cmd(SSD1306_HOR_MM); /* Set display SEG_REMAP */ cmd(SSD1306_SEG_REMAP); /* Set display DIR */ cmd(SSD1306_SET_COMSCANDEC); /* Set display COM */ cmd(SSD1306_SET_COMPINS); /* Send display COM command parameter */ cmd(SSD1306_CONFIG_COM_PINS); /* Set display CONTRAST */ cmd(SSD1306_SET_CONTRAST); /* Send display CONTRAST command parameter */ cmd(SSD1306_CONTRAST_VAL); /* Set display PRECHARGE */ cmd(SSD1306_SET_PRECHARGE); /* Send display PRECHARGE command parameter */ cmd(SSD1306_PRECHARGE_VAL); /* Set display VCOMH */ cmd(SSD1306_SET_VCOMDETECT); /* Send display VCOMH command parameter */ cmd(SSD1306_VCOMH_VAL); /* Set display ALL-ON */ cmd(SSD1306_DISPLAYALLON_RESUME); /* Set display to NORMAL-DISPLAY */ cmd(SSD1306_NORMAL_DISPLAY); /* Set display to DEACTIVATE_SCROLL */ cmd(SSD1306_DEACTIVATE_SCROLL); /* Set display to TURN-ON */ cmd(SSD1306_DISPLAYON); } /**************************************************************** * Function Name : transfer * Description : Transfer the frame buffer onto the display * Returns : NONE. * Params : NONE. ****************************************************************/ void ssd1306::transfer() { data(); for (auto i = 0; i < 1024; ) { for (auto& byte : chunk_) byte = screen_[i++]; if (spi_.write(chunk_, 16)) exit(1); memset(chunk_, 0x00, 16); } } /**************************************************************** * Function Name : Display * Description : 1. Resets the column and page addresses. * 2. Displays the contents of the memory buffer. * Returns : NONE. * Params : NONE. * Note : Each new form can be preceded by a clearDisplay. ****************************************************************/ void ssd1306::display() { init_col_pg_addrs(SSD1306_COL_START_ADDR, SSD1306_COL_END_ADDR, SSD1306_PG_START_ADDR, SSD1306_PG_END_ADDR); transfer(); } /**************************************************************** * Function Name : Init_Col_PG_addrs * Description : Sets the column and page, start and * end addresses. * Returns : NONE. * Params : @col_start_addr: Column start address * @col_end_addr: Column end address * @pg_start_addr: Page start address * @pg_end_addr: Page end address ****************************************************************/ void ssd1306::init_col_pg_addrs(unsigned char col_start_addr, unsigned char col_end_addr, unsigned char pg_start_addr, unsigned char pg_end_addr) { /* Send COLMN address setting command */ cmd(SSD1306_SET_COL_ADDR); /* Set COLMN start address */ cmd(col_start_addr); /* Set COLMN end address */ cmd(col_end_addr); /* Send PAGE address setting command */ cmd(SSD1306_PAGEADDR); /* Set PAGE start address */ cmd(pg_start_addr); /* Set PAGE end address */ cmd(pg_end_addr); } /**************************************************************** * Function Name : setRotation * Description : Set the display rotation * Returns : NONE. * Params : @x: Display rotation parameter ****************************************************************/ void ssd1306::set_rotation(unsigned char x) { rotation_ = x & 3; switch(rotation_) { case 0: case 2: width_ = SSD1306_LCDWIDTH; height_ = SSD1306_LCDHEIGHT; break; case 1: case 3: width_ = SSD1306_LCDHEIGHT; height_ = SSD1306_LCDWIDTH; break; default: break; } } /**************************************************************** * Function Name : startscrollright * Description : Activate a right handed scroll for rows start * through stop * Returns : NONE. * Params : @start: Start location * @stop: Stop location * HINT. : the display is 16 rows tall. To scroll the whole * display, run: display.scrollright(0x00, 0x0F) ****************************************************************/ void ssd1306::start_scroll_right(unsigned char start, unsigned char stop) { /* Send SCROLL horizontal right command */ cmd(SSD1306_RIGHT_HORIZONTAL_SCROLL); cmd(0x00); cmd(start); cmd(0x00); cmd(stop); cmd(0x00); cmd(0xFF); /* Send SCROLL Activate command */ cmd(SSD1306_ACTIVATE_SCROLL); } /**************************************************************** * Function Name : startscrollleft * Description : Activate a left handed scroll for rows start * through stop * Returns : NONE. * Params : @start: Start location * @stop: Stop location * HINT. : the display is 16 rows tall. To scroll the whole * display, run: display.scrollright(0x00, 0x0F) ****************************************************************/ void ssd1306::start_scroll_left(unsigned char start, unsigned char stop) { /* Send SCROLL horizontal left command */ cmd(SSD1306_LEFT_HORIZONTAL_SCROLL); cmd(0x00); cmd(start); cmd(0x00); cmd(stop); cmd(0x00); cmd(0xFF); /* Send SCROLL Activate command */ cmd(SSD1306_ACTIVATE_SCROLL); } /**************************************************************** * Function Name : startscrolldiagright * Description : Activate a diagonal scroll for rows start * through stop * Returns : NONE. * Params : @start: Start location * @stop: Stop location * HINT. : the display is 16 rows tall. To scroll the whole * display, run: display.scrollright(0x00, 0x0F) ****************************************************************/ void ssd1306::start_scroll_diag_right(unsigned char start, unsigned char stop) { /* Send SCROLL diagonal right command */ cmd(SSD1306_SET_VERTICAL_SCROLL_AREA); cmd(0x00); cmd(SSD1306_LCDHEIGHT); cmd(SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL); cmd(0x00); cmd(start); cmd(0x00); cmd(stop); cmd(0x01); /* Send SCROLL Activate command */ cmd(SSD1306_ACTIVATE_SCROLL); } /**************************************************************** * Function Name : startscrolldiagleft * Description : Activate a diagonal scroll for rows start * through stop * Returns : NONE. * Params : @start: Start location * @stop: Stop location * HINT. : the display is 16 rows tall. To scroll the whole * display, run: display.scrollright(0x00, 0x0F) ****************************************************************/ void ssd1306::start_scroll_diag_left(unsigned char start, unsigned char stop) { /* Send SCROLL diagonal right command */ cmd(SSD1306_SET_VERTICAL_SCROLL_AREA); cmd(0x00); cmd(SSD1306_LCDHEIGHT); cmd(SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL); cmd(0x00); cmd(start); cmd(0x00); cmd(stop); cmd(0x01); /* Send SCROLL Activate command */ cmd(SSD1306_ACTIVATE_SCROLL); } /**************************************************************** * Function Name : stopscroll * Description : Stop scrolling * Returns : NONE. * Params : NONE. ****************************************************************/ void ssd1306::stop_scroll() { cmd(SSD1306_DEACTIVATE_SCROLL); } /**************************************************************** * Function Name : invertDisplay * Description : Invert or Normalize the display * Returns : NONE. * Params : @i: 0x00 to Normal and 0x01 for Inverting ****************************************************************/ void ssd1306::invert_display(unsigned char i) { cmd(i ? SSD1306_INVERTDISPLAY : SSD1306_NORMAL_DISPLAY); } /**************************************************************** * Function Name : drawPixel * Description : Draw a pixel * Returns : -1 on error and 0 on success * Params : @x: X - Co-ordinate * @y: Y - Co-ordinate * @color: Color ****************************************************************/ signed char ssd1306::draw_pixel(short x, short y, short color) { /* Return if co-ordinates are out of display dimension's range */ if (x < 0 || x >= width_ || y < 0 || y >= height_) return -1; switch (rotation_) { case 1: std::swap(x,y); x = width_ - x - 1; break; case 2: x = width_ - x - 1; y = height_ - y - 1; break; case 3: std::swap(x,y); y = height_ - y - 1; break; default: break; } /* x is the column */ switch(color) { case WHITE: screen_[x + y/8 * SSD1306_LCDWIDTH] |= 1 << (y & 7); break; case BLACK: screen_[x + y/8 * SSD1306_LCDWIDTH] &= ~(1 << (y & 7)); break; case INVERSE: screen_[x + y/8 * SSD1306_LCDWIDTH] ^= 1 << (y & 7); break; default: break; } return 0; } /**************************************************************** * Function Name : writeLine * Description : Bresenham's algorithm * Returns : NONE * Params : @x0: X0 Co-ordinate * @y0: Y0 Co-ordinate * @x1: X1 Co-ordinate * @y1: Y1 Co-ordinate * @color: Pixel color ****************************************************************/ void ssd1306::write_line(short x0, short y0, short x1, short y1, short color) { auto steep = abs(y1 - y0) > abs(x1 - x0); if (steep) { std::swap(x0, y0); std::swap(x1, y1); } if (x0 > x1) { std::swap(x0, x1); std::swap(y0, y1); } short dx = x1 - x0; short dy = abs(y1 - y0); short err = dx / 2; short ystep = y0 < y1 ? 1 : -1; for (; x0 <= x1; ++x0) { if (steep) { draw_pixel(y0, x0, color); } else { draw_pixel(x0, y0, color); } err -= dy; if (err < 0) { y0 += ystep; err += dx; } } } /* (x,y) is topmost point; if unsure, calling function should sort endpoints or call writeLine() instead */ void ssd1306::draw_fast_v_line(short x, short y,short h, short color) { write_line(x, y, x, y + h - 1, color); } /* (x,y) is topmost point; if unsure, calling function should sort endpoints or call writeLine() instead */ void ssd1306::write_fast_v_line(short x, short y, short h, short color) { draw_fast_v_line(x, y, h, color); } /* (x,y) is leftmost point; if unsure, calling function should sort endpoints or call writeLine() instead */ void ssd1306::draw_fast_h_line(short x, short y,short w, short color) { write_line(x, y, x + w - 1, y, color); } // (x,y) is leftmost point; if unsure, calling function // should sort endpoints or call writeLine() instead void ssd1306::write_fast_h_line(short x, short y, short w, short color) { draw_fast_h_line(x, y, w, color); } /**************************************************************** * Function Name : drawCircleHelper * Description : Draw a.... * Returns : NONE * Params : @x: X Co-ordinate * @y: Y Co-ordinate * @w: Width * @h: height * @r: Corner radius * @color: Pixel color ****************************************************************/ void ssd1306::draw_circle_helper(short x0, short y0, short r, unsigned char cornername, short color) { short f = 1 - r; short dd_f_x = 1; short dd_f_y = -2 * r; short x = 0; short y = r; while (x= 0) { y--; dd_f_y += 2; f += dd_f_y; } x++; dd_f_x += 2; f += dd_f_x; if (cornername & 0x4) { draw_pixel(x0 + x, y0 + y, color); draw_pixel(x0 + y, y0 + x, color); } if (cornername & 0x2) { draw_pixel(x0 + x, y0 - y, color); draw_pixel(x0 + y, y0 - x, color); } if (cornername & 0x8) { draw_pixel(x0 - y, y0 + x, color); draw_pixel(x0 - x, y0 + y, color); } if (cornername & 0x1) { draw_pixel(x0 - y, y0 - x, color); draw_pixel(x0 - x, y0 - y, color); } } } /**************************************************************** * Function Name : drawLine * Description : Draw line between two points * Returns : NONE * Params : @x0: X0 Starting X Co-ordinate * @y0: Y0 Starting Y Co-ordinate * @x1: X1 Ending X Co-ordinate * @y1: Y1 Ending Y Co-ordinate * @color: Pixel color ****************************************************************/ void ssd1306::draw_line(short x0, short y0, short x1, short y1, short color) { if (x0 == x1) { if (y0 > y1) std::swap(y0, y1); draw_fast_v_line(x0, y0, y1 - y0 + 1, color); } else if (y0 == y1) { if (x0 > x1) std::swap(x0, x1); draw_fast_h_line(x0, y0, x1 - x0 + 1, color); } else { write_line(x0, y0, x1, y1, color); } } /**************************************************************** * Function Name : drawRect * Description : Draw a rectangle * Returns : NONE * Params : @x: Corner X Co-ordinate * @y: Corner Y Co-ordinate * @w: Width in pixels * @h: Height in pixels * @color: Pixel color ****************************************************************/ void ssd1306::draw_rect(short x, short y, short w, short h, short color) { //startWrite(); write_fast_h_line(x, y, w, color); write_fast_h_line(x, y+h-1, w, color); write_fast_v_line(x, y, h, color); write_fast_v_line(x+w-1, y, h, color); //endWrite(); } /**************************************************************** * Function Name : fillRect * Description : Fill the rectangle * Returns : NONE * Params : @x: Starting X Co-ordinate * @y: Starting Y Co-ordinate * @w: Width in pixels * @h: Height in pixels * @color: Pixel color ****************************************************************/ void ssd1306::fill_rect(short x, short y, short w, short h, short color) { //startWrite(); for (auto i = x; i < x + w; ++i) { write_fast_v_line(i, y, h, color); } //endWrite(); } /**************************************************************** * Function Name : drawCircle * Description : Draw a circle * Returns : NONE * Params : @x: Center X Co-ordinate * @y: Center Y Co-ordinate * @r: Radius in pixels * @color: Pixel color ****************************************************************/ void ssd1306::draw_circle(short x0, short y0, short r, short color) { short f = 1 - r; short dd_f_x = 1; short dd_f_y = -2 * r; short x = 0; short y = r; //startWrite(); draw_pixel(x0 , y0 + r, color); draw_pixel(x0 , y0 - r, color); draw_pixel(x0 + r, y0 , color); draw_pixel(x0 - r, y0 , color); while (x < y) { if (f >= 0) { y--; dd_f_y += 2; f += dd_f_y; } x++; dd_f_x += 2; f += dd_f_x; draw_pixel(x0 + x, y0 + y, color); draw_pixel(x0 - x, y0 + y, color); draw_pixel(x0 + x, y0 - y, color); draw_pixel(x0 - x, y0 - y, color); draw_pixel(x0 + y, y0 + x, color); draw_pixel(x0 - y, y0 + x, color); draw_pixel(x0 + y, y0 - x, color); draw_pixel(x0 - y, y0 - x, color); } } /**************************************************************** * Function Name : fillCircleHelper * Description : Used to do circles and roundrects * Returns : NONE * Params : @x: Center X Co-ordinate * @y: Center Y Co-ordinate * @r: Radius in pixels * @cornername: Corner radius in pixels * @color: Pixel color ****************************************************************/ void ssd1306::fill_circle_helper(short x0, short y0, short r, unsigned char cornername, short delta, short color) { short f = 1 - r; short dd_f_x = 1; short dd_f_y = -2 * r; short x = 0; short y = r; while (x= 0) { y--; dd_f_y += 2; f += dd_f_y; } x++; dd_f_x += 2; f += dd_f_x; if (cornername & 0x1) { write_fast_v_line(x0+x, y0-y, 2*y+1+delta, color); write_fast_v_line(x0+y, y0-x, 2*x+1+delta, color); } if (cornername & 0x2) { write_fast_v_line(x0-x, y0-y, 2*y+1+delta, color); write_fast_v_line(x0-y, y0-x, 2*x+1+delta, color); } } } /**************************************************************** * Function Name : fillCircle * Description : Fill the circle * Returns : NONE * Params : @x0: Center X Co-ordinate * @y0: Center Y Co-ordinate * @r: Radius in pixels * @color: Pixel color ****************************************************************/ void ssd1306::fill_circle(short x0, short y0, short r, short color) { write_fast_v_line(x0, y0-r, 2*r+1, color); fill_circle_helper(x0, y0, r, 3, 0, color); } /**************************************************************** * Function Name : drawTriangle * Description : Draw a triangle * Returns : NONE * Params : @x0: Corner-1 X Co-ordinate * @y0: Corner-1 Y Co-ordinate * @x1: Corner-2 X Co-ordinate * @y1: Corner-2 Y Co-ordinate * @x2: Corner-3 X Co-ordinate * @y2: Corner-3 Y Co-ordinate * @color: Pixel color ****************************************************************/ void ssd1306::draw_triangle(short x0, short y0, short x1, short y1, short x2, short y2, short color) { draw_line(x0, y0, x1, y1, color); draw_line(x1, y1, x2, y2, color); draw_line(x2, y2, x0, y0, color); } /**************************************************************** * Function Name : fillTriangle * Description : Fill a triangle * Returns : NONE * Params : @x0: Corner-1 X Co-ordinate * @y0: Corner-1 Y Co-ordinate * @x1: Corner-2 X Co-ordinate * @y1: Corner-2 Y Co-ordinate * @x2: Corner-3 X Co-ordinate * @y2: Corner-3 Y Co-ordinate * @color: Pixel color ****************************************************************/ void ssd1306::fill_triangle(short x0, short y0, short x1, short y1, short x2, short y2, short color) { short a, b, y, last; // Sort coordinates by Y order (y2 >= y1 >= y0) if (y0 > y1) { std::swap(y0, y1); std::swap(x0, x1); } if (y1 > y2) { std::swap(y2, y1); std::swap(x2, x1); } if (y0 > y1) { std::swap(y0, y1); std::swap(x0, x1); } //startWrite(); if (y0 == y2) { // Handle awkward all-on-same-line case as its own thing a = b = x0; if (x1 < a) a = x1; else if (x1 > b) b = x1; if (x2 < a) a = x2; else if (x2 > b) b = x2; write_fast_h_line(a, y0, b-a+1, color); // endWrite(); return; } auto dx01 = x1 - x0; auto dy01 = y1 - y0; auto dx02 = x2 - x0; auto dy02 = y2 - y0; auto dx12 = x2 - x1; auto dy12 = y2 - y1; auto sa = 0; auto sb = 0; // For upper part of triangle, find scanline crossings for segments // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1 // is included here (and second loop will be skipped, avoiding a /0 // error there), otherwise scanline y1 is skipped here and handled // in the second loop...which also avoids a /0 error here if y0=y1 // (flat-topped triangle). if (y1 == y2) last = y1; // Include y1 scanline else last = y1-1; // Skip it for (y=y0; y<=last; y++) { a = x0 + sa / dy01; b = x0 + sb / dy02; sa += dx01; sb += dx02; /* longhand: a = x0 + (x1 - x0) * (y - y0) / (y1 - y0); b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); */ if (a > b) std::swap(a,b); write_fast_h_line(a, y, b-a+1, color); } // For lower part of triangle, find scanline crossings for segments // 0-2 and 1-2. This loop is skipped if y1=y2. sa = dx12 * (y - y1); sb = dx02 * (y - y0); for (; y<=y2; y++) { a = x1 + sa / dy12; b = x0 + sb / dy02; sa += dx12; sb += dx02; /* longhand: a = x1 + (x2 - x1) * (y - y1) / (y2 - y1); b = x0 + (x2 - x0) * (y - y0) / (y2 - y0); */ if (a > b) std::swap(a,b); write_fast_h_line(a, y, b-a+1, color); } //endWrite(); } /**************************************************************** * Function Name : drawRoundRect * Description : Draw a rounded rectangle * Returns : NONE * Params : @x: X Co-ordinate * @y: Y Co-ordinate * @w: Width * @h: height * @r: Corner radius * @color: Pixel color ****************************************************************/ void ssd1306::draw_round_rect(short x, short y, short w, short h, short r, short color) { // smarter version write_fast_h_line(x + r , y , w - 2 * r, color); // Top write_fast_h_line(x + r , y + h - 1, w - 2 * r, color); // Bottom write_fast_v_line(x , y + r , h - 2 * r, color); // Left write_fast_v_line(x + w - 1, y + r , h - 2 * r, color); // Right // draw four corners draw_circle_helper(x + r , y + r , r, 1, color); draw_circle_helper(x + w - r - 1, y + r , r, 2, color); draw_circle_helper(x + w - r - 1, y + h - r - 1, r, 4, color); draw_circle_helper(x + r , y + h - r - 1, r, 8, color); } /**************************************************************** * Function Name : fillRoundRect * Description : Fill a rounded rectangle * Returns : NONE * Params : @x: X Co-ordinate * @y: Y Co-ordinate * @w: Width * @h: height * @r: Corner radius * @color: Pixel color ****************************************************************/ void ssd1306::fill_round_rect(short x, short y, short w, short h, short r, short color) { // smarter version fill_rect(x + r, y, w - 2 * r, h, color); // draw four corners fill_circle_helper(x + w - r - 1, y + r, r, 1, h - 2 * r - 1, color); fill_circle_helper(x + r , y + r, r, 2, h - 2 * r - 1, color); } /*---------------------------------------------------------------------------- * BITMAP API's ----------------------------------------------------------------------------*/ /**************************************************************** * Function Name : drawBitmap * Description : Draw a bitmap * Returns : NONE * Params : @x: X Co-ordinate * @y: Y Co-ordinate * @bitmap: bitmap to display * @w: Width * @h: height * @color: Pixel color ****************************************************************/ void ssd1306::draw_bitmap(short x, short y, const unsigned char* bitmap, short w, short h, short color) { unsigned char byte = 0; short byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte for (auto j=0; j= width_) || (y >= height_) || ((x + 6 * size - 1) < 0) || ((y + 8 * size - 1) < 0)) return; // Handle 'classic' charset behavior if (!cp437_ && (c >= 176)) c++; // Char bitmap = 5 columns for (char i=0; i<5; i++ ) { auto line = PGM_READ_BYTE(&font[c * 5 + i]); for (char j=0; j<8; j++, line >>= 1) { if (line & 1) { if (size == 1) draw_pixel(x+i, y+j, color); else fill_rect(x+i*size, y+j*size, size, size, color); } else if (bg != color) { if (size == 1) draw_pixel(x+i, y+j, bg); else fill_rect(x+i*size, y+j*size, size, size, bg); } } } // If opaque, draw vertical line for last column if (bg != color) { if (size == 1) write_fast_v_line(x+5, y, 8, bg); else fill_rect(x+5*size, y, size, 8*size, bg); } } else { // Character is assumed previously filtered by write() to eliminate // newlines, returns, non-printable characters, etc. Calling // drawChar() directly with 'bad' characters of font may cause mayhem! c -= static_cast(PGM_READ_BYTE(&gfx_font_->first)); auto glyph = &(static_cast(PGM_READ_POINTER(&gfx_font_->glyph))[c]); auto bitmap = static_cast(PGM_READ_POINTER(&gfx_font_->bitmap)); short bo = PGM_READ_WORD(&glyph->bitmap_offset); auto w = PGM_READ_BYTE(&glyph->width); auto h = PGM_READ_BYTE(&glyph->height); char xo = PGM_READ_BYTE(&glyph->x_offset); char yo = PGM_READ_BYTE(&glyph->y_offset); short xo16 = 0, yo16 = 0; if (size > 1) { xo16 = xo; yo16 = yo; } // Todo: Add character clipping here // NOTE: THERE IS NO 'BACKGROUND' COLOR OPTION ON CUSTOM FONTS. // THIS IS ON PURPOSE AND BY DESIGN. The background color feature // has typically been used with the 'classic' font to overwrite old // screen contents with new data. This ONLY works because the // characters are a uniform size; it's not a sensible thing to do with // proportionally-spaced fonts with glyphs of varying sizes (and that // may overlap). To replace previously-drawn text when using a custom // font, use the getTextBounds() function to determine the smallest // rectangle encompassing a string, erase the area with fillRect(), // then draw new text. This WILL unfortunately 'blink' the text, but // is unavoidable. Drawing 'background' pixels will NOT fix this, // only creates a new set of problems. Have an idea to work around // this (a canvas object type for MCUs that can afford the RAM and // displays supporting setAddrWindow() and pushColors()), but haven't // implemented this yet. unsigned char bits = 0, bit = 0; for (unsigned char yy = 0; yy < h; yy++) { for (unsigned char xx=0; xx width_) { // Off right? cursor_x_ = 0; // Reset x to zero, cursor_y_ += textsize_ * 8; // advance y one line } draw_char(cursor_x_, cursor_y_, c, textcolor_, textbgcolor_, textsize_); cursor_x_ += textsize_ * 6; // Advance x one char } } else { // Custom font if (c == '\n') { cursor_x_ = 0; cursor_y_ += static_cast(textsize_) * static_cast(PGM_READ_BYTE(&gfx_font_->y_advance)); } else if (c != '\r') { auto first = PGM_READ_BYTE(&gfx_font_->first); if (c >= first && c <= static_cast(PGM_READ_BYTE(&gfx_font_->last))) { auto glyph = &static_cast(PGM_READ_POINTER(&gfx_font_->glyph))[c - first]; auto w = PGM_READ_BYTE(&glyph->width); auto h = PGM_READ_BYTE(&glyph->height); if (w > 0 && h > 0) { // Is there an associated bitmap? auto xo = static_cast(PGM_READ_BYTE(&glyph->x_offset)); // sic if (wrap_ && cursor_x_ + textsize_ * (xo + w) > width_) { cursor_x_ = 0; cursor_y_ += static_cast(textsize_) *static_cast(PGM_READ_BYTE(&gfx_font_->y_advance)); } draw_char(cursor_x_, cursor_y_, c, textcolor_, textbgcolor_, textsize_); } cursor_x_ += static_cast(PGM_READ_BYTE(&glyph->x_advance)) * static_cast(textsize_); } } } return 1; } /**************************************************************** * Function Name : print * Description : Base function for printing strings * Returns : No. of characters printed * Params : @buffer: Ptr to buffer containing the string * @size: Length of the string. ****************************************************************/ short ssd1306::print(const unsigned char *buffer, short size) { short n = 0; while (size--) { if (oled_write(*buffer++)) n++; else break; } return n; } ssd1306::ssd1306( unsigned bus, unsigned device, unsigned reset, unsigned dc) : spi_(bus, device), reset_(reset), dc_(dc) { reset_.set_direction(gpio::output); reset_.set_value(gpio::low); usleep(50'000); reset_.set_value(gpio::high); dc_.set_direction(gpio::output); dc_.stream_open(); } ssd1306::~ssd1306() { dc_.stream_close(); } /**************************************************************** * Function Name : print_str * Description : Print strings * Returns : No. of characters printed * Params : @strPtr: Ptr to buffer containing the string ****************************************************************/ short ssd1306::print_str(const char *strPtr) { return print(TO_CONST_UCHAR_STR(strPtr), strlen(strPtr)); } /**************************************************************** * Function Name : println * Description : Move to next line * Returns : No. of characters printed * Params : NONE. ****************************************************************/ short ssd1306::println() { return print_str("\r\n"); } /**************************************************************** * Function Name : print_strln * Description : Print strings and move to next line * Returns : No. of characters printed * Params : @strPtr: Ptr to buffer containing the string ****************************************************************/ short ssd1306::print_strln(const char *strPtr) { auto n = print(TO_CONST_UCHAR_STR(strPtr), strlen(strPtr)); n += print_str("\r\n"); return n; } /*---------------------------------------------------------------------------- * NUMBERS HANDLING API's ----------------------------------------------------------------------------*/ /**************************************************************** * Function Name : printNumber * Description : Base function to print unsigned numbers * Returns : No. of characters printed * Params : @n: Number * @base: Base e.g. HEX, BIN... ****************************************************************/ short ssd1306::print_number(unsigned long n, unsigned char base) { char buf[8 * sizeof(long) + 1]; // Assumes 8-bit chars plus zero byte. auto *str = &buf[sizeof(buf) - 1]; *str = '\0'; // prevent crash if called with base == 1 if (base < 2) base = 10; do { auto m = n; n /= base; char c = m - base * n; *--str = c < 10 ? c + '0' : c + 'A' - 10; } while(n); //return oled_write((unsigned char)str); return print_str(str); } /**************************************************************** * Function Name : printNumber_UL * Description : Print unsigned long data types * Returns : No. of characters printed * Params : @n: Number * @base: Base e.g. HEX, BIN... ****************************************************************/ short ssd1306::print_number_ul(unsigned long n, int base) { if (base == 0) return oled_write(n); return print_number(n, base); } /**************************************************************** * Function Name : printNumber_UL_ln * Description : Print unsigned long & advance to next line * Returns : No. of characters printed * Params : @n: Number * @base: Base e.g. HEX, BIN... ****************************************************************/ short ssd1306::print_number_ul_ln(unsigned long num, int base) { auto n = print_number(num, base); n += println(); return n; } /**************************************************************** * Function Name : printNumber_UI * Description : Print unsigned int data types * Returns : No. of characters printed * Params : @n: Number * @base: Base e.g. HEX, BIN... ****************************************************************/ short ssd1306::print_number_ui(unsigned int n, int base) { return print_number(static_cast(n), base); } /**************************************************************** * Function Name : printNumber_UI_ln * Description : Print unsigned int & advance to next line * Returns : No. of characters printed * Params : @n: Number * @base: Base e.g. HEX, BIN... ****************************************************************/ short ssd1306::print_number_ui_ln(unsigned int n, int base) { auto a = print_number(static_cast(n), base); a += println(); return a; } /**************************************************************** * Function Name : printNumber_UC * Description : Print unsigned char data types * Returns : No. of characters printed * Params : @n: Number * @base: Base e.g. HEX, BIN... ****************************************************************/ short ssd1306::print_number_uc(unsigned char b, int base) { return print_number(static_cast(b), base); } /**************************************************************** * Function Name : printNumber_UC_ln * Description : Print unsigned char & advance to next line * Returns : No. of characters printed * Params : @n: Number * @base: Base e.g. HEX, BIN... ****************************************************************/ short ssd1306::print_number_uc_ln(unsigned char b, int base) { auto n = print_number(static_cast(b), base); n += println(); return n; } /**************************************************************** * Function Name : printNumber_L * Description : Print Long data types * Returns : No. of characters printed * Params : @n: Number * @base: Base e.g. HEX, BIN... ****************************************************************/ short ssd1306::print_number_l(long n, int base) { if (base == 0) { return oled_write(n); } if (base == 10) { if (n < 0) { auto t = oled_write('-'); n = -n; return print_number(n, 10) + t; } return print_number(n, 10); } return print_number(n, base); } /**************************************************************** * Function Name : printNumber_UC_ln * Description : Print long & advance to next line * Returns : No. of characters printed * Params : @n: Number * @base: Base e.g. HEX, BIN... ****************************************************************/ short ssd1306::print_number_l_ln(long num, int base) { auto n = print_number_l(num, base); n += println(); return n; } /**************************************************************** * Function Name : printNumber_I * Description : Print int data types * Returns : No. of characters printed * Params : @n: Number * @base: Base e.g. HEX, BIN... ****************************************************************/ short ssd1306::print_number_i(int n, int base) { return print_number_l(static_cast(n), base); } /**************************************************************** * Function Name : printNumber_I_ln * Description : Print int & advance to next line * Returns : No. of characters printed * Params : @n: Number * @base: Base e.g. HEX, BIN... ****************************************************************/ short ssd1306::print_number_i_ln(int n, int base) { auto a = print_number_l(static_cast(n), base); a += println(); return a; } /**************************************************************** * Function Name : printFloat * Description : Print floating Pt. No's. * Returns : No. of characters printed * Params : @n: Number * @digits: Resolution ****************************************************************/ short ssd1306::print_float(double number, unsigned char digits) { short n = 0; // Round correctly so that print(1.999, 2) prints as "2.00" auto rounding = 0.5; if (std::isnan(number)) return print_str("nan"); if (std::isinf(number)) return print_str("inf"); if (number > 4294967040.0) return print_str("ovf"); // constant determined empirically if (number < -4294967040.0) return print_str("ovf"); // constant determined empirically // Handle negative numbers if (number < 0.0) { n += oled_write('-'); number = -number; } for (auto i = 0; i < digits; ++i) rounding /= 10.0; number += rounding; // Extract the integer part of the number and print it auto int_part = static_cast(number); auto remainder = number - static_cast(int_part); n += print_number_ul(int_part,DEC); // Print the decimal point, but only if there are digits beyond if (digits > 0) { n += print_str("."); } // Extract digits from the remainder one at a time while(digits-- > 0) { remainder *= 10.0; double to_print = static_cast(remainder); n += print_number_i(to_print,DEC); remainder -= to_print; } return n; } /**************************************************************** * Function Name : printFloat_ln * Description : Print floating Pt. No and advance to next line * Returns : No. of characters printed * Params : @n: Number * @digits: Resolution ****************************************************************/ short ssd1306::print_float_ln(double num, int digits) { auto n = print_float(num, digits); n += println(); return n; }