#include #include #include #include #include #include #include #include #include #include "gpio.h" #include "lcd.h" #include "elog.h" #define USE_FB #define BK_GPIO 98 //PD2 #define DC_GPIO 99 //PD3, 96+3=99 #define RST_GPIO 100 //PD4, 96+4=100 #if 1 #define LOGD log_d #define LOGE log_e #else #define LOGD printf #define LOGE printf #endif #define BIT(x) (1<>8))) #ifdef USE_FB #define DEV_PATH "/dev/fb0" typedef struct fb_fix_screeninfo fb_finfo_t; typedef struct fb_var_screeninfo fb_vinfo_t; #else #define DEV_PATH "/dev/spidev1.0" #endif typedef struct { int x; int y; int w; int h; }lcd_rect_t; typedef struct { int width; int height; int color; int rotate; int fd; #ifdef USE_FB uint16_t *buf; #else int io; uint8_t *buf; #endif int buflen; }lcd_handle_t; static lcd_handle_t lcdHandle={0}; static int ssd1309_init(lcd_handle_t *h); static void ssd1309_refresh(lcd_handle_t *h); static int lcd_hw_init(lcd_handle_t *h) { h->fd = open(DEV_PATH, O_RDWR); if (h->fd < 0) { LOGE("___ open %s failed\n", DEV_PATH); return -1; } #ifdef USE_FB fb_vinfo_t vinfo; if (ioctl(h->fd, FBIOGET_VSCREENINFO, &vinfo)) { LOGE("___ get screen info failed\n"); return -1; } h->buflen = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8; h->buf = (uint16_t *)mmap(0, h->buflen, PROT_READ | PROT_WRITE, MAP_SHARED, h->fd, 0); if ((int)h->buf == -1) { LOGE("Error: failed to map framebuffer device to memory\n"); return -1; } memset(h->buf, 0, h->buflen); #else h->io = gpio_init(DC_GPIO, GPIO_OUT); h->buflen = h->width*h->height/8; h->buf = (uint8_t*)calloc(1, h->buflen); if (!h->buf) { LOGE("malloc buffer falied\n"); return -1; } #if 0 uint8_t mode = SPI_MODE_0; if (ioctl(fd, SPI_IOC_WR_MODE, &mode) == -1) { LOGE("Can't set SPI mode\n"); return -1; } uint32_t speed = 8000000; if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) == -1) { LOGE("Can't set max speed\n"); return -1; } #endif ssd1309_init(h); #endif return 0; } static int lcd_hw_write(lcd_handle_t *h, void *data, int len) { int r; struct spi_ioc_transfer tr = { .tx_buf = (unsigned long)data, .rx_buf = 0, .len = len, .speed_hz = 8000000, .bits_per_word = 8, }; r = ioctl(h->fd, SPI_IOC_MESSAGE(1), &tr); if (r < 1) { LOGE("Can't send spi message"); } return 0; } static void lcd_write_reg(lcd_handle_t *h, uint8_t reg) { #ifndef USE_FB gpio_set(h->io, 0); lcd_hw_write(h, ®, 1); #endif } static int ssd1309_reset(void) { int fd = gpio_init(RST_GPIO, GPIO_OUT); gpio_set(fd, 0); usleep(100*1000); gpio_set(fd, 1); gpio_deinit(fd); return 0; } static void ssd1309_on(lcd_handle_t *h, int flag) { if(flag) { lcd_write_reg(h,0x8D); lcd_write_reg(h,0x14); lcd_write_reg(h,0xAF); } else { lcd_write_reg(h,0x8D); lcd_write_reg(h,0x10); lcd_write_reg(h,0xAE); } } static int ssd1309_init(lcd_handle_t *h) { ssd1309_reset(); lcd_write_reg(h,0xFD); lcd_write_reg(h,0x12); lcd_write_reg(h,0xAE);//--turn off oled panel lcd_write_reg(h,0xd5);//--set display clock divide ratio/oscillator frequency lcd_write_reg(h,0xA0); lcd_write_reg(h,0xA8);//--set multiplex ratio(1 to 64) lcd_write_reg(h,0x3f);//--1/64 duty lcd_write_reg(h,0xD3);//-set display offset Shift Mapping RAM Counter (0x00~0x3F) lcd_write_reg(h,0x00);//-not offset lcd_write_reg(h,0x40);//--set start line address Set Mapping RAM Display Start Line (0x00~0x3F) lcd_write_reg(h,0xA1);//--Set SEG/Column Mapping 0xa0左右反置 0xa1正常 lcd_write_reg(h,0xC8);//Set COM/Row Scan Direction 0xc0上下反置 0xc8正常 lcd_write_reg(h,0xDA);//--set com pins hardware configuration lcd_write_reg(h,0x12); lcd_write_reg(h,0x81);//--set contrast control register lcd_write_reg(h,0x7F);// Set SEG Output Current Brightness lcd_write_reg(h,0xD9);//--set pre-charge period lcd_write_reg(h,0x82);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock lcd_write_reg(h,0xDB);//--set vcomh lcd_write_reg(h,0x34);//Set VCOM Deselect Level lcd_write_reg(h,0xA4);// Disable Entire Display On (0xa4/0xa5) lcd_write_reg(h,0xA6);// Disable Inverse Display On (0xa6/a7) ssd1309_refresh(h); ssd1309_on(h,1); return 0; } static void ssd1309_refresh(lcd_handle_t *h) { #ifndef USE_FB int i,n,offset; for(i=0;i<8;i++) { lcd_write_reg(h,0xb0+i); //设置行起始地址 lcd_write_reg(h,0x00); //设置低列起始地址 lcd_write_reg(h,0x10); //设置高列起始地址 gpio_set(h->io, 1); offset = i*128; for(n=0;n<128;n++) { lcd_hw_write(h, &h->buf[offset+n], 1); } } #endif } static int lcd_hw_deinit(lcd_handle_t *h) { #ifdef USE_FB munmap(h->buf, h->buflen); #else gpio_deinit(h->io); free(h->buf); #endif close(h->fd); return 0; } static inline int set_point(lcd_handle_t *h, int x, int y, int data) { int pos,bit; if(h->rotate==180) { x = h->width-1-x; y = h->height-1-y; } #ifdef USE_FB h->buf[y*h->width+x] = (uint16_t)data; #else pos = y / 8; /* get y page */ bit = y % 8; /* get y point */ if (data) { /* if 1 */ h->buf[pos*h->width+x] |= BIT(bit); /* set 1 */ } else { h->buf[pos*h->width+x] &= ~BIT(bit); /* set 0 */ } #endif return 0; } static int get_color_bits(int color) { if(color==COLOR_WB) { return 1; } else if(color==COLOR_RGB8) { return 8; } else if(color==COLOR_RGB565) { return 16; } else { return 32; } } //////////////////////////////////////////////////////////// void lcd_init(int width, int height, int color) { lcd_handle_t *h=&lcdHandle; h->width = width; h->height = height; h->color = color; h->rotate = 0; lcd_hw_init(h); } void lcd_deinit(void) { lcd_handle_t *h=&lcdHandle; lcd_hw_deinit(h); } void lcd_rotate(int rotate) { lcd_handle_t *h=&lcdHandle; if(rotate==0 || rotate==180) { h->rotate = rotate; } } int lcd_get_rotate(void) { lcd_handle_t *h=&lcdHandle; return h->rotate; } void lcd_set_backlight(int on) { lcd_handle_t *h=&lcdHandle; } void lcd_fill(int color) { lcd_handle_t *h=&lcdHandle; lcd_fill_rect(0, 0, h->width, h->height, color); } void lcd_draw_line(int x1, int y1, int x2, int y2, int color) { int incx = 0, incy = 0; int delta_x = 0, delta_y = 0; int distance = 0; int t = 0; int x = 0, y = 0; int x_temp = 0, y_temp = 0; lcd_handle_t *h=&lcdHandle; /* 画斜线(Bresenham算法) */ delta_x = x2 - x1; delta_y = y2 - y1; if(delta_x > 0) { //斜线(从左到右) incx = 1; } else if(delta_x == 0) { //垂直斜线(竖线) incx = 0; } else { //斜线(从右到左) incx = -1; delta_x = -delta_x; } if(delta_y > 0) { //斜线(从左到右) incy = 1; } else if(delta_y == 0) { //水平斜线(水平线) incy = 0; } else { //斜线(从右到左) incy = -1; delta_y = -delta_y; } /* 计算画笔打点距离(取两个间距中的最大值) */ if(delta_x > delta_y) { distance = delta_x; } else { distance = delta_y; } /* 开始打点 */ x = x1; y = y1; //第一个点无效,所以t的次数加一 for(t = 0; t <= distance + 1;t++) { set_point(h, x, y, color); /* 判断离实际值最近的像素点 */ x_temp += delta_x; if(x_temp > distance) { //x方向越界,减去距离值,为下一次检测做准备 x_temp -= distance; //在x方向递增打点 x += incx; } y_temp += delta_y; if(y_temp > distance) { //y方向越界,减去距离值,为下一次检测做准备 y_temp -= distance; //在y方向递增打点 y += incy; } } } void lcd_draw_rect(int x, int y, int w, int h, int color) { int x2,y2; x2 = x + w; y2 = y + h; lcd_draw_line(x,y,x2,y,color); lcd_draw_line(x,y,x,y2,color); lcd_draw_line(x,y2,x2,y2,color); lcd_draw_line(x,y,x2,y2,color); } void lcd_fill_rect(int x, int y, int w, int h, int color) { int i,j; lcd_handle_t *h2=&lcdHandle; for(i=x; ifdata.height) { if(vert==VERTICAL_CENTER) { y0 += (h-fdata.height)/2; } else if (vert==VERTICAL_BOTTOM) { y0 += h-fdata.height; } y = y0; } for(i=0; iwidth) && (yheight)) { if(tmp&0x01) { set_point(hd, x, y, color); } else { set_point(hd, x, y, bgcolor); } } tmp >>= 1; y++; if((y-y0)>=fdata.height) { y=y0; x++; break; } } } return fdata.width; } void lcd_draw_string(int x, int y, int w, int h, char *str, int font, int color, int bgcolor, int hori, int vert) { int r,sw,xmax=x+w; lcd_rect_t rect; uint8_t *pstr=(uint8_t*)str; lcd_handle_t *h2=&lcdHandle; sw = font_width_str(font, pstr); if(sw>w) { sw = w; } if(hori==HORIZONTAL_LEFT) { rect.x = x; } else if(hori==HORIZONTAL_RIGHT) { rect.x = x+w-sw; } else if(hori==HORIZONTAL_CENTER) { rect.x = x+(w-sw)/2; } else { return; } if(rect.x+sw>=h2->width) { rect.w = h2->width-rect.x; } else { rect.w = sw; } rect.y = y; rect.h = h; while(*pstr) { r = lcd_draw_char(h2, rect.x, rect.y, rect.w, rect.h, pstr, font, color, bgcolor, vert); rect.x += r; pstr = font_next(pstr); if(!pstr || x>=xmax) { break; } } }