Teensy 4.1 PT8211 with driver using DMA and SPI

Status
Not open for further replies.

hoek67

Active member
Moving from a platform with built in 12 bit DAC * 2 to the Teensy 4.1 so looking at the DAC options after reading a lot of forum chat on the subject.

I have used 14 bit MCP4922/MCP4921 but thought with the extra grunt to go for 16 bit output.

Looked at MAX5216 which seems nice and an affordable price (with a 50Mhz clock off memory). However it's only single channel.

On the STM32 I had optimized SPI non-blocking DMA which I'm hoping to be able to use again.

Found https://sparklogic.ru/code-snipplets/i2s-example-code.html that uses SPI DMA to drive the pt8211 chip so I'm hoping to do this.

Each sample is sent during an interrupt and each sample should be well and truly sent before the next one is requested.

The protocol seems quite simple given I've created several display drivers from scratch.

My logic would be :-

For each sample (interrupt)

Flip WS to left channel

grab the next sample(s) from the cyclic sound buffer

write left 2 bytes as per spec via non-blocking DMA

return


--- interrupt for when left transfer complete
flip WS to right channel
if stereo send 2 bytes as per spec (similar to left) and don't bother with interrupt when complete
if mono just set MOSI = low (should fill right with all 0's)


For sending 2 bytes... will probably look at SPI FIFO as DMA usually has a lot of overhead to set up. (wont be able to check this til I get a Teensy to test with)
 
Maybe you can reuse code from the audio library?

https://github.com/PaulStoffregen/Audio/blob/master/output_pt8211.cpp

https://github.com/PaulStoffregen/Audio/blob/master/output_pt8211_2.cpp

The audio library is designed around small blocks of 128 samples, which is far more efficient than running an interrupt and tiny DMA transfer for each sample. But if you really want to structure your project around one-sample-at-a-time processing, you certainly can. Whether DMA is even helpful for that usage is a good question.

If you've never used the audio library, this 31 page tutorial is the place to start.

https://www.pjrc.com/store/audio_tutorial_kit.html
 
Thanks for the quick links Paul! I'll have a read as it all helps.

Writing to the DAC on the STM32F4 was nice... 2 direct port writes. (very spoilt)

I wrote an audio lib years ago that I've been using and it's integrated quite deeply into my main library. In saying that it usually only takes a day to add new hardware to be able to hook into it.

When playing audio it's quite flexible as I tell it what I want to do every sample. It may just send straight to a DAC and to a speaker or feed into a FFT for some visual effects. I also stuffed around a lot with TTS and hit a snag because I could never get the lookup index into memory. (which will now be possible).

The lib will stream/play .wav files from SD or memory and supports uLaw 16 bit and mono/stereo. I also got the Helix MP3 integer codec working but that chewed too much memory codewise.

Also managed 60 fps playback with sound on an OLED ssd1351... main trick being video was synced to the audio and not the other way around. Basically full video frame is followed by all the audio for that frame... then the next video frame follows... along with it's audio. Every time the audio goes to the next block it updates the video frame. Other trick was to have 1 file pointer for the audio and another for the video and both video and audio in 512 byte blocks so no partial blocks are read.

I ordered 2 4.1's from PJRC shop but also 1 over-priced one locally that I'm hoping to get in the mail this week as really can't wait to see what it can do.

Really love the amount of memory available now, the FPU, built in SDIO SD and the raw Mhz available if needed.
 
Do yourself a favor and take a quick look at the audio library and design tool :)

And yrs, video is cool:
This was with a Teensy 3.6, many years ago:

(Why is the video not landscape anymore? Heh?)
 
Last edited:
Maybe also check out Kurt's ILI9341_t3n library, which uses a frame buffer and DMA for very efficient updates. It recently got quite a bit of testing when we were playing with FlexIO interface to video cameras.
 
Yes, this demo uses my old (and outdated) ILI9341 T3 DMA lib that Kurt used as the basis for his great library.
 
I had a good look at ILI9341 code some time ago and TBO it's a nice display with fast SPI rate.

Was going to attach my code... but seems no attach.

header (very small)
Code:
#pragma once

#define OLED_DRIVER_NAME ILI9341
#define OLED_CHIP_LOW 9341
#define OLED_CHIP_HIGH OLED_CHIP_LOW

#define OLED_CLASS cTFT_ILI9341
#define OLED_BPP 16


#define MADCTL_MY  0x80  ///< Bottom to top
#define MADCTL_MX  0x40  ///< Right to left
#define MADCTL_MV  0x20  ///< Reverse Mode
#define MADCTL_ML  0x10  ///< LCD refresh Bottom to top
#define MADCTL_RGB 0x00  ///< Red-Green-Blue pixel order
#define MADCTL_BGR 0x08  ///< Blue-Green-Red pixel order
#define MADCTL_MH  0x04  ///< LCD refresh right to left

#define OLED_NO_TRANSLATE   // need this if the device has a swap flag that's used

#define OLED_HAS_WINDOW_X _writeCommand(0x2a); _write16(x1); _write16(x2);  
#define OLED_HAS_WINDOW_Y _writeCommand(0x2b); _write16(y1); _write16(y2);

#define OLED_READ_VERSION _writeCommand(0xd9); _write8(0x10 + 1); _writeCommand(0xd3);  _read8(); buff[0] = _read8(); \
         _writeCommand(0xd9); _write8(0x10 + 2); _writeCommand(0xd3);  _read8(); buff[1] = _read8(); \
	_writeCommand(0xd9); _write8(0x10 + 3); _writeCommand(0xd3);  _read8(); buff[2] = _read8(); \
_writeCommand(0xd9); _write8(0x10 + 4); _writeCommand(0xd3);  _read8(); buff[3] = _read8(); 

#define OLED_HAS_CONTRAST  _writeCommand(0x51); _write8(contrast);  

#define OLED_WRITE_RAM  _writeCommand(0x2c);

#define OLED_ROTATION  uint8_t m = 0; \
    m_rotation = rot & 1; \
    if (!m_rotation)  { m = (MADCTL_MX | MADCTL_BGR); }	else  { m = (MADCTL_MV | MADCTL_BGR); } _writeCommand(ILI9341_MADCTL); _writeData(m); 
	

#include "display.inl"

The .cpp is 2 lines!

Code:
#include "TFT_ILI9341.h"
#include "display_base.inl"

The display_base.inl code is used for all drivers... as 99% of the functionality is the same except for some codes and omissions.

Code:
#include "ks_utils.h"

#define _DEBUG 1

uint8_t OLED_CLASS::_get_chip_id()
{
#if defined(OLED_READ_VERSION)
	uint8_t buff[4];

	DEV_CHIP_START
	OLED_READ_VERSION
	DEV_CHIP_END
	
	Serial.println("Version CARD:");
	Serial.println(buff[0], HEX);
	Serial.println(buff[1], HEX);
	Serial.println(buff[2], HEX);
	Serial.println(buff[3], HEX);

	return buff[1];
#else
	return 0;
#endif
}



void OLED_CLASS::_writeCommand(uint8_t c)
	{
		_modeCommand();  // command
		_write8(c);
		_modeData(); // must leave as data by default
	}


bool OLED_CLASS::_setWindow(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
	if (m_rotation) 
	{ 
		_translate(&x1, &y1, &x2, &y2); 
	} 
	
	if (_checkWindow(x1, y1, x2, y2))
	{
		OLED_HAS_WINDOW_X
		OLED_HAS_WINDOW_Y

		return true;
	}

	return false;
}


