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)를 통해 윈도우 화면 미러링 방법

쉽게 설명한 파티클 필터(particle filter) 동작 원리와 예제

base64 인코딩 디코딩 예제 c 소스

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