I bought a Waveshare 128x128 RGB OLED display that uses the SSD1351 controller. I started writing some code for the Milk-V Duo, using the WiringX SPI library commands. After wasting a few hours I realized, once again, that WiringX just doesn't work. It needs a lot of work. The way it is now is useless.

So, back to bitbang SPI. No problems - SPI is super simple. The only thing I ran into is that the WiringX digitalWrite command is SLOW, and my code, with no delays whatsoever, updates the display way too slowly. Oh well, good enough for now. Some inline assembly code would get some speed out of this thing, but I would need a good datasheet for the Duo's MCU to do that.

As usual, Solomon Systech's datasheet is super cryptic and has some errors. It took me lots of trial and error and test code to figure out what they meant by some things, and to find out that they describe their startup sequence the opposite of how you should do it. What fun!

Photos and videos of the display are terrible because the display refresh gets caught at random times by my Pixel 7 phone camera, which has no shutter speed control, and, in fact, no advanced controls whatsoever. Don't buy a Google phone. I don't completely hate the Pixel, but my old Samsung was so much better.

Super simple demo code. This isn't even remotely close to being a driver yet. It's just the most minimal code for getting the display initialized and putting some colors on the screen. I'll do something more ambitious later:

ssd1351_128x128_rgb.c

#include <unistd.h>
#include <wiringx.h>
#include "ssd1351_128x128_rgb.h"

int rst = 15;       //reset - physical pin 20
int dc = 14;        //D/C - physical pin 19
int clk = 6;        //clk - physical pin 9
int dat = 7;        //dat - physical pin 10
unsigned char framebuff[0x8000];

int main(void){
  init();
  cls();

  while(1){
    for (int x=0;x<0x8000;x+=2){
      framebuff[x] = 0xf8;
      framebuff[x+1] = 0x00;
    }
    transbuff();

    for (int x=0;x<0x8000;x+=2){
      framebuff[x] = 0x07;
      framebuff[x+1] = 0xe0;
    }
    transbuff();

    for (int x=0;x<0x8000;x+=2){
      framebuff[x] = 0x00;
      framebuff[x+1] = 0x1f;
    }
    transbuff();
  }
}

void cls(void){
  for(int x=0;x<0x8000;x++)
    framebuff[x] = 0;
  transbuff();
}

//write framebuff to GDDRAM
void transbuff(){
  digitalWrite(dc,LOW);      //command
  spiwrite(0x5c);
  digitalWrite(dc,HIGH);      //data
  for(int x=0;x<0x8000;x++)
    spiwrite(framebuff[x]);
}

void spiwrite(unsigned char p){
  for(int x=0;x<8;x++){
    if(p & 0x80)
      digitalWrite(dat,HIGH);
    else
      digitalWrite(dat,LOW);
    digitalWrite(clk,HIGH);
    digitalWrite(clk,LOW);
    p <<= 1;
  }
}  

int init(){
  //wiringx init
  if(wiringXSetup("duo", NULL) == -1){
    wiringXGC();
  }

  pinMode(dc,PINMODE_OUTPUT);
  pinMode(rst,PINMODE_OUTPUT);
  pinMode(clk,PINMODE_OUTPUT);
  pinMode(dat,PINMODE_OUTPUT);
  digitalWrite(clk,LOW);

  //OLED init
  usleep(6000);           //OLED reset
  digitalWrite(rst,1);
  usleep(10000);
  digitalWrite(rst,0);
  usleep(50000);
  digitalWrite(rst,1);

  digitalWrite(dc,0);     //send command
  spiwrite(0xae);         //display OFF

  spiwrite(0xfd);         //unlock MCU protect
  digitalWrite(dc,1);     //send data
  spiwrite(0x12);

  digitalWrite(dc,0);
  spiwrite(0xfd);         //make a2 accessible
  digitalWrite(dc,1);
  spiwrite(0xb1);

  digitalWrite(dc,0);
  spiwrite(0xa2);         //set vertical scroll by row
  digitalWrite(dc,1);
  spiwrite(0x00);

  digitalWrite(dc,0);
  spiwrite(0xb3);         //clock speed
  digitalWrite(dc,1);
  spiwrite(0xf0);         //max clock speed

  digitalWrite(dc,0);
  spiwrite(0xa0);         //remap/color depth 65k
  digitalWrite(dc,1);
  spiwrite(0x74);

  digitalWrite(dc,0);
  spiwrite(0xaf);         //display ON
}

ssd1351_128x128_rgb.h

void transbuff(void);
void cls(void);
void spiwrite(unsigned char);
int init(void);

Next Post Previous Post