C++ 渲染填充曲面时,BW显示上的着色效果更好
开场白 我终于解决了AT32UC3xxxx和SSD1306 OLED I2C显示器之间的硬件不兼容问题(两者都有硬件缺陷,导致它们不兼容),从而允许我以400KBaud(每帧约26.6ms)的速度使用硬件I2C。因此,我决定重写我的旧驱动程序,通过添加填充曲面(三角形、四边形),而不仅仅是直线和图案线(我已经实现了),来利用新的速度 问题是显示器是128x64像素,但没有颜色或灰色阴影,只有BW开/关 例如,为了渲染旋转立方体,我需要以某种方式区分曲面。我在考虑随机填充模式,其中表面填充到一定百分比,而不是颜色 这里是我当前的代码(整个库,但没有bug,它可以正常工作): LCD_SSD1306_I2C.hC++ 渲染填充曲面时,BW显示上的着色效果更好,c++,algorithm,graphics,rasterizing,shading,C++,Algorithm,Graphics,Rasterizing,Shading,开场白 我终于解决了AT32UC3xxxx和SSD1306 OLED I2C显示器之间的硬件不兼容问题(两者都有硬件缺陷,导致它们不兼容),从而允许我以400KBaud(每帧约26.6ms)的速度使用硬件I2C。因此,我决定重写我的旧驱动程序,通过添加填充曲面(三角形、四边形),而不仅仅是直线和图案线(我已经实现了),来利用新的速度 问题是显示器是128x64像素,但没有颜色或灰色阴影,只有BW开/关 例如,为了渲染旋转立方体,我需要以某种方式区分曲面。我在考虑随机填充模式,其中表面填充到一定百
//------------------------------------------------------------------------------------------
//--- SSD1306 I2C OLED LCD driver ver 2.000 ------------------------------------------------
//------------------------------------------------------------------------------------------
#ifndef _LCD_SSD1306_I2C_h
#define _LCD_SSD1306_I2C_h
//------------------------------------------------------------------------------------------
#define SSD1306_SETCONTRAST 0x81
#define SSD1306_DISPLAYALLON_RESUME 0xA4
#define SSD1306_DISPLAYALLON 0xA5
#define SSD1306_NORMALDISPLAY 0xA6
#define SSD1306_INVERTDISPLAY 0xA7
#define SSD1306_DISPLAYOFF 0xAE
#define SSD1306_DISPLAYON 0xAF
#define SSD1306_SETDISPLAYOFFSET 0xD3
#define SSD1306_SETCOMPINS 0xDA
#define SSD1306_SETVCOMDETECT 0xDB
#define SSD1306_SETDISPLAYCLOCKDIV 0xD5
#define SSD1306_SETPRECHARGE 0xD9
#define SSD1306_SETMULTIPLEX 0xA8
#define SSD1306_SETLOWCOLUMN 0x00
#define SSD1306_SETHIGHCOLUMN 0x10
#define SSD1306_SETSTARTLINE 0x40
#define SSD1306_MEMORYMODE 0x20
#define SSD1306_COLUMNADDR 0x21
#define SSD1306_PAGEADDR 0x22
#define SSD1306_COMSCANINC 0xC0
#define SSD1306_COMSCANDEC 0xC8
#define SSD1306_SEGREMAP 0xA0
#define SSD1306_CHARGEPUMP 0x8D
#define SSD1306_SWITCHCAPVCC 0x2
// Scrolling #defines
#define SSD1306_ACTIVATE_SCROLL 0x2F
#define SSD1306_DEACTIVATE_SCROLL 0x2E
#define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3
#define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26
#define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27
#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29
#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A
//------------------------------------------------------------------------------------------
//#define I2C_send(adr,buf,siz){}
//------------------------------------------------------------------------------------------
#ifndef _brv8_tab
#define _brv8_tab
static const U8 brv8[256] = // 8 bit bit reversal
{
0,128,64,192,32,160,96,224,16,144,80,208,48,176,112,240,8,136,72,200,40,168,104,232,24,152,
88,216,56,184,120,248,4,132,68,196,36,164,100,228,20,148,84,212,52,180,116,244,12,140,76,204,
44,172,108,236,28,156,92,220,60,188,124,252,2,130,66,194,34,162,98,226,18,146,82,210,50,178,
114,242,10,138,74,202,42,170,106,234,26,154,90,218,58,186,122,250,6,134,70,198,38,166,102,230,
22,150,86,214,54,182,118,246,14,142,78,206,46,174,110,238,30,158,94,222,62,190,126,254,1,129,
65,193,33,161,97,225,17,145,81,209,49,177,113,241,9,137,73,201,41,169,105,233,25,153,89,217,57,
185,121,249,5,133,69,197,37,165,101,229,21,149,85,213,53,181,117,245,13,141,77,205,45,173,109,
237,29,157,93,221,61,189,125,253,3,131,67,195,35,163,99,227,19,147,83,211,51,179,115,243,11,139,
75,203,43,171,107,235,27,155,91,219,59,187,123,251,7,135,71,199,39,167,103,231,23,151,87,215,55,
183,119,247,15,143,79,207,47,175,111,239,31,159,95,223,63,191,127,255
};
#endif
//------------------------------------------------------------------------------------------
class LCD_SSD1306_I2C // max 96 lines
{
public:
// screen
int adr; // I2C adr
int xs,ys,sz; // resoluiton
U8 _scr[((128*96)>>3)+1]; // screen buffer
U8 *scr;
U8 *pyx[96]; // scan lines
// pattern
U32 pat,pat_m,pat_b; // binary pattern,max used bit mask,actual bit mask
// filling
U32 seed;
int bufl[96];
int bufr[96];
// system api
void init(int _adr,int _xs,int _ys); // initialize LCD: I2C_adr,xs,ys
void _command(U8 cmd); // *internal* do not cal it (sends command to LCD over I2C)
void rfsscr(); // copy actual screen buffer to LCD (by I2C)
// gfx rendering col = <0,1>
void clrscr(); // clear screen buffer
void rotate(int ang); // rotate 180 deg
void pixel(int x,int y,bool col); // set/res pixel
bool pixel(int x,int y); // get pixel
void line(int x0,int y0,int x1,int y1,bool col); // line
void triangle(int x0,int y0,int x1,int y1,int x2,int y2,bool col);// triangle
void quad(int x0,int y0,int x1,int y1,int x2,int y2,int x3,int y3,bool col);
void rect(int x0,int y0,int x1,int y1,bool col); // rectangle using diagonal points
// patern rendering
void pat_set(char *s); // set binary pattern from bianry number string MSB renders first
void pat_beg(); // set pattern state to start of pattern
void pat_line(int x0,int y0,int x1,int y1,bool col);
void pat_triangle(int x0,int y0,int x1,int y1,int x2,int y2,bool col);
void pat_quad(int x0,int y0,int x1,int y1,int x2,int y2,int x3,int y3,bool col);
void pat_rect(int x0,int y0,int x1,int y1,bool col);
// filled polygons col = <0,255>
void _fill_line(int x0,int y0,int x1,int y1); // *internal* do not call it (render line into bufl/bufr)
void _fill_seed(); // *internal* do not call it (reset seed)
U8 _fill_rand(); // *internal* do not call it (get pseudo random number)
void fill_triangle(int x0,int y0,int x1,int y1,int x2,int y2,U8 col);
void fill_quad(int x0,int y0,int x1,int y1,int x2,int y2,int x3,int y3,U8 col);
// text rendering
void prnchr(int x,int y,char c); // render char at x,y (y is rounded to multiple of 8)
void prntxt(int x,int y,const char *txt); // render text at x,y (y is rounded to multiple of 8)
};
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::_command(U8 cmd)
{
U8 buf[2]=
{
0x00, // 0x40 data/command
cmd,
};
I2C_send(adr,buf,2);
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::init(int _adr,int _xs,int _ys)
{
int y;
adr=_adr;
xs=_xs;
ys=_ys;
sz=xs*(ys>>3);
const bool _external_Vcc=false;
// VRAM buffer
scr=_scr+1; // skip first Byte (VRAM/command selection)
for (y=0;y<ys;y++) pyx[y]=scr+((y>>3)*xs); // scanlines for fast direct pixel access
clrscr();
// Init sequence
U8 comPins = 0x02;
U8 contrast = 0x8F;
if((xs == 128) && (ys == 32)) { comPins = 0x02; contrast = 0x8F; }
else if((xs == 128) && (ys == 64)) { comPins = 0x12; contrast = (_external_Vcc) ? 0x9F : 0xCF; }
else if((xs == 96) && (ys == 16)) { comPins = 0x02; contrast = (_external_Vcc) ? 0x10 : 0xAF; }
else {} // Other screens
static U8 init0[27]=
{
0x00, // commands
SSD1306_DISPLAYOFF, // 0xAE
SSD1306_SETDISPLAYCLOCKDIV,0x80, // 0xD5
SSD1306_SETMULTIPLEX,ys-1, // 0xA8
SSD1306_SETDISPLAYOFFSET,0x00, // 0xD3 no offset
SSD1306_SETSTARTLINE | 0x0, // line 0
SSD1306_CHARGEPUMP,(_external_Vcc)?0x10:0x14, // 0x8D
SSD1306_MEMORYMODE,0x00, // 0x20 horizontal (scanlines)
SSD1306_SEGREMAP | 0x1,
SSD1306_COMSCANDEC,
SSD1306_SETCOMPINS,comPins,
SSD1306_SETCONTRAST,contrast,
SSD1306_SETPRECHARGE,(_external_Vcc)?0x22:0xF1, // 0xd9
SSD1306_SETVCOMDETECT,0x40, // 0xDB
SSD1306_DISPLAYALLON_RESUME, // 0xA4
SSD1306_NORMALDISPLAY, // 0xA6
SSD1306_DEACTIVATE_SCROLL,
SSD1306_DISPLAYON, // Main screen turn on
};
I2C_send(adr,init0,sizeof(init0));
// init default pattern
pat_set("111100100");
// clear filling buffers
for (y=0;y<96;y++)
{
bufl[y]=-1;
bufr[y]=-1;
}
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::clrscr()
{
for (int a=0;a<sz;a++) scr[a]=0x00;
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::rotate(int ang)
{
U8 a0,a1;
int x0,y0,x1,y1;
if (ang==180)
for (y0=0,y1=ys-8;y0<y1;y0+=8,y1-=8)
for (x0=0,x1=xs-1;x0<xs;x0++,x1--)
{
a0=brv8[pyx[y0][x0]];
a1=brv8[pyx[y1][x1]];
pyx[y0][x0]=a1;
pyx[y1][x1]=a0;
}
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::rfsscr()
{
static const U8 init1[] =
{
0x00, // commands
SSD1306_MEMORYMODE,0, // horizontal addresing mode
SSD1306_COLUMNADDR,0,xs-1, // Column start/end address (0/127 reset)
SSD1306_PAGEADDR,0,(ys>>3)-1, // Page start/end address (0 reset)
};
I2C_send(adr,(U8*)init1,sizeof(init1));
_scr[0]=0x40; // 0x40 VRAM
// SW I2C can pass whole VRAM in single packet
// I2C_send(adr,_scr,sz+1);
// HW I2C must use packets up to 255 bytes so 128+1 (as TWIM0 on UC3 has 8 bit counter)
int i,n=128; U8 *p=_scr,a;
for (i=0;i<sz;i+=n){ a=p[0]; p[0]=0x40; I2C_send(adr,p,n+1); p[0]=a; p+=n; }
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::pixel(int x, int y,bool col)
{
// clip to screen
if ((x<0)||(x>=xs)||(y<0)||(y>=ys)) return;
// add or remove bit
if (col) pyx[y][x] |= (1<<(y&7));
else pyx[y][x] &= (255)^(1<<(y&7));
}
//------------------------------------------------------------------------------------------
bool LCD_SSD1306_I2C::pixel(int x, int y)
{
// clip to screen
if ((x<0)||(x>=xs)||(y<0)||(y>=ys)) return false;
// get bit
return ((pyx[y][x]>>(y&7))&1);
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::line(int x0,int y0,int x1,int y1,bool col)
{
int i,n,cx,cy,sx,sy;
// line DDA parameters
x1-=x0; sx=0; if (x1>0) sx=+1; if (x1<0) { sx=-1; x1=-x1; } if (x1) x1++; n=x1;
y1-=y0; sy=0; if (y1>0) sy=+1; if (y1<0) { sy=-1; y1=-y1; } if (y1) y1++; if (n<y1) n=y1;
// single pixel (not a line)
if (!n){ pixel(x0,y0,col); return; }
// ND DDA algo i is parameter
for (cx=cy=n,i=0;i<n;i++)
{
pixel(x0,y0,col);
cx-=x1; if (cx<=0){ cx+=n; x0+=sx; }
cy-=y1; if (cy<=0){ cy+=n; y0+=sy; }
}
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::pat_line(int x0,int y0,int x1,int y1,bool col)
{
bool ccc;
int i,n,cx,cy,sx,sy;
// line DDA parameters
x1-=x0; sx=0; if (x1>0) sx=+1; if (x1<0) { sx=-1; x1=-x1; } if (x1) x1++; n=x1;
y1-=y0; sy=0; if (y1>0) sy=+1; if (y1<0) { sy=-1; y1=-y1; } if (y1) y1++; if (n<y1) n=y1;
// single pixel (not a line)
if (!n){ pixel(x0,y0,col); return; }
// ND DDA algo i is parameter
for (cx=cy=n,i=0;i<n;i++)
{
ccc=(pat&pat_b); ccc^=(!col);
pat_b>>=1; if (!pat_b) pat_b=pat_m;
pixel(x0,y0,ccc);
cx-=x1; if (cx<=0){ cx+=n; x0+=sx; }
cy-=y1; if (cy<=0){ cy+=n; y0+=sy; }
}
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::_fill_line(int x0,int y0,int x1,int y1)
{
int i,n,cx,cy,sx,sy,*buf;
// line DDA parameters
x1-=x0; sx=0; if (x1>0) sx=+1; if (x1<0) { sx=-1; x1=-x1; } if (x1) x1++; n=x1;
y1-=y0; sy=0; if (y1>0) sy=+1; if (y1<0) { sy=-1; y1=-y1; } if (y1) y1++; if (n<y1) n=y1;
// single pixel (not a line)
if (!n)
{
if ((y0>=0)&&(y0<ys))
{
bufl[y0]=x0;
bufr[y0]=x0;
}
return;
}
// target buffer depend on y direction
if (sy>0) buf=bufl; else buf=bufr;
// ND DDA algo i is parameter
for (cx=cy=n,i=0;i<n;i++)
{
if ((y0>=0)&&(y0<ys)) buf[y0]=x0;
cx-=x1; if (cx<=0){ cx+=n; x0+=sx; }
cy-=y1; if (cy<=0){ cy+=n; y0+=sy; }
}
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::_fill_seed()
{
seed=0x017A357E1;
// RandSeed=0x017A357E1;
}
//------------------------------------------------------------------------------------------
U8 LCD_SSD1306_I2C::_fill_rand()
{
U32 a,b,c;
a= seed &0x0FFFF;
b=(seed>>16)&0x0FFFF;
seed<<=11;
seed^=(a<<16);
seed&=0x0FFFF0000;
seed|=b+17;
return seed&255;
// return Random(256);
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::triangle(int x0,int y0,int x1,int y1,int x2,int y2,bool col)
{
line(x0,y0,x1,y1,col);
line(x1,y1,x2,y2,col);
line(x2,y2,x0,y0,col);
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::pat_triangle(int x0,int y0,int x1,int y1,int x2,int y2,bool col)
{
pat_line(x0,y0,x1,y1,col);
pat_line(x1,y1,x2,y2,col);
pat_line(x2,y2,x0,y0,col);
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::fill_triangle(int x0,int y0,int x1,int y1,int x2,int y2,U8 col)
{
int x,y,X0,X1,Y0,Y1;
// y range to render
Y0=Y1=y0;
if (Y0>y1) Y0=y1;
if (Y1<y1) Y1=y1;
if (Y0>y2) Y0=y2;
if (Y1<y2) Y1=y2;
// clip to screen in y axis
if ((Y1<0)||(Y0>=ys)) return;
if (Y0< 0) Y0= 0;
if (Y1>=ys) Y1=ys-1;
// clear buffers
for (y=Y0;y<=Y1;y++)
{
bufl[y]=xs;
bufr[y]=-1;
}
// render circumference
_fill_line(x0,y0,x1,y1);
_fill_line(x1,y1,x2,y2);
_fill_line(x2,y2,x0,y0);
// fill horizontal lines
_fill_seed();
for (y=Y0;y<=Y1;y++)
{
// x range to render
X0=bufl[y];
X1=bufr[y];
if (X0>X1){ x=X0; X0=X1; X1=x; }
// clip to screen in y axis
if ((X1<0)||(X0>=xs)) continue;
if (X0< 0) X0= 0;
if (X1>=xs) X1=xs-1;
if (col== 0) for (x=X0;x<=X1;x++) pixel(x,y,0);
else if (col==255) for (x=X0;x<=X1;x++) pixel(x,y,1);
else for (x=X0;x<=X1;x++) pixel(x,y,(_fill_rand()<=col));
}
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::quad(int x0,int y0,int x1,int y1,int x2,int y2,int x3,int y3,bool col)
{
line(x0,y0,x1,y1,col);
line(x1,y1,x2,y2,col);
line(x2,y2,x3,y3,col);
line(x3,y3,x0,y0,col);
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::pat_quad(int x0,int y0,int x1,int y1,int x2,int y2,int x3,int y3,bool col)
{
pat_line(x0,y0,x1,y1,col);
pat_line(x1,y1,x2,y2,col);
pat_line(x2,y2,x3,y3,col);
pat_line(x3,y3,x0,y0,col);
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::fill_quad(int x0,int y0,int x1,int y1,int x2,int y2,int x3,int y3,U8 col)
{
int x,y,X0,X1,Y0,Y1;
// y range to render
Y0=Y1=y0;
if (Y0>y1) Y0=y1;
if (Y1<y1) Y1=y1;
if (Y0>y2) Y0=y2;
if (Y1<y2) Y1=y2;
if (Y0>y3) Y0=y3;
if (Y1<y3) Y1=y3;
// clip to screen in y axis
if ((Y1<0)||(Y0>=ys)) return;
if (Y0< 0) Y0= 0;
if (Y1>=ys) Y1=ys-1;
// clear buffers
for (y=Y0;y<=Y1;y++)
{
bufl[y]=xs;
bufr[y]=-1;
}
// render circumference
_fill_line(x0,y0,x1,y1);
_fill_line(x1,y1,x2,y2);
_fill_line(x2,y2,x3,y3);
_fill_line(x3,y3,x0,y0);
// fill horizontal lines
_fill_seed();
for (y=Y0;y<=Y1;y++)
{
// x range to render
X0=bufl[y];
X1=bufr[y];
if (X0>X1){ x=X0; X0=X1; X1=x; }
// clip to screen in y axis
if ((X1<0)||(X0>=xs)) continue;
if (X0< 0) X0= 0;
if (X1>=xs) X1=xs-1;
if (col== 0) for (x=X0;x<=X1;x++) pixel(x,y,0);
else if (col==255) for (x=X0;x<=X1;x++) pixel(x,y,1);
else for (x=X0;x<=X1;x++) pixel(x,y,(_fill_rand()<=col));
}
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::rect(int x0,int y0,int x1,int y1,bool col)
{
line(x0,y0,x1,y0,col);
line(x1,y0,x1,y1,col);
line(x1,y1,x0,y1,col);
line(x0,y1,x0,y0,col);
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::pat_rect(int x0,int y0,int x1,int y1,bool col)
{
pat_line(x0,y0,x1,y0,col);
pat_line(x1,y0,x1,y1,col);
pat_line(x1,y1,x0,y1,col);
pat_line(x0,y1,x0,y0,col);
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::prnchr(int x,int y,char c)
{
y&=0xFFFFFFF8; // multiple of 8
if ((y<0)||(y>ys-8)) return;
int i,a;
a=c; a<<=3;
for (i=0;i<8;i++,x++,a++)
if ((x>=0)&&(x<xs))
pyx[y][x]=font[a];
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::prntxt(int x,int y,const char *txt)
{
for (;*txt;txt++,x+=8) prnchr(x,y,*txt);
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::pat_set(char *s)
{
int i=1;
pat=0;
if (s!=NULL)
for (i=0;(*s)&&(i<32);s++,i++)
{
pat<<=1;
if (*s=='1') pat|=1;
}
if (!i) i=1;
pat_m=1<<(i-1);
pat_beg();
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::pat_beg()
{
pat_b=pat_m;
}
//------------------------------------------------------------------------------------------
#undef I2C_send
//------------------------------------------------------------------------------------------
#endif
//------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------
//---SSD1306 I2C OLED LCD驱动程序版本2.000------------------------------------------------
//------------------------------------------------------------------------------------------
#ifndef液晶显示器SSD1306 I2C h
#定义液晶显示器SSD1306 I2C h
//------------------------------------------------------------------------------------------
#定义SSD1306_设置对比度0x81
#定义SSD1306\u DISPLAYALLON\u RESUME 0xA4
#在0xA5上定义SSD1306_DISPLAYALLON
#定义SSD1306_正常显示0xA6
#定义SSD1306_反转显示0xA7
#定义SSD1306_DISPLAYOFF 0xAE
#在0xAF上定义SSD1306_显示
#定义SSD1306_SETDISPLAYOFFSET 0xD3
#定义SSD1306_SETCOMPINS 0xDA
#定义SSD1306_SETVCOMDETECT 0xDB
#定义SSD1306_SETDISPLAYCLOCKDIV 0xD5
#定义SSD1306_设置预充电0xD9
#定义SSD1306_设置多路复用0xA8
#定义SSD1306_setlow列0x00
#定义SSD1306_SETHIGHCOLUMN 0x10
#定义SSD1306_设置起始线0x40
#定义SSD1306_内存模式0x20
#定义SSD1306_COLUMNADDR 0x21
#定义SSD1306_PAGEADDR 0x22
#定义SSD1306\u COMSCANINC 0xC0
#定义SSD1306_COMSCANDEC 0xC8
#定义SSD1306_SEGREMAP 0xA0
#定义SSD1306_充电泵0x8D
#定义SSD1306_SWITCHCAPVCC 0x2
//滚动#定义
#定义SSD1306_激活_滚动0x2F
#定义SSD1306\u停用\u滚动0x2E
#定义SSD1306设置垂直滚动区域0xA3
#定义SSD1306\u右\u水平\u滚动0x26
#定义SSD1306\u左\u水平\u滚动0x27
#定义SSD1306_垂直_和_右_水平_滚动0x29
#定义SSD1306_垂直_和_左_水平_滚动0x2A
//------------------------------------------------------------------------------------------
//#定义I2C_发送(adr、buf、siz){}
//------------------------------------------------------------------------------------------
#ifndef\u brv8\u选项卡
#定义_brv8_选项卡
静态常量U8 brv8[256]=//8位反转
{
0,128,64,192,32,160,96,224,16,144,80,208,48,176,112,240,8,136,72,200,40,168,104,232,24,152,
88,216,56,184,120,248,4,132,68,196,36,164,100,228,20,148,84,212,52,180,116,244,12,140,76,204,
44,172,108,236,28,156,92,220,60,188,124,252,2,130,66,194,34,162,98,226,18,146,82,210,50,178,
114,242,10,138,74,202,42,170,106,234,26,154,90,218,58,186,122,250,6,134,70,198,38,166,102,230,
22,150,86,214,54,182,118,246,14,142,78,206,46,174,110,238,30,158,94,222,62,190,126,254,1,129,
65,193,33,161,97,225,17,145,81,209,49,177,113,241,9,137,73,201,41,169,105,233,25,153,89,217,57,
185,121,249,5,133,69,197,37,165,101,229,21,149,85,213,53,181,117,245,13,141,77,205,45,173,109,
237,29,157,93,221,61,189,125,253,3,131,67,195,35,163,99,227,19,147,83,211,51,179,115,243,11,139,
75,203,43,171,107,235,27,155,91,219,59,187,123,251,7,135,71,199,39,167,103,231,23,151,87,215,55,
183,119,247,15,143,79,207,47,175,111,239,31,159,95,223,63,191,127,255
};
#恩迪夫
//------------------------------------------------------------------------------------------
类别LCD_SSD1306_I2C//最多96行
{
公众:
//屏风
int adr;//I2C adr
int xs,ys,sz;//resoluiton
U8 scr[(128*96)>>3)+1];//屏幕缓冲区
U8*scr;
U8*pyx[96];//扫描行
//图案
U32 pat,pat_m,pat_b;//二进制模式,最大使用位掩码,实际位掩码
//填充物
U32种子;
int bufl[96];
int bufr[96];
//系统api
void init(int _adr,int _xs,int _ys);//初始化LCD:I2C_adr,xs,ys
无效_命令(U8 cmd);//*内部*不校准(通过I2C向LCD发送命令)
void rfsscr();//将实际屏幕缓冲区复制到LCD(通过I2C)
//gfx渲染列=
void clrsc();//清除屏幕缓冲区
空心旋转(内角);//旋转180度
无效像素(int x,int y,bool col);//设置/恢复像素
布尔像素(int x,int y);//获取像素
虚线(int x0,int y0,int x1,int y1,bool col);//线
空三角形(int x0,int y0,int x1,int y1,int x2,int y2,bool col);//三角形
空四元组(整数x0、整数y0、整数x1、整数y1、整数x2、整数y2、整数x3、整数y3、布尔列);
void rect(int x0,int y0,int x1,int y1,bool col);//使用对角点的矩形
//模式渲染
void pat_set(char*s);//首先从二进制数字字符串MSB中设置二进制模式
void pat_beg();//将模式状态设置为模式的开始
虚线(整数x0、整数y0、整数x1、整数y1、布尔列);
空心三角形(int x0,int y0,int x1,int y1,int x2,int y2,bool col);
无效四元组(整数x0、整数y0、整数x1、整数y1、整数x2、整数y2、整数x3、整数y3、布尔列);
无效模式(整数x0、整数y0、整数x1、整数y1、布尔列);
//填充多边形列=
空隙_填充线(int x0,int y0,int x1,int y1);//*内部*不
//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop
#include "win_main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMain *Main;
const int sz=4; // LCD pixel size
int xs,ys; // LCD resolution * sz
int *psz; // screen pixel to LCD pixel
Graphics::TBitmap *bmp; // screen buffer
//---------------------------------------------------------------------------
typedef BYTE U8; // here use data types you got like unsigned __int8_t ...
typedef WORD U16;
typedef DWORD U32;
void I2C_send(int adr,U8 *buf,int siz){}
//#include "font_8x8.h" // font file
static U8 font[256<<3]; // empty font instead (no printing used)
#include "LCD_SSD1306_I2C.h"
LCD_SSD1306_I2C lcd;
//---------------------------------------------------------------------------
const float cube_pos[]=
{
// x y z //ix
-1.0,+1.0,-1.0, //0
+1.0,+1.0,-1.0, //1
+1.0,-1.0,-1.0, //2
-1.0,-1.0,-1.0, //3
-1.0,-1.0,+1.0, //4
+1.0,-1.0,+1.0, //5
+1.0,+1.0,+1.0, //6
-1.0,+1.0,+1.0, //7
-1.0,-1.0,-1.0, //3
+1.0,-1.0,-1.0, //2
+1.0,-1.0,+1.0, //5
-1.0,-1.0,+1.0, //4
+1.0,-1.0,-1.0, //2
+1.0,+1.0,-1.0, //1
+1.0,+1.0,+1.0, //6
+1.0,-1.0,+1.0, //5
+1.0,+1.0,-1.0, //1
-1.0,+1.0,-1.0, //0
-1.0,+1.0,+1.0, //7
+1.0,+1.0,+1.0, //6
-1.0,+1.0,-1.0, //0
-1.0,-1.0,-1.0, //3
-1.0,-1.0,+1.0, //4
-1.0,+1.0,+1.0, //7
};
const float cube_nor[]=
{
// nx ny nz //ix
0.0, 0.0,-1.0, //0
0.0, 0.0,-1.0, //1
0.0, 0.0,-1.0, //2
0.0, 0.0,-1.0, //3
0.0, 0.0,+1.0, //4
0.0, 0.0,+1.0, //5
0.0, 0.0,+1.0, //6
0.0, 0.0,+1.0, //7
0.0,-1.0, 0.0, //0
0.0,-1.0, 0.0, //1
0.0,-1.0, 0.0, //5
0.0,-1.0, 0.0, //4
+1.0, 0.0, 0.0, //1
+1.0, 0.0, 0.0, //2
+1.0, 0.0, 0.0, //6
+1.0, 0.0, 0.0, //5
0.0,+1.0, 0.0, //2
0.0,+1.0, 0.0, //3
0.0,+1.0, 0.0, //7
0.0,+1.0, 0.0, //6
-1.0, 0.0, 0.0, //3
-1.0, 0.0, 0.0, //0
-1.0, 0.0, 0.0, //4
-1.0, 0.0, 0.0, //7
};
//---------------------------------------------------------------------------
void matrix_mul_pos(float *c,const float *a,const float *b)
{
float q[3];
q[0]=(a[ 0]*b[0])+(a[ 4]*b[1])+(a[ 8]*b[2])+(a[12]);
q[1]=(a[ 1]*b[0])+(a[ 5]*b[1])+(a[ 9]*b[2])+(a[13]);
q[2]=(a[ 2]*b[0])+(a[ 6]*b[1])+(a[10]*b[2])+(a[14]);
for(int i=0;i<3;i++) c[i]=q[i];
}
void matrix_mul_dir(float *c,const float *a,const float *b)
{
float q[3];
q[0]=(a[ 0]*b[0])+(a[ 4]*b[1])+(a[ 8]*b[2]);
q[1]=(a[ 1]*b[0])+(a[ 5]*b[1])+(a[ 9]*b[2]);
q[2]=(a[ 2]*b[0])+(a[ 6]*b[1])+(a[10]*b[2]);
for(int i=0;i<3;i++) c[i]=q[i];
}
void matrix_mul_mat(float *c,float *a,float *b)
{
float q[16];
q[ 0]=(a[ 0]*b[ 0])+(a[ 1]*b[ 4])+(a[ 2]*b[ 8])+(a[ 3]*b[12]);
q[ 1]=(a[ 0]*b[ 1])+(a[ 1]*b[ 5])+(a[ 2]*b[ 9])+(a[ 3]*b[13]);
q[ 2]=(a[ 0]*b[ 2])+(a[ 1]*b[ 6])+(a[ 2]*b[10])+(a[ 3]*b[14]);
q[ 3]=(a[ 0]*b[ 3])+(a[ 1]*b[ 7])+(a[ 2]*b[11])+(a[ 3]*b[15]);
q[ 4]=(a[ 4]*b[ 0])+(a[ 5]*b[ 4])+(a[ 6]*b[ 8])+(a[ 7]*b[12]);
q[ 5]=(a[ 4]*b[ 1])+(a[ 5]*b[ 5])+(a[ 6]*b[ 9])+(a[ 7]*b[13]);
q[ 6]=(a[ 4]*b[ 2])+(a[ 5]*b[ 6])+(a[ 6]*b[10])+(a[ 7]*b[14]);
q[ 7]=(a[ 4]*b[ 3])+(a[ 5]*b[ 7])+(a[ 6]*b[11])+(a[ 7]*b[15]);
q[ 8]=(a[ 8]*b[ 0])+(a[ 9]*b[ 4])+(a[10]*b[ 8])+(a[11]*b[12]);
q[ 9]=(a[ 8]*b[ 1])+(a[ 9]*b[ 5])+(a[10]*b[ 9])+(a[11]*b[13]);
q[10]=(a[ 8]*b[ 2])+(a[ 9]*b[ 6])+(a[10]*b[10])+(a[11]*b[14]);
q[11]=(a[ 8]*b[ 3])+(a[ 9]*b[ 7])+(a[10]*b[11])+(a[11]*b[15]);
q[12]=(a[12]*b[ 0])+(a[13]*b[ 4])+(a[14]*b[ 8])+(a[15]*b[12]);
q[13]=(a[12]*b[ 1])+(a[13]*b[ 5])+(a[14]*b[ 9])+(a[15]*b[13]);
q[14]=(a[12]*b[ 2])+(a[13]*b[ 6])+(a[14]*b[10])+(a[15]*b[14]);
q[15]=(a[12]*b[ 3])+(a[13]*b[ 7])+(a[14]*b[11])+(a[15]*b[15]);
for(int i=0;i<16;i++) c[i]=q[i];
}
//---------------------------------------------------------------------------
float deg=M_PI/180.0,angx=0.0,angy=0.0,angz=5.0*deg;
float view_FOVx=128.0*tan(30.0*deg)*0.5;
void obj2scr(int &x,int &y,const float *m,const float *pos)
{
float p[3],d;
x=0; y=0;
matrix_mul_pos(p,m,pos);
if (fabs(p[2])>1e-3) d=view_FOVx/p[2]; else d=0.0;
p[0]*=d; x=2.5*p[0]; x+=64;
p[1]*=d; y=2.5*p[1]; y+=32;
}
//---------------------------------------------------------------------------
void TMain::draw()
{
int i,j,n,nz;
int x,y,x0,y0,x1,y1,x2,y2,x3,y3;
lcd.clrscr();
// modelview
float p[3],c,s,m[16],m0[16]=
{
1.0, 0.0, 0.0,0.0,
0.0, 1.0, 0.0,0.0,
0.0, 0.0, 1.0,0.0,
0.0, 0.0, 7.0,1.0,
};
c=cos(angx); s=sin(angx);
float rx[16]= { 1, 0, 0, 0,
0, c, s, 0,
0,-s, c, 0,
0, 0, 0, 1 };
c=cos(angy); s=sin(angy);
float ry[16]= { c, 0, s, 0,
0, 1, 0, 0,
-s, 0, c, 0,
0, 0, 0, 1 };
c=cos(angz); s=sin(angz);
float rz[16]= { c, s, 0, 0,
-s, c, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1 };
matrix_mul_mat(m,rx,ry);
matrix_mul_mat(m,m,rz);
matrix_mul_mat(m,m,m0);
angx=fmod(angx+1.0*deg,2.0*M_PI);
angy=fmod(angy+5.0*deg,2.0*M_PI);
angz=fmod(angz+2.0*deg,2.0*M_PI);
n=6*4*3;
for (i=0;i<n;)
{
matrix_mul_dir(p,m,cube_nor+i);
nz=float(-255.0*p[2]*fabs(p[2]));
obj2scr(x0,y0,m,cube_pos+i); i+=3;
obj2scr(x1,y1,m,cube_pos+i); i+=3;
obj2scr(x2,y2,m,cube_pos+i); i+=3;
obj2scr(x3,y3,m,cube_pos+i); i+=3;
if (nz>0)
{
nz=100+((150*nz)>>8);
lcd.fill_quad(x0,y0,x1,y1,x2,y2,x3,y3,nz);
}
}
lcd.rfsscr();
// copy LCD to Canvas to see result
if (1)
{
DWORD col[2]={0x00100018,0x00FFFFFF},*p;
int x,y,xx,yy;
for ( y=0,yy=psz[y];y<ys;y++,yy=psz[y])
for (p=(DWORD*)bmp->ScanLine[y],x=0,xx=psz[x];x<xs;x++,xx=psz[x])
p[x]=col[lcd.pixel(xx,yy)];
}
Canvas->Draw(0,0,bmp);
}
//---------------------------------------------------------------------------
__fastcall TMain::TMain(TComponent* Owner) : TForm(Owner)
{
lcd.init(0x3C,128,64);
bmp=new Graphics::TBitmap;
bmp->HandleType=bmDIB;
bmp->PixelFormat=pf32bit;
xs=lcd.xs*sz;
ys=lcd.ys*sz;
bmp->SetSize(xs,ys);
ClientWidth=xs;
ClientHeight=ys;
int i,n;
n=xs; if (n<ys) n=ys;
psz=new int[n+1];
for (i=0;i<n;i++) psz[i]=i/sz; psz[n]=0;
}
//---------------------------------------------------------------------------
void __fastcall TMain::FormDestroy(TObject *Sender)
{
delete[] psz;
delete bmp;
}
//---------------------------------------------------------------------------
void __fastcall TMain::tim_redrawTimer(TObject *Sender)
{
draw();
}
//---------------------------------------------------------------------------
//------------------------------------------------------------------------------------------
//--- SSD1306 I2C OLED LCD driver ver 2.001 ------------------------------------------------
//------------------------------------------------------------------------------------------
/*
[Notes]
+ I2C transfere size is not limitted on LCD side
- No memory address reset command present so any corruption while VRAM transfere permanently damages output untill power on/off but somehow after some timeout or overflow the adress is reseted
- UC3 HW I2C limits up to 255 bytes per packet
- UC3 glitch on SDA before ACK which confuse LCD to read instead of write operation and do not ACK
*/
#ifndef _LCD_SSD1306_I2C_h
#define _LCD_SSD1306_I2C_h
//------------------------------------------------------------------------------------------
#define SSD1306_SETCONTRAST 0x81
#define SSD1306_DISPLAYALLON_RESUME 0xA4
#define SSD1306_DISPLAYALLON 0xA5
#define SSD1306_NORMALDISPLAY 0xA6
#define SSD1306_INVERTDISPLAY 0xA7
#define SSD1306_DISPLAYOFF 0xAE
#define SSD1306_DISPLAYON 0xAF
#define SSD1306_SETDISPLAYOFFSET 0xD3
#define SSD1306_SETCOMPINS 0xDA
#define SSD1306_SETVCOMDETECT 0xDB
#define SSD1306_SETDISPLAYCLOCKDIV 0xD5
#define SSD1306_SETPRECHARGE 0xD9
#define SSD1306_SETMULTIPLEX 0xA8
#define SSD1306_SETLOWCOLUMN 0x00
#define SSD1306_SETHIGHCOLUMN 0x10
#define SSD1306_SETSTARTLINE 0x40
#define SSD1306_MEMORYMODE 0x20
#define SSD1306_COLUMNADDR 0x21
#define SSD1306_PAGEADDR 0x22
#define SSD1306_COMSCANINC 0xC0
#define SSD1306_COMSCANDEC 0xC8
#define SSD1306_SEGREMAP 0xA0
#define SSD1306_CHARGEPUMP 0x8D
#define SSD1306_SWITCHCAPVCC 0x2
// Scrolling #defines
#define SSD1306_ACTIVATE_SCROLL 0x2F
#define SSD1306_DEACTIVATE_SCROLL 0x2E
#define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3
#define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26
#define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27
#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29
#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A
//------------------------------------------------------------------------------------------
//#define I2C_send(adr,buf,siz){}
//------------------------------------------------------------------------------------------
#ifndef _brv8_tab
#define _brv8_tab
static const U8 brv8[256] = // 8 bit bit reversal
{
0,128,64,192,32,160,96,224,16,144,80,208,48,176,112,240,8,136,72,200,40,168,104,232,24,152,
88,216,56,184,120,248,4,132,68,196,36,164,100,228,20,148,84,212,52,180,116,244,12,140,76,204,
44,172,108,236,28,156,92,220,60,188,124,252,2,130,66,194,34,162,98,226,18,146,82,210,50,178,
114,242,10,138,74,202,42,170,106,234,26,154,90,218,58,186,122,250,6,134,70,198,38,166,102,230,
22,150,86,214,54,182,118,246,14,142,78,206,46,174,110,238,30,158,94,222,62,190,126,254,1,129,
65,193,33,161,97,225,17,145,81,209,49,177,113,241,9,137,73,201,41,169,105,233,25,153,89,217,57,
185,121,249,5,133,69,197,37,165,101,229,21,149,85,213,53,181,117,245,13,141,77,205,45,173,109,
237,29,157,93,221,61,189,125,253,3,131,67,195,35,163,99,227,19,147,83,211,51,179,115,243,11,139,
75,203,43,171,107,235,27,155,91,219,59,187,123,251,7,135,71,199,39,167,103,231,23,151,87,215,55,
183,119,247,15,143,79,207,47,175,111,239,31,159,95,223,63,191,127,255
};
#endif
static const U8 shade8x8[17*8]= // 17 shade patterns 8x8 pixels
{
0, 0, 0, 0, 0, 0, 0, 0,
17, 0, 0, 0, 17, 0, 0, 0,
17, 0, 68, 0, 17, 0, 68, 0,
68, 17, 68, 0, 68, 17, 68, 0,
85, 0,170, 0, 85, 0,170, 0,
68,170, 0,170, 68,170, 0,170,
68,170, 17,170, 68,170, 17,170,
85,136, 85,170, 85,136, 85,170,
85,170, 85,170, 85,170, 85,170,
85,238, 85,170,119,170, 85,170,
221,170,119,170,221,170,119,170,
221,170,255,170,221,170,255,170,
85,255,170,255, 85,255,170,255,
221,119,221,255,221,119,221,255,
119,255,221,255,119,255,221,255,
119,255,255,255,119,255,255,255,
255,255,255,255,255,255,255,255
};
//------------------------------------------------------------------------------------------
class LCD_SSD1306_I2C // max 96 lines
{
public:
// screen
int adr; // I2C adr
int xs,ys,sz; // resoluiton
U8 _scr[((128*96)>>3)+1]; // screen buffer
U8 *scr;
U8 *pyx[96]; // scan lines
// pattern
U32 pat,pat_m,pat_b; // binary pattern,max used bit mask,actual bit mask
// filling
int bufl[96];
int bufr[96];
// system api
void init(int _adr,int _xs,int _ys); // initialize LCD: I2C_adr,xs,ys
void _command(U8 cmd); // *internal* do not cal it (sends command to LCD over I2C)
void rfsscr(); // copy actual screen buffer to LCD (by I2C)
// gfx rendering col = <0,1>
void clrscr(); // clear screen buffer
void rotate(int ang); // rotate 180 deg
void pixel(int x,int y,bool col); // set/res pixel
bool pixel(int x,int y); // get pixel
void line(int x0,int y0,int x1,int y1,bool col); // line
void triangle(int x0,int y0,int x1,int y1,int x2,int y2,bool col);// triangle
void quad(int x0,int y0,int x1,int y1,int x2,int y2,int x3,int y3,bool col);
void rect(int x0,int y0,int x1,int y1,bool col); // rectangle using diagonal points
// patern rendering
void pat_set(char *s); // set binary pattern from bianry number string MSB renders first
void pat_beg(); // set pattern state to start of pattern
void pat_line(int x0,int y0,int x1,int y1,bool col);
void pat_triangle(int x0,int y0,int x1,int y1,int x2,int y2,bool col);
void pat_quad(int x0,int y0,int x1,int y1,int x2,int y2,int x3,int y3,bool col);
void pat_rect(int x0,int y0,int x1,int y1,bool col);
// filled polygons col = <0,255>
void _fill_line(int x0,int y0,int x1,int y1); // *internal* do not call it (render line into bufl/bufr)
void _fill(int Y0,int Y1,U8 col); // *internal* do not call it (render bufl/bufr onto screen)
void fill_triangle(int x0,int y0,int x1,int y1,int x2,int y2,U8 col);
void fill_quad(int x0,int y0,int x1,int y1,int x2,int y2,int x3,int y3,U8 col);
// text rendering
void prnchr(int x,int y,char c); // render char at x,y (y is rounded to multiple of 8)
void prntxt(int x,int y,const char *txt); // render text at x,y (y is rounded to multiple of 8)
};
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::_command(U8 cmd)
{
U8 buf[2]=
{
0x00, // 0x40 data/command
cmd,
};
I2C_send(adr,buf,2);
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::init(int _adr,int _xs,int _ys)
{
int y;
adr=_adr;
xs=_xs;
ys=_ys;
sz=xs*(ys>>3);
const bool _external_Vcc=false;
// VRAM buffer
scr=_scr+1; // skip first Byte (VRAM/command selection)
for (y=0;y<ys;y++) pyx[y]=scr+((y>>3)*xs); // scanlines for fast direct pixel access
clrscr();
// Init sequence
U8 comPins = 0x02;
U8 contrast = 0x8F;
if((xs == 128) && (ys == 32)) { comPins = 0x02; contrast = 0x8F; }
else if((xs == 128) && (ys == 64)) { comPins = 0x12; contrast = (_external_Vcc) ? 0x9F : 0xCF; }
else if((xs == 96) && (ys == 16)) { comPins = 0x02; contrast = (_external_Vcc) ? 0x10 : 0xAF; }
else {} // Other screens
static U8 init0[27]=
{
0x00, // commands
SSD1306_DISPLAYOFF, // 0xAE
SSD1306_SETDISPLAYCLOCKDIV,0x80, // 0xD5
SSD1306_SETMULTIPLEX,ys-1, // 0xA8
SSD1306_SETDISPLAYOFFSET,0x00, // 0xD3 no offset
SSD1306_SETSTARTLINE | 0x0, // line 0
SSD1306_CHARGEPUMP,(_external_Vcc)?0x10:0x14, // 0x8D
SSD1306_MEMORYMODE,0x00, // 0x20 horizontal (scanlines)
SSD1306_SEGREMAP | 0x1,
SSD1306_COMSCANDEC,
SSD1306_SETCOMPINS,comPins,
SSD1306_SETCONTRAST,contrast,
SSD1306_SETPRECHARGE,(_external_Vcc)?0x22:0xF1, // 0xd9
SSD1306_SETVCOMDETECT,0x40, // 0xDB
SSD1306_DISPLAYALLON_RESUME, // 0xA4
SSD1306_NORMALDISPLAY, // 0xA6
SSD1306_DEACTIVATE_SCROLL,
SSD1306_DISPLAYON, // Main screen turn on
};
I2C_send(adr,init0,sizeof(init0));
// init default pattern
pat_set("111100100");
// clear filling buffers
for (y=0;y<96;y++)
{
bufl[y]=-1;
bufr[y]=-1;
}
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::clrscr()
{
for (int a=0;a<sz;a++) scr[a]=0x00;
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::rotate(int ang)
{
U8 a0,a1;
int x0,y0,x1,y1;
if (ang==180)
for (y0=0,y1=ys-8;y0<y1;y0+=8,y1-=8)
for (x0=0,x1=xs-1;x0<xs;x0++,x1--)
{
a0=brv8[pyx[y0][x0]];
a1=brv8[pyx[y1][x1]];
pyx[y0][x0]=a1;
pyx[y1][x1]=a0;
}
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::rfsscr()
{
static const U8 init1[] =
{
0x00, // commands
SSD1306_MEMORYMODE,0, // horizontal addresing mode
SSD1306_COLUMNADDR,0,xs-1, // Column start/end address (0/127 reset)
SSD1306_PAGEADDR,0,(ys>>3)-1, // Page start/end address (0 reset)
};
I2C_send(adr,(U8*)init1,sizeof(init1));
_scr[0]=0x40; // 0x40 VRAM
// SW I2C can pass whole VRAM in single packet
// I2C_send(adr,_scr,sz+1);
// HW I2C must use packets up to 255 bytes so 128+1 (as TWIM0 on UC3 has 8 bit counter)
int i,n=128; U8 *p=_scr,a;
for (i=0;i<sz;i+=n){ a=p[0]; p[0]=0x40; I2C_send(adr,p,n+1); p[0]=a; p+=n; }
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::pixel(int x, int y,bool col)
{
// clip to screen
if ((x<0)||(x>=xs)||(y<0)||(y>=ys)) return;
// add or remove bit
if (col) pyx[y][x] |= (1<<(y&7));
else pyx[y][x] &= (255)^(1<<(y&7));
}
//------------------------------------------------------------------------------------------
bool LCD_SSD1306_I2C::pixel(int x, int y)
{
// clip to screen
if ((x<0)||(x>=xs)||(y<0)||(y>=ys)) return false;
// get bit
return ((pyx[y][x]>>(y&7))&1);
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::line(int x0,int y0,int x1,int y1,bool col)
{
int i,n,cx,cy,sx,sy;
// line DDA parameters
x1-=x0; sx=0; if (x1>0) sx=+1; if (x1<0) { sx=-1; x1=-x1; } if (x1) x1++; n=x1;
y1-=y0; sy=0; if (y1>0) sy=+1; if (y1<0) { sy=-1; y1=-y1; } if (y1) y1++; if (n<y1) n=y1;
// single pixel (not a line)
if (!n){ pixel(x0,y0,col); return; }
// ND DDA algo i is parameter
for (cx=cy=n,i=0;i<n;i++)
{
pixel(x0,y0,col);
cx-=x1; if (cx<=0){ cx+=n; x0+=sx; }
cy-=y1; if (cy<=0){ cy+=n; y0+=sy; }
}
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::pat_line(int x0,int y0,int x1,int y1,bool col)
{
bool ccc;
int i,n,cx,cy,sx,sy;
// line DDA parameters
x1-=x0; sx=0; if (x1>0) sx=+1; if (x1<0) { sx=-1; x1=-x1; } if (x1) x1++; n=x1;
y1-=y0; sy=0; if (y1>0) sy=+1; if (y1<0) { sy=-1; y1=-y1; } if (y1) y1++; if (n<y1) n=y1;
// single pixel (not a line)
if (!n){ pixel(x0,y0,col); return; }
// ND DDA algo i is parameter
for (cx=cy=n,i=0;i<n;i++)
{
ccc=(pat&pat_b); ccc^=(!col);
pat_b>>=1; if (!pat_b) pat_b=pat_m;
pixel(x0,y0,ccc);
cx-=x1; if (cx<=0){ cx+=n; x0+=sx; }
cy-=y1; if (cy<=0){ cy+=n; y0+=sy; }
}
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::_fill_line(int x0,int y0,int x1,int y1)
{
int i,n,cx,cy,sx,sy,*buf;
// line DDA parameters
x1-=x0; sx=0; if (x1>0) sx=+1; if (x1<0) { sx=-1; x1=-x1; } if (x1) x1++; n=x1;
y1-=y0; sy=0; if (y1>0) sy=+1; if (y1<0) { sy=-1; y1=-y1; } if (y1) y1++; if (n<y1) n=y1;
// single pixel (not a line)
if (!n)
{
if ((y0>=0)&&(y0<ys))
{
bufl[y0]=x0;
bufr[y0]=x0;
}
return;
}
// target buffer depend on y direction
if (sy>0) buf=bufl; else buf=bufr;
// ND DDA algo i is parameter
for (cx=cy=n,i=0;i<n;i++)
{
if ((y0>=0)&&(y0<ys)) buf[y0]=x0;
cx-=x1; if (cx<=0){ cx+=n; x0+=sx; }
cy-=y1; if (cy<=0){ cy+=n; y0+=sy; }
}
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::_fill(int Y0,int Y1,U8 col)
{
U8 shd;
int x,y,X0,X1,i;
// select shade pattern
i=col;
if (i< 0) i=0;
if (i>17) i=17;
i<<=3;
// fill horizontal lines
for (y=Y0;y<=Y1;y++)
{
shd=shade8x8[i+(y&7)];
// x range to render
X0=bufl[y];
X1=bufr[y];
if (X0>X1){ x=X0; X0=X1; X1=x; }
// clip to screen in y axis
if ((X1<0)||(X0>=xs)) continue;
if (X0< 0) X0= 0;
if (X1>=xs) X1=xs-1;
if (col== 0) for (x=X0;x<=X1;x++) pixel(x,y,0);
else if (col==255) for (x=X0;x<=X1;x++) pixel(x,y,1);
else for (x=X0;x<=X1;x++) pixel(x,y,shd&(1<<(x&7)));
}
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::triangle(int x0,int y0,int x1,int y1,int x2,int y2,bool col)
{
line(x0,y0,x1,y1,col);
line(x1,y1,x2,y2,col);
line(x2,y2,x0,y0,col);
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::pat_triangle(int x0,int y0,int x1,int y1,int x2,int y2,bool col)
{
pat_line(x0,y0,x1,y1,col);
pat_line(x1,y1,x2,y2,col);
pat_line(x2,y2,x0,y0,col);
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::fill_triangle(int x0,int y0,int x1,int y1,int x2,int y2,U8 col)
{
int y,Y0,Y1;
// y range to render
Y0=Y1=y0;
if (Y0>y1) Y0=y1;
if (Y1<y1) Y1=y1;
if (Y0>y2) Y0=y2;
if (Y1<y2) Y1=y2;
// clip to screen in y axis
if ((Y1<0)||(Y0>=ys)) return;
if (Y0< 0) Y0= 0;
if (Y1>=ys) Y1=ys-1;
// clear buffers
for (y=Y0;y<=Y1;y++)
{
bufl[y]=xs;
bufr[y]=-1;
}
// render circumference
_fill_line(x0,y0,x1,y1);
_fill_line(x1,y1,x2,y2);
_fill_line(x2,y2,x0,y0);
// fill horizontal lines
_fill(Y0,Y1,col);
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::quad(int x0,int y0,int x1,int y1,int x2,int y2,int x3,int y3,bool col)
{
line(x0,y0,x1,y1,col);
line(x1,y1,x2,y2,col);
line(x2,y2,x3,y3,col);
line(x3,y3,x0,y0,col);
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::pat_quad(int x0,int y0,int x1,int y1,int x2,int y2,int x3,int y3,bool col)
{
pat_line(x0,y0,x1,y1,col);
pat_line(x1,y1,x2,y2,col);
pat_line(x2,y2,x3,y3,col);
pat_line(x3,y3,x0,y0,col);
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::fill_quad(int x0,int y0,int x1,int y1,int x2,int y2,int x3,int y3,U8 col)
{
int y,Y0,Y1;
// y range to render
Y0=Y1=y0;
if (Y0>y1) Y0=y1;
if (Y1<y1) Y1=y1;
if (Y0>y2) Y0=y2;
if (Y1<y2) Y1=y2;
if (Y0>y3) Y0=y3;
if (Y1<y3) Y1=y3;
// clip to screen in y axis
if ((Y1<0)||(Y0>=ys)) return;
if (Y0< 0) Y0= 0;
if (Y1>=ys) Y1=ys-1;
// clear buffers
for (y=Y0;y<=Y1;y++)
{
bufl[y]=xs;
bufr[y]=-1;
}
// render circumference
_fill_line(x0,y0,x1,y1);
_fill_line(x1,y1,x2,y2);
_fill_line(x2,y2,x3,y3);
_fill_line(x3,y3,x0,y0);
// fill horizontal lines
_fill(Y0,Y1,col);
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::rect(int x0,int y0,int x1,int y1,bool col)
{
line(x0,y0,x1,y0,col);
line(x1,y0,x1,y1,col);
line(x1,y1,x0,y1,col);
line(x0,y1,x0,y0,col);
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::pat_rect(int x0,int y0,int x1,int y1,bool col)
{
pat_line(x0,y0,x1,y0,col);
pat_line(x1,y0,x1,y1,col);
pat_line(x1,y1,x0,y1,col);
pat_line(x0,y1,x0,y0,col);
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::prnchr(int x,int y,char c)
{
y&=0xFFFFFFF8; // multiple of 8
if ((y<0)||(y>ys-8)) return;
int i,a;
a=c; a<<=3;
for (i=0;i<8;i++,x++,a++)
if ((x>=0)&&(x<xs))
pyx[y][x]=font[a];
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::prntxt(int x,int y,const char *txt)
{
for (;*txt;txt++,x+=8) prnchr(x,y,*txt);
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::pat_set(char *s)
{
int i=1;
pat=0;
if (s!=NULL)
for (i=0;(*s)&&(i<32);s++,i++)
{
pat<<=1;
if (*s=='1') pat|=1;
}
if (!i) i=1;
pat_m=1<<(i-1);
pat_beg();
}
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::pat_beg()
{
pat_b=pat_m;
}
//------------------------------------------------------------------------------------------
#undef I2C_send
//------------------------------------------------------------------------------------------
#endif
//------------------------------------------------------------------------------------------
void LCD_SSD1306_I2C::_fill(int Y0,int Y1,U8 col)
{
int x,y,X0,X1,i;
const U8 colmax=17;
// bayer like dithering using precomputed shader patterns
if (dither_mode==0)
{
U8 shd;
// select shade pattern
i=col;
if (i< 0) i=0;
if (i>17) i=17;
i<<=3;
// fill horizontal lines
for (y=Y0;y<=Y1;y++)
{
shd=shade8x8[i+(y&7)];
// x range to render
X0=bufl[y];
X1=bufr[y];
if (X0>X1){ x=X0; X0=X1; X1=x; }
// clip to screen in y axis
if ((X1<0)||(X0>=xs)) continue;
if (X0< 0) X0= 0;
if (X1>=xs) X1=xs-1;
if (col== 0) for (x=X0;x<=X1;x++) pixel(x,y,0);
else if (col==colmax) for (x=X0;x<=X1;x++) pixel(x,y,1);
else for (x=X0;x<=X1;x++) pixel(x,y,shd&(1<<(x&7)));
}
}
// Floyd–Steinberg dithering
if (dither_mode==1)
{
int c0,c1,c2,rows[256+4],*r0=rows+1,*r1=rows+128+3,*rr,thr=colmax/2;
// clear error;
c0=0; for (x=0;x<256+4;x++) rows[x]=0;
// fill horizontal lines
for (y=Y0;y<=Y1;y++)
{
// x range to render
X0=bufl[y];
X1=bufr[y];
if (X0>X1){ x=X0; X0=X1; X1=x; }
// clip to screen in y axis
if ((X1<0)||(X0>=xs)) continue;
if (X0< 0) X0= 0;
if (X1>=xs) X1=xs-1;
if (col== 0) for (x=X0;x<=X1;x++) pixel(x,y,0);
else if (col==colmax) for (x=X0;x<=X1;x++) pixel(x,y,1);
else for (x=X0;x<=X1;x++)
{
c0=col; c0+=r0[x]; ; r0[x]=0;
if (c0>thr){ pixel(x,y,1); c0-=colmax; }
else pixel(x,y,0);
c2=c0;
c1=(3*c0)/16; r1[x-1] =c1; c2-=c1;
c1=(5*c0)/16; r1[x ] =c1; c2-=c1;
c1=( c0)/16; r1[x+1] =c1; c2-=c1;
r0[x+1]+=c2;
}
rr=r0; r0=r1; r1=rr;
}
}
}