void OLED_CLASS::_setContrast(uint8_t contrast) // always 0-255 and scaled ... to do
{
	#if defined(OLED_HAS_CONTRAST)
		OLED_HAS_CONTRAST	
	#endif
}

// C vars

extern uint32_t g_last_abp_freq;
extern uint32_t g_last_freq;

bool OLED_CLASS::writeInitCommands(const uint8_t *arr_cmds, uint8_t pin_cs, bool debug, SPISettings settings)
{
	m_init = false;
	m_enabled = false;

	memset(&m_header, 0, sizeof(struct strInitHeader));

	if (!arr_cmds)
	{
		return 0;
	}

	// todo - check header etc

	memcpy((void *)&m_header, (const void *)arr_cmds, sizeof(struct strInitHeader));

	if (m_header.m_magic != DEV_INIT_MAGIC)
	{

#if _DEBUG
			if (debug)
			{
				Serial.println(F("Device:Header mismatch!"));
			}
#endif
		return 0;
	}
	
	if (m_header.m_version < OLED_CHIP_LOW || m_header.m_version > OLED_CHIP_HIGH)
	{
#if _DEBUG
			if (debug)
			{
				Serial.println(F("Device:Chip mismatch!"));
			}
#endif
		return 0;
	}

	// init SPi Bus now

	if (settings.m_clk == 0)
	{
		settings.m_clk = m_header.m_mhz * 1000000; // use chip speed
	}

	if (!_begin(0, getChipName(), pin_cs, settings)) // todo
	{
		return 0;
	}

	m_deft_w = m_header.m_width;
	m_deft_h = m_header.m_height;
	
	arr_cmds += sizeof(struct strInitHeader);

	//delay(200);
	//fastDigitalWrite(m_pin_reset, LOW);
	//delay(200);
	//fastDigitalWrite(m_pin_reset, HIGH);
	//delay(200);

	// setSCKDiv(ks::getMaxSCKDiv(m_header.m_mhz, false)); todo

#if _DEBUG
	if (debug)
	{
		char buff[200];
		sprintf(buff, "\nChip:%s\nDefault SCK:%dMhz\nActual SCK:%dhz\nENABLE=0x%02x\nDISABLE=0x%02x\nINVERT=0x%02x\nNORMAL=0x%02x\nWRITE_RAM=%c\n\n", 
			getChipName(),
			(int)m_header.m_mhz, 
			(int)g_last_freq, 
			(int)m_header.m_code_enable, 
			(int)m_header.m_code_disable, 
			(int)m_header.m_code_invert, 
			(int)m_header.m_code_normal, 
			init_getCommandType() ? 'Y' : 'N');
		Serial.println(buff);
	}
#endif
	
#if _DEBUG
	if (debug)
	{
		Serial.println(F("Device commands :"));
	}
#endif

	int commands = 0;

	DEV_CHIP_START

	// _setEnabled(false);   let driver do this

	while (*arr_cmds)
	{
		int cmds = *arr_cmds;

		if (cmds)
		{
			commands++;
		}

		if (cmds == DRV_DELAY_MS)
		{
			#if _DEBUG
			if (debug)
			{
				Serial.print("Delay ");
				Serial.print((int)arr_cmds[1]);
				Serial.print("ms");
			}
			#endif
			delay(arr_cmds[1]);
			arr_cmds += 2;
		}
		else if (cmds == DRV_RESET_LOW)
		{
			#if _DEBUG
			if (debug)
			{
				Serial.print("Reset:LOW");
			}
			#endif
			fastDigitalWrite(m_pin_reset, LOW);
			arr_cmds += 1;
		}
		else if (cmds == DRV_RESET_HIGH)
		{
			#if _DEBUG
			if (debug)
			{
				Serial.print("Reset:HIGH");
			}
			#endif
			fastDigitalWrite(m_pin_reset, HIGH);
			arr_cmds += 1;
		}
		else
		{
		for (int i = 1; i <= cmds; i++)
		{
		#if _DEBUG
			char cd = 'C';
		#endif

			if (i == 1 || init_getCommandType() == false)
			{
				_writeCommand(arr_cmds[i]);
			}
			else
			{
			#if _DEBUG
				cd = 'D';
			#endif
				_writeData(arr_cmds[i]);
			}

		#if _DEBUG
			if (debug)
			{
				Serial.print(cd);
				Serial.print(":0x");

				if (arr_cmds[i] < 16)
				{
					Serial.print("0"); // leading 0
				}
				Serial.print((int)arr_cmds[i], HEX);
				Serial.print(F(" "));
			}
		#endif
		}

		//delay(20); // 25 ms .... todo put this in the header

		arr_cmds += (cmds + 1);
	}

#if _DEBUG
		if (debug)
		{
			Serial.println(F(""));
		}
#endif
	}
	
	// common end tasks

	_setInvert(false);

	_setRotation(0);

	clearScreen(0);	// need to wipe display before shown

	_setEnabled(true);

	DEV_CHIP_END

	m_init = true;
	m_enabled = true;

	_get_chip_id();
		
	return commands;
}


bool OLED_CLASS::begin(const uint8_t *init, uint8_t pin_cs, uint8_t pin_reset, uint8_t pin_dc, SPISettings settings) //software  SPI
{
	m_init = false;

	if (!pin_reset || !pin_dc) return false;

	//setParms(init, pin_dc, pin_reset);

	m_enabled = false;

	m_pin_dc = pinInitialize(pin_dc, OUTPUT, LOW);
	m_pin_reset = pinInitialize(pin_reset, OUTPUT, HIGH);

	m_invert = false;

	writeInitCommands(init, pin_cs, true, settings);

#if OLED_CHIP_LOW == 5110
	if (m_init)
	{
		DEV_CHIP_START
		_setNokiaParms(-1, true, -1);  // pass < 0 to leave as is
		DEV_CHIP_END
	}
#endif

	return m_init;
}



bool OLED_CLASS::_setRotation(uint8_t rot)
{
	#if defined(OLED_ROTATION)
	OLED_ROTATION

	return true;
	#else
	return false;
	#endif
}


#if !defined(OLED_NO_TRANSLATE) // some drivers allow a flag to swap x/y

void OLED_CLASS::_translate(uint16_t *x1, uint16_t *y1) // should inline to nothing if no translate
{
	#if defined(OLED_ROTATION)
	
		if (m_rotation == 1)
		{
			swap(uint16_t, *x1, *y1);
		}
		#endif
}

void OLED_CLASS::_translate(uint16_t *x1, uint16_t *y1, uint16_t *x2, uint16_t *y2)
{
	#if defined(OLED_ROTATION)
	_translate(x1, y1);
	_translate(x2, y2);
	#endif
}

#endif

static uint16_t line_buffer[2][320]; // esp32 etc has small stack!!!!

#if (OLED_BPP == 1) // native 1BPP
uint32_t OLED_CLASS::renderScreen1BPP(void *buff, uint16_t p0, uint16_t p1, uint16_t x0, uint16_t x1)  // returns time to render in uS - pointer to raw 1bpp pixel data
{	
	if (!getEnabled()) { return 0; }

	uint32_t t0 = micros();
		
	DEV_CHIP_START

	if (_setWindow(x0, p0, x1, p1))
	{
		_enableWriteRAM();		
		_write(buff, (x1 - x0 + 1)*(p1 - p0 + 1));			
	}

	DEV_CHIP_END

	return micros() - t0;
}

