Adafruit_GrayOLED.cpp (15385B)
1 /*! 2 * @file Adafruit_GrayOLED.cpp 3 * 4 * This is documentation for Adafruit's generic library for grayscale 5 * OLED displays: http://www.adafruit.com/category/63_98 6 * 7 * These displays use I2C or SPI to communicate. I2C requires 2 pins 8 * (SCL+SDA) and optionally a RESET pin. SPI requires 4 pins (MOSI, SCK, 9 * select, data/command) and optionally a reset pin. Hardware SPI or 10 * 'bitbang' software SPI are both supported. 11 * 12 * Adafruit invests time and resources providing this open source code, 13 * please support Adafruit and open-source hardware by purchasing 14 * products from Adafruit! 15 * 16 */ 17 18 #if !defined(__AVR_ATtiny85__) // Not for ATtiny, at all 19 20 #include "Adafruit_GrayOLED.h" 21 #include <Adafruit_GFX.h> 22 23 // SOME DEFINES AND STATIC VARIABLES USED INTERNALLY ----------------------- 24 25 #define grayoled_swap(a, b) \ 26 (((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b))) ///< No-temp-var swap operation 27 28 // CONSTRUCTORS, DESTRUCTOR ------------------------------------------------ 29 30 /*! 31 @brief Constructor for I2C-interfaced OLED displays. 32 @param bpp Bits per pixel, 1 for monochrome, 4 for 16-gray 33 @param w 34 Display width in pixels 35 @param h 36 Display height in pixels 37 @param twi 38 Pointer to an existing TwoWire instance (e.g. &Wire, the 39 microcontroller's primary I2C bus). 40 @param rst_pin 41 Reset pin (using Arduino pin numbering), or -1 if not used 42 (some displays might be wired to share the microcontroller's 43 reset pin). 44 @param clkDuring 45 Speed (in Hz) for Wire transmissions in library calls. 46 Defaults to 400000 (400 KHz), a known 'safe' value for most 47 microcontrollers, and meets the OLED datasheet spec. 48 Some systems can operate I2C faster (800 KHz for ESP32, 1 MHz 49 for many other 32-bit MCUs), and some (perhaps not all) 50 Many OLED's can work with this -- so it's optionally be specified 51 here and is not a default behavior. (Ignored if using pre-1.5.7 52 Arduino software, which operates I2C at a fixed 100 KHz.) 53 @param clkAfter 54 Speed (in Hz) for Wire transmissions following library 55 calls. Defaults to 100000 (100 KHz), the default Arduino Wire 56 speed. This is done rather than leaving it at the 'during' speed 57 because other devices on the I2C bus might not be compatible 58 with the faster rate. (Ignored if using pre-1.5.7 Arduino 59 software, which operates I2C at a fixed 100 KHz.) 60 @note Call the object's begin() function before use -- buffer 61 allocation is performed there! 62 */ 63 Adafruit_GrayOLED::Adafruit_GrayOLED(uint8_t bpp, uint16_t w, uint16_t h, 64 TwoWire *twi, int8_t rst_pin, 65 uint32_t clkDuring, uint32_t clkAfter) 66 : Adafruit_GFX(w, h), i2c_preclk(clkDuring), i2c_postclk(clkAfter), 67 buffer(NULL), dcPin(-1), csPin(-1), rstPin(rst_pin), _bpp(bpp) { 68 i2c_dev = NULL; 69 _theWire = twi; 70 } 71 72 /*! 73 @brief Constructor for SPI GrayOLED displays, using software (bitbang) 74 SPI. 75 @param bpp Bits per pixel, 1 for monochrome, 4 for 16-gray 76 @param w 77 Display width in pixels 78 @param h 79 Display height in pixels 80 @param mosi_pin 81 MOSI (master out, slave in) pin (using Arduino pin numbering). 82 This transfers serial data from microcontroller to display. 83 @param sclk_pin 84 SCLK (serial clock) pin (using Arduino pin numbering). 85 This clocks each bit from MOSI. 86 @param dc_pin 87 Data/command pin (using Arduino pin numbering), selects whether 88 display is receiving commands (low) or data (high). 89 @param rst_pin 90 Reset pin (using Arduino pin numbering), or -1 if not used 91 (some displays might be wired to share the microcontroller's 92 reset pin). 93 @param cs_pin 94 Chip-select pin (using Arduino pin numbering) for sharing the 95 bus with other devices. Active low. 96 @note Call the object's begin() function before use -- buffer 97 allocation is performed there! 98 */ 99 Adafruit_GrayOLED::Adafruit_GrayOLED(uint8_t bpp, uint16_t w, uint16_t h, 100 int8_t mosi_pin, int8_t sclk_pin, 101 int8_t dc_pin, int8_t rst_pin, 102 int8_t cs_pin) 103 : Adafruit_GFX(w, h), dcPin(dc_pin), csPin(cs_pin), rstPin(rst_pin), 104 _bpp(bpp) { 105 106 spi_dev = new Adafruit_SPIDevice(cs_pin, sclk_pin, -1, mosi_pin, 1000000); 107 } 108 109 /*! 110 @brief Constructor for SPI GrayOLED displays, using native hardware SPI. 111 @param bpp Bits per pixel, 1 for monochrome, 4 for 16-gray 112 @param w 113 Display width in pixels 114 @param h 115 Display height in pixels 116 @param spi 117 Pointer to an existing SPIClass instance (e.g. &SPI, the 118 microcontroller's primary SPI bus). 119 @param dc_pin 120 Data/command pin (using Arduino pin numbering), selects whether 121 display is receiving commands (low) or data (high). 122 @param rst_pin 123 Reset pin (using Arduino pin numbering), or -1 if not used 124 (some displays might be wired to share the microcontroller's 125 reset pin). 126 @param cs_pin 127 Chip-select pin (using Arduino pin numbering) for sharing the 128 bus with other devices. Active low. 129 @param bitrate 130 SPI clock rate for transfers to this display. Default if 131 unspecified is 8000000UL (8 MHz). 132 @note Call the object's begin() function before use -- buffer 133 allocation is performed there! 134 */ 135 Adafruit_GrayOLED::Adafruit_GrayOLED(uint8_t bpp, uint16_t w, uint16_t h, 136 SPIClass *spi, int8_t dc_pin, 137 int8_t rst_pin, int8_t cs_pin, 138 uint32_t bitrate) 139 : Adafruit_GFX(w, h), dcPin(dc_pin), csPin(cs_pin), rstPin(rst_pin), 140 _bpp(bpp) { 141 142 spi_dev = new Adafruit_SPIDevice(cs_pin, bitrate, SPI_BITORDER_MSBFIRST, 143 SPI_MODE0, spi); 144 } 145 146 /*! 147 @brief Destructor for Adafruit_GrayOLED object. 148 */ 149 Adafruit_GrayOLED::~Adafruit_GrayOLED(void) { 150 if (buffer) { 151 free(buffer); 152 buffer = NULL; 153 } 154 if (spi_dev) 155 delete spi_dev; 156 if (i2c_dev) 157 delete i2c_dev; 158 } 159 160 // LOW-LEVEL UTILS --------------------------------------------------------- 161 162 /*! 163 @brief Issue single command byte to OLED, using I2C or hard/soft SPI as 164 needed. 165 @param c The single byte command 166 */ 167 void Adafruit_GrayOLED::oled_command(uint8_t c) { 168 if (i2c_dev) { // I2C 169 uint8_t buf[2] = {0x00, c}; // Co = 0, D/C = 0 170 i2c_dev->write(buf, 2); 171 } else { // SPI (hw or soft) -- transaction started in calling function 172 digitalWrite(dcPin, LOW); 173 spi_dev->write(&c, 1); 174 } 175 } 176 177 // Issue list of commands to GrayOLED 178 /*! 179 @brief Issue multiple bytes of commands OLED, using I2C or hard/soft SPI as 180 needed. 181 @param c Pointer to the command array 182 @param n The number of bytes in the command array 183 @returns True for success on ability to write the data in I2C. 184 */ 185 186 bool Adafruit_GrayOLED::oled_commandList(const uint8_t *c, uint8_t n) { 187 if (i2c_dev) { // I2C 188 uint8_t dc_byte = 0x00; // Co = 0, D/C = 0 189 if (!i2c_dev->write((uint8_t *)c, n, true, &dc_byte, 1)) { 190 return false; 191 } 192 } else { // SPI -- transaction started in calling function 193 digitalWrite(dcPin, LOW); 194 if (!spi_dev->write((uint8_t *)c, n)) { 195 return false; 196 } 197 } 198 return true; 199 } 200 201 // ALLOCATE & INIT DISPLAY ------------------------------------------------- 202 203 /*! 204 @brief Allocate RAM for image buffer, initialize peripherals and pins. 205 Note that subclasses must call this before other begin() init 206 @param addr 207 I2C address of corresponding oled display. 208 SPI displays (hardware or software) do not use addresses, but 209 this argument is still required. Default if unspecified is 0x3C. 210 @param reset 211 If true, and if the reset pin passed to the constructor is 212 valid, a hard reset will be performed before initializing the 213 display. If using multiple oled displays on the same bus, and 214 if they all share the same reset pin, you should only pass true 215 on the first display being initialized, false on all others, 216 else the already-initialized displays would be reset. Default if 217 unspecified is true. 218 @return true on successful allocation/init, false otherwise. 219 Well-behaved code should check the return value before 220 proceeding. 221 @note MUST call this function before any drawing or updates! 222 */ 223 bool Adafruit_GrayOLED::_init(uint8_t addr, bool reset) { 224 225 // attempt to malloc the bitmap framebuffer 226 if ((!buffer) && 227 !(buffer = (uint8_t *)malloc(_bpp * WIDTH * ((HEIGHT + 7) / 8)))) { 228 return false; 229 } 230 231 // Reset OLED if requested and reset pin specified in constructor 232 if (reset && (rstPin >= 0)) { 233 pinMode(rstPin, OUTPUT); 234 digitalWrite(rstPin, HIGH); 235 delay(10); // VDD goes high at start, pause 236 digitalWrite(rstPin, LOW); // Bring reset low 237 delay(10); // Wait 10 ms 238 digitalWrite(rstPin, HIGH); // Bring out of reset 239 delay(10); 240 } 241 242 // Setup pin directions 243 if (_theWire) { // using I2C 244 i2c_dev = new Adafruit_I2CDevice(addr, _theWire); 245 // look for i2c address: 246 if (!i2c_dev || !i2c_dev->begin()) { 247 return false; 248 } 249 } else { // Using one of the SPI modes, either soft or hardware 250 if (!spi_dev || !spi_dev->begin()) { 251 return false; 252 } 253 pinMode(dcPin, OUTPUT); // Set data/command pin as output 254 } 255 256 clearDisplay(); 257 258 // set max dirty window 259 window_x1 = 0; 260 window_y1 = 0; 261 window_x2 = WIDTH - 1; 262 window_y2 = HEIGHT - 1; 263 264 return true; // Success 265 } 266 267 // DRAWING FUNCTIONS ------------------------------------------------------- 268 269 /*! 270 @brief Set/clear/invert a single pixel. This is also invoked by the 271 Adafruit_GFX library in generating many higher-level graphics 272 primitives. 273 @param x 274 Column of display -- 0 at left to (screen width - 1) at right. 275 @param y 276 Row of display -- 0 at top to (screen height -1) at bottom. 277 @param color 278 Pixel color, one of: MONOOLED_BLACK, MONOOLED_WHITE or 279 MONOOLED_INVERT. 280 @note Changes buffer contents only, no immediate effect on display. 281 Follow up with a call to display(), or with other graphics 282 commands as needed by one's own application. 283 */ 284 void Adafruit_GrayOLED::drawPixel(int16_t x, int16_t y, uint16_t color) { 285 if ((x >= 0) && (x < width()) && (y >= 0) && (y < height())) { 286 // Pixel is in-bounds. Rotate coordinates if needed. 287 switch (getRotation()) { 288 case 1: 289 grayoled_swap(x, y); 290 x = WIDTH - x - 1; 291 break; 292 case 2: 293 x = WIDTH - x - 1; 294 y = HEIGHT - y - 1; 295 break; 296 case 3: 297 grayoled_swap(x, y); 298 y = HEIGHT - y - 1; 299 break; 300 } 301 302 // adjust dirty window 303 window_x1 = min(window_x1, x); 304 window_y1 = min(window_y1, y); 305 window_x2 = max(window_x2, x); 306 window_y2 = max(window_y2, y); 307 308 if (_bpp == 1) { 309 switch (color) { 310 case MONOOLED_WHITE: 311 buffer[x + (y / 8) * WIDTH] |= (1 << (y & 7)); 312 break; 313 case MONOOLED_BLACK: 314 buffer[x + (y / 8) * WIDTH] &= ~(1 << (y & 7)); 315 break; 316 case MONOOLED_INVERSE: 317 buffer[x + (y / 8) * WIDTH] ^= (1 << (y & 7)); 318 break; 319 } 320 } 321 if (_bpp == 4) { 322 uint8_t *pixelptr = &buffer[x / 2 + (y * WIDTH / 2)]; 323 // Serial.printf("(%d, %d) -> offset %d\n", x, y, x/2 + (y * WIDTH / 2)); 324 if (x % 2 == 0) { // even, left nibble 325 uint8_t t = pixelptr[0] & 0x0F; 326 t |= (color & 0xF) << 4; 327 pixelptr[0] = t; 328 } else { // odd, right lower nibble 329 uint8_t t = pixelptr[0] & 0xF0; 330 t |= color & 0xF; 331 pixelptr[0] = t; 332 } 333 } 334 } 335 } 336 337 /*! 338 @brief Clear contents of display buffer (set all pixels to off). 339 @note Changes buffer contents only, no immediate effect on display. 340 Follow up with a call to display(), or with other graphics 341 commands as needed by one's own application. 342 */ 343 void Adafruit_GrayOLED::clearDisplay(void) { 344 memset(buffer, 0, _bpp * WIDTH * ((HEIGHT + 7) / 8)); 345 // set max dirty window 346 window_x1 = 0; 347 window_y1 = 0; 348 window_x2 = WIDTH - 1; 349 window_y2 = HEIGHT - 1; 350 } 351 352 /*! 353 @brief Return color of a single pixel in display buffer. 354 @param x 355 Column of display -- 0 at left to (screen width - 1) at right. 356 @param y 357 Row of display -- 0 at top to (screen height -1) at bottom. 358 @return true if pixel is set (usually MONOOLED_WHITE, unless display invert 359 mode is enabled), false if clear (MONOOLED_BLACK). 360 @note Reads from buffer contents; may not reflect current contents of 361 screen if display() has not been called. 362 */ 363 bool Adafruit_GrayOLED::getPixel(int16_t x, int16_t y) { 364 if ((x >= 0) && (x < width()) && (y >= 0) && (y < height())) { 365 // Pixel is in-bounds. Rotate coordinates if needed. 366 switch (getRotation()) { 367 case 1: 368 grayoled_swap(x, y); 369 x = WIDTH - x - 1; 370 break; 371 case 2: 372 x = WIDTH - x - 1; 373 y = HEIGHT - y - 1; 374 break; 375 case 3: 376 grayoled_swap(x, y); 377 y = HEIGHT - y - 1; 378 break; 379 } 380 return (buffer[x + (y / 8) * WIDTH] & (1 << (y & 7))); 381 } 382 return false; // Pixel out of bounds 383 } 384 385 /*! 386 @brief Get base address of display buffer for direct reading or writing. 387 @return Pointer to an unsigned 8-bit array, column-major, columns padded 388 to full byte boundary if needed. 389 */ 390 uint8_t *Adafruit_GrayOLED::getBuffer(void) { return buffer; } 391 392 // OTHER HARDWARE SETTINGS ------------------------------------------------- 393 394 /*! 395 @brief Enable or disable display invert mode (white-on-black vs 396 black-on-white). Handy for testing! 397 @param i 398 If true, switch to invert mode (black-on-white), else normal 399 mode (white-on-black). 400 @note This has an immediate effect on the display, no need to call the 401 display() function -- buffer contents are not changed, rather a 402 different pixel mode of the display hardware is used. When 403 enabled, drawing MONOOLED_BLACK (value 0) pixels will actually draw 404 white, MONOOLED_WHITE (value 1) will draw black. 405 */ 406 void Adafruit_GrayOLED::invertDisplay(bool i) { 407 oled_command(i ? GRAYOLED_INVERTDISPLAY : GRAYOLED_NORMALDISPLAY); 408 } 409 410 /*! 411 @brief Adjust the display contrast. 412 @param level The contrast level from 0 to 0x7F 413 @note This has an immediate effect on the display, no need to call the 414 display() function -- buffer contents are not changed. 415 */ 416 void Adafruit_GrayOLED::setContrast(uint8_t level) { 417 uint8_t cmd[] = {GRAYOLED_SETCONTRAST, level}; 418 oled_commandList(cmd, 2); 419 } 420 421 #endif /* ATTIN85 not supported */