parent
7235fa6c3f
commit
62cd3977c7
1 changed files with 440 additions and 0 deletions
@ -0,0 +1,440 @@ |
|||||||
|
/**
|
||||||
|
* The MIT License (MIT) |
||||||
|
* |
||||||
|
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn |
||||||
|
* Copyright (c) 2018 by Fabrice Weinberg |
||||||
|
* Copyright (c) 2024 by Heltec AutoMation |
||||||
|
* 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. |
||||||
|
* |
||||||
|
* ThingPulse invests considerable time and money to develop these open source libraries. |
||||||
|
* Please support us by buying our products (and not the clones) from |
||||||
|
* https://thingpulse.com
|
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef ST7789Spi_h |
||||||
|
#define ST7789Spi_h |
||||||
|
|
||||||
|
#include "OLEDDisplay.h" |
||||||
|
#include <SPI.h> |
||||||
|
|
||||||
|
|
||||||
|
#define ST_CMD_DELAY 0x80 // special signifier for command lists
|
||||||
|
|
||||||
|
#define ST77XX_NOP 0x00 |
||||||
|
#define ST77XX_SWRESET 0x01 |
||||||
|
#define ST77XX_RDDID 0x04 |
||||||
|
#define ST77XX_RDDST 0x09 |
||||||
|
|
||||||
|
#define ST77XX_SLPIN 0x10 |
||||||
|
#define ST77XX_SLPOUT 0x11 |
||||||
|
#define ST77XX_PTLON 0x12 |
||||||
|
#define ST77XX_NORON 0x13 |
||||||
|
|
||||||
|
#define ST77XX_INVOFF 0x20 |
||||||
|
#define ST77XX_INVON 0x21 |
||||||
|
#define ST77XX_DISPOFF 0x28 |
||||||
|
#define ST77XX_DISPON 0x29 |
||||||
|
#define ST77XX_CASET 0x2A |
||||||
|
#define ST77XX_RASET 0x2B |
||||||
|
#define ST77XX_RAMWR 0x2C |
||||||
|
#define ST77XX_RAMRD 0x2E |
||||||
|
|
||||||
|
#define ST77XX_PTLAR 0x30 |
||||||
|
#define ST77XX_TEOFF 0x34 |
||||||
|
#define ST77XX_TEON 0x35 |
||||||
|
#define ST77XX_MADCTL 0x36 |
||||||
|
#define ST77XX_COLMOD 0x3A |
||||||
|
|
||||||
|
#define ST77XX_MADCTL_MY 0x80 |
||||||
|
#define ST77XX_MADCTL_MX 0x40 |
||||||
|
#define ST77XX_MADCTL_MV 0x20 |
||||||
|
#define ST77XX_MADCTL_ML 0x10 |
||||||
|
#define ST77XX_MADCTL_RGB 0x00 |
||||||
|
|
||||||
|
#define ST77XX_RDID1 0xDA |
||||||
|
#define ST77XX_RDID2 0xDB |
||||||
|
#define ST77XX_RDID3 0xDC |
||||||
|
#define ST77XX_RDID4 0xDD |
||||||
|
|
||||||
|
// Some ready-made 16-bit ('565') color settings:
|
||||||
|
#define ST77XX_BLACK 0x0000 |
||||||
|
#define ST77XX_WHITE 0xFFFF |
||||||
|
#define ST77XX_RED 0xF800 |
||||||
|
#define ST77XX_GREEN 0x07E0 |
||||||
|
#define ST77XX_BLUE 0x001F |
||||||
|
#define ST77XX_CYAN 0x07FF |
||||||
|
#define ST77XX_MAGENTA 0xF81F |
||||||
|
#define ST77XX_YELLOW 0xFFE0 |
||||||
|
#define ST77XX_ORANGE 0xFC00 |
||||||
|
|
||||||
|
#define LED_A_ON LOW |
||||||
|
|
||||||
|
#ifdef ESP_PLATFORM |
||||||
|
#undef LED_A_ON |
||||||
|
#define LED_A_ON HIGH |
||||||
|
#define rtos_free free |
||||||
|
#define rtos_malloc malloc |
||||||
|
//SPIClass SPI1(HSPI);
|
||||||
|
#endif |
||||||
|
class ST7789Spi : public OLEDDisplay { |
||||||
|
private: |
||||||
|
uint8_t _rst; |
||||||
|
uint8_t _dc; |
||||||
|
uint8_t _cs; |
||||||
|
uint8_t _ledA; |
||||||
|
int _miso; |
||||||
|
int _mosi; |
||||||
|
int _clk; |
||||||
|
SPIClass * _spi; |
||||||
|
SPISettings _spiSettings; |
||||||
|
uint16_t _RGB=0xFFFF; |
||||||
|
uint8_t _buffheight; |
||||||
|
public: |
||||||
|
/* pass _cs as -1 to indicate "do not use CS pin", for cases where it is hard wired low */ |
||||||
|
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) { |
||||||
|
this->_spi = spiClass; |
||||||
|
this->_rst = _rst; |
||||||
|
this->_dc = _dc; |
||||||
|
this->_cs = _cs; |
||||||
|
this->_mosi=mosi; |
||||||
|
this->_miso=miso; |
||||||
|
this->_clk=clk; |
||||||
|
//this->_ledA = _ledA;
|
||||||
|
_spiSettings = SPISettings(40000000, MSBFIRST, SPI_MODE0); |
||||||
|
setGeometry(g,width,height); |
||||||
|
} |
||||||
|
|
||||||
|
bool connect(){ |
||||||
|
this->_buffheight=displayHeight / 8; |
||||||
|
this->_buffheight+=displayHeight % 8 ? 1:0; |
||||||
|
pinMode(_cs, OUTPUT); |
||||||
|
pinMode(_dc, OUTPUT); |
||||||
|
//pinMode(_ledA, OUTPUT);
|
||||||
|
if (_cs != (uint8_t) -1) { |
||||||
|
pinMode(_cs, OUTPUT); |
||||||
|
}
|
||||||
|
pinMode(_rst, OUTPUT); |
||||||
|
|
||||||
|
#ifdef ESP_PLATFORM |
||||||
|
_spi->begin(_clk,_miso,_mosi,-1); |
||||||
|
#else |
||||||
|
_spi->begin(); |
||||||
|
#endif |
||||||
|
_spi->setClockDivider (SPI_CLOCK_DIV2); |
||||||
|
|
||||||
|
// Pulse Reset low for 10ms
|
||||||
|
digitalWrite(_rst, HIGH); |
||||||
|
delay(1); |
||||||
|
digitalWrite(_rst, LOW); |
||||||
|
delay(10); |
||||||
|
digitalWrite(_rst, HIGH); |
||||||
|
_spi->begin (); |
||||||
|
//digitalWrite(_ledA, LED_A_ON);
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void display(void) { |
||||||
|
#ifdef OLEDDISPLAY_DOUBLE_BUFFER |
||||||
|
|
||||||
|
uint16_t minBoundY = UINT16_MAX; |
||||||
|
uint16_t maxBoundY = 0; |
||||||
|
|
||||||
|
uint16_t minBoundX = UINT16_MAX; |
||||||
|
uint16_t maxBoundX = 0; |
||||||
|
|
||||||
|
uint16_t x, y; |
||||||
|
|
||||||
|
// Calculate the Y bounding box of changes
|
||||||
|
// and copy buffer[pos] to buffer_back[pos];
|
||||||
|
for (y = 0; y < _buffheight; y++) { |
||||||
|
for (x = 0; x < displayWidth; x++) { |
||||||
|
//Serial.printf("x %d y %d\r\n",x,y);
|
||||||
|
uint16_t pos = x + y * displayWidth; |
||||||
|
if (buffer[pos] != buffer_back[pos]) { |
||||||
|
minBoundY = min(minBoundY, y); |
||||||
|
maxBoundY = max(maxBoundY, y); |
||||||
|
minBoundX = min(minBoundX, x); |
||||||
|
maxBoundX = max(maxBoundX, x); |
||||||
|
} |
||||||
|
buffer_back[pos] = buffer[pos]; |
||||||
|
} |
||||||
|
yield(); |
||||||
|
} |
||||||
|
|
||||||
|
// If the minBoundY wasn't updated
|
||||||
|
// we can savely assume that buffer_back[pos] == buffer[pos]
|
||||||
|
// holdes true for all values of pos
|
||||||
|
if (minBoundY == UINT16_MAX) return; |
||||||
|
|
||||||
|
set_CS(LOW); |
||||||
|
_spi->beginTransaction(_spiSettings); |
||||||
|
|
||||||
|
for (y = minBoundY; y <= maxBoundY; y++) |
||||||
|
{ |
||||||
|
for(int temp = 0; temp<8;temp++) |
||||||
|
{ |
||||||
|
//setAddrWindow(minBoundX,y*8+temp,maxBoundX-minBoundX+1,1);
|
||||||
|
setAddrWindow(minBoundX,y*8+temp,maxBoundX-minBoundX+1,1); |
||||||
|
//setAddrWindow(y*8+temp,minBoundX,1,maxBoundX-minBoundX+1);
|
||||||
|
uint32_t const pixbufcount = maxBoundX-minBoundX+1; |
||||||
|
uint16_t *pixbuf = (uint16_t *)rtos_malloc(2 * pixbufcount); |
||||||
|
for (x = minBoundX; x <= maxBoundX; x++) |
||||||
|
{ |
||||||
|
pixbuf[x-minBoundX] = ((buffer[x + y * displayWidth]>>temp)&0x01)==1?_RGB:0; |
||||||
|
} |
||||||
|
#ifdef ESP_PLATFORM |
||||||
|
_spi->transferBytes((uint8_t *)pixbuf, NULL, 2 * pixbufcount); |
||||||
|
#else |
||||||
|
_spi->transfer(pixbuf, NULL, 2 * pixbufcount); |
||||||
|
#endif |
||||||
|
rtos_free(pixbuf); |
||||||
|
} |
||||||
|
} |
||||||
|
_spi->endTransaction(); |
||||||
|
set_CS(HIGH); |
||||||
|
|
||||||
|
#else |
||||||
|
set_CS(LOW); |
||||||
|
_spi->beginTransaction(_spiSettings); |
||||||
|
uint8_t x, y; |
||||||
|
for (y = 0; y < _buffheight; y++) |
||||||
|
{ |
||||||
|
for(int temp = 0; temp<8;temp++) |
||||||
|
{ |
||||||
|
//setAddrWindow(minBoundX,y*8+temp,maxBoundX-minBoundX+1,1);
|
||||||
|
//setAddrWindow(minBoundX,y*8+temp,maxBoundX-minBoundX+1,1);
|
||||||
|
setAddrWindow(y*8+temp,0,1,displayWidth); |
||||||
|
uint32_t const pixbufcount = displayWidth; |
||||||
|
uint16_t *pixbuf = (uint16_t *)rtos_malloc(2 * pixbufcount); |
||||||
|
for (x = 0; x < displayWidth; x++) |
||||||
|
{ |
||||||
|
pixbuf[x] = ((buffer[x + y * displayWidth]>>temp)&0x01)==1?_RGB:0; |
||||||
|
} |
||||||
|
#ifdef ESP_PLATFORM |
||||||
|
_spi->transferBytes((uint8_t *)pixbuf, NULL, 2 * pixbufcount); |
||||||
|
#else |
||||||
|
_spi->transfer(pixbuf, NULL, 2 * pixbufcount); |
||||||
|
#endif |
||||||
|
rtos_free(pixbuf); |
||||||
|
} |
||||||
|
} |
||||||
|
_spi->endTransaction(); |
||||||
|
set_CS(HIGH); |
||||||
|
|
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
virtual void resetOrientation() { |
||||||
|
uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MX; |
||||||
|
sendCommand(ST77XX_MADCTL); |
||||||
|
WriteData(madctl); |
||||||
|
delay(10); |
||||||
|
} |
||||||
|
|
||||||
|
virtual void flipScreenVertically() { |
||||||
|
uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MY; |
||||||
|
sendCommand(ST77XX_MADCTL); |
||||||
|
WriteData(madctl); |
||||||
|
delay(10); |
||||||
|
} |
||||||
|
|
||||||
|
virtual void mirrorScreen() { |
||||||
|
uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MX|ST77XX_MADCTL_MY; |
||||||
|
sendCommand(ST77XX_MADCTL); |
||||||
|
WriteData(madctl); |
||||||
|
delay(10); |
||||||
|
} |
||||||
|
|
||||||
|
virtual void setRotation(uint8_t r) { |
||||||
|
uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MX; |
||||||
|
if (r == 1) { madctl = 0xC0; } |
||||||
|
if (r == 2) { madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MY; } |
||||||
|
if (r == 3) { madctl = 0x00; } |
||||||
|
sendCommand(ST77XX_MADCTL); |
||||||
|
WriteData(madctl); |
||||||
|
delay(10); |
||||||
|
} |
||||||
|
|
||||||
|
void setRGB(uint16_t c) |
||||||
|
{ |
||||||
|
|
||||||
|
this->_RGB=0x00|c>>8|c<<8&0xFF00; |
||||||
|
} |
||||||
|
|
||||||
|
void displayOn(void) { |
||||||
|
//sendCommand(DISPLAYON);
|
||||||
|
} |
||||||
|
|
||||||
|
void displayOff(void) { |
||||||
|
//sendCommand(DISPLAYOFF);
|
||||||
|
} |
||||||
|
|
||||||
|
//#define ST77XX_MADCTL_MY 0x80
|
||||||
|
//#define ST77XX_MADCTL_MX 0x40
|
||||||
|
//#define ST77XX_MADCTL_MV 0x20
|
||||||
|
//#define ST77XX_MADCTL_ML 0x10
|
||||||
|
protected: |
||||||
|
// Send all the init commands
|
||||||
|
virtual void sendInitCommands() |
||||||
|
{ |
||||||
|
sendCommand(ST77XX_SWRESET); // 1: Software reset, no args, w/delay
|
||||||
|
delay(150); |
||||||
|
|
||||||
|
sendCommand(ST77XX_SLPOUT); // 2: Out of sleep mode, no args, w/delay
|
||||||
|
delay(10); |
||||||
|
|
||||||
|
sendCommand(ST77XX_COLMOD); // 3: Set color mode, 16-bit color
|
||||||
|
WriteData(0x55);
|
||||||
|
delay(10); |
||||||
|
|
||||||
|
sendCommand(ST77XX_MADCTL); // 4: Mem access ctrl (directions), Row/col addr, bottom-top refresh
|
||||||
|
WriteData(0x08);
|
||||||
|
|
||||||
|
sendCommand(ST77XX_CASET); // 5: Column addr set,
|
||||||
|
WriteData(0x00);
|
||||||
|
WriteData(0x00); // XSTART = 0
|
||||||
|
WriteData(0x00);
|
||||||
|
WriteData(240); // XEND = 240
|
||||||
|
|
||||||
|
sendCommand(ST77XX_RASET); // 6: Row addr set,
|
||||||
|
WriteData(0x00);
|
||||||
|
WriteData(0x00); // YSTART = 0
|
||||||
|
WriteData(320>>8);
|
||||||
|
WriteData(320&0xFF); // YSTART = 320
|
||||||
|
|
||||||
|
sendCommand(ST77XX_SLPOUT); // 7: hack
|
||||||
|
delay(10); |
||||||
|
|
||||||
|
sendCommand(ST77XX_NORON); // 8: Normal display on, no args, w/delay
|
||||||
|
delay(10); |
||||||
|
|
||||||
|
sendCommand(ST77XX_DISPON); // 9: Main screen turn on, no args, delay
|
||||||
|
delay(10); |
||||||
|
|
||||||
|
sendCommand(ST77XX_INVON); // 10: invert
|
||||||
|
delay(10); |
||||||
|
|
||||||
|
//uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MX;
|
||||||
|
uint8_t madctl = ST77XX_MADCTL_RGB|ST77XX_MADCTL_MV|ST77XX_MADCTL_MX; |
||||||
|
sendCommand(ST77XX_MADCTL); |
||||||
|
WriteData(madctl); |
||||||
|
delay(10); |
||||||
|
setRGB(ST77XX_GREEN); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private: |
||||||
|
|
||||||
|
void setAddrWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h) { |
||||||
|
x += (320-displayWidth)/2; |
||||||
|
y += (240-displayHeight)/2; |
||||||
|
uint32_t xa = ((uint32_t)x << 16) | (x + w - 1); |
||||||
|
uint32_t ya = ((uint32_t)y << 16) | (y + h - 1); |
||||||
|
|
||||||
|
writeCommand(ST77XX_CASET); // Column addr set
|
||||||
|
SPI_WRITE32(xa); |
||||||
|
|
||||||
|
writeCommand(ST77XX_RASET); // Row addr set
|
||||||
|
SPI_WRITE32(ya); |
||||||
|
|
||||||
|
writeCommand(ST77XX_RAMWR); // write to RAM
|
||||||
|
} |
||||||
|
int getBufferOffset(void) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
inline void set_CS(bool level) { |
||||||
|
if (_cs != (uint8_t) -1) { |
||||||
|
digitalWrite(_cs, level); |
||||||
|
} |
||||||
|
}; |
||||||
|
inline void sendCommand(uint8_t com) __attribute__((always_inline)){ |
||||||
|
set_CS(HIGH); |
||||||
|
digitalWrite(_dc, LOW); |
||||||
|
set_CS(LOW); |
||||||
|
_spi->beginTransaction(_spiSettings); |
||||||
|
_spi->transfer(com); |
||||||
|
_spi->endTransaction(); |
||||||
|
set_CS(HIGH); |
||||||
|
digitalWrite(_dc, HIGH); |
||||||
|
} |
||||||
|
|
||||||
|
inline void WriteData(uint8_t data) __attribute__((always_inline)){ |
||||||
|
digitalWrite(_cs, LOW); |
||||||
|
_spi->beginTransaction(_spiSettings); |
||||||
|
_spi->transfer(data); |
||||||
|
_spi->endTransaction(); |
||||||
|
digitalWrite(_cs, HIGH); |
||||||
|
} |
||||||
|
void SPI_WRITE32(uint32_t l) |
||||||
|
{ |
||||||
|
_spi->transfer(l >> 24); |
||||||
|
_spi->transfer(l >> 16); |
||||||
|
_spi->transfer(l >> 8); |
||||||
|
_spi->transfer(l); |
||||||
|
} |
||||||
|
void writeCommand(uint8_t cmd) { |
||||||
|
digitalWrite(_dc, LOW); |
||||||
|
_spi->transfer(cmd); |
||||||
|
digitalWrite(_dc, HIGH); |
||||||
|
} |
||||||
|
|
||||||
|
// Private functions
|
||||||
|
void setGeometry(OLEDDISPLAY_GEOMETRY g, uint16_t width, uint16_t height) { |
||||||
|
this->geometry = g; |
||||||
|
|
||||||
|
switch (g) { |
||||||
|
case GEOMETRY_128_128: |
||||||
|
this->displayWidth = 128; |
||||||
|
this->displayHeight = 128; |
||||||
|
break; |
||||||
|
case GEOMETRY_128_64: |
||||||
|
this->displayWidth = 128; |
||||||
|
this->displayHeight = 64; |
||||||
|
break; |
||||||
|
case GEOMETRY_128_32: |
||||||
|
this->displayWidth = 128; |
||||||
|
this->displayHeight = 32; |
||||||
|
break; |
||||||
|
case GEOMETRY_64_48: |
||||||
|
this->displayWidth = 64; |
||||||
|
this->displayHeight = 48; |
||||||
|
break; |
||||||
|
case GEOMETRY_64_32: |
||||||
|
this->displayWidth = 64; |
||||||
|
this->displayHeight = 32; |
||||||
|
break; |
||||||
|
case GEOMETRY_RAWMODE: |
||||||
|
this->displayWidth = width > 0 ? width : 128; |
||||||
|
this->displayHeight = height > 0 ? height : 64; |
||||||
|
break; |
||||||
|
} |
||||||
|
uint8_t tmp=displayHeight % 8; |
||||||
|
uint8_t _buffheight=displayHeight / 8; |
||||||
|
|
||||||
|
if(tmp!=0) |
||||||
|
_buffheight++; |
||||||
|
this->displayBufferSize = displayWidth * _buffheight ; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
#endif |
Loading…
Reference in new issue