├── README ├── LICENSE ├── driver ├── ssd1306.h └── i2c_master.h ├── font.h ├── i2c_master.c └── ssd1306.c /README: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derkst/ESP8266-OLED/HEAD/README -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Derived license(applies to graphics library part) 2 | -------------------------------------------------- 3 | 4 | Software License Agreement (BSD License) 5 | 6 | Copyright (c) 2012 Adafruit Industries. All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are met: 10 | - Redistributions of source code must retain the above copyright notice, 11 | this list of conditions and the following disclaimer. 12 | - Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | POSSIBILITY OF SUCH DAMAGE. 26 | 27 | -------------------------------------------------------------------------------- /driver/ssd1306.h: -------------------------------------------------------------------------------- 1 | #ifndef _SSD1306_H 2 | #define _SSD1306_H 3 | 4 | #define BLACK 0 5 | #define WHITE 1 6 | #define INVERSE 2 7 | 8 | typedef enum{ 9 | SCROLL_RIGHT = 0x26, 10 | SCROLL_LEFT = 0x2A 11 | }SCROLL_DIR; 12 | 13 | typedef enum{ 14 | SCROLL_SPEED_0 = 0x03, // slowest 15 | SCROLL_SPEED_1 = 0x02, 16 | SCROLL_SPEED_2 = 0x01, 17 | SCROLL_SPEED_3 = 0x06, 18 | SCROLL_SPEED_4 = 0x00, 19 | SCROLL_SPEED_5 = 0x05, 20 | SCROLL_SPEED_6 = 0x04, 21 | SCROLL_SPEED_7 = 0x07 // fastest 22 | }SCROLL_SPEED; 23 | 24 | typedef enum{ 25 | SCROLL_PAGE_0 = 0, 26 | SCROLL_PAGE_1, 27 | SCROLL_PAGE_2, 28 | SCROLL_PAGE_3, 29 | SCROLL_PAGE_4, 30 | SCROLL_PAGE_5, 31 | SCROLL_PAGE_6, 32 | SCROLL_PAGE_7 33 | }SCROLL_AREA; 34 | 35 | void display_init( uint8 i2caddr ); 36 | void display_off( uint8 i2caddr ); 37 | void display_update(void); 38 | void display_clear(void); 39 | void display_stopscroll(void); 40 | void display_scroll( SCROLL_AREA start, SCROLL_AREA end, SCROLL_DIR dir, SCROLL_SPEED speed ); 41 | void display_contrast( uint8_t contrast ); 42 | void display_invert( uint8_t invert ); 43 | 44 | 45 | void gfx_drawPixel(int16_t x, int16_t y, uint16_t color); 46 | void gfx_drawLine( int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color ); 47 | void gfx_setCursor( int16_t x, int16_t y ); 48 | void gfx_setTextSize( uint8_t size ); 49 | void gfx_setTextColor( uint16_t color ); 50 | void gfx_setTextBg( uint16_t background ); 51 | void gfx_write( uint8_t ch ); 52 | int16_t gfx_width(void); 53 | int16_t gfx_height(void); 54 | void gfx_print( const char* s ); 55 | void gfx_println( const char* s ); 56 | void gfx_drawRect( int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color ); 57 | void gfx_fillRect( int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color ); 58 | void gfx_drawCircle( int16_t x0, int16_t y0, int16_t r,uint16_t color ); 59 | void gfx_drawTriangle( int16_t x0, int16_t y0,int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color ); 60 | void gfx_setRotation( uint8_t x ); 61 | 62 | #endif // _SSD1306_H 63 | 64 | -------------------------------------------------------------------------------- /driver/i2c_master.h: -------------------------------------------------------------------------------- 1 | #ifndef __I2C_MASTER_H__ 2 | #define __I2C_MASTER_H__ 3 | 4 | 5 | // === NOTE: ======================== 6 | // either the esp8266-07/12 pads are labeld incorrectly or the definitions for 7 | // PERIPHS_IO_MUX_GPIO4/5_U are wrong in eagle_soc.h. 8 | // Anyway, somwhere they are swapped and SCL is actually on GPIO5 and SDA on 9 | // GPIO4 on the 07 and 12 modules with the definitions as below: 10 | #define I2C_MASTER_SDA_MUX PERIPHS_IO_MUX_GPIO5_U 11 | #define I2C_MASTER_SCL_MUX PERIPHS_IO_MUX_GPIO4_U 12 | #define I2C_MASTER_SDA_GPIO 5 13 | #define I2C_MASTER_SCL_GPIO 4 14 | #define I2C_MASTER_SDA_FUNC FUNC_GPIO5 15 | #define I2C_MASTER_SCL_FUNC FUNC_GPIO4 16 | 17 | 18 | /* 19 | #define I2C_MASTER_SDA_MUX PERIPHS_IO_MUX_MTCK_U 20 | #define I2C_MASTER_SCL_MUX PERIPHS_IO_MUX_MTMS_U 21 | #define I2C_MASTER_SDA_GPIO 13 22 | #define I2C_MASTER_SCL_GPIO 14 23 | #define I2C_MASTER_SDA_FUNC FUNC_GPIO13 24 | #define I2C_MASTER_SCL_FUNC FUNC_GPIO14 25 | */ 26 | 27 | /* 28 | #define I2C_MASTER_SDA_MUX PERIPHS_IO_MUX_GPIO2_U 29 | #define I2C_MASTER_SCL_MUX PERIPHS_IO_MUX_MTMS_U 30 | #define I2C_MASTER_SDA_GPIO 2 31 | #define I2C_MASTER_SCL_GPIO 14 32 | #define I2C_MASTER_SDA_FUNC FUNC_GPIO2 33 | #define I2C_MASTER_SCL_FUNC FUNC_GPIO14 34 | */ 35 | 36 | //#define I2C_MASTER_SDA_MUX PERIPHS_IO_MUX_GPIO2_U 37 | //#define I2C_MASTER_SCL_MUX PERIPHS_IO_MUX_GPIO0_U 38 | //#define I2C_MASTER_SDA_GPIO 2 39 | //#define I2C_MASTER_SCL_GPIO 0 40 | //#define I2C_MASTER_SDA_FUNC FUNC_GPIO2 41 | //#define I2C_MASTER_SCL_FUNC FUNC_GPIO0 42 | 43 | #if 0 44 | #define I2C_MASTER_GPIO_SET(pin) \ 45 | gpio_output_set(1<= 0; i--) { 271 | dat = wrdata >> i; 272 | i2c_master_setDC(dat, 0); 273 | i2c_master_wait(I2C_DELAY_US); 274 | i2c_master_setDC(dat, 1); 275 | i2c_master_wait(I2C_DELAY_US); 276 | 277 | if (i == 0) { 278 | i2c_master_wait(I2C_DELAY_US/2+1); //// 279 | } 280 | 281 | i2c_master_setDC(dat, 0); 282 | i2c_master_wait(I2C_DELAY_US); 283 | } 284 | } 285 | 286 | // check for device existence 287 | // return: 0 slave @ i2caddr does not exist, 1 slave @ i2caddr is present 288 | uint8 i2c_slave_exists( uint8 i2caddr ){ 289 | 290 | i2c_master_start(); 291 | i2c_master_writeByte(i2caddr<<1); 292 | uint8 ack = i2c_master_getAck(); 293 | i2c_master_stop(); 294 | 295 | return (ack ? 0 : 1); 296 | 297 | } 298 | 299 | -------------------------------------------------------------------------------- /ssd1306.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | SSD1306 OLED I2C driver 4 | 5 | Derk Steggewentz, 3/2015 6 | 7 | This is a I2C driver for SSD1306 OLED displays including graphics library. 8 | 9 | 10 | The graphics library part (gfx_ functions) is based on the Adafruit graphics library. 11 | 12 | 13 | No license restrictions on my part, do whatever you want as long as you follow the 14 | inherited license requirements (applying to the Adafruit graphics library part). 15 | 16 | 17 | ===== derived license (for graphics library) ====== 18 | 19 | Copyright (c) 2013 Adafruit Industries. All rights reserved. 20 | 21 | Redistribution and use in source and binary forms, with or without 22 | modification, are permitted provided that the following conditions are met: 23 | - Redistributions of source code must retain the above copyright notice, 24 | this list of conditions and the following disclaimer. 25 | - Redistributions in binary form must reproduce the above copyright notice, 26 | this list of conditions and the following disclaimer in the documentation 27 | and/or other materials provided with the distribution. 28 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 29 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 32 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 33 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 34 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 35 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 36 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 37 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 38 | POSSIBILITY OF SUCH DAMAGE. 39 | 40 | */ 41 | 42 | 43 | 44 | #include 45 | #include "espmissingincludes.h" 46 | #include "osapi.h" 47 | 48 | 49 | #if ENABLE_DEBUG 50 | #define LOG_MSG os_printf 51 | #else 52 | #define LOG_MSG(...) 53 | #endif 54 | 55 | #include "driver/i2c_master.h" 56 | #include "driver/ssd1306.h" 57 | #include "font.h" 58 | 59 | 60 | #define DISPLAYWIDTH 128 61 | #define DISPLAYHEIGHT 64 62 | 63 | #define SSD1306_SETCONTRAST 0x81 64 | #define SSD1306_DISPLAYALLON_RESUME 0xA4 65 | #define SSD1306_DISPLAYALLON 0xA5 66 | #define SSD1306_NORMALDISPLAY 0xA6 67 | #define SSD1306_INVERTDISPLAY 0xA7 68 | #define SSD1306_DISPLAYOFF 0xAE 69 | #define SSD1306_DISPLAYON 0xAF 70 | #define SSD1306_SETDISPLAYOFFSET 0xD3 71 | #define SSD1306_SETCOMPINS 0xDA 72 | #define SSD1306_SETVCOMDETECT 0xDB 73 | #define SSD1306_SETDISPLAYCLOCKDIV 0xD5 74 | #define SSD1306_SETPRECHARGE 0xD9 75 | #define SSD1306_SETMULTIPLEX 0xA8 76 | #define SSD1306_SETLOWCOLUMN 0x00 77 | #define SSD1306_SETHIGHCOLUMN 0x10 78 | #define SSD1306_SETSTARTLINE 0x40 79 | #define SSD1306_MEMORYMODE 0x20 80 | #define SSD1306_COLUMNADDR 0x21 81 | #define SSD1306_PAGEADDR 0x22 82 | #define SSD1306_COMSCANINC 0xC0 83 | #define SSD1306_COMSCANDEC 0xC8 84 | #define SSD1306_SEGREMAP 0xA0 85 | #define SSD1306_CHARGEPUMP 0x8D 86 | #define SSD1306_EXTERNALVCC 0x1 87 | #define SSD1306_SWITCHCAPVCC 0x2 88 | #define SSD1306_ACTIVATE_SCROLL 0x2F 89 | #define SSD1306_DEACTIVATE_SCROLL 0x2E 90 | 91 | 92 | // I2C result status 93 | #define TRANSFER_CMPLT (0x00u) 94 | #define TRANSFER_ERROR (0xFFu) 95 | 96 | 97 | static uint32 display_write_buf( uint8* buf, uint16_t size ); 98 | 99 | void gfx_init( int16_t width, int16_t height ); 100 | 101 | static uint8 _i2caddr; 102 | 103 | // display memory buffer ( === MUST INCLUDE === the preceding I2C 0x40 control byte for the display) 104 | static uint8_t SSD1306_buffer[DISPLAYHEIGHT * DISPLAYWIDTH / 8 + 1] = { 0x40 }; 105 | // pointer to actual display memory buffer 106 | static uint8_t* _displaybuf = SSD1306_buffer+1; 107 | static uint16_t _displaybuf_size = sizeof(SSD1306_buffer) - 1; 108 | 109 | // see data sheet page 25 for Graphic Display Data RAM organization 110 | // 8 pages, each page a row of DISPLAYWIDTH bytes 111 | // start address of of row: y/8*DISPLAYWIDTH 112 | // x pos in row: == x 113 | #define GDDRAM_ADDRESS(X,Y) ((_displaybuf)+((Y)/8)*(DISPLAYWIDTH)+(X)) 114 | 115 | // lower 3 bit of y determine vertical pixel position (pos 0...7) in GDDRAM byte 116 | // (y&0x07) == position of pixel on row (page). LSB is top, MSB bottom 117 | #define GDDRAM_PIXMASK(Y) (1 << ((Y)&0x07)) 118 | 119 | #define PIXEL_ON(X,Y) (*GDDRAM_ADDRESS(x,y) |= GDDRAM_PIXMASK(y)) 120 | #define PIXEL_OFF(X,Y) (*GDDRAM_ADDRESS(x,y) &= ~GDDRAM_PIXMASK(y)) 121 | #define PIXEL_TOGGLE(X,Y) (*GDDRAM_ADDRESS(x,y) ^= GDDRAM_PIXMASK(y)) 122 | 123 | 124 | 125 | // call before first use of other functions 126 | void display_init( uint8 i2caddr ){ 127 | 128 | _i2caddr = i2caddr; 129 | gfx_init( DISPLAYWIDTH, DISPLAYHEIGHT ); 130 | 131 | uint8 cmdbuf[] = { 132 | 0x00, 133 | SSD1306_DISPLAYOFF, 134 | SSD1306_SETDISPLAYCLOCKDIV, 135 | 0x80, 136 | SSD1306_SETMULTIPLEX, 137 | 0x3f, 138 | SSD1306_SETDISPLAYOFFSET, 139 | 0x00, 140 | SSD1306_SETSTARTLINE | 0x0, 141 | SSD1306_CHARGEPUMP, 142 | 0x14, 143 | SSD1306_MEMORYMODE, 144 | 0x00, 145 | SSD1306_SEGREMAP | 0x1, 146 | SSD1306_COMSCANDEC, 147 | SSD1306_SETCOMPINS, 148 | 0x12, 149 | SSD1306_SETCONTRAST, 150 | 0xcf, 151 | SSD1306_SETPRECHARGE, 152 | 0xf1, 153 | SSD1306_SETVCOMDETECT, 154 | 0x40, 155 | SSD1306_DISPLAYALLON_RESUME, 156 | SSD1306_NORMALDISPLAY, 157 | SSD1306_DISPLAYON 158 | }; 159 | 160 | display_write_buf( cmdbuf, sizeof(cmdbuf) ); 161 | } 162 | 163 | // useful to turn off display if not used by application 164 | // w/o going through the init process 165 | void display_off( uint8 i2caddr ){ 166 | 167 | _i2caddr = i2caddr; 168 | uint8 cmdbuf[] = { 169 | 0x00, 170 | SSD1306_DISPLAYOFF 171 | }; 172 | display_write_buf( cmdbuf, sizeof(cmdbuf) ); 173 | //SSD1306_command(SSD1306_DISPLAYOFF); // 0xAE 174 | } 175 | 176 | 177 | // for submitting command sequences: 178 | // buf[0] must be 0x00 179 | // for submitting bulk data (writing to display RAM): 180 | // buf[0] must be 0x40 181 | static uint32 display_write_buf( uint8* buf, uint16_t size ){ 182 | 183 | uint8 ack; 184 | 185 | i2c_master_start(); 186 | i2c_master_writeByte(_i2caddr<<1); 187 | ack = i2c_master_getAck(); 188 | 189 | if (ack) { 190 | LOG_MSG("i2c address not acknowledged, display_write_buf\n" ); 191 | i2c_master_stop(); 192 | return TRANSFER_ERROR; 193 | } 194 | 195 | //uint16_t txlen = sizeof(_buffer); 196 | uint16_t i = 0; 197 | 198 | for( i = 0 ; i < size ; i++ ){ 199 | i2c_master_writeByte( buf[i] ); 200 | ack = i2c_master_getAck(); 201 | if (ack) { 202 | LOG_MSG("i2c data byte not acknowledged, display_write_buf\n" ); 203 | i2c_master_stop(); 204 | return TRANSFER_ERROR; 205 | } 206 | } 207 | 208 | i2c_master_stop(); 209 | return TRANSFER_CMPLT; 210 | 211 | } 212 | 213 | // used by gfx_ functions. Needs to be implemented by display_ 214 | static void display_setPixel( int16_t x, int16_t y, uint16_t color ){ 215 | 216 | if( (x < 0) || (x >= DISPLAYWIDTH) || (y < 0) || (y >= DISPLAYHEIGHT) ) 217 | return; 218 | 219 | switch( color ){ 220 | case WHITE: 221 | PIXEL_ON(x,y); 222 | break; 223 | case BLACK: 224 | PIXEL_OFF(x,y); 225 | break; 226 | case INVERSE: 227 | PIXEL_TOGGLE(x,y); 228 | break; 229 | } 230 | } 231 | 232 | 233 | void display_clear(void){ 234 | os_memset( _displaybuf, 0x00, _displaybuf_size ); 235 | SSD1306_buffer[0] = 0x40; // to be sure its there 236 | } 237 | 238 | 239 | // contrast: 0 ...255 240 | void display_contrast( uint8_t contrast ){ 241 | 242 | uint8 cmdbuf[] = { 243 | 0x00, 244 | SSD1306_SETCONTRAST, 245 | contrast 246 | }; 247 | display_write_buf( cmdbuf, sizeof(cmdbuf) ); 248 | } 249 | 250 | 251 | // invert <> 0 for inverse display, invert == 0 for normal display 252 | void display_invert( uint8_t invert ){ 253 | 254 | uint8 cmdbuf[] = { 255 | 0x00, 256 | 0 257 | }; 258 | cmdbuf[1] = invert ? SSD1306_INVERTDISPLAY : SSD1306_NORMALDISPLAY; 259 | display_write_buf( cmdbuf, sizeof(cmdbuf) ); 260 | } 261 | 262 | 263 | void display_update(void) { 264 | 265 | uint8 cmdbuf[] = { 266 | 0x00, 267 | SSD1306_COLUMNADDR, 268 | 0, // start 269 | DISPLAYWIDTH-1, // end 270 | SSD1306_PAGEADDR, 271 | 0, // start 272 | 7 // end 273 | }; 274 | display_write_buf( cmdbuf, sizeof(cmdbuf) ); 275 | display_write_buf( SSD1306_buffer, sizeof(SSD1306_buffer) ); 276 | } 277 | 278 | 279 | // draws horizontal or vertical line 280 | // Note: no check for valid coords, this needs to be done by caller 281 | // should only be called from gfx_hvline which is doing all validity checking 282 | static void display_line( int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color ){ 283 | 284 | if( x1 == x2 ){ 285 | // vertical 286 | uint8_t* pstart = GDDRAM_ADDRESS(x1,y1); 287 | uint8_t* pend = GDDRAM_ADDRESS(x2,y2); 288 | uint8_t* ptr = pstart; 289 | 290 | while( ptr <= pend ){ 291 | 292 | uint8_t mask; 293 | if( ptr == pstart ){ 294 | // top 295 | uint8_t lbit = y1 % 8; 296 | // bottom (line can be very short, all inside this one byte) 297 | uint8_t ubit = lbit + y2 - y1; 298 | if( ubit >= 7 ) 299 | ubit = 7; 300 | mask = ((1 << (ubit-lbit+1)) - 1) << lbit; 301 | }else if( ptr == pend ){ 302 | // top is always bit 0, that makes it easy 303 | // bottom 304 | mask = (1 << (y2 % 8)) - 1; 305 | } 306 | 307 | if( ptr == pstart || ptr == pend ){ 308 | switch( color ){ 309 | case WHITE: *ptr |= mask; break; 310 | case BLACK: *ptr &= ~mask; break; 311 | case INVERSE: *ptr ^= mask; break; 312 | }; 313 | }else{ 314 | switch( color ){ 315 | case WHITE: *ptr = 0xff; break; 316 | case BLACK: *ptr = 0x00; break; 317 | case INVERSE: *ptr ^= 0xff; break; 318 | }; 319 | } 320 | 321 | ptr += DISPLAYWIDTH; 322 | } 323 | }else{ 324 | // horizontal 325 | uint8_t* pstart = GDDRAM_ADDRESS(x1,y1); 326 | uint8_t* pend = pstart + x2 - x1; 327 | uint8_t pixmask = GDDRAM_PIXMASK(y1); 328 | 329 | uint8_t* ptr = pstart; 330 | while( ptr <= pend ){ 331 | switch( color ){ 332 | case WHITE: *ptr |= pixmask; break; 333 | case BLACK: *ptr &= ~pixmask; break; 334 | case INVERSE: *ptr ^= pixmask; break; 335 | }; 336 | ptr++; 337 | } 338 | } 339 | } 340 | 341 | 342 | 343 | void display_stopscroll(void){ 344 | 345 | uint8 cmdbuf[] = { 346 | 0x00, 347 | SSD1306_DEACTIVATE_SCROLL 348 | }; 349 | display_write_buf( cmdbuf, sizeof(cmdbuf) ); 350 | } 351 | 352 | void display_scroll( SCROLL_AREA start, SCROLL_AREA end, SCROLL_DIR dir, SCROLL_SPEED speed ){ 353 | 354 | uint8 cmdbuf[] = { 355 | 0x00, 356 | dir, // 0x26 or 0x2a 357 | 0x00, // dummy byte 358 | start, // start page 359 | speed, // scroll step interval in terms of frame frequency 360 | end, // end page 361 | 0x00, // dummy byte 362 | 0xFF, // dummy byte 363 | SSD1306_ACTIVATE_SCROLL // 0x2F 364 | }; 365 | display_write_buf( cmdbuf, sizeof(cmdbuf) ); 366 | } 367 | 368 | 369 | 370 | // ============================================================ 371 | // graphics library stuff 372 | 373 | int16_t WIDTH, HEIGHT; // This is the 'raw' display w/h - never changes 374 | static int16_t _width, _height; // Display w/h as modified by current rotation 375 | static int16_t cursor_x, cursor_y; 376 | static uint16_t textcolor, textbgcolor; 377 | static uint8_t textsize; 378 | uint8_t rotation; 379 | uint8_t wrap; // If set, 'wrap' text at right edge of display 380 | 381 | 382 | 383 | void gfx_init( int16_t width, int16_t height ){ 384 | WIDTH = width; 385 | HEIGHT = height; 386 | _width = WIDTH; 387 | _height = HEIGHT; 388 | 389 | rotation = 0; 390 | cursor_y = cursor_x = 0; 391 | textsize = 1; 392 | textcolor = textbgcolor = 0xFFFF; 393 | wrap = 1; 394 | } 395 | 396 | // Return the size of the display (per current rotation) 397 | int16_t gfx_width(void){ 398 | return _width; 399 | } 400 | 401 | int16_t gfx_height(void){ 402 | return _height; 403 | } 404 | 405 | uint8_t gfx_rotation(void){ 406 | return rotation; 407 | } 408 | 409 | void gfx_setCursor( int16_t x, int16_t y ){ 410 | cursor_x = x; 411 | cursor_y = y; 412 | } 413 | 414 | void gfx_setTextSize( uint8_t size ){ 415 | textsize = (size > 0) ? size : 1; 416 | } 417 | 418 | void gfx_setTextColor( uint16_t color ){ 419 | // For 'transparent' background, we'll set the bg 420 | // to the same as fg instead of using a flag 421 | textcolor = textbgcolor = color; 422 | } 423 | 424 | void gfx_setTextBg( uint16_t color ){ 425 | textbgcolor = color; 426 | } 427 | 428 | void gfx_setTextWrap( uint8 w ){ 429 | wrap = w; 430 | } 431 | 432 | void gfx_setRotation( uint8_t x ){ 433 | 434 | rotation = (x & 3); 435 | switch( rotation ){ 436 | case 0: 437 | case 2: 438 | _width = WIDTH; 439 | _height = HEIGHT; 440 | break; 441 | case 1: 442 | case 3: 443 | _width = HEIGHT; 444 | _height = WIDTH; 445 | break; 446 | } 447 | } 448 | 449 | static void gfx_rotation_adjust( int16_t* px, int16_t* py ){ 450 | 451 | int16_t y0 = *py; 452 | 453 | switch( rotation ){ 454 | case 1: 455 | *py = *px; 456 | *px = WIDTH - y0 - 1; 457 | break; 458 | case 2: 459 | *px = WIDTH - *px - 1; 460 | *py = HEIGHT - *py - 1; 461 | break; 462 | case 3: 463 | *py = HEIGHT - *px - 1; 464 | *px = y0; 465 | break; 466 | } 467 | } 468 | 469 | void gfx_drawPixel( int16_t x, int16_t y, uint16_t color ){ 470 | 471 | if( (x < 0) || (x >= _width) || (y < 0) || (y >= _height) ) 472 | return; 473 | 474 | gfx_rotation_adjust( &x, &y ); 475 | 476 | display_setPixel(x,y,color); 477 | } 478 | 479 | // helper function for gfx_drawLine, handles special cases of horizontal and vertical lines 480 | static void gfx_hvLine( int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color ){ 481 | 482 | if( x1 != x2 && y1 != y2 ){ 483 | // neither vertical nor horizontal 484 | return; 485 | } 486 | 487 | // bounds check 488 | if( rotation == 1 || rotation == 3 ){ 489 | if( x1 < 0 || x1 >= HEIGHT || x2 < 0 || x2 >= HEIGHT ) 490 | return; 491 | if( y1 < 0 || y1 >= WIDTH || y2 < 0 || y2 >= WIDTH ) 492 | return; 493 | }else{ 494 | if( y1 < 0 || y1 >= HEIGHT || y2 < 0 || y2 >= HEIGHT ) 495 | return; 496 | if( x1 < 0 || x1 >= WIDTH || x2 < 0 || x2 >= WIDTH ) 497 | return; 498 | } 499 | 500 | gfx_rotation_adjust( &x1, &y1 ); 501 | gfx_rotation_adjust( &x2, &y2 ); 502 | 503 | // ensure coords are from left to right and top to bottom 504 | if( (x1 == x2 && y2 < y1) || (y1 == y2 && x2 < x1) ){ 505 | // swap as needed 506 | int16_t t = x1; x1 = x2; x2 = t; 507 | t = y1; y1 = y2; y2 = t; 508 | } 509 | 510 | display_line( x1, y1, x2, y2, color ); 511 | } 512 | 513 | // always use this function for line drawing 514 | void gfx_drawLine( int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color ){ 515 | 516 | if( x0 == x1 || y0 == y1 ){ 517 | // vertical and horizontal lines can be drawn faster 518 | gfx_hvLine( x0, y0, x1, y1, color ); 519 | return; 520 | } 521 | 522 | int16_t t; 523 | 524 | int16_t steep = abs(y1 - y0) > abs(x1 - x0); 525 | if( steep ){ 526 | t = x0; x0 = y0; y0 = t; 527 | t = x1; x1 = y1; y1 = t; 528 | } 529 | if( x0 > x1 ){ 530 | t = x0; x0 = x1; x1 = t; 531 | t = y0; y0 = y1; y1 = t; 532 | } 533 | int16_t dx, dy; 534 | dx = x1 - x0; 535 | dy = abs(y1 - y0); 536 | int16_t err = dx / 2; 537 | int16_t ystep; 538 | if( y0 < y1 ){ 539 | ystep = 1; 540 | }else{ 541 | ystep = -1; 542 | } 543 | for( ; x0<=x1; x0++ ){ 544 | if( steep ){ 545 | gfx_drawPixel( y0, x0, color ); 546 | }else{ 547 | gfx_drawPixel( x0, y0, color ); 548 | } 549 | err -= dy; 550 | if( err < 0 ){ 551 | y0 += ystep; 552 | err += dx; 553 | } 554 | } 555 | } 556 | 557 | void gfx_drawRect( int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color ){ 558 | 559 | gfx_drawLine( x, y, x+w-1, y, color ); 560 | gfx_drawLine( x, y+h-1, x+w-1, y+h-1, color ); 561 | gfx_drawLine( x, y, x, y+h-1, color ); 562 | gfx_drawLine( x+w-1, y, x+w-1, y+h-1, color ); 563 | } 564 | 565 | void gfx_fillRect( int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color ){ 566 | int16_t i = 0; 567 | if( h > w ){ 568 | for( i = x ; i < x+w ; i++ ){ 569 | gfx_drawLine( i, y, i, y+h-1, color ); 570 | } 571 | }else{ 572 | for( i = y ; i < y+h ; i++ ){ 573 | gfx_drawLine( x, i, x+w-1, i, color ); 574 | } 575 | } 576 | } 577 | 578 | 579 | // circle outline 580 | void gfx_drawCircle( int16_t x0, int16_t y0, int16_t r,uint16_t color ){ 581 | 582 | int16_t f = 1 - r; 583 | int16_t ddF_x = 1; 584 | int16_t ddF_y = -2 * r; 585 | int16_t x = 0; 586 | int16_t y = r; 587 | gfx_drawPixel( x0 , y0+r, color ); 588 | gfx_drawPixel( x0 , y0-r, color ); 589 | gfx_drawPixel( x0+r, y0 , color ); 590 | gfx_drawPixel( x0-r, y0 , color ); 591 | while( x < y ){ 592 | if( f >= 0 ){ 593 | y--; 594 | ddF_y += 2; 595 | f += ddF_y; 596 | } 597 | x++; 598 | ddF_x += 2; 599 | f += ddF_x; 600 | gfx_drawPixel( x0 + x, y0 + y, color ); 601 | gfx_drawPixel( x0 - x, y0 + y, color ); 602 | gfx_drawPixel( x0 + x, y0 - y, color ); 603 | gfx_drawPixel( x0 - x, y0 - y, color ); 604 | gfx_drawPixel( x0 + y, y0 + x, color ); 605 | gfx_drawPixel( x0 - y, y0 + x, color ); 606 | gfx_drawPixel( x0 + y, y0 - x, color ); 607 | gfx_drawPixel( x0 - y, y0 - x, color ); 608 | } 609 | } 610 | 611 | void gfx_drawTriangle( int16_t x0, int16_t y0,int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color ){ 612 | 613 | gfx_drawLine( x0, y0, x1, y1, color ); 614 | gfx_drawLine( x1, y1, x2, y2, color ); 615 | gfx_drawLine( x2, y2, x0, y0, color ); 616 | } 617 | 618 | 619 | // Draw a character 620 | void gfx_drawChar( int16_t x, int16_t y, unsigned char c,uint16_t color, uint16_t bg, uint8_t size) { 621 | if( (x >= _width) || // Clip right 622 | (y >= _height) || // Clip bottom 623 | ((x + 6 * size - 1) < 0) || // Clip left 624 | ((y + 8 * size - 1) < 0)) // Clip top 625 | return; 626 | 627 | int8_t i = 0; 628 | for( i = 0 ; i < 6 ; i++ ){ 629 | uint8_t line; 630 | if( i == 5 ) 631 | line = 0x0; 632 | else 633 | line = font[(c*5)+i]; 634 | int8_t j = 0; 635 | for( j = 0; j < 8 ; j++ ){ 636 | if( line & 0x1 ){ 637 | if( size == 1 ) // default size 638 | gfx_drawPixel( x+i, y+j, color ); 639 | else { // big size 640 | gfx_fillRect( x+(i*size), y+(j*size), size, size, color ); 641 | } 642 | } else if( bg != color ){ 643 | if( size == 1 ) // default size 644 | gfx_drawPixel( x+i, y+j, bg ); 645 | else { // big size 646 | gfx_fillRect( x+i*size, y+j*size, size, size, bg ); 647 | } 648 | } 649 | line >>= 1; 650 | } 651 | } 652 | } 653 | 654 | void gfx_write( uint8_t ch ){ 655 | if( ch == '\n' ){ 656 | cursor_y += textsize*8; 657 | cursor_x = 0; 658 | }else if( ch == '\r' ){ 659 | // skip em 660 | }else{ 661 | gfx_drawChar(cursor_x, cursor_y, ch, textcolor, textbgcolor, textsize); 662 | cursor_x += textsize*6; 663 | if( wrap && (cursor_x > (_width - textsize*6)) ){ 664 | cursor_y += textsize*8; 665 | cursor_x = 0; 666 | } 667 | } 668 | } 669 | 670 | void gfx_print( const char* s ){ 671 | 672 | unsigned int len = os_strlen( s ); 673 | unsigned int i = 0; 674 | for( i = 0 ; i < len ; i++ ){ 675 | gfx_write( s[i] ); 676 | } 677 | } 678 | 679 | void gfx_println( const char* s ){ 680 | gfx_print( s ); 681 | gfx_write( '\n' ); 682 | } 683 | --------------------------------------------------------------------------------