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);

Next Post Previous Post