#endif

#if (OLED_BPP == 4) // native 4bpp

uint32_t OLED_CLASS::renderScreen1BPP(void *p_buff, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
	if (!getEnabled()) { return 0; }

		uint8_t *buff = (uint8_t*)p_buff;

		uint32_t t0 = micros();

		DEV_CHIP_START

			if (_setWindow(x0, y0, x1, y1)) // m_width - 1, m_height - 1);
			{
				_enableWriteRAM();
				
				uint8_t *addr = buff; // buff + (y >> 3)* w;

				for (int16_t y = y1 - y0 + 1; y--;)
				{
					uint8_t *lb = (uint8_t *)line_buffer[y & 1];

					for (int16_t x = (x1 - x0 + 1) / 4; x--;)
					{
						lb[0] = (*addr & 0x80) ? 0xF0 : 0;
						lb[0] |= (*addr & 0x40) ? 0x0F : 0;

						lb[1] = (*addr & 0x20) ? 0xF0 : 0;
						lb[1] |= (*addr & 0x10) ? 0x0F : 0;

						lb[2] = (*addr & 0x08) ? 0xF0 : 0;
						lb[2] |= (*addr & 0x04) ? 0x0F : 0;

						lb[3] = (*addr & 0x02) ? 0xF0 : 0;
						lb[3] |= (*addr & 0x01) ? 0x0F : 0;
						
						lb += 4;
						addr++;						
					}

					_do_wait();
					
					_write(line_buffer[y & 1], x1 - x0 + 1, false);
				}

				_do_wait();
			}

		DEV_CHIP_END

		return micros() - t0;
}



uint32_t OLED_CLASS::renderScreen2BPP(void *p_buff, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) // 2 BPP format -> 4BPP screen
{
	uint8_t colors[] = { 0x00, 0x55, 0xAA, 0xFF };

	if (!getEnabled()) { return 0; }

		uint8_t *buff = (uint8_t*)p_buff;

		uint32_t t0 = micros();

		DEV_CHIP_START

			if (_setWindow(x0, y0, x1, y1)) // m_width - 1, m_height - 1);
			{
				_enableWriteRAM();
				
				uint8_t *addr = buff; // buff + (y >> 3)* w;

				for (int16_t y = y1 - y0 + 1; y--;)
				{
					uint8_t *lb = (uint8_t *)line_buffer[y & 1];

					for (int16_t x = (x1 - x0 + 1) / 2; x--;)
					{
						*lb = colors[(*addr >> 6)] & 0xf0;
						*lb |= colors[(*addr >> 4) & 3] & 0x0f;
						lb++;

						*lb = colors[(*addr >> 2) & 3] & 0xf0;
						*lb |= colors[(*addr) & 3] & 0x0f;
						lb++;
																		
						addr++;						
					}

					_do_wait();
					
					_write(line_buffer[y & 1], x1 - x0 + 1, false);
				}

				_do_wait();
			}

		DEV_CHIP_END

		return micros() - t0;
}

uint32_t OLED_CLASS::renderScreen4BPP(void *buff, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
	if (!getEnabled()) { return 0; }

	uint32_t t0 = micros();

	DEV_CHIP_START

	if (_setWindow(x1, y1, x2, y2))
	{
		_enableWriteRAM();
		_write(buff, ((x2 - x1 + 1) * (y2 - y1 + 1)));
	}

	DEV_CHIP_END

	return micros() - t0;
}

#endif

/*
This dumps entire screen window in 1 hit... address mode is HORIZONTAL not default of PAGE

The 13XX has a weird layout and because of this some mucking around is needed to get the 1 bit pixel format into the hardware buffer ASAP
*/




#if (OLED_BPP == 16)

void OLED_CLASS::drawPixelSW(uint16_t x1, uint16_t y1, uint16_t color)
{
	DEV_CHIP_START

	if (_setWindow(x1, y1, x1, y1))
	{
		_enableWriteRAM();
		_write16(color); 
	}

	DEV_CHIP_END
}

uint32_t OLED_CLASS::renderWindow16BPP(void* buff, int16_t x0, int16_t y0, int16_t w, int16_t h)
{
	if (!getEnabled()) { return 0; }

	uint32_t t0 = micros();

	DEV_CHIP_START

		if (_setWindow(x0, y0, x0 + w - 1, y0 + h - 1))
		{
			_enableWriteRAM();
			_write(buff, w * h * 2);
		}

	DEV_CHIP_END

		return micros() - t0;
}


uint32_t OLED_CLASS::renderWindow1BPP(void *p_buff, int16_t x0, int16_t y0, int16_t w, int16_t h)
{
		if (!getEnabled()) { return 0; }

		uint8_t *buff = (uint8_t*)p_buff;

		uint32_t t0 = micros();

		DEV_CHIP_START

			if (_setWindow(x0, y0, x0 + w - 1, y0 + h - 1)) // m_width - 1, m_height - 1);
			{
				_enableWriteRAM();
				
				uint8_t *addr = buff; // buff + (y >> 3)* w;

				for (int16_t y = h; y--;)
				{
					uint16_t *lb = line_buffer[y & 1];

					for (int16_t x = w >> 3; x--;)
					{
						lb[0] = (*addr & 0x80) ? 0xFFFF : 0;
						lb[1] = (*addr & 0x40) ? 0xFFFF : 0;
						lb[2] = (*addr & 0x20) ? 0xFFFF : 0;
						lb[3] = (*addr & 0x10) ? 0xFFFF : 0;
						lb[4] = (*addr & 0x08) ? 0xFFFF : 0;
						lb[5] = (*addr & 0x04) ? 0xFFFF : 0;
						lb[6] = (*addr & 0x02) ? 0xFFFF : 0;
						lb[7] = (*addr & 0x01) ? 0xFFFF : 0;
						
						lb += 8;
						addr++;						
					}

					_do_wait();
					
					_write(line_buffer[y & 1], w * 2, false);
				}

				_do_wait();
			}

		DEV_CHIP_END

		return micros() - t0;
}

uint32_t OLED_CLASS::renderWindow2BPP(void *p_buff, int16_t x0, int16_t y0, int16_t w, int16_t h)
{
	if (!getEnabled()) { return 0; }

	uint8_t* buff = (uint8_t*)p_buff;

	uint32_t t0 = micros();

	DEV_CHIP_START

		if (_setWindow(x0, y0, x0 + w - 1, y0 + h - 1)) // m_width - 1, m_height - 1);
		{
			_enableWriteRAM();

			uint8_t *addr = buff; // buff + (y >> 3)* w;
			
			uint16_t cols[4]; 

			cols[0] = 0;
			cols[1] = ks::getColorRGB565FromGray8(0x55, true);
			cols[2] = ks::getColorRGB565FromGray8(0xBB, true);
			cols[3] = 0xFFFF;

			for (int16_t y = h; y--;)
			{
				uint16_t *lb = line_buffer[y & 1];

				for (int16_t x = w >> 2; x--;)
				{
					lb[0] = cols[(*addr & 0xc0) >> 6]; 
					lb[1] = cols[(*addr & (0xc0 >> 2)) >> 4];
					lb[2] = cols[(*addr & (0xc0 >> 4)) >> 2];
					lb[3] = cols[(*addr & (0xc0 >> 6)) >> 0];

					lb += 4;
					addr++;
				}

				_do_wait();

				_write(line_buffer[y & 1], w << 1, false);
			}

			_do_wait();
		}

	DEV_CHIP_END

		return micros() - t0;
}

