| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 | /** * 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 <esp_heap_caps.h>#include <esp_log.h>#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[] = "SSD1322";struct PrivateSpace {	uint8_t *iRAM, *Shadowbuffer;	uint8_t ReMap, PageSize;	uint8_t Offset;};// Functions are not declared to minimize # of linesstatic void WriteDataByte( 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 ) {	Device->WriteCommand( Device, 0x15 );	Device->WriteData( Device, &Start, 1 );	Device->WriteData( Device, &End, 1 );}static void SetRowAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {	Device->WriteCommand( Device, 0x75 );	Device->WriteData( Device, &Start, 1 );	Device->WriteData( Device, &End, 1 );}static void Update( struct GDS_Device* Device ) {	struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;			// RAM is by columns of 4 pixels ...	SetColumnAddress( Device, Private->Offset, Private->Offset + 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) {				uint16_t *optr = (uint16_t*) Private->iRAM, *iptr = (uint16_t*) (Private->Shadowbuffer + (r - page + 1) * Device->Width / 2);				SetRowAddress( Device, r - page + 1, r );				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;			}				page = 0;		}		}	#else	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 );		}		}	#endif	}static void SetLayout( struct GDS_Device* Device, bool HFlip, bool VFlip, bool Rotate ) { 	struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;	Private->ReMap = HFlip ? (Private->ReMap & ~(1 << 1)) : (Private->ReMap | (1 << 1));	Private->ReMap = VFlip ? (Private->ReMap | (1 << 4)) : (Private->ReMap & ~(1 << 4));	Device->WriteCommand( Device, 0xA0 );	Device->WriteData( Device, &Private->ReMap, 1 );	WriteDataByte( Device, 0x11 );		}	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, 0xC1 );    Device->WriteData( Device, &Contrast, 1 );}static bool Init( struct GDS_Device* Device ) {	struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;		// these displays seems to be layout centered (1 column = 4 pixels of 4 bits each, little endian)	Private->Offset = (480 - Device->Width) / 4 / 2;		// 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	Private->iRAM = heap_caps_malloc( Private->PageSize * Device->Width / 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );	ESP_LOGI(TAG, "SSD1322 with offset %u, page %u, iRAM %p", Private->Offset, Private->PageSize, Private->iRAM);				// need to be off and disable display RAM	Device->DisplayOff( Device );    Device->WriteCommand( Device, 0xA5 );		// Display Offset    Device->WriteCommand( Device, 0xA2 );    WriteDataByte( Device, 0 );		// Display Start Line    Device->WriteCommand( Device, 0xA1 );	WriteDataByte( Device, 0x00 );		// set flip modes	Private->ReMap = 0;	Device->SetLayout( Device, false, false, false);		// set Display Enhancement    Device->WriteCommand( Device, 0xB4 );	WriteDataByte( Device, 0xA0 );	WriteDataByte( Device, 0xB5 );		// set Clocks    Device->WriteCommand( Device, 0xB3 );	WriteDataByte( Device, 0xB2 ); // 0x91 seems to be common but is too slow for 5.5'		// set MUX	Device->WriteCommand( Device, 0xCA );	WriteDataByte( Device, Device->Height - 1 );		// phase 1 & 2 period	Device->WriteCommand( Device, 0xB1 );	WriteDataByte( Device, 0xE3 );	// 0xE2 was recommended		// set pre-charge V 	Device->WriteCommand( Device, 0xBB );	WriteDataByte( Device, 0x0F); // 0x1F causes column interferences 		// set COM deselect voltage	Device->WriteCommand( Device, 0xBE );	WriteDataByte( Device, 0x07 );		// no Display Inversion    Device->WriteCommand( Device, 0xA6 );		// gone with the wind	Device->DisplayOn( Device );	Device->Update( Device );		return true;}	static const struct GDS_Device SSD1322 = {	.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,	.SetLayout = SetLayout,	.Update = Update, .Init = Init,	.Mode = GDS_GRAYSCALE, .Depth = 4,};	struct GDS_Device* SSD1322_Detect(char *Driver, struct GDS_Device* Device) {	if (!strcasestr(Driver, "SSD1322")) return NULL;			if (!Device) Device = calloc(1, sizeof(struct GDS_Device));		*Device = SSD1322;					return Device;}
 |