Merge remote-tracking branch 'origin/master' into master-cmake

Conflicts:
	components/raop/raop.c
	components/raop/rtp.c
	main/cmd_squeezelite.c
This commit is contained in:
Sebastien
2020-03-08 09:54:50 -04:00
48 changed files with 951 additions and 481 deletions

View File

@@ -1,28 +1,5 @@
idf_component_register(SRCS display.c
SH1106.c
SSD1306.c
SSD132x.c
core/ifaces/default_if_i2c.c
core/ifaces/default_if_spi.c
core/gds_draw.c
core/gds_font.c
core/gds_image.c
core/gds_text.c
core/gds.c
fonts/font_droid_sans_fallback_11x13.c
fonts/font_droid_sans_fallback_15x17.c
fonts/font_droid_sans_fallback_24x28.c
fonts/font_droid_sans_mono_13x24.c
fonts/font_droid_sans_mono_16x31.c
fonts/font_droid_sans_mono_7x13.c
fonts/font_liberation_mono_13x21.c
fonts/font_liberation_mono_17x30.c
fonts/font_liberation_mono_9x15.c
fonts/font_line_1.c
fonts/font_line_2.c
fonts/font_tarable7seg_16x32.c
fonts/font_tarable7seg_32x64.c
idf_component_register(SRC_DIRS . core core/ifaces fonts
REQUIRES services
INCLUDE_DIRS . fonts core
)
@@ -30,4 +7,11 @@ idf_component_register(SRCS display.c
set_source_files_properties(display.c
PROPERTIES COMPILE_FLAGS
-Wno-format-overflow
)
)
#target_link_libraries(${COMPONENT_LIB} PUBLIC
# -Wl,--whole-archive
# $<TARGET_PROPERTY,INTERFACE_LINK_LIBRARIES>
# -Wl,--no-whole-archive
#)

View File

@@ -21,6 +21,10 @@
static char TAG[] = "SH1106";
struct PrivateSpace {
uint8_t *Shadowbuffer;
};
// Functions are not declared to minimize # of lines
static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
@@ -36,9 +40,10 @@ static void SetPageAddress( struct GDS_Device* Device, uint8_t Start, uint8_t En
static void Update( struct GDS_Device* Device ) {
#ifdef SHADOW_BUFFER
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
// not sure the compiler does not have to redo all calculation in for loops, so local it is
int width = Device->Width, rows = Device->Height / 8;
uint8_t *optr = Device->Shadowbuffer, *iptr = Device->Framebuffer;
uint8_t *optr = Private->Shadowbuffer, *iptr = Device->Framebuffer;
// by row, find first and last columns that have been updated
for (int r = 0; r < rows; r++) {
@@ -55,7 +60,7 @@ static void Update( struct GDS_Device* Device ) {
if (first--) {
SetColumnAddress( Device, first, last );
SetPageAddress( Device, r, r);
Device->WriteData( Device, Device->Shadowbuffer + r*width + first, last - first + 1);
Device->WriteData( Device, Private->Shadowbuffer + r*width + first, last - first + 1);
}
}
#else
@@ -79,26 +84,15 @@ static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
}
static bool Init( struct GDS_Device* Device ) {
Device->FramebufferSize = ( Device->Width * Device->Height ) / 8;
// benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy
#ifdef SHADOW_BUFFER
Device->Framebuffer = calloc( 1, Device->FramebufferSize );
NullCheck( Device->Framebuffer, return false );
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
#ifdef USE_IRAM
if (Device->IF == IF_SPI) Device->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
if (Device->IF == GDS_IF_SPI) Private->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
else
#endif
Device->Shadowbuffer = malloc( Device->FramebufferSize );
NullCheck( Device->Shadowbuffer, return false );
memset(Device->Shadowbuffer, 0xFF, Device->FramebufferSize);
#else // not SHADOW_BUFFER
#ifdef USE_IRAM
// benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy
if (Device->IF == IF_SPI) Device->Framebuffer = heap_caps_calloc( 1, Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
else
#endif
Device->Framebuffer = calloc( 1, Device->FramebufferSize );
Private->Shadowbuffer = malloc( Device->FramebufferSize );
NullCheck( Private->Shadowbuffer, return false );
memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize);
#endif
// need to be off and disable display RAM
@@ -143,16 +137,17 @@ static const struct GDS_Device SH1106 = {
.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
.SetVFlip = SetVFlip, .SetHFlip = SetHFlip,
.Update = Update, .Init = Init,
//.DrawPixelFast = GDS_DrawPixelFast,
//.ClearWindow = ClearWindow,
};
struct GDS_Device* SH1106_Detect(char *Driver, struct GDS_Device* Device) {
if (!strcasestr(Driver, "SH1106")) return NULL;
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
*Device = SH1106;
*Device = SH1106;
Device->Depth = 1;
#if !defined SHADOW_BUFFER && defined USE_IRAM
Device->Alloc = GDS_ALLOC_IRAM_SPI;
#endif
ESP_LOGI(TAG, "SH1106 driver");
return Device;

View File

@@ -21,7 +21,11 @@
static char TAG[] = "SSD1306";
// Functions are not deckared to minimize # of lines
struct PrivateSpace {
uint8_t *Shadowbuffer;
};
// Functions are not declared to minimize # of lines
static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
Device->WriteCommand( Device, 0x21 );
@@ -36,12 +40,14 @@ static void SetPageAddress( struct GDS_Device* Device, uint8_t Start, uint8_t En
static void Update( struct GDS_Device* Device ) {
#ifdef SHADOW_BUFFER
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
// not sure the compiler does not have to redo all calculation in for loops, so local it is
int width = Device->Width, rows = Device->Height / 8;
uint8_t *optr = Device->Shadowbuffer, *iptr = Device->Framebuffer;
int width = Device->Width, pages = Device->Height / 8;
uint8_t *optr = Private->Shadowbuffer, *iptr = Device->Framebuffer;
int CurrentPage = -1, FirstCol = -1, LastCol = -1;
// by row, find first and last columns that have been updated
for (int r = 0; r < rows; r++) {
for (int p = 0; p < pages; p++) {
uint8_t first = 0, last;
for (int c = 0; c < width; c++) {
if (*iptr != *optr) {
@@ -53,13 +59,26 @@ static void Update( struct GDS_Device* Device ) {
// now update the display by "byte rows"
if (first--) {
SetColumnAddress( Device, first, last );
SetPageAddress( Device, r, r);
Device->WriteData( Device, Device->Shadowbuffer + r*width + first, last - first + 1);
// only set column when useful, saves a fair bit of CPU
if (first > FirstCol && first <= FirstCol + 4 && last < LastCol && last >= LastCol - 4) {
first = FirstCol;
last = LastCol;
} else {
SetColumnAddress( Device, first, last );
FirstCol = first;
LastCol = last;
}
// Set row only when needed, otherwise let auto-increment work
if (p != CurrentPage) SetPageAddress( Device, p, Device->Height / 8 - 1 );
CurrentPage = p + 1;
// actual write
Device->WriteData( Device, Private->Shadowbuffer + p*width + first, last - first + 1 );
}
}
#else
// automatic counter and end Page/Column
// automatic counter and end Page/Column (we assume Height % 8 == 0)
SetColumnAddress( Device, 0, Device->Width - 1);
SetPageAddress( Device, 0, Device->Height / 8 - 1);
Device->WriteData( Device, Device->Framebuffer, Device->FramebufferSize );
@@ -77,26 +96,15 @@ static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
}
static bool Init( struct GDS_Device* Device ) {
Device->FramebufferSize = ( Device->Width * Device->Height ) / 8;
// benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy
#ifdef SHADOW_BUFFER
Device->Framebuffer = calloc( 1, Device->FramebufferSize );
NullCheck( Device->Framebuffer, return false );
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
#ifdef USE_IRAM
if (Device->IF == IF_SPI) Device->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
if (Device->IF == GDS_IF_SPI) Private->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
else
#endif
Device->Shadowbuffer = malloc( Device->FramebufferSize );
NullCheck( Device->Shadowbuffer, return false );
memset(Device->Shadowbuffer, 0xFF, Device->FramebufferSize);
#else // not SHADOW_BUFFER
#ifdef USE_IRAM
// benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy
if (Device->IF == IF_SPI) Device->Framebuffer = heap_caps_calloc( 1, Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
else
#endif
Device->Framebuffer = calloc( 1, Device->FramebufferSize );
Private->Shadowbuffer = malloc( Device->FramebufferSize );
NullCheck( Private->Shadowbuffer, return false );
memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize);
#endif
// need to be off and disable display RAM
@@ -106,6 +114,10 @@ static bool Init( struct GDS_Device* Device ) {
// charge pump regulator, do direct init
Device->WriteCommand( Device, 0x8D );
Device->WriteCommand( Device, 0x14 );
// set Clocks
Device->WriteCommand( Device, 0xD5 );
Device->WriteCommand( Device, ( 0x08 << 4 ) | 0x00 );
// COM pins HW config (alternative:EN if 64, DIS if 32, remap:DIS) - some display might need something different
Device->WriteCommand( Device, 0xDA );
@@ -114,6 +126,12 @@ static bool Init( struct GDS_Device* Device ) {
// MUX Ratio
Device->WriteCommand( Device, 0xA8 );
Device->WriteCommand( Device, Device->Height - 1);
// Page & GDDRAM Start Column High/Low
/*
Device->WriteCommand( Device, 0x00 );
Device->WriteCommand( Device, 0x10 );
Device->WriteCommand( Device, 0xB0 );
*/
// Display Offset
Device->WriteCommand( Device, 0xD3 );
Device->WriteCommand( Device, 0 );
@@ -125,9 +143,6 @@ static bool Init( struct GDS_Device* Device ) {
Device->SetHFlip( Device, false );
// no Display Inversion
Device->WriteCommand( Device, 0xA6 );
// set Clocks
Device->WriteCommand( Device, 0xD5 );
Device->WriteCommand( Device, ( 0x08 << 4 ) | 0x00 );
// set Adressing Mode Horizontal
Device->WriteCommand( Device, 0x20 );
Device->WriteCommand( Device, 0 );
@@ -144,8 +159,6 @@ static const struct GDS_Device SSD1306 = {
.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
.SetVFlip = SetVFlip, .SetHFlip = SetHFlip,
.Update = Update, .Init = Init,
//.DrawPixelFast = GDS_DrawPixelFast,
//.ClearWindow = ClearWindow,
};
struct GDS_Device* SSD1306_Detect(char *Driver, struct GDS_Device* Device) {
@@ -154,6 +167,9 @@ struct GDS_Device* SSD1306_Detect(char *Driver, struct GDS_Device* Device) {
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
*Device = SSD1306;
Device->Depth = 1;
#if !defined SHADOW_BUFFER && defined USE_IRAM
Device->Alloc = GDS_ALLOC_IRAM_SPI;
#endif
ESP_LOGI(TAG, "SSD1306 driver");
return Device;

View File

@@ -26,8 +26,8 @@ static char TAG[] = "SSD132x";
enum { SSD1326, SSD1327 };
struct SSD132x_Private {
uint8_t *iRAM;
struct PrivateSpace {
uint8_t *iRAM, *Shadowbuffer;
uint8_t ReMap, PageSize;
uint8_t Model;
};
@@ -67,13 +67,13 @@ static void SetRowAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End
}
static void Update4( struct GDS_Device* Device ) {
struct SSD132x_Private *Private = (struct SSD132x_Private*) Device->Private;
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
// always update by full lines
SetColumnAddress( Device, 0, Device->Width / 2 - 1);
#ifdef SHADOW_BUFFER
uint16_t *optr = (uint16_t*) Device->Shadowbuffer, *iptr = (uint16_t*) Device->Framebuffer;
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++) {
@@ -92,10 +92,10 @@ static void Update4( struct GDS_Device* Device ) {
SetRowAddress( Device, r - page + 1, r );
// own use of IRAM has not proven to be much better than letting SPI do its copy
if (Private->iRAM) {
memcpy(Private->iRAM, Device->Shadowbuffer + (r - page + 1) * Device->Width / 2, page * Device->Width / 2 );
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, Device->Shadowbuffer + (r - page + 1) * Device->Width / 2, page * Device->Width / 2 );
Device->WriteData( Device, Private->Shadowbuffer + (r - page + 1) * Device->Width / 2, page * Device->Width / 2 );
}
dirty = false;
}
@@ -122,9 +122,11 @@ static void Update4( struct GDS_Device* Device ) {
*/
static void Update1( struct GDS_Device* Device ) {
#ifdef SHADOW_BUFFER
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
// not sure the compiler does not have to redo all calculation in for loops, so local it is
int width = Device->Width / 8, rows = Device->Height;
uint8_t *optr = Device->Shadowbuffer, *iptr = Device->Framebuffer;
uint8_t *optr = Private->Shadowbuffer, *iptr = Device->Framebuffer;
int CurrentRow = -1, FirstCol = -1, LastCol = -1;
// by row, find first and last columns that have been updated
for (int r = 0; r < rows; r++) {
@@ -139,9 +141,22 @@ static void Update1( struct GDS_Device* Device ) {
// now update the display by "byte rows"
if (first--) {
SetColumnAddress( Device, first, last );
SetRowAddress( Device, r, r);
Device->WriteData( Device, Device->Shadowbuffer + r*width + first, last - first + 1);
// only set column when useful, saves a fair bit of CPU
if (first > FirstCol && first <= FirstCol + 4 && last < LastCol && last >= LastCol - 4) {
first = FirstCol;
last = LastCol;
} else {
SetColumnAddress( Device, first, last );
FirstCol = first;
LastCol = last;
}
// Set row only when needed, otherwise let auto-increment work
if (r != CurrentRow) SetRowAddress( Device, r, Device->Height - 1 );
CurrentRow = r + 1;
// actual write
Device->WriteData( Device, Private->Shadowbuffer + r*width + first, last - first + 1 );
}
}
#else
@@ -161,15 +176,15 @@ static void IRAM_ATTR DrawPixel1Fast( struct GDS_Device* Device, int X, int Y, i
*FBOffset ^= BIT( 7 - XBit );
} else {
// we might be able to save the 7-Xbit using BitRemap (A0 bit 2)
*FBOffset = ( Color == GDS_COLOR_WHITE ) ? *FBOffset | BIT( 7 - XBit ) : *FBOffset & ~BIT( 7 - XBit );
*FBOffset = ( Color == GDS_COLOR_BLACK ) ? *FBOffset & ~BIT( XBit ) : *FBOffset | BIT( XBit );
}
}
static void ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color ) {
uint8_t _Color = Color == GDS_COLOR_BLACK ? 0: 0xff;
uint8_t Width = Device->Width >> 3;
int Width = Device->Width >> 3;
uint8_t *optr = Device->Framebuffer;
for (int r = y1; r <= y2; r++) {
int c = x1;
// for a row that is not on a boundary, not column opt can be done, so handle all columns on that line
@@ -180,24 +195,35 @@ static void ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int
c += chunk * 8;
while (c <= x2) DrawPixel1Fast( Device, c++, r, Color );
}
Device->Dirty = true;
}
static void DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color ) {
uint8_t *optr = Device->Framebuffer;
if (!Height) Height = Device->Height;
if (!Width) Width = Device->Width;
int DWidth = Device->Width >> 3;
// just do bitreverse and if BitRemap works, there will be even nothing to do
for (int i = Height * Width >> 3; --i >= 0;) *optr++ = BitReverseTable256[*Data++];
// Dirty is set for us
// Two consecutive bits of source data are split over two different bytes of framebuffer
for (int c = 0; c < Width; c++) {
uint8_t shift = c & 0x07, bit = ~(1 << shift);
uint8_t *optr = Device->Framebuffer + (c >> 3);
// we need to linearize code to let compiler better optimize
for (int r = Height >> 3; --r >= 0;) {
uint8_t Byte = BitReverseTable256[*Data++];
*optr = (*optr & bit) | ((Byte & 0x01) << shift); optr += DWidth; Byte >>= 1;
*optr = (*optr & bit) | ((Byte & 0x01) << shift); optr += DWidth; Byte >>= 1;
*optr = (*optr & bit) | ((Byte & 0x01) << shift); optr += DWidth; Byte >>= 1;
*optr = (*optr & bit) | ((Byte & 0x01) << shift); optr += DWidth; Byte >>= 1;
*optr = (*optr & bit) | ((Byte & 0x01) << shift); optr += DWidth; Byte >>= 1;
*optr = (*optr & bit) | ((Byte & 0x01) << shift); optr += DWidth; Byte >>= 1;
*optr = (*optr & bit) | ((Byte & 0x01) << shift); optr += DWidth; Byte >>= 1;
*optr = (*optr & bit) | ((Byte & 0x01) << shift); optr += DWidth;
}
}
}
static void SetHFlip( struct GDS_Device* Device, bool On ) {
struct SSD132x_Private *Private = (struct SSD132x_Private*) Device->Private;
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
if (Private->Model == SSD1326) Private->ReMap = On ? (Private->ReMap | ((1 << 0) | (1 << 2))) : (Private->ReMap & ~((1 << 0) | (1 << 2)));
else Private->ReMap = On ? (Private->ReMap | ((1 << 0) | (1 << 1))) : (Private->ReMap & ~((1 << 0) | (1 << 1)));
Device->WriteCommand( Device, 0xA0 );
@@ -205,7 +231,7 @@ static void SetHFlip( struct GDS_Device* Device, bool On ) {
}
static void SetVFlip( struct GDS_Device *Device, bool On ) {
struct SSD132x_Private *Private = (struct SSD132x_Private*) Device->Private;
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
if (Private->Model == SSD1326) Private->ReMap = On ? (Private->ReMap | (1 << 1)) : (Private->ReMap & ~(1 << 1));
else Private->ReMap = On ? (Private->ReMap | (1 << 4)) : (Private->ReMap & ~(1 << 4));
Device->WriteCommand( Device, 0xA0 );
@@ -221,50 +247,32 @@ static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
}
static bool Init( struct GDS_Device* Device ) {
struct SSD132x_Private *Private = (struct SSD132x_Private*) Device->Private;
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));
Private->PageSize = Device->Height / (Device->Height / Private->PageSize) ;
#ifdef USE_IRAM
// let SPI driver allocate memory, it has not proven to be more efficient
if (Device->IF == IF_SPI) Private->iRAM = heap_caps_malloc( Private->PageSize * Device->Width / 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
#endif
Device->FramebufferSize = ( Device->Width * Device->Height ) / 2;
Device->Framebuffer = calloc( 1, Device->FramebufferSize );
NullCheck( Device->Framebuffer, return false );
// benchmarks showed little gain to have SPI memory already in IRAM vs letting driver copy
#ifdef SHADOW_BUFFER
Device->Framebuffer = calloc( 1, Device->FramebufferSize );
NullCheck( Device->Framebuffer, return false );
#ifdef USE_IRAM
if (Device->IF == IF_SPI) {
if (Device->IF == GDS_IF_SPI) {
if (Device->Depth == 1) {
Device->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
Private->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
} else {
Device->Shadowbuffer = malloc( Device->FramebufferSize );
Private->Shadowbuffer = malloc( Device->FramebufferSize );
Private->iRAM = heap_caps_malloc( Private->PageSize * Device->Width / 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
}
} else
#endif
Device->Shadowbuffer = malloc( Device->FramebufferSize );
memset(Device->Shadowbuffer, 0xFF, Device->FramebufferSize);
#else // not SHADOW_BUFFER
#ifdef USE_IRAM
if (Device->IF == IF_SPI) {
if (Device->Depth == 1) {
Device->Framebuffer = heap_caps_calloc( 1, Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
} else {
Device->Framebuffer = calloc( 1, Device->FramebufferSize );
Private->iRAM = heap_caps_malloc( Private->PageSize * Device->Width / 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
}
} else
#endif
Device->Framebuffer = calloc( 1, Device->FramebufferSize );
Private->Shadowbuffer = malloc( Device->FramebufferSize );
memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize);
#else
#ifdef USE_IRAM
if (Device->Depth == 4 && Device->IF == GDS_IF_SPI) Private->iRAM = heap_caps_malloc( Private->PageSize * Device->Width / 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
#endif
#endif
ESP_LOGI(TAG, "SSD1326/7 with bit depth %u, page %u, iRAM %p", Device->Depth, Private->PageSize, Private->iRAM);
// need to be off and disable display RAM
@@ -315,23 +323,28 @@ static const struct GDS_Device SSD132x = {
struct GDS_Device* SSD132x_Detect(char *Driver, struct GDS_Device* Device) {
uint8_t Model;
int Depth;
if (!strcasestr(Driver, "SSD1326")) Model = SSD1326;
else if (!strcasestr(Driver, "SSD1327")) Model = SSD1327;
if (strcasestr(Driver, "SSD1326")) Model = SSD1326;
else if (strcasestr(Driver, "SSD1327")) Model = SSD1327;
else return NULL;
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
*Device = SSD132x;
((struct SSD132x_Private*) Device->Private)->Model = Model;
sscanf(Driver, "%*[^:]:%c", &Device->Depth);
((struct PrivateSpace*) Device->Private)->Model = Model;
sscanf(Driver, "%*[^:]:%u", &Depth);
Device->Depth = Depth;
if (Model == SSD1326 && Device->Depth == 1) {
Device->Update = Update1;
Device->DrawPixelFast = DrawPixel1Fast;
Device->DrawBitmapCBR = DrawBitmapCBR;
Device->ClearWindow = ClearWindow;
#if !defined SHADOW_BUFFER && defined USE_IRAM
Device->Alloc = GDS_ALLOC_IRAM_SPI;
#endif
} else {
Device->Depth = 4;
}

View File

@@ -0,0 +1,252 @@
/**
* Copyright (c) 2017-2018 Tara Keeling
* 2020 Philippe G.
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include <esp_log.h>
#include "gds.h"
#include "gds_private.h"
static char TAG[] = "SSD1675";
const unsigned char EPD_lut_full_update[] = {
0x80,0x60,0x40,0x00,0x00,0x00,0x00, //LUT0: BB: VS 0 ~7
0x10,0x60,0x20,0x00,0x00,0x00,0x00, //LUT1: BW: VS 0 ~7
0x80,0x60,0x40,0x00,0x00,0x00,0x00, //LUT2: WB: VS 0 ~7
0x10,0x60,0x20,0x00,0x00,0x00,0x00, //LUT3: WW: VS 0 ~7
0x00,0x00,0x00,0x00,0x00,0x00,0x00, //LUT4: VCOM: VS 0 ~7
0x03,0x03,0x00,0x00,0x02, // TP0 A~D RP0
0x09,0x09,0x00,0x00,0x02, // TP1 A~D RP1
0x03,0x03,0x00,0x00,0x02, // TP2 A~D RP2
0x00,0x00,0x00,0x00,0x00, // TP3 A~D RP3
0x00,0x00,0x00,0x00,0x00, // TP4 A~D RP4
0x00,0x00,0x00,0x00,0x00, // TP5 A~D RP5
0x00,0x00,0x00,0x00,0x00, // TP6 A~D RP6
0x15,0x41,0xA8,0x32,0x30,0x0A,
};
const unsigned char EPD_lut_partial_update[]= { //20 bytes
0x00,0x00,0x00,0x00,0x00,0x00,0x00, //LUT0: BB: VS 0 ~7
0x80,0x00,0x00,0x00,0x00,0x00,0x00, //LUT1: BW: VS 0 ~7
0x40,0x00,0x00,0x00,0x00,0x00,0x00, //LUT2: WB: VS 0 ~7
0x00,0x00,0x00,0x00,0x00,0x00,0x00, //LUT3: WW: VS 0 ~7
0x00,0x00,0x00,0x00,0x00,0x00,0x00, //LUT4: VCOM: VS 0 ~7
0x0A,0x00,0x00,0x00,0x00, // TP0 A~D RP0
0x00,0x00,0x00,0x00,0x00, // TP1 A~D RP1
0x00,0x00,0x00,0x00,0x00, // TP2 A~D RP2
0x00,0x00,0x00,0x00,0x00, // TP3 A~D RP3
0x00,0x00,0x00,0x00,0x00, // TP4 A~D RP4
0x00,0x00,0x00,0x00,0x00, // TP5 A~D RP5
0x00,0x00,0x00,0x00,0x00, // TP6 A~D RP6
0x15,0x41,0xA8,0x32,0x30,0x0A,
};
struct PrivateSpace {
int ReadyPin;
uint16_t Height;
};
// Functions are not declared to minimize # of lines
void WaitReady( struct GDS_Device* Device) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
if (Private->ReadyPin >= 0) {
int count = 4*1000;
while (gpio_get_level( Private->ReadyPin ) && count) {
vTaskDelay( pdMS_TO_TICKS(100) );
count -= 100;
}
} else {
vTaskDelay( pdMS_TO_TICKS(2000) );
}
}
static void WriteByte( struct GDS_Device* Device, uint8_t Data ) {
Device->WriteData( Device, &Data, 1 );
}
static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
// start might be greater than end if we decrement
Device->WriteCommand( Device, 0x44 );
Device->WriteData( Device, &Start, 1 );
Device->WriteData( Device, &End, 1 );
// we obviously want to start ... from the start
Device->WriteCommand( Device, 0x4e );
WriteByte( Device, Start );
}
static void SetRowAddress( struct GDS_Device* Device, uint16_t Start, uint16_t End ) {
// start might be greater than end if we decrement
Device->WriteCommand( Device, 0x45 );
WriteByte( Device, Start );
WriteByte( Device, Start >> 8 );
WriteByte( Device, End );
WriteByte( Device, End >> 8 );
// we obviously want to start ... from the start
Device->WriteCommand( Device, 0x4f );
WriteByte( Device, Start );
WriteByte( Device, Start >> 8 );
}
static void Update( struct GDS_Device* Device ) {
uint8_t *iptr = Device->Framebuffer;
Device->WriteCommand( Device, 0x24 );
// this is awfully slow, but e-ink are slow anyway ...
for (int i = Device->FramebufferSize; --i >= 0;) {
WriteByte( Device, ~*iptr++ );
}
Device->WriteCommand( Device, 0x22 );
WriteByte( Device, 0xC7);
Device->WriteCommand( Device, 0X20 );
WaitReady( Device );
}
// remember that for these ELD drivers W and H are "inverted"
static void IRAM_ATTR DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color ) {
uint32_t YBit = ( Y & 0x07 );
Y>>= 3;
uint8_t* FBOffset = Device->Framebuffer + ( ( Y * Device->Width ) + X );
*FBOffset = ( Color == GDS_COLOR_BLACK ) ? *FBOffset & ~BIT( 7-YBit ) : *FBOffset | BIT( 7-YBit );
}
static void ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color ) {
for (int r = y1; r <= y2; r++) {
for (int c = x1; c <= x2; c++) {
DrawPixelFast( Device, c, r, Color );
}
}
}
static void DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color ) {
if (!Height) Height = Device->Height;
if (!Width) Width = Device->Width;
// just do row/column swap
for (int r = 0; r < Height; r++) {
uint8_t *optr = Device->Framebuffer + r*Device->Width, *iptr = Data + r;
for (int c = Width; --c >= 0;) {
*optr++ = *iptr;
iptr += Height;
}
}
}
static bool Init( struct GDS_Device* Device ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
// need to re-adjust framebuffer because these are non % 8
Private->Height = Device->Height;
if (Device->Height & 0x07) Device->Height = ((Device->Height >> 3) + 1) << 3;
Device->FramebufferSize = Device->Width * Device->Height / 8;
Device->Framebuffer = calloc(1, Device->FramebufferSize);
NullCheck( Device->Framebuffer, return false );
if (Private->ReadyPin >= 0) {
gpio_pad_select_gpio( Private->ReadyPin );
gpio_set_pull_mode( Private->ReadyPin, GPIO_PULLUP_ONLY);
gpio_set_direction( Private->ReadyPin, GPIO_MODE_INPUT );
}
// soft reset
vTaskDelay(pdMS_TO_TICKS( 2000 ));
Device->WriteCommand( Device, 0x12 );
WaitReady( Device );
Device->WriteCommand( Device, 0x74 );
WriteByte( Device, 0x54 );
Device->WriteCommand( Device, 0x7e );
WriteByte( Device, 0x3B );
Device->WriteCommand( Device, 0x3c );
WriteByte( Device, 0x03 );
Device->WriteCommand( Device, 0x2c );
WriteByte( Device, 0x55 );
Device->WriteCommand( Device, 0x03 );
WriteByte( Device, EPD_lut_full_update[70] );
Device->WriteCommand( Device, 0x04 );
WriteByte( Device, EPD_lut_full_update[71] );
WriteByte( Device, EPD_lut_full_update[72] );
WriteByte( Device, EPD_lut_full_update[73] );
Device->WriteCommand( Device, 0x3a );
WriteByte( Device, EPD_lut_full_update[74] );
Device->WriteCommand( Device, 0x3b );
WriteByte( Device, EPD_lut_full_update[75] );
Device->WriteCommand( Device, 0X32 );
for (int i = 0; i < 70; i++) {
WriteByte( Device, EPD_lut_full_update[i] );
}
// now deal with funny X/Y layout (W and H are "inverted")
Device->WriteCommand( Device, 0x01 );
WriteByte( Device, Device->Width - 1 );
WriteByte( Device, (Device->Width - 1) >> 8 );
WriteByte( Device, (0 << 0) );
/*
Start from 0, Ymax, incX, decY. Starting from X=Height would be difficult
as we would hit the extra bits added because height % 8 != 0 and they are
not written by the DrawPixel. By starting from X=0 we are aligned and
doing incY is like a clockwise 90° rotation (vs otherwise we would virtually
do a counter-clockwise rotation but again have the "border" issue.
*/
Device->WriteCommand( Device, 0x11 );
WriteByte( Device, (1 << 2) | (0 << 1) | (1 << 0));
// must be in order with increment/decrement i.e start might be > end if we decrement
SetColumnAddress( Device, 0x0, (Device->Height >> 3) - 1 );
SetRowAddress ( Device, Device->Width - 1, 0 );
WaitReady( Device );
Update( Device );
return true;
}
static const struct GDS_Device SSD1675 = {
.DrawBitmapCBR = DrawBitmapCBR, .ClearWindow = ClearWindow,
.DrawPixelFast = DrawPixelFast,
.Update = Update, .Init = Init,
};
struct GDS_Device* SSD1675_Detect(char *Driver, struct GDS_Device* Device) {
if (!strcasestr(Driver, "SSD1675")) return NULL;
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
*Device = SSD1675;
Device->Depth = 1;
Device->Alloc = GDS_ALLOC_NONE;
char *p;
struct PrivateSpace* Private = (struct PrivateSpace*) Device->Private;
Private->ReadyPin = -1;
if ((p = strcasestr(Driver, "ready")) != NULL) Private->ReadyPin = atoi(strchr(p, '=') + 1);
ESP_LOGI(TAG, "SSD1675 driver with ready GPIO %d", Private->ReadyPin);
return Device;
}

View File

@@ -95,7 +95,7 @@ void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2,
memset( Device->Framebuffer, Color | (Color << 4), Device->FramebufferSize );
} else {
uint8_t _Color = Color | (Color << 4);
uint8_t Width = Device->Width;
int Width = Device->Width;
uint8_t *optr = Device->Framebuffer;
// try to do byte processing as much as possible
for (int r = y1; r <= y2; r++) {
@@ -113,7 +113,7 @@ void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2,
}
}
}
// make sure diplay will do update
Device->Dirty = true;
}
@@ -132,10 +132,29 @@ bool GDS_Reset( struct GDS_Device* Device ) {
return true;
}
void GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast ) { Device->SetContrast( Device, Contrast); }
void GDS_SetHFlip( struct GDS_Device* Device, bool On ) { Device->SetHFlip( Device, On ); }
void GDS_SetVFlip( struct GDS_Device* Device, bool On ) { Device->SetVFlip( Device, On ); }
bool GDS_Init( struct GDS_Device* Device ) {
Device->FramebufferSize = (Device->Width * Device->Height) / (8 / Device->Depth);
// allocate FB unless explicitely asked not to
if (!(Device->Alloc & GDS_ALLOC_NONE)) {
if ((Device->Alloc & GDS_ALLOC_IRAM) || ((Device->Alloc & GDS_ALLOC_IRAM_SPI) && Device->IF == GDS_IF_SPI)) {
heap_caps_calloc( 1, Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
} else {
Device->Framebuffer = calloc( 1, Device->FramebufferSize );
}
NullCheck( Device->Framebuffer, return false );
}
bool Res = Device->Init( Device );
if (!Res) free(Device->Framebuffer);
return Res;
}
void GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast ) { if (Device->SetContrast) Device->SetContrast( Device, Contrast); }
void GDS_SetHFlip( struct GDS_Device* Device, bool On ) { if (Device->SetHFlip) Device->SetHFlip( Device, On ); }
void GDS_SetVFlip( struct GDS_Device* Device, bool On ) { if (Device->SetVFlip) Device->SetVFlip( Device, On ); }
int GDS_GetWidth( struct GDS_Device* Device ) { return Device->Width; }
int GDS_GetHeight( struct GDS_Device* Device ) { return Device->Height; }
void GDS_DisplayOn( struct GDS_Device* Device ) { Device->DisplayOn( Device ); }
void GDS_DisplayOff( struct GDS_Device* Device ) { Device->DisplayOff( Device ); }
void GDS_DisplayOn( struct GDS_Device* Device ) { if (Device->DisplayOn) Device->DisplayOn( Device ); }
void GDS_DisplayOff( struct GDS_Device* Device ) { if (Device->DisplayOff) Device->DisplayOff( Device ); }

View File

@@ -15,11 +15,12 @@
enum { GDS_COLOR_L0 = 0, GDS_COLOR_L1, GDS_COLOR_L2, GDS_COLOR_L3, GDS_COLOR_L4, GDS_COLOR_L5, GDS_COLOR_L6, GDS_COLOR_L7,
GDS_COLOR_L8, GDS_COLOR_L9, GDS_COLOR_L10, GDS_COLOR_L11, GDS_COLOR_L12, GDS_COLOR_L13, GDS_COLOR_L14, GDS_COLOR_L15,
GDS_COLOR_MAX
};
#define GDS_COLOR_BLACK GDS_COLOR_L0
#define GDS_COLOR_WHITE GDS_COLOR_L15
#define GDS_COLOR_XOR 2
#define GDS_COLOR_BLACK (0)
#define GDS_COLOR_WHITE (-1)
#define GDS_COLOR_XOR (GDS_COLOR_MAX + 1)
struct GDS_Device;
struct GDS_FontDef;

View File

@@ -58,7 +58,7 @@ void GDS_DrawHLine( struct GDS_Device* Device, int x, int y, int Width, int Colo
if (XEnd >= Device->Width) XEnd = Device->Width - 1;
if (y < 0) y = 0;
else if (y >= Device->Height) x = Device->Height - 1;
else if (y >= Device->Height) y = Device->Height - 1;
for ( ; x < XEnd; x++ ) GDS_DrawPixelFast( Device, x, y, Color );
}
@@ -195,11 +195,11 @@ void GDS_DrawBox( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int
void GDS_DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color ) {
if (!Height) Height = Device->Height;
if (!Width) Width = Device->Width;
Height >>= 3;
if (Device->DrawBitmapCBR) {
Device->DrawBitmapCBR( Device, Data, Width, Height, Color );
} else if (Device->Depth == 1) {
Height >>= 3;
// need to do row/col swap and bit-reverse
for (int r = 0; r < Height; r++) {
uint8_t *optr = Device->Framebuffer + r*Device->Width, *iptr = Data + r;
@@ -211,6 +211,7 @@ void GDS_DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int
} else if (Device->Depth == 4) {
uint8_t *optr = Device->Framebuffer;
int LineLen = Device->Width >> 1;
Height >>= 3;
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
@@ -237,6 +238,7 @@ void GDS_DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int
if (++r == Height) { c++; r = 0; optr = Device->Framebuffer + (c >> 1); }
}
} else {
Height >>= 3;
// don't know bitdepth, use brute-force solution
for (int i = Width * Height, r = 0, c = 0; --i >= 0;) {
uint8_t Byte = *Data++;

View File

@@ -21,6 +21,7 @@ typedef struct {
struct { // DirectDraw
struct GDS_Device * Device;
int XOfs, YOfs;
int XMin, YMin;
int Depth;
};
};
@@ -38,9 +39,7 @@ static unsigned OutHandler(JDEC *Decoder, void *Bitmap, JRECT *Frame) {
uint8_t *Pixels = (uint8_t*) Bitmap;
for (int y = Frame->top; y <= Frame->bottom; y++) {
if (y < Context->YOfs) continue;
for (int x = Frame->left; x <= Frame->right; x++) {
if (x < Context->XOfs) continue;
// Convert the 888 to RGB565
uint16_t Value = (*Pixels++ & ~0x07) << 8;
Value |= (*Pixels++ & ~0x03) << 3;
@@ -57,12 +56,14 @@ static unsigned OutHandlerDirect(JDEC *Decoder, void *Bitmap, JRECT *Frame) {
int Shift = 8 - Context->Depth;
for (int y = Frame->top; y <= Frame->bottom; y++) {
if (y < Context->YMin) continue;
for (int x = Frame->left; x <= Frame->right; x++) {
if (x < Context->XMin) continue;
// Convert the 888 to RGB565
int Value = ((Pixels[0]*11 + Pixels[1]*59 + Pixels[2]*30) / 100) >> Shift;
Pixels += 3;
// used DrawPixel and not "fast" version as X,Y may be beyond screen
GDS_DrawPixel( Context->Device, Context->XOfs + x, Context->YOfs + y, Value);
GDS_DrawPixel( Context->Device, x + Context->XOfs, y + Context->YOfs, Value);
}
}
return 1;
@@ -90,11 +91,16 @@ static uint16_t* DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scal
Decoder.scale = Scale;
if (Res == JDR_OK && !SizeOnly) {
// find the scaling factor
Context.OutData = malloc(Decoder.width * Decoder.height * sizeof(uint16_t));
// find the scaling factor
uint8_t N = 0, ScaleInt = ceil(1.0 / Scale);
ScaleInt--; ScaleInt |= ScaleInt >> 1; ScaleInt |= ScaleInt >> 2; ScaleInt++;
while (ScaleInt >>= 1) N++;
if (N > 3) {
ESP_LOGW(TAG, "Image will not fit %dx%d", Decoder.width, Decoder.height);
N = 3;
}
// ready to decode
if (Context.OutData) {
@@ -102,7 +108,7 @@ static uint16_t* DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scal
Context.Height = Decoder.height / (1 << N);
if (Width) *Width = Context.Width;
if (Height) *Height = Context.Height;
Res = jd_decomp(&Decoder, OutHandler, N > 3 ? 3 : N);
Res = jd_decomp(&Decoder, OutHandler, N);
if (Res != JDR_OK) {
ESP_LOGE(TAG, "Image decoder: jd_decode failed (%d)", Res);
}
@@ -202,16 +208,23 @@ bool GDS_DrawJPEG( struct GDS_Device* Device, uint8_t *Source, int x, int y, int
uint8_t Ratio = XRatio < YRatio ? ceil(1/XRatio) : ceil(1/YRatio);
Ratio--; Ratio |= Ratio >> 1; Ratio |= Ratio >> 2; Ratio++;
while (Ratio >>= 1) N++;
if (N > 3) {
ESP_LOGW(TAG, "Image will not fit %dx%d", Decoder.width, Decoder.height);
N = 3;
}
Context.Width /= 1 << N;
Context.Height /= 1 << N;
}
// then place it
if (Fit & GDS_IMAGE_CENTER_X) Context.XOfs = x + ((Device->Width - x) - Context.Width) / 2;
if (Fit & GDS_IMAGE_CENTER_Y) Context.YOfs = y + ((Device->Height - y) - Context.Height) / 2;
if (Fit & GDS_IMAGE_CENTER_X) Context.XOfs = (Device->Width + x - Context.Width) / 2;
if (Fit & GDS_IMAGE_CENTER_Y) Context.YOfs = (Device->Height + y - Context.Height) / 2;
Context.XMin = x - Context.XOfs;
Context.YMin = y - Context.YOfs;
// do decompress & draw
Res = jd_decomp(&Decoder, OutHandlerDirect, N > 3 ? 3 : N);
Res = jd_decomp(&Decoder, OutHandlerDirect, N);
if (Res == JDR_OK) {
Ret = true;
} else {

View File

@@ -7,6 +7,10 @@
#include "gds.h"
#include "gds_err.h"
#define GDS_ALLOC_NONE 0x80
#define GDS_ALLOC_IRAM 0x01
#define GDS_ALLOC_IRAM_SPI 0x02
#define GDS_CLIPDEBUG_NONE 0
#define GDS_CLIPDEBUG_WARNING 1
#define GDS_CLIPDEBUG_ERROR 2
@@ -55,8 +59,8 @@ typedef bool ( *WriteDataProc ) ( struct GDS_Device* Device, const uint8_t* Data
struct spi_device_t;
typedef struct spi_device_t* spi_device_handle_t;
#define IF_SPI 0
#define IF_I2C 1
#define GDS_IF_SPI 0
#define GDS_IF_I2C 1
struct GDS_Device {
uint8_t IF;
@@ -82,8 +86,9 @@ struct GDS_Device {
uint16_t Width;
uint16_t Height;
uint8_t Depth;
uint8_t* Framebuffer, *Shadowbuffer;
uint8_t Alloc;
uint8_t* Framebuffer;
uint16_t FramebufferSize;
bool Dirty;
@@ -95,12 +100,13 @@ struct GDS_Device {
// various driver-specific method
// must always provide
bool (*Init)( struct GDS_Device* Device);
void (*Update)( struct GDS_Device* Device );
// may provide if supported
void (*SetContrast)( struct GDS_Device* Device, uint8_t Contrast );
void (*DisplayOn)( struct GDS_Device* Device );
void (*DisplayOff)( struct GDS_Device* Device );
void (*SetHFlip)( struct GDS_Device* Device, bool On );
void (*SetVFlip)( struct GDS_Device* Device, bool On );
void (*Update)( struct GDS_Device* Device );
// must provide for depth other than 1 (vertical) and 4 (may provide for optimization)
void (*DrawPixelFast)( struct GDS_Device* Device, int X, int Y, int Color );
void (*DrawBitmapCBR)(struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color );
@@ -117,6 +123,7 @@ struct GDS_Device {
};
bool GDS_Reset( struct GDS_Device* Device );
bool GDS_Init( struct GDS_Device* Device );
static inline bool IsPixelVisible( struct GDS_Device* Device, int x, int y ) {
bool Result = (
@@ -152,7 +159,7 @@ static inline void IRAM_ATTR GDS_DrawPixel1Fast( struct GDS_Device* Device, int
if ( Color == GDS_COLOR_XOR ) {
*FBOffset ^= BIT( YBit );
} else {
*FBOffset = ( Color == GDS_COLOR_WHITE ) ? *FBOffset | BIT( YBit ) : *FBOffset & ~BIT( YBit );
*FBOffset = ( Color == GDS_COLOR_BLACK ) ? *FBOffset & ~BIT( YBit ) : *FBOffset | BIT( YBit );
}
}
@@ -160,7 +167,7 @@ static inline void IRAM_ATTR GDS_DrawPixel4Fast( struct GDS_Device* Device, int
uint8_t* FBOffset;
FBOffset = Device->Framebuffer + ( (Y * Device->Width >> 1) + (X >> 1));
*FBOffset = X & 0x01 ? (*FBOffset & 0x0f) | (Color << 4) : ((*FBOffset & 0xf0) | Color);
*FBOffset = X & 0x01 ? (*FBOffset & 0x0f) | ((Color & 0x0f) << 4) : ((*FBOffset & 0xf0) | (Color & 0x0f));
}
static inline void IRAM_ATTR GDS_DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color ) {

View File

@@ -73,7 +73,7 @@ bool GDS_I2CAttachDevice( struct GDS_Device* Device, int Width, int Height, int
Device->WriteData = I2CDefaultWriteData;
Device->Address = I2CAddress;
Device->RSTPin = RSTPin;
Device->IF = IF_I2C;
Device->IF = GDS_IF_I2C;
Device->Width = Width;
Device->Height = Height;
@@ -83,7 +83,7 @@ bool GDS_I2CAttachDevice( struct GDS_Device* Device, int Width, int Height, int
GDS_Reset( Device );
}
return Device->Init( Device );
return GDS_Init( Device );
}
static bool I2CDefaultWriteBytes( int Address, bool IsCommand, const uint8_t* Data, size_t DataLength ) {

View File

@@ -58,7 +58,7 @@ bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int
Device->SPIHandle = SPIDevice;
Device->RSTPin = RSTPin;
Device->CSPin = CSPin;
Device->IF = IF_SPI;
Device->IF = GDS_IF_SPI;
Device->Width = Width;
Device->Height = Height;
@@ -68,7 +68,7 @@ bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int
GDS_Reset( Device );
}
return Device->Init( Device );
return GDS_Init( Device );
}
static bool SPIDefaultWriteBytes( spi_device_handle_t SPIHandle, int WriteMode, const uint8_t* Data, size_t DataLength ) {

View File

@@ -59,8 +59,8 @@ static EXT_RAM_ATTR struct {
static void displayer_task(void *args);
struct GDS_Device *display;
extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect;
GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, NULL };
extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect, SSD1675_Detect;
GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, SSD1675_Detect, NULL };
/****************************************************************************************
*
@@ -86,15 +86,18 @@ void display_init(char *welcome) {
// so far so good
if (display && width > 0 && height > 0) {
int RST_pin = -1;
if ((p = strcasestr(config, "reset")) != NULL) RST_pin = atoi(strchr(p, '=') + 1);
// Detect driver interface
if (strstr(config, "I2C") && i2c_system_port != -1) {
int address = 0x3C;
if ((p = strcasestr(config, "address")) != NULL) address = atoi(strchr(p, '=') + 1);
init = true;
GDS_I2CInit( i2c_system_port, -1, -1, i2c_system_speed ) ;
GDS_I2CAttachDevice( display, width, height, address, -1 );
GDS_I2CAttachDevice( display, width, height, address, RST_pin );
ESP_LOGI(TAG, "Display is I2C on port %u", address);
} else if (strstr(config, "SPI") && spi_system_host != -1) {
@@ -105,7 +108,7 @@ void display_init(char *welcome) {
init = true;
GDS_SPIInit( spi_system_host, spi_system_dc_gpio );
GDS_SPIAttachDevice( display, width, height, CS_pin, -1, speed );
GDS_SPIAttachDevice( display, width, height, CS_pin, RST_pin, speed );
ESP_LOGI(TAG, "Display is SPI host %u with cs:%d", spi_system_host, CS_pin);
} else {
@@ -189,7 +192,7 @@ static void displayer_task(void *args) {
// handler elapsed track time
if (displayer.timer && displayer.state == DISPLAYER_ACTIVE) {
char counter[12];
char counter[16];
TickType_t tick = xTaskGetTickCount();
uint32_t elapsed = (tick - displayer.tick) * portTICK_PERIOD_MS;
@@ -198,8 +201,8 @@ static void displayer_task(void *args) {
displayer.tick = tick;
displayer.elapsed += elapsed / 1000;
xSemaphoreGive(displayer.mutex);
if (displayer.elapsed < 3600) sprintf(counter, "%5u:%02u", displayer.elapsed / 60, displayer.elapsed % 60);
else sprintf(counter, "%2u:%02u:%02u", displayer.elapsed / 3600, (displayer.elapsed % 3600) / 60, displayer.elapsed % 60);
if (displayer.elapsed < 3600) snprintf(counter, 16, "%5u:%02u", displayer.elapsed / 60, displayer.elapsed % 60);
else snprintf(counter, 16, "%2u:%02u:%02u", displayer.elapsed / 3600, (displayer.elapsed % 3600) / 60, displayer.elapsed % 60);
GDS_TextLine(display, 1, GDS_TEXT_RIGHT, (GDS_TEXT_CLEAR | GDS_TEXT_CLEAR_EOL) | GDS_TEXT_UPDATE, counter);
timer_sleep = 1000;
} else timer_sleep = max(1000 - elapsed, 0);
@@ -289,13 +292,14 @@ void displayer_metadata(char *artist, char *album, char *title) {
/****************************************************************************************
*
*/
void displayer_scroll(char *string, int speed) {
void displayer_scroll(char *string, int speed, int pause) {
// need a display!
if (!display) return;
xSemaphoreTake(displayer.mutex, portMAX_DELAY);
if (speed) displayer.speed = speed;
if (pause) displayer.pause = pause;
displayer.offset = 0;
strncpy(displayer.string, string, SCROLLABLE_SIZE);
displayer.string[SCROLLABLE_SIZE] = '\0';

View File

@@ -42,7 +42,7 @@ enum displayer_time_e { DISPLAYER_ELAPSED, DISPLAYER_REMAINING };
enum display_bus_cmd_e { DISPLAY_BUS_TAKE, DISPLAY_BUS_GIVE };
bool (*display_bus)(void *from, enum display_bus_cmd_e cmd);
void displayer_scroll(char *string, int speed);
void displayer_scroll(char *string, int speed, int pause);
void displayer_control(enum displayer_cmd_e cmd, ...);
void displayer_metadata(char *artist, char *album, char *title);
void displayer_timer(enum displayer_time_e mode, int elapsed, int duration);