uint32_t OLED_CLASS::renderWindow4BPP(void *p_buff, uint16_t *pal, int16_t x0, int16_t y0, int16_t w, int16_t h)
{
	if (!getEnabled()) { return 0; }

	if (!p_buff) { return 0; }

	if (!pal) { return 0; }

	uint32_t t0 = micros();

	DEV_CHIP_START

		if (_setWindow(x0, y0, x0 + w - 1, y0 + h - 1)) // m_width - 1, m_height - 1);
		{
			_enableWriteRAM();

			uint8_t *addr = (uint8_t *)p_buff; // +(y * m_width) / 2;

			w >>= 1;

			for (int16_t y = h; y--;)
			{
				uint16_t *buff = line_buffer[y & 1];

				for (int16_t i = w; i--;)
				{
					buff[0] = pal[*addr >> 4];
					buff[1] = pal[*addr & 0x0f];
					buff += 2;
					addr++;
				}

				_do_wait();
				_write(line_buffer[y & 1], w << 2, false);
			}

			_do_wait();
		}
	DEV_CHIP_END

		return micros() - t0;
}


uint32_t OLED_CLASS::renderWindow8BPP(void* p_buff, uint16_t* pal, int16_t x0, int16_t y0, int16_t w, int16_t h)
{
	if (!getEnabled()) { return 0; }

	if (!p_buff) { return 0; }

	if (!pal) { return 0; }

	uint32_t t0 = micros();

	DEV_CHIP_START

		if (_setWindow(x0, y0, x0 + w - 1, y0 + h - 1)) // m_width - 1, m_height - 1);
		{
			_enableWriteRAM();

			uint8_t *addr = (uint8_t*)p_buff; // +(y * m_width) / 2;

			for (int16_t y = h; h--; )
			{
				uint16_t *buff = line_buffer[y & 1];

				for (int16_t i = w; i--; )
				{
					*buff = pal[*addr];
					buff++;
					addr++;
				}

				_do_wait();
				
				_write(line_buffer[y & 1], w * 2, false);
			}

			_do_wait();
		}
	DEV_CHIP_END

		return micros() - t0;
}



uint32_t OLED_CLASS::renderScreen16BPP(void *buff, int16_t x0, int16_t y0, int16_t x1, int16_t y1)
{
	if (!getEnabled()) { return 0; }
	
	uint32_t t0 = micros();

	DEV_CHIP_START

		if (_setWindow(x0, y0, x1, y1))
		{
			_enableWriteRAM();
			_write(buff, (x1 - x0 + 1) * (y1 - y0 + 1) * 2);
		}

	DEV_CHIP_END

	return micros() - t0;
}

uint32_t OLED_CLASS::renderScreen1BPP(void *p_buff)
{
	if (!getEnabled()) { return 0; }

	uint8_t *buff = (uint8_t *)p_buff;

	uint32_t t0 = micros();

	DEV_CHIP_START

		if (_setWindow(0, 0, m_header.m_width - 1, m_header.m_height - 1)) // m_width - 1, m_height - 1);
		{
			_enableWriteRAM();

			uint8_t b = 0;

			for (int y = 0; y < m_header.m_height; y++)
			{
				uint8_t *addr = buff + (y >> 3) * m_header.m_width;
				uint8_t mask = 1 << (y & 7);

				for (uint16_t x = m_header.m_width; x--;)
				{
					line_buffer[b][x] = (addr[x] & mask) ? 0xFFFF : 0;					
				}

				if (y) // still waiting??
				{
					_do_wait();
				}
				_write(line_buffer[b], m_header.m_width * 2, false);
				b ^= 1;
			}

			_do_wait();
		}

	DEV_CHIP_END

	return micros() - t0;
}

uint32_t OLED_CLASS::renderScreen4BPP(void *buff, uint16_t *pal)
{
	if (!getEnabled()) { return 0; }

	if (!buff) { return 0;	}

	if (!pal) { return 0;	}

	uint32_t t0 = micros();

	DEV_CHIP_START

		if (_setWindow(0, 0, m_header.m_width - 1, m_header.m_height - 1)) // m_width - 1, m_height - 1);
		{
			_enableWriteRAM();

			uint8_t b = 0;

			uint8_t *addr = (uint8_t*)buff; // +(y * m_width) / 2;

			for (int16_t y = 0; y < m_header.m_height; y++)
			{
				uint16_t *buff = line_buffer[b];

				for (uint16_t i = (m_header.m_width >> 1); i--;)
				{
					*buff = pal[*addr >> 4];
					buff++;
					*buff = pal[*addr & 0x0f];
					buff++;

					addr++;
				}

				if (y)
				{
					_do_wait();
				}
				_write(line_buffer[b], m_header.m_width * 2, false);
				b ^= 1;
			}

			_do_wait();
		}
	DEV_CHIP_END

	return micros() - t0;
}

uint32_t OLED_CLASS::renderScreen8BPP(void *p_buff, uint16_t *pal)
{
	if (!getEnabled()) { return 0; }

	if (!p_buff) { return 0;	}

	if (!pal) { return 0;	}

	uint32_t t0 = micros();

	DEV_CHIP_START

		if (_setWindow(0, 0, m_header.m_width - 1, m_header.m_height - 1)) // m_width - 1, m_height - 1);
		{
			_enableWriteRAM();

			uint8_t b = 0;

			uint8_t *addr = (uint8_t *)p_buff; // +(y * m_width) / 2;

			for (int16_t y = 0; y < m_header.m_height; y++)
			{
				uint16_t *buff = line_buffer[b];

				for (uint16_t i = m_header.m_width; i--; )
				{
					*buff = pal[*addr];
					buff++;
					addr++;
				}

				if (y)
				{
					_do_wait();
				}

				_write(line_buffer[b], m_header.m_width * 2, false);
				b ^= 1;
			}

			_do_wait();
		}
	DEV_CHIP_END

	return micros() - t0;
}

#endif

#if (OLED_BPP == 8)

uint32_t OLED_CLASS::renderScreen8BPP(void *buff, int16_t x0, int16_t y0, int16_t x1, int16_t y1)
{
	if (!getEnabled()) { return 0; }

	uint32_t t0 = micros();

	DEV_CHIP_START

	if (_setWindow(x0, y0, x1, y1))
	{
		_enableWriteRAM();
		_write(buff, (x1 - x0 + 1)*(y1 - y0 + 1));			
	}
	
	DEV_CHIP_END

	return micros() - t0;
}

#endif
 
also... as you know every device has different startup code and 99% of the battle is usually getting it to talk and play nice.

I opted for the generic approach meaning each device state has a lookup that's pulled in if the driver/state is used.

From this it knows the BPP, width, height and the startup/reset sequence. I also try and document exactly what the display is and where I bought it etc.


The file below is what I use for probably 7+ display that have all been tested.

The ILI9431 was the last one I coded and it took < 2 hours to fully implement because of the reuse.

Code:
#pragma once


#define SPLIT16(a) ((a) & 255), ((a) >> 8)
#define SPLIT32(a) (uint8_t)((a) & 255), (uint8_t)(((a) >> 8) & 255), (uint8_t)(((a) >> 16) & 255), (uint8_t)(((a) >> 24) & 255)

