I create an OLED display class for the SSD1306 and how it works is pretty straight forward. The display is buffered (offscreen method) and at performing an update, it 'dumps' the buffer to the display via I2C, in this case a total of 512 bytes (128x32 pixels) but can be also 1024 bytes (128x64 pixels).
The offscreen method is to avoid flicker and performs faster because it only updates when I want it to update. Very usefull with animations for example.
Nothing fancy however on many updates, its an expensive operation and mostly not neccessary because of little screen changes. I'm also curious about the frame rate, with less updates needed, does it perform better.
So I want to be the class is smart enough to send only the changes, so I don't need to take care about speed/performing issues. This is partly working already (by a double buffer), to detect the changes and send only those, however, appear on wrong position. This is caused by previous method, with a simple dump, it is not required to specify each pixel pair location, it just pumping an array of values.
What kind of command/instruction of the SSD1306 do I need to set target position or am i able to skip values? I cannot figure it out using the datasheet.
Here is my code (AVR-C):
// Send start address
if (useRegisters) // Send TWI Start
{
// Send start address
TWCR = _BV(TWEN) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);
while(TOD_CHK_WAIT((TWCR & _BV(TWINT))) == 0) {};
TWDR = twiAddress<<1;
TWCR = _BV(TWEN) | _BV(TWINT) | _BV(TWEA);
while (TOD_CHK_WAIT((TWCR & _BV(TWINT)) == 0)) {};
TWDR = TOD_DATA_CONTINUE;
TWCR = _BV(TWEN) | _BV(TWINT) | _BV(TWEA);
while(TOD_CHK_WAIT((TWCR & _BV(TWINT)) == 0)) {};
}
else
{
pinSendStart( twiAddress<<1 );
pinWaitForAck();
pinWriteByte(TOD_DATA_CONTINUE);
pinWaitForAck();
}
// Dump buffer
for (uint16_t i=0; i < cacheSize; i++) // Send data
{
bool bUpdate = ((!doubleBuffer) || ( doubleBuffer && (displayCache[cacheSize-1+i] != displayCache[i])));
if( doubleBufferFirstTime || bUpdate )
{
if( doubleBuffer )
{ displayCache[cacheSize-1+i]=displayCache[i]; }
if( useRegisters )
{
if( !doubleBufferFirstTime && doubleBuffer )
{
// Set location must be here
// /* X */ set( TOD_SET_COLUMN_ADDR, x ); //width-1 );
// /* Y */ set( TOD_SET_PAGE_ADDR , y ); //height-1 );
}
TWDR = displayCache[i];
TWCR = _BV(TWEN) | _BV(TWINT) | _BV(TWEA); // Clear TWINT to proceed
while(TOD_CHK_WAIT((TWCR & _BV(TWINT)) == 0)) {}; // Wait for TWI to be ready
}
else {
pinWriteByte(displayCache[i]);
pinWaitForAck();
}
}
}
doubleBufferFirstTime=false;
if( useRegisters ) // Send TWI Stop
{ TWCR = _BV(TWEN)| _BV(TWINT) | _BV(TWSTO); } // Send STOP
else { pinSendStop(); }
I tried to use the COLUMN_ADDR (21h) and PAGE_ADDR (22h) to set the offset, the device doesn't seems to like it (lock up). What can I use to change the offset or skip a specific offset?
This is the setup of the display:
set( TOD_SET_DISPLAY_CLOCK_DIV_RATIO , 0x80 );
set( TOD_SET_MULTIPLEX_RATIO , height-1 );
set( TOD_SET_DISPLAY_OFFSET , 0x0 );
set( TOD_SET_START_LINE | 0x0 );
set( TOD_CHARGE_PUMP , 0x14 );
set( TOD_MEMORY_ADDR_MODE , 0x00 );
set( TOD_SET_SEGMENT_REMAP | 0x1 );
set( TOD_COM_SCAN_DIR_DEC );
set( TOD_SET_COM_PINS , ( height > 32 )?0x12:0x02 );
set( TOD_SET_CONTRAST_CONTROL , 0xCF );
set( TOD_SET_PRECHARGE_PERIOD , 0xF1 );
set( TOD_SET_VCOM_DETECT , 0x40 );
set( TOD_DISPLAY_ALL_ON_RESUME );
set( TOD_NORMAL_DISPLAY );
set( TOD_DEACTIVATE_SCROLL );
show();
Edit 21 okt 2017
See my posted answer.