在 Milk-V Duo 上使用 SSD1309

转载自:SSD1309 On the Milk-V Duo | Futz's Microcontrollers & Robotics

接线了一个使用SSD1309控制器的128x64 OLED显示屏,与SSD1306非常相似。我修改了我的1306代码以使其工作,但…没有初始化。尝试了很多方法,最终连接了复位线(此显示屏引出了它 - 128x32的没有)并编写了几行代码以执行正确的上电顺序。这使它开始工作了。我还查阅了数据手册的命令部分,并清除了每个命令,使其与我设置的命令一致。清理了很多杂乱的内容,显示效果还不错。

但是,显示屏偶尔会随机更改其屏幕起始地址并开始出现这种情况:

我花了很多时间试图找出原因。我尝试为显示屏使用独立电源供应 - 没用。

我以为面包板和长电线可能会引起问题 - 重新布线更紧凑 - 没有帮助。

尝试使用WiringX的i2c命令 - 那个库没用。它可能需要单独的start()和stop()命令,并且肯定需要一个数据流命令(start()然后一系列数据/命令,然后stop())。

所以我又回到了我的bitbang代码。但是损坏的显示地址问题仍然存在。

最终治愈它的(我希望)是调整start()和stop()的时间。我延长了tHSTART和tSSTOP,现在似乎稳定了。在更改它们之前,它们与最小值相差不大。自从那次更改以来,它就没有出现问题。

目前我的代码如下。我使用NOP(空操作指令),因为C的usleep()命令在小延迟时无效。NOP允许我通过逻辑分析仪的试错来微调非常紧凑的i2c时间:

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