diff --git a/components/display/SH1122.c b/components/display/SH1122.c new file mode 100644 index 000000000..867daf9c1 --- /dev/null +++ b/components/display/SH1122.c @@ -0,0 +1,182 @@ +/** + * Copyright (c) 2017-2018 Tara Keeling + * 2020 Philippe G. + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include +#include +#include +#include +#include +#include + +#include "gds.h" +#include "gds_private.h" + +#define SHADOW_BUFFER +#define PAGE_BLOCK 1024 + +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +static char TAG[] = "SH1122"; + +struct PrivateSpace { + uint8_t *iRAM, *Shadowbuffer; + uint8_t PageSize; +}; + +// Functions are not declared to minimize # of lines + +static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) { + Device->WriteCommand( Device, 0x10 | (Start >> 4) ); + Device->WriteCommand( Device, 0x00 | (Start & 0x0f) ); +} + +static void SetRowAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) { + Device->WriteCommand( Device, 0xB0 ); + Device->WriteCommand( Device, Start ); +} + +static void Update( struct GDS_Device* Device ) { + struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private; + + // RAM is by columns of 4 pixels ... + SetColumnAddress( Device, 0, Device->Width / 4 - 1); + +#ifdef SHADOW_BUFFER + uint16_t *optr = (uint16_t*) Private->Shadowbuffer, *iptr = (uint16_t*) Device->Framebuffer; + bool dirty = false; + + for (int r = 0, page = 0; r < Device->Height; r++) { + // look for change and update shadow (cheap optimization = width always / by 2) + for (int c = Device->Width / 2 / 2; --c >= 0;) { + if (*optr != *iptr) { + dirty = true; + *optr = *iptr; + } + iptr++; optr++; + } + + // one line done, check for page boundary + if (++page == Private->PageSize) { + if (dirty) { + SetRowAddress( Device, r - page + 1, r ); + if (Private->iRAM) { + memcpy(Private->iRAM, Private->Shadowbuffer + (r - page + 1) * Device->Width / 2, page * Device->Width / 2 ); + Device->WriteData( Device, Private->iRAM, Device->Width * page / 2 ); + } else { + Device->WriteData( Device, Private->Shadowbuffer + (r - page + 1) * Device->Width / 2, page * Device->Width / 2); + } + dirty = false; + } + page = 0; + } + } +#else + SetRowAddress( Device, 0, Device->Height - 1 ); + for (int r = 0; r < Device->Height; r += Private->PageSize) { + if (Private->iRAM) { + memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width / 2, Private->PageSize * Device->Width / 2 ); + Device->WriteData( Device, Private->iRAM, Private->PageSize * Device->Width / 2 ); + } else { + Device->WriteData( Device, Device->Framebuffer + r * Device->Width / 2, Private->PageSize * Device->Width / 2 ); + } + } +#endif +} + +static void DrawPixelFast4( struct GDS_Device* Device, int X, int Y, int Color ) { + uint8_t* FBOffset = Device->Framebuffer + ( (Y * Device->Width >> 1) + (X >> 1)); + *FBOffset = X & 0x01 ? ((*FBOffset & 0xf0) | (Color & 0x0f)) : (*FBOffset & 0x0f) | ((Color & 0x0f) << 4); +} + +static void SetLayout( struct GDS_Device* Device, struct GDS_Layout *Layout ) { + if (Layout->HFlip) { + Device->WriteCommand( Device, 0x40 + 0x20 ); + Device->WriteCommand( Device, 0xA1 ); + } else { + Device->WriteCommand( Device, 0x40 + 0x00 ); + Device->WriteCommand( Device, 0xA0 ); + } + Device->WriteCommand( Device, Layout->VFlip ? 0xC8 : 0xC0 ); + Device->WriteCommand( Device, Layout->Invert ? 0xA7 : 0xA6 ); +} + +static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAF ); } +static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAE ); } + +static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) { + Device->WriteCommand( Device, 0x81 ); + Device->WriteCommand( Device, Contrast ); +} + +static bool Init( struct GDS_Device* Device ) { + struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private; + + // find a page size that is not too small is an integer of height + Private->PageSize = min(8, PAGE_BLOCK / (Device->Width / 2)); + while (Private->PageSize && Device->Height != (Device->Height / Private->PageSize) * Private->PageSize) Private->PageSize--; + +#ifdef SHADOW_BUFFER + Private->Shadowbuffer = malloc( Device->FramebufferSize ); + memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize); +#endif + + // only use iRAM for SPI + if (Device->IF == GDS_IF_SPI) { + Private->iRAM = heap_caps_malloc( Private->PageSize * Device->Width / 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); + } + + ESP_LOGI(TAG, "SH1122 page %u, iRAM %p", Private->PageSize, Private->iRAM); + + // need to be off and disable display RAM + Device->DisplayOff( Device ); + Device->WriteCommand( Device, 0xA5 ); + + // Display Offset + Device->WriteCommand( Device, 0xD3 ); + Device->WriteCommand( Device, 0 ); + + // set flip modes + struct GDS_Layout Layout = { }; + Device->SetLayout( Device, &Layout ); + + // set Clocks => check value + Device->WriteCommand( Device, 0xD5 ); + Device->WriteCommand( Device, ( 0x04 << 4 ) | 0x00 ); + + // MUX Ratio => fixed + Device->WriteCommand( Device, 0xA8 ); + Device->WriteCommand( Device, Device->Height - 1); + + // no Display Inversion + Device->WriteCommand( Device, 0xA6 ); + + // gone with the wind + Device->WriteCommand( Device, 0xA4 ); + Device->DisplayOn( Device ); + Device->Update( Device ); + + return true; +} + +static const struct GDS_Device SH1122 = { + .DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast, + .DrawPixelFast = DrawPixelFast4, + .SetLayout = SetLayout, + .Update = Update, .Init = Init, + .Mode = GDS_GRAYSCALE, .Depth = 4, + .HighNibble = true, +}; + +struct GDS_Device* SH1122_Detect(char *Driver, struct GDS_Device* Device) { + if (!strcasestr(Driver, "SH1122")) return NULL; + + if (!Device) Device = calloc(1, sizeof(struct GDS_Device)); + *Device = SH1122; + + return Device; +} \ No newline at end of file diff --git a/components/display/SSD1322.c b/components/display/SSD1322.c index 73929d687..d27022506 100644 --- a/components/display/SSD1322.c +++ b/components/display/SSD1322.c @@ -71,8 +71,8 @@ static void Update( struct GDS_Device* Device ) { if (dirty) { uint16_t *optr = (uint16_t*) Private->iRAM, *iptr = (uint16_t*) (Private->Shadowbuffer + (r - page + 1) * Device->Width / 2); SetRowAddress( Device, r - page + 1, r ); + // need byte swapping for (int i = page * Device->Width / 2 / 2; --i >= 0; iptr++) *optr++ = (*iptr >> 8) | (*iptr << 8); - //memcpy(Private->iRAM, Private->Shadowbuffer + (r - page + 1) * Device->Width / 2, page * Device->Width / 2 ); Device->WriteCommand( Device, 0x5c ); Device->WriteData( Device, Private->iRAM, Device->Width * page / 2 ); dirty = false; @@ -84,14 +84,10 @@ static void Update( struct GDS_Device* Device ) { for (int r = 0; r < Device->Height; r += Private->PageSize) { SetRowAddress( Device, r, r + Private->PageSize - 1 ); Device->WriteCommand( Device, 0x5c ); - if (Private->iRAM) { - uint16_t *optr = (uint16_t*) Private->iRAM, *iptr = (uint16_t*) (Device->Framebuffer + r * Device->Width / 2); - for (int i = Private->PageSize * Device->Width / 2 / 2; --i >= 0; iptr++) *optr++ = (*iptr >> 8) | (*iptr << 8); - //memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width / 2, Private->PageSize * Device->Width / 2 ); - Device->WriteData( Device, Private->iRAM, Private->PageSize * Device->Width / 2 ); - } else { - Device->WriteData( Device, Device->Framebuffer + r * Device->Width / 2, Private->PageSize * Device->Width / 2 ); - } + // need byte swapping + uint16_t *optr = (uint16_t*) Private->iRAM, *iptr = (uint16_t*) (Device->Framebuffer + r * Device->Width / 2); + for (int i = Private->PageSize * Device->Width / 2 / 2; --i >= 0; iptr++) *optr++ = (*iptr >> 8) | (*iptr << 8); + Device->WriteData( Device, Private->iRAM, Private->PageSize * Device->Width / 2 ); } #endif } diff --git a/components/display/core/gds_draw.c b/components/display/core/gds_draw.c index 3e3947eac..727e1f990 100644 --- a/components/display/core/gds_draw.c +++ b/components/display/core/gds_draw.c @@ -213,37 +213,65 @@ void GDS_DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int iptr += Height; } } - } else if (Device->Depth == 4) { + } else if (Device->Depth == 4) { uint8_t *optr = Device->Framebuffer; int LineLen = Device->Width >> 1; Height >>= 3; Color &= 0x0f; - - for (int i = Width * Height, r = 0, c = 0; --i >= 0;) { - uint8_t Byte = BitReverseTable256[*Data++]; - // we need to linearize code to let compiler better optimize - if (c & 0x01) { - *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; - *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; - *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; - *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; - *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; - *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; - *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; - *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; - } else { - *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; - *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; - *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; - *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; - *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; - *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; - *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; - *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; - } - // end of a column, move to next one - if (++r == Height) { c++; r = 0; optr = Device->Framebuffer + (c >> 1); } + + if (Device->HighNibble) { + for (int i = Width * Height, r = 0, c = 0; --i >= 0;) { + uint8_t Byte = BitReverseTable256[*Data++]; + // we need to linearize code to let compiler better optimize + if (c & 0x01) { + *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; + } else { + *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; + } + // end of a column, move to next one + if (++r == Height) { c++; r = 0; optr = Device->Framebuffer + (c >> 1); } + } + } else { + for (int i = Width * Height, r = 0, c = 0; --i >= 0;) { + uint8_t Byte = BitReverseTable256[*Data++]; + // we need to linearize code to let compiler better optimize + if (c & 0x01) { + *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; + } else { + *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; + *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; + } + // end of a column, move to next one + if (++r == Height) { c++; r = 0; optr = Device->Framebuffer + (c >> 1); } + } } } else if (Device->Depth == 8) { uint8_t *optr = Device->Framebuffer; diff --git a/components/display/core/gds_private.h b/components/display/core/gds_private.h index c25dd4d93..74741e042 100644 --- a/components/display/core/gds_private.h +++ b/components/display/core/gds_private.h @@ -98,6 +98,7 @@ struct GDS_Device { uint16_t Width, TextWidth; uint16_t Height; uint8_t Depth, Mode; + bool HighNibble; uint8_t Alloc; uint8_t* Framebuffer; diff --git a/components/display/display.c b/components/display/display.c index e5d0747d5..1e4705188 100644 --- a/components/display/display.c +++ b/components/display/display.c @@ -63,6 +63,7 @@ static EXT_RAM_ATTR struct { } displayer; static const char *known_drivers[] = {"SH1106", + "SH1122", "SSD1306", "SSD1322", "SSD1326", @@ -79,8 +80,8 @@ static void displayer_task(void *args); static void display_sleep(void); struct GDS_Device *display; -extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect, ILI9341_Detect; -GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect, ILI9341_Detect, NULL }; +extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect, SH1122_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect, ILI9341_Detect; +GDS_DetectFunc *drivers[] = { SH1106_Detect, SH1122_Detect, SSD1306_Detect, SSD132x_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect, ILI9341_Detect, NULL }; /**************************************************************************************** *