1 /* 2 * Author: Marc Graham <marc (at) m2ag.net> 3 * Copyright (c) 2015 Intel Corporation 4 * 5 * Adapted from ssd1308 library. 6 * Author: Yevgeniy Kiveisha <yevgeniy.kiveisha (at) intel.com> 7 * Copyright (c) 2014 Intel Corporation. 8 * 9 * Permission is hereby granted, free of charge, to any person obtaining 10 * a copy of this software and associated documentation files (the 11 * "Software"), to deal in the Software without restriction, including 12 * without limitation the rights to use, copy, modify, merge, publish, 13 * distribute, sublicense, and/or sell copies of the Software, and to 14 * permit persons to whom the Software is furnished to do so, subject to 15 * the following conditions: 16 * 17 * The above copyright notice and this permission notice shall be 18 * included in all copies or substantial portions of the Software. 19 * 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 */ 28 29 #include <string> 30 #include <unistd.h> 31 32 #include "hd44780_bits.h" 33 #include "ssd1306.h" 34 35 using namespace upm; 36 37 SSD1306::SSD1306(int bus_in, int addr_in) : m_i2c_lcd_control(bus_in) 38 { 39 int vccstate = SSD1306_SWITCHCAPVCC; 40 _vccstate = vccstate; 41 42 int LCD_CMD = 0x00; 43 44 m_lcd_control_address = addr_in; 45 m_name = "SSD1306"; 46 47 mraa::Result error = m_i2c_lcd_control.address(m_lcd_control_address); 48 49 if (error != mraa::SUCCESS) { 50 throw std::runtime_error(std::string(__FUNCTION__) + 51 ": mraa_i2c_address() failed"); 52 return; 53 } 54 55 error = m_i2c_lcd_control.frequency(mraa::I2C_FAST); 56 57 if (error != mraa::SUCCESS) { 58 throw std::invalid_argument(std::string(__FUNCTION__) + 59 ": mraa_i2c_frequency(MRAA_I2C_FAST) failed"); 60 return; 61 } 62 63 m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_OFF); // display off 64 usleep(4500); 65 //ADD 1306 stuff 66 // Init sequence for 128x64 OLED module // 0xAE 67 m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETDISPLAYCLOCKDIV); // 0xD5 68 m_i2c_lcd_control.writeReg(LCD_CMD, 0x80); // the suggested ratio 0x80 69 m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETMULTIPLEX); // 0xA8 70 m_i2c_lcd_control.writeReg(LCD_CMD, 0x3F); 71 m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETDISPLAYOFFSET); // 0xD3 72 m_i2c_lcd_control.writeReg(LCD_CMD, 0x0); // no offset 73 m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETSTARTLINE | 0x0); // line #0 74 m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_CHARGEPUMP); // 0x8D 75 if (vccstate == SSD1306_EXTERNALVCC) { 76 m_i2c_lcd_control.writeReg(LCD_CMD, 0x10); 77 } else { 78 m_i2c_lcd_control.writeReg(LCD_CMD,0x14); 79 } 80 m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_MEMORYMODE); // 0x20 81 m_i2c_lcd_control.writeReg(LCD_CMD, 0x00); // 0x0 act like ks0108 82 m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SEGREMAP | 0x1); 83 m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_COMSCANDEC); 84 m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETCOMPINS); // 0xDA 85 m_i2c_lcd_control.writeReg(LCD_CMD, 0x12); 86 m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETCONTRAST); // 0x81 87 if (vccstate == SSD1306_EXTERNALVCC) { 88 m_i2c_lcd_control.writeReg(LCD_CMD, 0x9F); 89 } else { 90 m_i2c_lcd_control.writeReg(LCD_CMD, 0xCF); 91 } 92 m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETPRECHARGE); // 0xd9 93 if (vccstate == SSD1306_EXTERNALVCC) { 94 m_i2c_lcd_control.writeReg(LCD_CMD, 0x22); 95 } else { 96 m_i2c_lcd_control.writeReg(LCD_CMD,0xF1); 97 } 98 m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETVCOMDETECT); // 0xDB 99 m_i2c_lcd_control.writeReg(LCD_CMD, 0x40); 100 m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_DISPLAYALLON_RESUME); // 0xA4 101 m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_SET_NORMAL_1306); // 0xA6 102 103 //END 1306 Stuff 104 m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_ON); // display on 105 usleep(4500); 106 setNormalDisplay(); // set to normal display '1' is ON 107 108 clear(); 109 setAddressingMode(PAGE); 110 } 111 112 SSD1306::~SSD1306() 113 { 114 } 115 116 mraa::Result 117 SSD1306::draw(uint8_t* data, int bytes) 118 { 119 mraa::Result error = mraa::SUCCESS; 120 121 setAddressingMode(HORIZONTAL); 122 for (int idx = 0; idx < bytes; idx++) { 123 m_i2c_lcd_control.writeReg(LCD_DATA, data[idx]); 124 } 125 126 return error; 127 } 128 129 /* 130 * ************** 131 * virtual area 132 * ************** 133 */ 134 mraa::Result 135 SSD1306::write(std::string msg) 136 { 137 mraa::Result error = mraa::SUCCESS; 138 139 setAddressingMode(PAGE); 140 for (std::string::size_type i = 0; i < msg.size(); ++i) { 141 writeChar(msg[i]); 142 } 143 144 return error; 145 } 146 147 mraa::Result 148 SSD1306::setCursor(int row, int column) 149 { 150 mraa::Result error = mraa::SUCCESS; 151 152 error = m_i2c_lcd_control.writeReg(LCD_CMD, BASE_PAGE_START_ADDR + row); // set page address 153 error = m_i2c_lcd_control.writeReg(LCD_CMD, 154 BASE_LOW_COLUMN_ADDR + (8 * column & 0x0F)); // set column 155 // lower address 156 error = m_i2c_lcd_control.writeReg(LCD_CMD, 157 BASE_HIGH_COLUMN_ADDR + 158 ((8 * column >> 4) & 0x0F)); // set column higher address 159 160 return error; 161 } 162 163 mraa::Result 164 SSD1306::clear() 165 { 166 mraa::Result error = mraa::SUCCESS; 167 uint8_t columnIdx, rowIdx; 168 169 m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_OFF); // display off 170 for (rowIdx = 0; rowIdx < 8; rowIdx++) { 171 setCursor(rowIdx, 0); 172 173 // clear all columns 174 for (columnIdx = 0; columnIdx < 16; columnIdx++) { 175 writeChar(' '); 176 } 177 } 178 m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_ON); // display on 179 home(); 180 181 return error; 182 } 183 184 mraa::Result 185 SSD1306::home() 186 { 187 return setCursor(0, 0); 188 } 189 190 /* 191 * ************** 192 * private area 193 * ************** 194 */ 195 mraa::Result 196 SSD1306::writeChar(uint8_t value) 197 { 198 mraa::Result rv; 199 if (value < 0x20 || value > 0x7F) { 200 value = 0x20; // space 201 } 202 203 for (uint8_t idx = 0; idx < 8; idx++) { 204 rv = m_i2c_lcd_control.writeReg(LCD_DATA, BasicFont[value - 32][idx]); 205 } 206 207 return rv; 208 } 209 210 mraa::Result 211 SSD1306::setNormalDisplay() 212 { 213 return m_i2c_lcd_control.writeReg(LCD_CMD, 214 DISPLAY_CMD_SET_NORMAL_1306); // set to normal display '1' is 215 // ON 216 } 217 218 mraa::Result 219 SSD1306::setAddressingMode(displayAddressingMode mode) 220 { 221 mraa::Result rv; 222 rv =m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_MEM_ADDR_MODE); // set addressing mode 223 rv =m_i2c_lcd_control.writeReg(LCD_CMD, mode); // set page addressing mode 224 return rv; 225 } 226 227 228 mraa::Result 229 SSD1306::invert(bool i) 230 { 231 mraa::Result rv; 232 if(i){ 233 rv = m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_SET_INVERT_1306); 234 } else { 235 rv = m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_SET_NORMAL_1306); 236 } 237 return rv; 238 } 239 240 241 void SSD1306::startscrollright(uint8_t start, uint8_t stop){ 242 m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_RIGHT_HORIZONTAL_SCROLL); 243 m_i2c_lcd_control.writeReg(LCD_CMD,0X00); 244 m_i2c_lcd_control.writeReg(LCD_CMD,start); 245 m_i2c_lcd_control.writeReg(LCD_CMD,0X00); 246 m_i2c_lcd_control.writeReg(LCD_CMD,stop); 247 m_i2c_lcd_control.writeReg(LCD_CMD,0X00); 248 m_i2c_lcd_control.writeReg(LCD_CMD,0XFF); 249 m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_ACTIVATE_SCROLL); 250 } 251 252 253 void SSD1306::startscrollleft(uint8_t start, uint8_t stop){ 254 m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_LEFT_HORIZONTAL_SCROLL); 255 m_i2c_lcd_control.writeReg(LCD_CMD,0X00); 256 m_i2c_lcd_control.writeReg(LCD_CMD,start); 257 m_i2c_lcd_control.writeReg(LCD_CMD,0X00); 258 m_i2c_lcd_control.writeReg(LCD_CMD,stop); 259 m_i2c_lcd_control.writeReg(LCD_CMD,0X00); 260 m_i2c_lcd_control.writeReg(LCD_CMD,0XFF); 261 m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_ACTIVATE_SCROLL); 262 } 263 264 void SSD1306::startscrolldiagright(uint8_t start, uint8_t stop){ 265 m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SET_VERTICAL_SCROLL_AREA); 266 m_i2c_lcd_control.writeReg(LCD_CMD, 0X00); 267 m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_LCDHEIGHT); 268 m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL); 269 m_i2c_lcd_control.writeReg(LCD_CMD, 0X00); 270 m_i2c_lcd_control.writeReg(LCD_CMD, start); 271 m_i2c_lcd_control.writeReg(LCD_CMD, 0X00); 272 m_i2c_lcd_control.writeReg(LCD_CMD, stop); 273 m_i2c_lcd_control.writeReg(LCD_CMD, 0X01); 274 m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_ACTIVATE_SCROLL); 275 } 276 277 void SSD1306::startscrolldiagleft(uint8_t start, uint8_t stop){ 278 m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SET_VERTICAL_SCROLL_AREA); 279 m_i2c_lcd_control.writeReg(LCD_CMD, 0X00); 280 m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_LCDHEIGHT); 281 m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL); 282 m_i2c_lcd_control.writeReg(LCD_CMD, 0X00); 283 m_i2c_lcd_control.writeReg(LCD_CMD, start); 284 m_i2c_lcd_control.writeReg(LCD_CMD, 0X00); 285 m_i2c_lcd_control.writeReg(LCD_CMD, stop); 286 m_i2c_lcd_control.writeReg(LCD_CMD, 0X01); 287 m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_ACTIVATE_SCROLL); 288 } 289 290 void SSD1306::stopscroll(void){ 291 m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_DEACTIVATE_SCROLL); 292 } 293 294 // Dim the display 295 // dim = true: display is dimmed 296 // dim = false: display is normal 297 void SSD1306::dim(bool dim) { 298 uint8_t contrast; 299 300 if (dim) { 301 contrast = 0; // Dimmed display 302 } else { 303 if (_vccstate == SSD1306_EXTERNALVCC) { 304 contrast = 0x9F; 305 } else { 306 contrast = 0xCF; 307 } 308 } 309 // the range of contrast to too small to be really useful 310 // it is useful to dim the display 311 m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_SETCONTRAST); 312 m_i2c_lcd_control.writeReg(LCD_CMD,contrast); 313 } 314