sh1106 i2c oled linux용 드라이버 c 소스

본 글은 이 sh1106를 linux 사용자 모드에서 구동하는 c 소스를 소개한다.

sh1106은 i2c로 연결되고 단색이지만 사용하기 간편하고 간단한 ui를 구현하기 좋다. 아두이노등에서 많이들 사용하는 것으로 알고 있다.



드라이버 c 소스는 아래와 같다. 사용 예는 아래 main함수를 보면 알 수 있다.
Screen 버퍼에 픽셀을 그리고, sh1106_update함수를 통해 버퍼의 내용을 sh1106 화면에 뿌려주는 구조이다.

#define CMD_SETCONTRAST 0x81
#define CMD_DISPLAYALLON_RESUME 0xA4
#define CMD_DISPLAYALLON 0xA5
#define CMD_NORMALDISPLAY 0xA6
#define CMD_INVERTDISPLAY 0xA7
#define CMD_DISPLAYOFF 0xAE
#define CMD_DISPLAYON 0xAF
#define CMD_SETDISPLAYOFFSET 0xD3
#define CMD_SETCOMPINS 0xDA
#define CMD_SETVCOMDETECT 0xDB
#define CMD_SETDISPLAYCLOCKDIV 0xD5
#define CMD_SETPRECHARGE 0xD9
#define CMD_SETMULTIPLEX 0xA8
#define CMD_SETLOWCOLUMN 0x00
#define CMD_SETHIGHCOLUMN 0x10
#define CMD_SETSTARTLINE 0x40
#define CMD_MEMORYMODE 0x20
#define CMD_COMSCANINC 0xC0
#define CMD_COMSCANDEC 0xC8
#define CMD_SEGREMAP 0xA0
#define CMD_CHARGEPUMP 0x8D
#define CMD_PAGESTARTADDRESS 0xB0

typedef struct _sh1106dev_ctx_t
{
int fd;
int screen_w;
int screen_h;
int bpp;
char *screen_buffer;
char tx_buffer[256];
}sh1106dev_ctx_t;


int sh1106_write_cmd(sh1106dev_ctx_t *ctx, char cmd)
{
char buffer[2] = { 0, };
if (!ctx || ctx->fd <= 0) return -1;

buffer[0] = 0x00;
buffer[1] = cmd;
if (write(ctx->fd, buffer, 2) != 2)
{
return -1;
}

return 0;
}

int sh1106_write_data(sh1106dev_ctx_t *ctx, char *data, int len)
{
if (!ctx || ctx->fd <= 0 || !data || len > 255) return -1;

ctx->tx_buffer[0] = 0x40;
memcpy(&ctx->tx_buffer[1], data, sizeof(char)*len);
if (write(ctx->fd, ctx->tx_buffer, len + 1) != len + 1)
{
return -1;
}

return 0;
}


int sh1106_close(sh1106dev_ctx_t *ctx)
{
if (!ctx) return -1;

if (ctx->fd > 0)
{
close(ctx->fd);
ctx->fd = 0;
}

if (ctx->screen_buffer)
{
free(ctx->screen_buffer);
ctx->screen_buffer = NULL;
}

free(ctx);
ctx = NULL;

return 0;
}