#define DEV_INIT_MAGIC 0xDEADF00D
#define DEV_INIT_HEADER(v, w, h, offset, bpp, mhz, code_enable, code_disable, code_normal, code_invert) \
                         SPLIT32(DEV_INIT_MAGIC), \
                         SPLIT16(v), SPLIT16(w), SPLIT16(h), (offset), (bpp), (mhz), \
						 (code_enable), (code_disable), (code_normal), (code_invert) 


#define DRV_RESET_LOW 255
#define DRV_RESET_HIGH 254
#define DRV_DELAY_MS 243


struct __attribute__((packed)) strInitHeader // caution with alignment *** make sure header uint16 don't cross 4 byte boundary!!!!
{
	uint32_t m_magic; // allows us to see if pointing to what we expect 0xDEADF00D
	uint16_t m_version; // ie 1305, 7789, etc
	int16_t m_width;
	int16_t m_height; 
	int8_t m_offset; // some 1 bit modes have an offset
	uint8_t m_bpp; // 1, 4, 8, 16
	uint8_t m_mhz; // usually 20 for 20Mhz but if chip can go faster adjust
	uint8_t m_code_enable;  // command to disable display or 0
	uint8_t m_code_disable; // command to enable display or 0
	uint8_t m_code_normal; // code for normal display or 0
	uint8_t m_code_invert; // code of inverted display or 0
};

/*
ST7789 240*240 1.3"
*/

#define ST7789_240x240_XSTART 0
#define ST7789_240x240_YSTART 80


#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_COLMOD     0x3A
#define ST77XX_MADCTL     0x36

#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

// https://www.reddit.com/r/RASPBERRY_PI_PROJECTS/comments/9b3nun/has_anyone_gotten_the_waveshare_st7789v_13_lcd/
// https://www.waveshare.com/wiki/File:1.3inch_LCD_Module_code.7z

const PROGMEM uint8_t TFT_initST7789_240_240_16BPP[] =  // if no CS must be mode 3
{
 DEV_INIT_HEADER(7789, 240, 240, 0, 16, 20, ST77XX_DISPON, ST77XX_DISPOFF, ST77XX_INVOFF, ST77XX_INVON),
 DRV_DELAY_MS, 200,
	DRV_RESET_LOW,
	DRV_DELAY_MS, 200,
	DRV_RESET_HIGH,
	DRV_DELAY_MS, 200,
  2,ST77XX_MADCTL, 0x00,
  2,ST77XX_COLMOD,0x55,  //1=na, 2=na 4=display latch order, 8=RGB/BGR, 16=line address order, 32= page/col oder, 64=col addr order, 128=page addr order
  6,0xB2,0x0C,0x0C,0x00,0x33,0x33,
  2,0xB7,0x35,
  2,0xBB,0x19,
  2,0xC0,0x2C,
  2,0xC2,0x01,
  2,0xC3,0x12,
  2,0xC4,0x20,
  2,0xC6,0x0F,
  3,0xD0,0xA4,0xA1,
  15,0xE0,0xD0,0x04,0x0D,0x11,0x13,0x2B,0x3F,0x54,0x4C,0x18,0x0D,0x0B,0x1F,0x23,
  15,0xE1,0xD0,0x04,0x0C,0x11,0x13,0x2C,0x3F,0x44,0x51,0x2F,0x1F,0x1F,0x20,0x23,
 // 1,ST77XX_INVOFF,
  1,ST77XX_SLPOUT,
  //1,ST77XX_DISPON,
  0 };

/*
Nokia 5110

http://skpang.co.uk/catalog/images/lcd/graphic/docs/User_Manual_ET_LCD5110.pdf
*/


const PROGMEM uint8_t LCD_initNokia5110_84_48_1BPP[] =  // untested
{
	DEV_INIT_HEADER(5110, 84, 48, 0, 1, 20, 0, 0, 0x08, 0x0c),
	1, 0x20,  // normal instructuion set default
	0
};



/*
ST7735 - LCD
https://www.edaboard.com/showthread.php?258790-Initializing-a-ST7735-driven-lcd-spi-display
*/

//setReset(0,
//DelayMs(500,
//setReset(1,
//DelayMs(500,

// Some register settings
#define ST7735_MADCTL_BGR 0x08
#define ST7735_MADCTL_MH  0x04

#define ST7735_FRMCTR1    0xB1
#define ST7735_FRMCTR2    0xB2
#define ST7735_FRMCTR3    0xB3
#define ST7735_INVCTR     0xB4
#define ST7735_DISSET5    0xB6

#define ST7735_PWCTR1     0xC0
#define ST7735_PWCTR2     0xC1
#define ST7735_PWCTR3     0xC2
#define ST7735_PWCTR4     0xC3
#define ST7735_PWCTR5     0xC4
#define ST7735_VMCTR1     0xC5

#define ST7735_PWCTR6     0xFC

#define ST7735_GMCTRP1    0xE0
#define ST7735_GMCTRN1    0xE1



const PROGMEM uint8_t TFT_init7735B_128_160_16BPP[] =  // untested
{
	DEV_INIT_HEADER(7735, 128, 160, 0, 16, 20, 0x29, 0x28, 0x20, 0x21),
	DRV_DELAY_MS, 200,
	DRV_RESET_LOW,
	DRV_DELAY_MS, 200,
	DRV_RESET_HIGH,
	DRV_DELAY_MS, 200,
	
	2, ST77XX_COLMOD,  //  3: Set color mode, 1 arg + delay:
	  0x05,                         //     16-bit color

	4, ST7735_FRMCTR1,                 //  4: Frame rate control, 3 args + delay:
	  0x00,                         //     fastest refresh
	  0x06,                         //     6 lines front porch
	  0x03,                         //     3 lines back porch

	 2, ST77XX_MADCTL,                //  5: Mem access ctl (directions), 1 arg:
	  0xc0,                         //     Row/col addr, bottom-top refresh

	3, ST7735_DISSET5,                 //  6: Display settings #5, 2 args:
	  0x15,                         //     1 clk cycle nonoverlap, 2 cycle gate
									//     rise, 3 cycle osc equalize
	  0x02,                         //     Fix on VTL
	2, ST7735_INVCTR,                //  7: Display inversion control, 1 arg:
	  0x0,                          //     Line inversion

	3, ST7735_PWCTR1,   //  8: Power control, 2 args + delay:
	  0x02,                         //     GVDD = 4.7V
	  0x70,                         //     1.0uA

	2, ST7735_PWCTR2,                //  9: Power control, 1 arg, no delay:
	  0x05,                         //     VGH = 14.7V, VGL = -7.35V

	3, ST7735_PWCTR3,                // 10: Power control, 2 args, no delay:
	  0x01,                         //     Opamp current small
	  0x02,                         //     Boost frequency

	3, ST7735_VMCTR1,    // 11: Power control, 2 args + delay:
	  0x3C,                         //     VCOMH = 4V
	  0x38,                         //     VCOML = -1.1V      

	3, ST7735_PWCTR6,                // 12: Power control, 2 args, no delay:
	  0x11, 0x15,

	17, ST7735_GMCTRP1,               // 13: Gamma Adjustments (pos. polarity), 16 args + delay:
	  0x09, 0x16, 0x09, 0x20,       //     (Not entirely necessary, but provides
	  0x21, 0x1B, 0x13, 0x19,       //      accurate colors)
	  0x17, 0x15, 0x1E, 0x2B,
	  0x04, 0x05, 0x02, 0x0E,

	17, ST7735_GMCTRN1, // 14: Gamma Adjustments (neg. polarity), 16 args + delay:
	  0x0B, 0x14, 0x08, 0x1E,       //     (Not entirely necessary, but provides
	  0x22, 0x1D, 0x18, 0x1E,       //      accurate colors)
	  0x1B, 0x1A, 0x24, 0x2B,
	  0x06, 0x06, 0x02, 0x0F,

	0};

