转载自: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);