Wired up a 128x64 OLED display which uses the SSD1309 controller. Very similar to the SSD1306. I modified my 1306 code to work, and... No init. Tried tons of things until finally wiring up the reset line (this display pins it out - the 128x32 didn't) and writing a few lines of code to do the correct power-up sequence. That got it going. I also went through the datasheet command section and cleared out every command that came out of reset set to what I was setting it to anyway. Cleaned up a bunch of clutter and the display worked pretty good.
But the display would occasionally randomly change its screen start address and start doing this:
I spent huge amounts of time trying to pin down a reason for it. I tried separate power supply for the display - nope.
I thought maybe the breadboard and long wires might be causing trouble - rewired much tighter - didn't help.
Tried using WiringX's i2c commands - that library is useless. It probably needs separate start() and stop() commands, and definitely needs a data streaming command (start() and then a stream of data/commands and then stop()).
So I went back to my bitbang code. But the corrupted display address problem continued.
What finally cured it (I hope) was tinkering with start() and stop() timings. I lengthened both tHSTART and tSSTOP and it seems stable now. They were a bit close to minimum before I changed them. It hasn't messed up since that change.
Here's my code at present. I'm using NOPs because C's usleep() command is useless at small delay times. NOPs allow me to fine tune very tight i2c timings by trial and error with the logic analyzer:
ssd1309_128x64.c
//#include <stdio.h>
#include <stdlib.h>
//#include <time.h>
#include <math.h>
#include <unistd.h>
#include <wiringx.h>
#include "ssd1309_128x64.h"
int dat = 10; //physical pin 14 - wiringx pin 10
int clk = 11; //physical pin 15
unsigned char i2c_address = 0x3c;
unsigned char framebuff[1024];
int main(void){
int x1,y1,x2,y2,x3,y3,x4,y4;
int dx1,dy1,dx2,dy2,dx3,dy3,dx4,dy4;
init();
x1 = 20;
y1 = 5;
x2 = 30;
y2 = 10;
x3 = 100;
y3 = 15;
x4 = 74;
y4 = 12;
dx1 = 1;
dy1 = 1;
dx2 = 2;
dy2 = 1;
dx3 = -3;
dy3 = 2;
dx4 = -2;
dy4 = -1;
while(1){
cls();
line(x1,y1,x2,y2,1);
line(x2,y2,x3,y3,1);
line(x3,y3,x4,y4,1);
line(x4,y4,x1,y1,1);
x1 += dx1;
y1 += dy1;
x2 += dx2;
y2 += dy2;
x3 += dx3;
y3 += dy3;
x4 += dx4;
y4 += dy4;
if((x1 >= 127 - dx1) || (x1 <= 0 - dx1)){
dx1 = -dx1;
x1 += dx1;
}
if((y1 >= 63 - dy1) || (y1 <= 0 - dy1)){
dy1 = -dy1;
y1 += dy1;
}
if((x2 >= 127 - dx2) || (x2 <= 0 - dx2)){
dx2 = -dx2;
x2 += dx2;
}
if((y2 >= 63 - dy2) || (y2 <= 0 - dy2)){
dy2 = -dy2;
y2 += dy2;
}
if((x3 >= 127 - dx3) || (x3 <= 0 - dx3)){
dx3 = -dx3;
x3 += dx3;
}
if((y3 >= 63 - dy3) || (y3 <= 0 - dy3)){
dy3 = -dy3;
y3 += dy3;
}
if((x4 >= 127 - dx4) || (x4 <= 0 - dx4)){
dx4 = -dx4;
x4 += dx4;
}
if((y4 >= 63 - dy4) || (y4 <= 0 - dy4)){
dy4 = -dy4;
y4 += dy4;
}
transbuff();
}
}
void cls(void){
for(int x=0;x<1024;x++)
framebuff[x] = 0;
transbuff();
}
//DDA Line Algorithm - from geeksforgeeks.org
void line(int x1,int y1,int x2,int y2,unsigned char o){
int dx = x2 - x1; //#2
int dy = y2 - y1;
int steps = abs(dx) > abs(dy) ? abs(dx) : abs(dy);
float xinc = dx / (float)steps;
float yinc = dy / (float)steps;
float x = x1;
float y = y1;
for(int i = 0;i <= steps;i++){ //#5
setpix(round(x),round(y),o); //#6
x += xinc;
y += yinc;
}
}
//set or clear a pixel in framebuff[]
void setpix(int x,int y,unsigned char o){
int byte;
unsigned char bit, mask = 1;
byte = x + ((y / 8) * 128);
bit = y - ((y / 8) * 8);
mask <<= bit;
if(o)
framebuff[byte] |= mask;
else
framebuff[byte] ^= mask;
}
//write framebuff to GDDRAM
void transbuff(){
int x;
i2c_start();
i2c_write(0x40);
for(x=0;x<1024;x++)
i2c_write(framebuff[x]);
i2c_stop();
}
void init(){
//wiringx init
if(wiringXSetup("duo", NULL) == -1){
wiringXGC();
}
i2c_address <<= 1; //shift address over and clear write bit
//i2c pins init
pinMode(dat,PINMODE_OUTPUT);
pinMode(clk,PINMODE_OUTPUT);
pinMode(15,PINMODE_OUTPUT); //reset pin (physical pin 20)
digitalWrite(clk, LOW); //clk == 0 when set as output
digitalWrite(dat, LOW); //dat == 0 when set as output
pinMode(clk,PINMODE_INPUT); //clk pin high
pinMode(dat,PINMODE_INPUT); //dat pin high
//OLED init
usleep(6000); //OLED reset
digitalWrite(15,LOW);
usleep(6000);
digitalWrite(15,HIGH);
usleep(6000);
i2c_start();
i2c_write(0x00); //command stream
i2c_write(0xae); //display off
i2c_write(0x20); //set memory mode
i2c_write(0x00); //page mode - horizontal
i2c_write(0x21); //set column start & end address
i2c_write(0x00); //start
i2c_write(0x7f); //end
i2c_write(0x22); //set page start & end address
i2c_write(0x00); //start
i2c_write(0x07); //end
i2c_write(0xa4); //entire display on
i2c_write(0xa6); //normal display
i2c_write(0xaf); //set display ON or OFF | display ON
i2c_stop();
}
//*************************
//* bitbang I2C functions *
//*************************
unsigned char i2c_write(unsigned char x){
unsigned char i;
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
for(i=0;i<8;i++){ //clock out data byte
pinMode(dat,PINMODE_OUTPUT); //set data bit low
if(x & 0x80) //if output bit is high
pinMode(dat,PINMODE_INPUT); //then set data bit high
i2c_clock(); //clock it out
x <<= 1; //shift next bit into position
}
//get ack
pinMode(dat,PINMODE_INPUT); //set data high
pinMode(clk,PINMODE_INPUT); //set clock high
asm("nop"); //wait half a clock pulse
if(digitalRead(dat)) //sample the data bit
return(1); //if high then nack error
asm("nop"); //ack good, wait other half of clock pulse
pinMode(clk,PINMODE_OUTPUT); //set clock low
asm("nop");
pinMode(dat,PINMODE_INPUT); //set data high
return(0);
}
void i2c_start(void){ //send start condition
int x;
pinMode(dat,PINMODE_OUTPUT); //set data low
for(x=0;x<11;x++)
asm("nop");
pinMode(clk,PINMODE_OUTPUT); //set clock low
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
i2c_write(i2c_address);
}
void i2c_stop(void){ //send stop condition
int x;
pinMode(dat,PINMODE_OUTPUT); //set data low
for(x=0;x<11;x++)
asm("nop");
pinMode(clk,PINMODE_INPUT); //set clock high
for(x=0;x<14;x++) //stop delay
asm("nop");
pinMode(dat,PINMODE_INPUT); //set data high
asm("nop");
}
void i2c_clock(void){
int x;
pinMode(clk,PINMODE_INPUT); //set clock high
for(x=0;x<80;x++)
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
pinMode(clk,PINMODE_OUTPUT); //set clock low
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
}
ssd1309_128x64.h
void transbuff(void);
void setpix(int,int,unsigned char);
void line (int,int,int,int,unsigned char);
void cls(void);
void init(void);
unsigned char i2c_write(unsigned char);
void i2c_start(void);
void i2c_stop(void);
void i2c_clock(void);