//LCD_Rectangle(0, 0, 128, 160, 0, // black it out
//LCD_Command(0x29,//Display on

/*
SSD1327 16 color grayscale 128*128 etc

https://www.crystalfontz.com/controllers/SolomonSystech/SSD1327/434/
*/

const PROGMEM uint8_t OLED_init1327_128_128_4BPP[] = 
{
	DEV_INIT_HEADER(1327, 128, 128, 0, 4, 40, 0xaf, 0xae, 0xa4, 0xa7),    // 40Mhz
	DRV_DELAY_MS, 200,
	DRV_RESET_LOW,
	DRV_DELAY_MS, 200,
	DRV_RESET_HIGH,
	DRV_DELAY_MS, 200,
	1, 0xAE, // display off
	2, 0xA8,  127, //0x5F, // mux ratio ... 5f = 96
	2, 0xA0, 0x51, // set remap
	2, 0xAB, 0x01, // function selection A
	2, 0xA1, 0, // display start line
	2, 0xA2, 0, // vertical offset was 0x60
	2, 0x81, 0x80, // contrast
	2, 0xB1, 0xf1, // set phase length ... 0x51
	2, 0xB3, 0x00, // Set Front Clock Divider / Oscillator Frequency   //80Hz:0xc1 90Hz:0xe1   100Hz:0x00   110Hz:0x30 120Hz:0x50   130Hz:0x70     01
	1, 0xB9, // linear LUT
	2, 0xBC, 0x08, // Set Pre - charge voltage
	2, 0xBE, 0x0f, // Set VCOMH .. 0x07
	2, 0xB6, 0x0f,  // Set Second precharge Period  0x01
	2, 0xD5, 0x62, // Function Selection B
//	1, 0xA4, // normal display - reset
	1, 0x2E,  // stop scrolling
	2, 0xFD, 0x12, // command lock
	0 };


/*
SSD1331 - 8 bit BGR8 mode
*/
#define D1331_A0_MODE_256 0x00
#define D1331_A0_MODE_65K 0x40

#define D1331_A0_HORZ_INC 0x00
#define D1331_A0_VERT_INC 0x01

#define D1331_A0_RAM_COL_0_95 0x00
#define D1331_A0_RAM_COL_95_0 0x02

#define D1331_A0_ORDER_RGB 0x00
#define D1331_A0_ORDER_BGR 0x04

#define D1331_A0_LR_SWAP_COM_DISABLE 0x00
#define D1331_A0_LR_SWAP_COM_ENABLE  0x08

#define D1331_A0_SCAN_FROM_COM_MIN_MAX  0x00
#define D1331_A0_SCAN_FROM_COM_MAX_MIN  0x10

#define D1331_A0_COM_SPLIT_ODD_EVEN_ENABLE  0x00
#define D1331_A0_COM_SPLIT_ODD_EVEN_DISABLE 0x20

const PROGMEM uint8_t OLED_initSSD1331_96_64_8BPP[] =	 // untested ... once working add 65K mode
{
	DEV_INIT_HEADER(1331, 96, 64, 0, 8, 20, 0xaf, 0xae, 0xa4, 0xa7), //

	DRV_DELAY_MS, 200,
	DRV_RESET_LOW,
	DRV_DELAY_MS, 200,
	DRV_RESET_HIGH,
	DRV_DELAY_MS, 200,
	1, 0xae, //Display off
	2, 0x81, 128, //set contrast for colorA
	2, 0x82, 128 ,//set contrast for colorB
	2, 0x83, 128, //set contrast for colorC
	2, 0x87, 6, //master current control
	2, 0x8a, 100, //Set Second Pre-change Speed For ColorA
	2, 0x8b, 120, //Set Second Pre-change Speed For ColorB
	2, 0x8c, 100, //Set Second Pre-change Speed For ColorC
	2, 0xa0, D1331_A0_HORZ_INC | D1331_A0_RAM_COL_95_0 | D1331_A0_ORDER_BGR | D1331_A0_MODE_256 | D1331_A0_SCAN_FROM_COM_MAX_MIN | D1331_A0_COM_SPLIT_ODD_EVEN_DISABLE, //set re=map & data format .. 0x76
	2, 0xa1, 0, //set display start line
	2, 0xa2, 0, //set display offset
	//1, 0xa4, //set display mode normal
	2, 0xa8, 0x3f, //set multiplex ratio
	2, 0xad, 0x8e, //set master configuration
	2, 0xb0,  0, //set power save
	2, 0xb1, 0x31, //phase 1 and 2 period adjustment
	2, 0xb3, 0xf0, //display clock divider / oscillator frequency
	2, 0xbb, 58, //Set Pre-Change Level
	2, 0xbe, 62, //set vcomh
	//1, 0xaf, //set display on
	0
};


#define ILI9341_TFTWIDTH   240      ///< ILI9341 max TFT width
#define ILI9341_TFTHEIGHT  320      ///< ILI9341 max TFT height

#define ILI9341_NOP        0x00     ///< No-op register
#define ILI9341_SWRESET    0x01     ///< Software reset register
#define ILI9341_RDDID      0x04     ///< Read display identification information
#define ILI9341_RDDST      0x09     ///< Read Display Status

#define ILI9341_SLPIN      0x10     ///< Enter Sleep Mode
#define ILI9341_SLPOUT     0x11     ///< Sleep Out
#define ILI9341_PTLON      0x12     ///< Partial Mode ON
#define ILI9341_NORON      0x13     ///< Normal Display Mode ON

#define ILI9341_RDMODE     0x0A     ///< Read Display Power Mode
#define ILI9341_RDMADCTL   0x0B     ///< Read Display MADCTL
#define ILI9341_RDPIXFMT   0x0C     ///< Read Display Pixel Format
#define ILI9341_RDIMGFMT   0x0D     ///< Read Display Image Format
#define ILI9341_RDSELFDIAG 0x0F     ///< Read Display Self-Diagnostic Result

#define ILI9341_INVOFF     0x20     ///< Display Inversion OFF
#define ILI9341_INVON      0x21     ///< Display Inversion ON
#define ILI9341_GAMMASET   0x26     ///< Gamma Set
#define ILI9341_DISPOFF    0x28     ///< Display OFF
#define ILI9341_DISPON     0x29     ///< Display ON

#define ILI9341_CASET      0x2A     ///< Column Address Set
#define ILI9341_PASET      0x2B     ///< Page Address Set
#define ILI9341_RAMWR      0x2C     ///< Memory Write
#define ILI9341_RAMRD      0x2E     ///< Memory Read

#define ILI9341_PTLAR      0x30     ///< Partial Area
#define ILI9341_VSCRDEF    0x33     ///< Vertical Scrolling Definition
#define ILI9341_MADCTL     0x36     ///< Memory Access Control
#define ILI9341_VSCRSADD   0x37     ///< Vertical Scrolling Start Address
#define ILI9341_PIXFMT     0x3A     ///< COLMOD: Pixel Format Set

