浏览代码

adding SSD1675 driver

philippe44 5 年之前
父节点
当前提交
9759c0dbef

+ 1 - 1
components/display/SH1106.c

@@ -143,7 +143,7 @@ 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;

+ 7 - 7
components/display/SSD1306.c

@@ -42,12 +42,12 @@ 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;
+	int width = Device->Width, pages = Device->Height / 8;
 	uint8_t *optr = Private->Shadowbuffer, *iptr = Device->Framebuffer;
-	int CurrentRow = -1, FirstCol = -1, LastCol = -1;
+	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) {
@@ -70,15 +70,15 @@ static void Update( struct GDS_Device* Device ) {
 			}
 			
 			// Set row only when needed, otherwise let auto-increment work
-			if (r != CurrentRow) SetPageAddress( Device, r, Device->Height / 8 - 1 );
-			CurrentRow = r + 1;
+			if (p != CurrentPage) SetPageAddress( Device, p, Device->Height / 8 - 1 );
+			CurrentPage = p + 1;
 			
 			// actual write
-			Device->WriteData( Device, Private->Shadowbuffer + r*width + first, last - first + 1);
+			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 );

+ 2 - 6
components/display/SSD132x.c

@@ -162,7 +162,7 @@ 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( 7 - XBit ) : *FBOffset | BIT( 7 - XBit );
     }
 }
 
@@ -181,8 +181,6 @@ 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 ) {
@@ -193,8 +191,6 @@ static void DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, i
 	
 	// 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
 }
 
 static void SetHFlip( struct GDS_Device* Device, bool On ) { 
@@ -307,7 +303,7 @@ struct GDS_Device* SSD132x_Detect(char *Driver, struct GDS_Device* Device) {
 	
 	*Device = SSD132x;	
 	((struct PrivateSpace*) Device->Private)->Model = Model;
-	
+		
 	sscanf(Driver, "%*[^:]:%c", &Device->Depth);
 	
 	if (Model == SSD1326 && Device->Depth == 1) {

+ 252 - 0
components/display/SSD1675.c

@@ -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;
+}

+ 11 - 4
components/display/core/gds.c

@@ -133,11 +133,18 @@ bool GDS_Reset( struct GDS_Device* Device ) {
 }
 
 bool GDS_Init( struct GDS_Device* Device ) {
-	Device->FramebufferSize = ( Device->Width * Device->Height ) / (8  / Device->Depth);
 	
-	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 );
+	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);

+ 3 - 3
components/display/core/gds.h

@@ -13,13 +13,13 @@
  monochrome mode is not such type of screen, SH1106 and SSD1306 are
 */ 
 
-enum { 	GDS_COLOR_L0 = 0, GDS_COLOR_L1 = 1, GDS_COLOR_L2, GDS_COLOR_L3, GDS_COLOR_L4, GDS_COLOR_L5, GDS_COLOR_L6, GDS_COLOR_L7, 
+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_MAX - 1)
+#define GDS_COLOR_BLACK (0)
+#define GDS_COLOR_WHITE (-1)
 #define GDS_COLOR_XOR 	(GDS_COLOR_MAX + 1)
 
 struct GDS_Device;

+ 3 - 2
components/display/core/gds_private.h

@@ -7,6 +7,7 @@
 #include "gds.h"
 #include "gds_err.h"
 
+#define GDS_ALLOC_NONE		0x80
 #define GDS_ALLOC_IRAM		0x01
 #define GDS_ALLOC_IRAM_SPI	0x02
 
@@ -158,7 +159,7 @@ inline void IRAM_ATTR GDS_DrawPixel1Fast( struct GDS_Device* Device, int X, int
     if ( Color == GDS_COLOR_XOR ) {
         *FBOffset ^= BIT( YBit );
     } else {
-        *FBOffset = ( Color >= GDS_COLOR_WHITE / 2 ) ? *FBOffset | BIT( YBit ) : *FBOffset & ~BIT( YBit );
+        *FBOffset = ( Color == GDS_COLOR_BLACK ) ? *FBOffset & ~BIT( YBit ) : *FBOffset | BIT( YBit );
     }
 }
 
@@ -166,7 +167,7 @@ inline void IRAM_ATTR GDS_DrawPixel4Fast( struct GDS_Device* Device, int X, 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));
 }
 
 inline void IRAM_ATTR GDS_DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color ) {

+ 8 - 5
components/display/display.c

@@ -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 {