sh1106 i2c oled linux용 드라이버 c 소스
본 글은 이 sh1106를 linux 사용자 모드에서 구동하는 c 소스를 소개한다.
sh1106은 i2c로 연결되고 단색이지만 사용하기 간편하고 간단한 ui를 구현하기
좋다. 아두이노등에서 많이들 사용하는 것으로 알고 있다.
드라이버 c 소스는 아래와 같다. 사용 예는 아래 main함수를 보면 알 수 있다.
Screen 버퍼에 픽셀을 그리고, sh1106_update함수를 통해 버퍼의 내용을 sh1106 화면에 뿌려주는 구조이다.

드라이버 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;
}
[I2C 관련 포스트]
댓글
댓글 쓰기