#define ILI9341_FRMCTR1    0xB1     ///< Frame Rate Control (In Normal Mode/Full Colors)
#define ILI9341_FRMCTR2    0xB2     ///< Frame Rate Control (In Idle Mode/8 colors)
#define ILI9341_FRMCTR3    0xB3     ///< Frame Rate control (In Partial Mode/Full Colors)
#define ILI9341_INVCTR     0xB4     ///< Display Inversion Control
#define ILI9341_DFUNCTR    0xB6     ///< Display Function Control

#define ILI9341_PWCTR1     0xC0     ///< Power Control 1
#define ILI9341_PWCTR2     0xC1     ///< Power Control 2
#define ILI9341_PWCTR3     0xC2     ///< Power Control 3
#define ILI9341_PWCTR4     0xC3     ///< Power Control 4
#define ILI9341_PWCTR5     0xC4     ///< Power Control 5
#define ILI9341_VMCTR1     0xC5     ///< VCOM Control 1
#define ILI9341_VMCTR2     0xC7     ///< VCOM Control 2

#define ILI9341_RDID1      0xDA     ///< Read ID 1
#define ILI9341_RDID2      0xDB     ///< Read ID 2
#define ILI9341_RDID3      0xDC     ///< Read ID 3
#define ILI9341_RDID4      0xDD     ///< Read ID 4

#define ILI9341_GMCTRP1    0xE0     ///< Positive Gamma Correction
#define ILI9341_GMCTRN1    0xE1     ///< Negative Gamma Correction


const PROGMEM uint8_t TFT_initILI9341_240_320_16BPP[] =
{
  DEV_INIT_HEADER(9341, 240, 320, 0, 16, 20, 0x29, 0x28, 0x20, 0x21),
  DRV_DELAY_MS, 200,
	DRV_RESET_LOW,
	DRV_DELAY_MS, 200,
	DRV_RESET_HIGH,
	DRV_DELAY_MS, 200,
  1, 0x28, // disable
  4, 0xEF, 0x03, 0x80, 0x02,
  4, 0xCF, 0x00, 0xC1, 0x30,
  5, 0xED, 0x64, 0x03, 0x12, 0x81,
  4, 0xE8, 0x85, 0x00, 0x78,
  6, 0xCB, 0x39, 0x2C, 0x00, 0x34, 0x02,
  2, 0xF7, 0x20,
  3, 0xEA, 0x00, 0x00,
  2, ILI9341_PWCTR1  , 0x23,             // Power control VRH[5:0]
  2, ILI9341_PWCTR2  , 0x10,             // Power control SAP[2:0];BT[3:0]
  3, ILI9341_VMCTR1  , 0x3e, 0x28,       // VCM control
  2, ILI9341_VMCTR2  , 0x86,             // VCM control2
  2, ILI9341_MADCTL  , 0x48,             // Memory Access Control
  2, ILI9341_VSCRSADD, 0x00,             // Vertical scroll zero
  2, ILI9341_PIXFMT  , 0x55,
  3, ILI9341_FRMCTR1 , 0x00, 0x18,
  4, ILI9341_DFUNCTR , 0x08, 0x82, 0x27, // Display Function Control
  2, 0xF2, 0x00,                         // 3Gamma Function Disable
  2, ILI9341_GAMMASET , 0x01,             // Gamma curve selected
  16, ILI9341_GMCTRP1 , 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00,
  16, ILI9341_GMCTRN1 , 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F,
  1, ILI9341_SLPOUT,                // Exit Sleep
  //1, ILI9341_DISPON,                // Display on
  0
};




const PROGMEM uint8_t OLED_init1351_128_128_16BPP[] = // note 40Mhz
{
	DEV_INIT_HEADER(1351, 128, 128, 0, 16, 40, 0xaf, 0xae, 0xa6, 0xa7),
	DRV_DELAY_MS, 200,
	DRV_RESET_LOW,
	DRV_DELAY_MS, 200,
	DRV_RESET_HIGH,
	DRV_DELAY_MS, 200,
	2, 0xfd, 0x12,
	2, 0xfd, 0xb1, // Set Command Lock
	1, 0xae, // Display off
	2, 0xa0, 0x04, // 0x70 = ABC ... 0x74 = CBA color sequence
	2, 0xa2, 0,  // Set display offset
	//1, 0xa6,  // Normal display
	2, 0xab, 0x01,  // Set Function selection
					2, 0xb1, 0x32,  // Set pre & dis_charge
					2, 0xb3, 0xf1,  // clock & frequency
					4, 0xb4, 0xa0, 0xb5, 0x55,  // Set Segment LOW Voltage
					2, 0xb5, 0x0A,  // Set GPIO
					2, 0xb6,  0x01, // Set Second Pre-charge Period

					65, 0xb8,  //Set Gray Table
					0,  //0
					2,  //1
					3,  //2
					4,  //3
					5,  //4
					6,  //5
					7,  //6
					8,  //7
					9,  //8
					10,  //9
					11,  //10
					12,  //11
					13,  //12
					14,  //13
					15,  //14
					16,  //15
					17,  //16
					18,  //17
					19,  //18
					21,  //19
					23,  //20
					25,  //21
					27,  //22
					29,  //23
					31,  //24
					33,  //25
					35,  //26
					37,  //27
					39,  //28
					42,  //29
					45,  //30
					48,  //31
					51,  //32
					54,  //33
					57,  //34
					60,  //35
					63,  //36
					66,  //37
					69,  //38
					72,  //39
					76,  //40
					80,  //41
					84,  //42
					88,  //43
					92,  //44
					96,  //45
					100,  //46
					104,  //47
					108,  //48
					112,  //49
					116,  //50
					120,  //51
					125,  //52
					130,  //53
					135,  //54
					140,  //55
					145,  //56
					150,  //57
					155,  //58
					160,  //59
					165,  //60
					170,  //61
					175,  //62
					180,  //63
					
					2, 0xbb,  0x17, // Set pre-charge voltage of color A B C
					2, 0xbe,  0x05, // Set VcomH
					4, 0xc1, 0x88, 0x70, 0x88,  // Set contrast current for A B C
					//2, 0xc7,  0x0f, // Set master contrast
					2, 0xca, 0x7f,  // Duty
					//1, 0xaf,  // Display on
					0 };


const PROGMEM uint8_t OLED_init1305_128_64_1BPP_096[] =
{
	DEV_INIT_HEADER(1305, 128, 64, 0, 1, 20, 0xaf, 0xae, 0xa4, 0xa7), // check a7
	DRV_DELAY_MS, 200,
	DRV_RESET_LOW,
	DRV_DELAY_MS, 200,
	DRV_RESET_HIGH,
	DRV_DELAY_MS, 200,
	1,	0xae, // turn off oled panel
	2, 0xd5, 0x80, // set display clock divide ratio/oscillator frequency
	2, 0xa8, 0x3f, // set multiplex ratio(1 to 64)
	2, 0xd3, 0, // set display offset --- zero offset
	2, 0xad, 0x8e, // Set Master Configuration
	2, 0xd8, 0x14, // ... was 0x05 --Set Area Color Mode On/Off & Low Power Display Mode
	2, 0x8d, 0x14, //--set Charge Pump enable/disable
	2, 0xa1, 0xc8, //--set segment re-map 132 to 0
	2, 0xda, 0x12, //--Set COM Pins Hardware Configuration
	5, 0x91, 0x3f, 0x3f, 0x3f, 0x3f, //--Set current drive pulse width of BANK0, Color A, B and C.
	2, 0x81, 0x16, //--set contrast control register
	2, 0x82, 0x80, //--set contrast control register
	2, 0xd9, 0xf1, //0xf1, //--set pre-charge period
	2, 0xdb, 0x40, //--set vcomh
//	1, 0xa4, //Disable Entire Display On
	2, 0x20, 0x00, // Horizontal increment mode - DW
//	1,	0xaf,  //--turn on oled panel
	0
};