sh1106dev_ctx_t *sh1106_open(char *dev, int addr)
{
sh1106dev_ctx_t *ctx = NULL;

if (!dev || addr <= 0) return NULL;

ctx = (sh1106dev_ctx_t*)malloc(sizeof(sh1106dev_ctx_t));
if (!ctx) return NULL;

memset(ctx, 0, sizeof(sh1106dev_ctx_t));

ctx->fd = open(dev, O_RDWR);
if (ctx->fd <= 0)
{
sh1106_close(ctx);
return NULL;
}

if (ioctl(ctx->fd, I2C_SLAVE, addr)<0)
{
sh1106_close(ctx);
return NULL;
}

ctx->screen_w = 128;
ctx->screen_h = 64;
ctx->bpp = 1;
ctx->screen_buffer = (char*)malloc(ctx->screen_w*ctx->screen_h*ctx->bpp / 8 * sizeof(char));
if (!ctx->screen_buffer)
{
sh1106_close(ctx);
return NULL;
}

// Init sequence for 128x64 OLED module
sh1106_write_cmd(ctx, CMD_DISPLAYOFF);

sh1106_write_cmd(ctx, CMD_SETDISPLAYCLOCKDIV);
sh1106_write_cmd(ctx, 0x80); // the suggested ratio 0x80

sh1106_write_cmd(ctx, CMD_SETMULTIPLEX);
sh1106_write_cmd(ctx, 0x3F);

sh1106_write_cmd(ctx, CMD_SETDISPLAYOFFSET);
sh1106_write_cmd(ctx, 0x0);                                    // no offset

sh1106_write_cmd(ctx, CMD_SETSTARTLINE | 0x0);            // line #0

sh1106_write_cmd(ctx, CMD_CHARGEPUMP);
sh1106_write_cmd(ctx, 0x14);

sh1106_write_cmd(ctx, CMD_MEMORYMODE);
sh1106_write_cmd(ctx, 0x00);

sh1106_write_cmd(ctx, CMD_SEGREMAP | 0x1);

sh1106_write_cmd(ctx, CMD_COMSCANDEC); // 하상.. 방향... // CMD_COMSCANDEC <--> CMD_COMSCANINC
sh1106_write_cmd(ctx, CMD_SETCOMPINS);
sh1106_write_cmd(ctx, 0x12);

sh1106_write_cmd(ctx, CMD_SETCONTRAST);
sh1106_write_cmd(ctx, 0xCF);

sh1106_write_cmd(ctx, CMD_SETPRECHARGE);
sh1106_write_cmd(ctx, 0xF1);

sh1106_write_cmd(ctx, CMD_SETVCOMDETECT);
sh1106_write_cmd(ctx, 0x40);

sh1106_write_cmd(ctx, 0x2e);//Deactivate scroll
sh1106_write_cmd(ctx, CMD_DISPLAYALLON_RESUME);
sh1106_write_cmd(ctx, CMD_NORMALDISPLAY);
sh1106_write_cmd(ctx, CMD_DISPLAYON); //--turn on oled panel

return ctx;
}

int sh1106_clear(sh1106dev_ctx_t *ctx)
{
if (!ctx) return -1;
if (ctx->fd <= 0 || !ctx->screen_buffer) return -1;

memset(ctx->screen_buffer, 0, ctx->screen_w*ctx->screen_h*ctx->bpp / 8 * sizeof(char));

return 0;
}

int sh1106_draw_pixel(sh1106dev_ctx_t *ctx, int x, int y, unsigned int color)
{
if (!ctx) return -1;
if (ctx->fd <= 0 || !ctx->screen_buffer) return -1;
if (x >= ctx->screen_w || y >= ctx->screen_h) return -1;

#if 0 // ROTATE LCD
x = ctx->screen_w - x - 1;
y = ctx->screen_h - y - 1;
#endif

if (color)
{
ctx->screen_buffer[x + (y / 8)*ctx->screen_w] |= (1 << (y % 8));
}
else
{
ctx->screen_buffer[x + (y / 8)*ctx->screen_w] &= ~(1 << (y % 8));
}

return 0;
}

int sh1106_update(sh1106dev_ctx_t *ctx)
{
unsigned char m = 0;
if (!ctx) return -1;
if (ctx->fd <= 0 || !ctx->screen_buffer) return -1;

sh1106_write_cmd(ctx, CMD_SETLOWCOLUMN | 0x0);
sh1106_write_cmd(ctx, CMD_SETHIGHCOLUMN | 0x0);
sh1106_write_cmd(ctx, CMD_SETSTARTLINE | 0x0); // line #0

for (m = 0; m<ctx->screen_h / 8; m++)
{
sh1106_write_cmd(ctx, CMD_PAGESTARTADDRESS + m);
sh1106_write_cmd(ctx, CMD_SETLOWCOLUMN | 0x2);
sh1106_write_cmd(ctx, CMD_SETHIGHCOLUMN | 0x0);

sh1106_write_data(ctx, &ctx->screen_buffer[m*ctx->screen_w], ctx->screen_w);
}

return 0;
}


int main(int argc, char *argv[])
{
int x = 0;
sh1106dev_ctx_t *sh1106 = sh1106_open("/dev/i2c-0", 0x3c);

if (sh1106)
{
// clear screen
sh1106_clear(sh1106);

// draw line
for (x = 0; x < sh1106->screen_w; x++)
{
sh1106_draw_pixel(sh1106, x, sh1106->screen_h / 2, 1);
}

sh1106_update(sh1106);
sh1106_close(sh1106);
}

return 0;
}



댓글

이 블로그의 인기 게시물

간단한 cfar 알고리즘에 대해

windows에서 간단하게 크롬캐스트(Chromecast)를 통해 윈도우 화면 미러링 방법

간단한 칼만 필터(Kalman Filter) 소스 코드와 사용 예제

안드로이드(android) 전체 화면 시계 앱(clock app) 예제 코드

mkfs.fat Device or resource busy 에러 해결법