/ RNode_Firmware_CE_G2 / ST7789.h
ST7789.h
1 /** 2 * The MIT License (MIT) 3 * 4 * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn 5 * Copyright (c) 2018 by Fabrice Weinberg 6 * Copyright (c) 2024 by Heltec AutoMation 7 * Permission is hereby granted, free of charge, to any person obtaining a copy 8 * of this software and associated documentation files (the "Software"), to deal 9 * in the Software without restriction, including without limitation the rights 10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 * copies of the Software, and to permit persons to whom the Software is 12 * furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included in all 15 * copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 * SOFTWARE. 24 * 25 * ThingPulse invests considerable time and money to develop these open source libraries. 26 * Please support us by buying our products (and not the clones) from 27 * https://thingpulse.com 28 * 29 */ 30 31 #ifndef ST7789Spi_h 32 #define ST7789Spi_h 33 34 #include "OLEDDisplay.h" 35 #include <SPI.h> 36 37 38 #define ST_CMD_DELAY 0x80 // special signifier for command lists 39 40 #define ST77XX_NOP 0x00 41 #define ST77XX_SWRESET 0x01 42 #define ST77XX_RDDID 0x04 43 #define ST77XX_RDDST 0x09 44 45 #define ST77XX_SLPIN 0x10 46 #define ST77XX_SLPOUT 0x11 47 #define ST77XX_PTLON 0x12 48 #define ST77XX_NORON 0x13 49 50 #define ST77XX_INVOFF 0x20 51 #define ST77XX_INVON 0x21 52 #define ST77XX_DISPOFF 0x28 53 #define ST77XX_DISPON 0x29 54 #define ST77XX_CASET 0x2A 55 #define ST77XX_RASET 0x2B 56 #define ST77XX_RAMWR 0x2C 57 #define ST77XX_RAMRD 0x2E 58 59 #define ST77XX_PTLAR 0x30 60 #define ST77XX_TEOFF 0x34 61 #define ST77XX_TEON 0x35 62 #define ST77XX_MADCTL 0x36 63 #define ST77XX_COLMOD 0x3A 64 65 #define ST77XX_MADCTL_MY 0x80 66 #define ST77XX_MADCTL_MX 0x40 67 #define ST77XX_MADCTL_MV 0x20 68 #define ST77XX_MADCTL_ML 0x10 69 #define ST77XX_MADCTL_RGB 0x00 70 71 #define ST77XX_RDID1 0xDA 72 #define ST77XX_RDID2 0xDB 73 #define ST77XX_RDID3 0xDC 74 #define ST77XX_RDID4 0xDD 75 76 // Some ready-made 16-bit ('565') color settings: 77 #define ST77XX_BLACK 0x0000 78 #define ST77XX_WHITE 0xFFFF 79 #define ST77XX_RED 0xF800 80 #define ST77XX_GREEN 0x07E0 81 #define ST77XX_BLUE 0x001F 82 #define ST77XX_CYAN 0x07FF 83 #define ST77XX_MAGENTA 0xF81F 84 #define ST77XX_YELLOW 0xFFE0 85 #define ST77XX_ORANGE 0xFC00 86 87 #define LED_A_ON LOW 88 89 #ifdef ESP_PLATFORM 90 #undef LED_A_ON 91 #define LED_A_ON HIGH 92 #define rtos_free free 93 #define rtos_malloc malloc 94 //SPIClass SPI1(HSPI); 95 #endif 96 class ST7789Spi : public OLEDDisplay { 97 private: 98 uint8_t _rst; 99 uint8_t _dc; 100 uint8_t _cs; 101 uint8_t _ledA; 102 int _miso; 103 int _mosi; 104 int _clk; 105 SPIClass * _spi; 106 SPISettings _spiSettings; 107 uint16_t _RGB=0xFFFF; 108 uint8_t _buffheight; 109 public: 110 /* pass _cs as -1 to indicate "do not use CS pin", for cases where it is hard wired low */ 111 ST7789Spi(SPIClass *spiClass,uint8_t _rst, uint8_t _dc, uint8_t _cs, OLEDDISPLAY_GEOMETRY g = GEOMETRY_RAWMODE,uint16_t width=240,uint16_t height=320,int mosi=-1,int miso=-1,int clk=-1) { 112 this->_spi = spiClass; 113 this->_rst = _rst; 114 this->_dc = _dc; 115 this->_cs = _cs; 116 this->_mosi=mosi; 117 this->_miso=miso; 118 this->_clk=clk; 119 //this->_ledA = _ledA; 120 _spiSettings = SPISettings(40000000, MSBFIRST, SPI_MODE0); 121 setGeometry(g,width,height); 122 } 123 124 bool connect(){ 125 this->_buffheight=displayHeight / 8; 126 this->_buffheight+=displayHeight % 8 ? 1:0; 127 pinMode(_cs, OUTPUT); 128 pinMode(_dc, OUTPUT); 129 //pinMode(_ledA, OUTPUT); 130 if (_cs != (uint8_t) -1) { 131 pinMode(_cs, OUTPUT); 132 } 133 pinMode(_rst, OUTPUT); 134 135 #ifdef ESP_PLATFORM 136 _spi->begin(_clk,_miso,_mosi,-1); 137 #else 138 _spi->begin(); 139 #endif 140 _spi->setClockDivider (SPI_CLOCK_DIV2); 141 142 // Pulse Reset low for 10ms 143 digitalWrite(_rst, HIGH); 144 delay(1); 145 digitalWrite(_rst, LOW); 146 delay(10); 147 digitalWrite(_rst, HIGH); 148 _spi->begin (); 149 //digitalWrite(_ledA, LED_A_ON); 150 return true; 151 } 152 153 void display(void) { 154 #ifdef OLEDDISPLAY_DOUBLE_BUFFER 155 156 uint16_t minBoundY = UINT16_MAX; 157 uint16_t maxBoundY = 0; 158 159 uint16_t minBoundX = UINT16_MAX; 160 uint16_t maxBoundX = 0; 161 162 uint16_t x, y; 163 164 // Calculate the Y bounding box of changes 165 // and copy buffer[pos] to buffer_back[pos]; 166 for (y = 0; y < _buffheight; y++) { 167 for (x = 0; x < displayWidth; x++) { 168 //Serial.printf("x %d y %d\r\n",x,y); 169 uint16_t pos = x + y * displayWidth; 170 if (buffer[pos] != buffer_back[pos]) { 171 minBoundY = min(minBoundY, y); 172 maxBoundY = max(maxBoundY, y); 173 minBoundX = min(minBoundX, x); 174 maxBoundX = max(maxBoundX, x); 175 } 176 buffer_back[pos] = buffer[pos]; 177 } 178 yield(); 179 } 180 181 // If the minBoundY wasn't updated 182 // we can savely assume that buffer_back[pos] == buffer[pos] 183 // holdes true for all values of pos 184 if (minBoundY == UINT16_MAX) return; 185 186 set_CS(LOW); 187 _spi->beginTransaction(_spiSettings); 188 189 for (y = minBoundY; y <= maxBoundY; y++) 190 { 191 for(int temp = 0; temp<8;temp++) 192 { 193 //setAddrWindow(minBoundX,y*8+temp,maxBoundX-minBoundX+1,1); 194 setAddrWindow(minBoundX,y*8+temp,maxBoundX-minBoundX+1,1); 195 //setAddrWindow(y*8+temp,minBoundX,1,maxBoundX-minBoundX+1); 196 uint32_t const pixbufcount = maxBoundX-minBoundX+1; 197 uint16_t *pixbuf = (uint16_t *)rtos_malloc(2 * pixbufcount); 198 for (x = minBoundX; x <= maxBoundX; x++) 199 { 200 pixbuf[x-minBoundX] = ((buffer[x + y * displayWidth]>>temp)&0x01)==1?_RGB:0; 201 } 202 #ifdef ESP_PLATFORM 203 _spi->transferBytes((uint8_t *)pixbuf, NULL, 2 * pixbufcount); 204 #else 205 _spi->transfer(pixbuf, NULL, 2 * pixbufcount); 206 #endif 207 rtos_free(pixbuf); 208 } 209 } 210 _spi->endTransaction(); 211 set_CS(HIGH); 212 213 #else 214 set_CS(LOW); 215 _spi->beginTransaction(_spiSettings); 216 uint8_t x, y; 217 for (y = 0; y < _buffheight; y++) 218 { 219 for(int temp = 0; temp<8;temp++) 220 { 221 //setAddrWindow(minBoundX,y*8+temp,maxBoundX-minBoundX+1,1); 222 //setAddrWindow(minBoundX,y*8+temp,maxBoundX-minBoundX+1,1); 223 setAddrWindow(y*8+temp,0,1,displayWidth); 224 uint32_t const pixbufcount = displayWidth; 225 uint16_t *pixbuf = (uint16_t *)rtos_malloc(2 * pixbufcount); 226 for (x = 0; x < displayWidth; x++) 227 { 228 pixbuf[x] = ((buffer[x + y * displayWidth]>>temp)&0x01)==1?_RGB:0; 229 } 230 #ifdef ESP_PLATFORM 231 _spi->transferBytes((uint8_t *)pixbuf, NULL, 2 * pixbufcount); 232 #else 233 _spi->transfer(pixbuf, NULL, 2 * pixbufcount); 234 #endif 235 rtos_free(pixbuf); 236 } 237 } 238 _spi->endTransaction(); 239 set_CS(HIGH); 240 241 #endif 242 } 243 244 virtual void resetOrientation() { 245 uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MX; 246 sendCommand(ST77XX_MADCTL); 247 WriteData(madctl); 248 delay(10); 249 } 250 251 virtual void flipScreenVertically() { 252 uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MY; 253 sendCommand(ST77XX_MADCTL); 254 WriteData(madctl); 255 delay(10); 256 } 257 258 virtual void mirrorScreen() { 259 uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MX|ST77XX_MADCTL_MY; 260 sendCommand(ST77XX_MADCTL); 261 WriteData(madctl); 262 delay(10); 263 } 264 265 virtual void setRotation(uint8_t r) { 266 uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MX; 267 if (r == 1) { madctl = 0xC0; } 268 if (r == 2) { madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MY; } 269 if (r == 3) { madctl = 0x00; } 270 sendCommand(ST77XX_MADCTL); 271 WriteData(madctl); 272 delay(10); 273 } 274 275 void setRGB(uint16_t c) 276 { 277 278 this->_RGB=0x00|c>>8|c<<8&0xFF00; 279 } 280 281 void displayOn(void) { 282 //sendCommand(DISPLAYON); 283 } 284 285 void displayOff(void) { 286 //sendCommand(DISPLAYOFF); 287 } 288 289 //#define ST77XX_MADCTL_MY 0x80 290 //#define ST77XX_MADCTL_MX 0x40 291 //#define ST77XX_MADCTL_MV 0x20 292 //#define ST77XX_MADCTL_ML 0x10 293 protected: 294 // Send all the init commands 295 virtual void sendInitCommands() 296 { 297 sendCommand(ST77XX_SWRESET); // 1: Software reset, no args, w/delay 298 delay(150); 299 300 sendCommand(ST77XX_SLPOUT); // 2: Out of sleep mode, no args, w/delay 301 delay(10); 302 303 sendCommand(ST77XX_COLMOD); // 3: Set color mode, 16-bit color 304 WriteData(0x55); 305 delay(10); 306 307 sendCommand(ST77XX_MADCTL); // 4: Mem access ctrl (directions), Row/col addr, bottom-top refresh 308 WriteData(0x08); 309 310 sendCommand(ST77XX_CASET); // 5: Column addr set, 311 WriteData(0x00); 312 WriteData(0x00); // XSTART = 0 313 WriteData(0x00); 314 WriteData(240); // XEND = 240 315 316 sendCommand(ST77XX_RASET); // 6: Row addr set, 317 WriteData(0x00); 318 WriteData(0x00); // YSTART = 0 319 WriteData(320>>8); 320 WriteData(320&0xFF); // YSTART = 320 321 322 sendCommand(ST77XX_SLPOUT); // 7: hack 323 delay(10); 324 325 sendCommand(ST77XX_NORON); // 8: Normal display on, no args, w/delay 326 delay(10); 327 328 sendCommand(ST77XX_DISPON); // 9: Main screen turn on, no args, delay 329 delay(10); 330 331 sendCommand(ST77XX_INVON); // 10: invert 332 delay(10); 333 334 //uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MX; 335 uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MX; 336 sendCommand(ST77XX_MADCTL); 337 WriteData(madctl); 338 delay(10); 339 setRGB(ST77XX_GREEN); 340 } 341 342 343 private: 344 345 void setAddrWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { 346 x += (320-displayWidth)/2; 347 y += (240-displayHeight)/2; 348 uint32_t xa = ((uint32_t)x << 16) | (x + w - 1); 349 uint32_t ya = ((uint32_t)y << 16) | (y + h - 1); 350 351 writeCommand(ST77XX_CASET); // Column addr set 352 SPI_WRITE32(xa); 353 354 writeCommand(ST77XX_RASET); // Row addr set 355 SPI_WRITE32(ya); 356 357 writeCommand(ST77XX_RAMWR); // write to RAM 358 } 359 int getBufferOffset(void) { 360 return 0; 361 } 362 inline void set_CS(bool level) { 363 if (_cs != (uint8_t) -1) { 364 digitalWrite(_cs, level); 365 } 366 }; 367 inline void sendCommand(uint8_t com) __attribute__((always_inline)){ 368 set_CS(HIGH); 369 digitalWrite(_dc, LOW); 370 set_CS(LOW); 371 _spi->beginTransaction(_spiSettings); 372 _spi->transfer(com); 373 _spi->endTransaction(); 374 set_CS(HIGH); 375 digitalWrite(_dc, HIGH); 376 } 377 378 inline void WriteData(uint8_t data) __attribute__((always_inline)){ 379 digitalWrite(_cs, LOW); 380 _spi->beginTransaction(_spiSettings); 381 _spi->transfer(data); 382 _spi->endTransaction(); 383 digitalWrite(_cs, HIGH); 384 } 385 void SPI_WRITE32(uint32_t l) 386 { 387 _spi->transfer(l >> 24); 388 _spi->transfer(l >> 16); 389 _spi->transfer(l >> 8); 390 _spi->transfer(l); 391 } 392 void writeCommand(uint8_t cmd) { 393 digitalWrite(_dc, LOW); 394 _spi->transfer(cmd); 395 digitalWrite(_dc, HIGH); 396 } 397 398 // Private functions 399 void setGeometry(OLEDDISPLAY_GEOMETRY g, uint16_t width, uint16_t height) { 400 this->geometry = g; 401 402 switch (g) { 403 case GEOMETRY_128_128: 404 this->displayWidth = 128; 405 this->displayHeight = 128; 406 break; 407 case GEOMETRY_128_64: 408 this->displayWidth = 128; 409 this->displayHeight = 64; 410 break; 411 case GEOMETRY_128_32: 412 this->displayWidth = 128; 413 this->displayHeight = 32; 414 break; 415 case GEOMETRY_64_48: 416 this->displayWidth = 64; 417 this->displayHeight = 48; 418 break; 419 case GEOMETRY_64_32: 420 this->displayWidth = 64; 421 this->displayHeight = 32; 422 break; 423 case GEOMETRY_RAWMODE: 424 this->displayWidth = width > 0 ? width : 128; 425 this->displayHeight = height > 0 ? height : 64; 426 break; 427 } 428 uint8_t tmp=displayHeight % 8; 429 uint8_t _buffheight=displayHeight / 8; 430 431 if(tmp!=0) 432 _buffheight++; 433 this->displayBufferSize = displayWidth * _buffheight ; 434 } 435 436 437 438 }; 439 440 #endif