const PROGMEM uint8_t OLED_init1305_128_64_1BPP[] =
{
	DEV_INIT_HEADER(1305, 128, 64, 2, 1, 20, 0xaf, 0xae, 0xa4, 0xa7), // check a7
	DRV_DELAY_MS, 200,
	DRV_RESET_LOW,
	DRV_DELAY_MS, 200,
	DRV_RESET_HIGH,
	DRV_DELAY_MS, 200,
	1,	0xae, // turn off oled panel
	2, 0xd5, 0xf0, // set display clock divide ratio/oscillator frequency
	2, 0xa8, 63, // set multiplex ratio(1 to 64)
	2, 0xd3, 64, // set display offset
	2, 0xad, 0x8e, // Set Master Configuration
	2, 0xd8, 0x05, // ... was 0x05 --Set Area Color Mode On/Off & Low Power Display Mode
	2, 0xa1, 0xc8, //--set segment re-map 132 to 0
	2, 0xda, 0x12, //--Set COM Pins Hardware Configuration
	5, 0x91, 0x3f, 0x3f, 0x3f, 0x3f, //--Set current drive pulse width of BANK0, Color A, B and C.
	2, 0x81, 0x16, //--set contrast control register
	2, 0x82, 0x80, //--set contrast control register
	2, 0xd9, 0xf1, //0xf1, //--set pre-charge period
	2, 0xdb, 0x34, //--set vcomh
	2, 0x20, 0x00, // Horizontal increment mode - DW
//	1, 0xa4, //Disable Entire Display On
//	1, 0xaf,  //--turn on oled panel
	0
};

/*
128x64 1309
http://www.buydisplay.com/download/democode/ER-OLEDM024-2_Series_8080-8-bit_8080-4-bit_4-wire-spi_DemoCode.txt
*/

const PROGMEM uint8_t OLED_init1309_128_64_1BPP[] =
{
	DEV_INIT_HEADER(1309, 128, 64, 0, 1, 20, 0xaf, 0xae, 0xa4, 0xa7), // check a7
	DRV_DELAY_MS, 200,
	DRV_RESET_LOW,
	DRV_DELAY_MS, 200,
	DRV_RESET_HIGH,
	DRV_DELAY_MS, 200,
	1, 0xae, // turn off oled panel
	2, 0xd5, 0xa0, // set display clock divide ratio/oscillator frequency
	2, 0xa8, 63, // set multiplex ratio(1 to 64)
	2, 0xd3, 0, // set display offset
	1, 0x40, // Set Display Start Line
	2, 0xad, 0x8e, // Set Master Configuration
	2, 0xd8, 0x05, // ... was 0x05 --Set Area Color Mode On/Off & Low Power Display Mode
	2, 0xa1, 0xc8, //--set segment re-map 132 to 0
	2, 0xda, 0x12, //--Set COM Pins Hardware Configuration
	5, 0x91, 0x3f, 0x3f, 0x3f, 0x3f, //--Set current drive pulse width of BANK0, Color A, B and C.
	2, 0x81, 0x16, //--set contrast control register
	2, 0x82, 0x80, //--set contrast control register
	2, 0xd9, 0x25, //0xf1, //--set pre-charge period
	2, 0xdb, 0x34, //--set vcomh
	2, 0x20, 0x00, // Horizontal increment mode - DW
//	1, 0xa4, //Disable Entire Display On
//	1, 0xaf,  //--turn on oled panel
	0
};

/*
128x32
http://www.buydisplay.com/default/2-2-inch-128x32-oled-dot-matrix-display-module-with-pcb-blue-on-black
http://www.buydisplay.com/download/democode/ER-OLED0M23-1_8080-8-Bit_DemoCode.txt
*/

const PROGMEM uint8_t OLED_init1305_128_32_1BPP[] =
{
	DEV_INIT_HEADER(1305, 128, 32, 0, 1, 20, 0xaf, 0xae, 0xa4, 0xa7), // check a7
	DRV_DELAY_MS, 200,
	DRV_RESET_LOW,
	DRV_DELAY_MS, 200,
	DRV_RESET_HIGH,
	DRV_DELAY_MS, 200,
	1, 0xae, // turn off oled panel
	2, 0xd5, 0xa0, // set display clock divide ratio/oscillator frequency
	2, 0xa8, 31, // set multiplex ratio(1 to 64)
	2, 0xd3, 0, // set display offset
	2, 0xad, 0x8e, // Set Master Configuration
	2, 0xd8, 0x05, // ... was 0x05 --Set Area Color Mode On/Off & Low Power Display Mode
	2, 0xa1, 0xc8, //--set segment re-map 132 to 0
	2, 0xda, 0x12, //--Set COM Pins Hardware Configuration
	5, 0x91, 0x3f, 0x3f, 0x3f, 0x3f, //--Set current drive pulse width of BANK0, Color A, B and C.
	2, 0x81, 0x16, //--set contrast control register
	2, 0xd9, 0xd2, //0xf1, //--set pre-charge period
	2, 0xdb, 0x34, //--set vcomh
	2, 0x20, 0x00, // Horizontal increment mode - DW
	0
};

/*
256*64 - 1322
http://www.buydisplay.com/default/spi-oled-manufacturer-256x64-display-panel-supplier-green-on-black
http://www.buydisplay.com/download/democode/ER-OLEDM0.96-1_4-Wire-SPI_DemoCode.txt
*/

const PROGMEM uint8_t OLED_init1322_256_64_4BPP[] = // tested
{
	DEV_INIT_HEADER(1322, 256, 64, 0, 4, 40, 0xaf, 0xae, 0xa6, 0xa7),  // 40Mhz
	DRV_DELAY_MS, 200,
	DRV_RESET_LOW,
	DRV_DELAY_MS, 200,
	DRV_RESET_HIGH,
	DRV_DELAY_MS, 200,
	2, 0xfd, 0x12,
	1, 0xAE, /*DISPLAY OFF*/

	2, 0xB3, 0x91,/*DISPLAYDIVIDE CLOCKRADIO/OSCILLATAR FREQUANCY*/
	2, 0xCA, 0x3f, /*multiplex ratio*/

	2, 0xA2, 0x00, /*set offset*/
	2, 0xA1, 0x00, /*start line*/
	3, 0xA0, 0x14, 0x11,  /*set remap*/

	2, 0xb5, 0, // new

	2, 0xAB, 0x01,  /*funtion selection*/
	3, 0xB4, 0xA0, 0xfd,

	2, 0xC7, 0x0f, /*master contrast current control*/

	1, 0xB9, // default gray scale table

	2, 0xB1, 0xe2,  /*SET PHASE LENGTH*/
	3, 0xD1, 0x82, 0x20,  /**/
		
	
	2, 0xBB, 0x1f,  /*SET PRE-CHANGE VOLTAGE*/
	2, 0xB6, 0x08, /*SET SECOND PRE-CHARGE PERIOD*/

	2, 0xBE, 0x07, /* SET VCOMH */
	
//	1, 0xA6,  /*normal display*/
//	1, 0xAF, /*display ON*/
	0
};
 
Status
Not open for further replies.
Back
Top