一般我们绘制oLED时,都会考虑在内存中维护一个相同尺寸的缓存,在缓存上进行绘制,然后同步到oLED上。这里简单笔记下一些思路和代码。
假如我们需要绘制一个128*32大小的屏幕,每个像素点为单bit控制,那么我们可以建立一个如下buffer:
unsigned char oled_ram[128][4];
CONTENTS
绘制点
绘制点比较简单,直接找到对应的bit位置进行设置即可。
int oled_ram_point(int x, int y, oled_draw_mode_e mode) { if((x < 0)||(x >= 128)||(y < 0)||(y >= (4*8))) { return -1; } if(mode == OLED_DRAW_FILL) { oled_ram[x][y>>3] |= (1<<(y&0x7)); }else { oled_ram[x][y>>3] &= (0xFF-(1<<(y&0x7))); } return 0; }
绘制线
绘制水平和垂直线比较简单,但是如果要绘制任意角度,就比较复杂了。这里笔记一种思路:
假如我们要绘制一条任意角度的线,
1 计算起点(x1,y1)和终点(x2,y2)的水平间距x_diff(x1 – x2)和垂直间距y_diff(y1 – y2)。
2 如果一个点(x,y)在该线上,那么肯定有 (x – x1)/(y – y1) = x_diff / y_diff。
3 假如x_diff和y_diff的最大公约数为m,即
x_diff = x_diff_uint * m; y_diff = y_diff_uint * m;
那么,对于任一点,只要
x = x1 – x_diff_uint * n; y = y1 – y_diff_uint * n; 0=<n<=m
那么该点(x,y)就会在该线上。
static int divisor(int a, int b) { int temp; //比较两个数的大小,值大的数为a,值小的数为b if (a < b) { temp = a; a = b; b = temp; } //求余 while (b != 0) { temp = a % b; a = b; b = temp; } return a; } int oled_ram_line(int x1, int y1, int x2, int y2, oled_draw_mode_e mode) { int x_diff = x1 - x2; int y_diff = y1 - y2; int node_cnt = 0; if(x_diff == 0) { node_cnt = y1 > y2 ? (y1-y2):(y2-y1); }else if(y_diff == 0) { node_cnt = x1 > x2 ? (x1-x2):(x2-x1); }else { node_cnt = divisor(x_diff, y_diff); node_cnt = (node_cnt >= 0 ? node_cnt : (0 - node_cnt)) ; } x_diff = x_diff / node_cnt; y_diff = y_diff / node_cnt; int index = 0; for (index = 0; index <= node_cnt; index++) { oled_ram_point(x1 - index*x_diff, y1 - index*y_diff, mode); } return 0; }
绘制圆形
绘制圆形比较简单,这里用一张图片来显示思路,如下图,考虑到圆的对称性,对于任意一个在圆上的点(黄色),都有另外7个点也在圆上(蓝色)。因此我们只要找到45度内的点就可以了。从红色点(a,b)开始,沿着绿色箭头方向进行偏移,一直到达绿色点,符合条件的点就可以绘制为圆形。
int oled_ram_circle(int x, int y, int radius, oled_draw_mode_e mode) { int a = 0; int b = radius; while(2 * b * b >= radius * radius) { if(a*a + b*b >= radius*radius) { oled_ram_point(x + a, y - b, mode); oled_ram_point(x - a, y - b, mode); oled_ram_point(x + a, y + b, mode); oled_ram_point(x - a, y + b, mode); oled_ram_point(x + b, y + a, mode); oled_ram_point(x + b, y - a, mode); oled_ram_point(x - b, y - a, mode); oled_ram_point(x - b, y + a, mode); b--; }else { a++; } } return 0; }
绘制ASCII
ASCII码有很多取模方式,这里简单起见,以 阴码 行列式 低位在前的方式取模,比如一个16*8的字A,在字库数组中,A使用一个16个字节长度的数组来表示,数组索引如下图:
代码如下:
int oled_ram_ascii(int x, int y, unsigned char ascii_value, oled_ascii_size_e ascii_size, oled_draw_mode_e mode) { int asc_hight = 0; int asc_width = 0; unsigned char *p_asc_buf = NULL; ascii_value = ascii_value - ' '; switch (ascii_size) { default: case OLED_ASCII_8x6: { asc_hight = 8; asc_width = 6; p_asc_buf = asc2_0806[ascii_value]; break; } case OLED_ASCII_12x6: { asc_hight = 12; asc_width = 6; p_asc_buf = asc2_1206[ascii_value]; break; } case OLED_ASCII_16x8: { asc_hight = 16; asc_width = 8; p_asc_buf = asc2_1608[ascii_value]; break; } case OLED_ASCII_24x12: { asc_hight = 24; asc_width = 12; p_asc_buf = asc2_2412[ascii_value]; break; } } int start_x = 0; int start_y = 0; for(start_x = 0; start_x < asc_width; start_x++) { for(start_y = 0; start_y < asc_hight; start_y++) { if((p_asc_buf[start_x + asc_width * (start_y >> 3)] >> (start_y & 0x07)) & 0x01 == 1) { oled_ram_point(x + start_x, y+ start_y, mode); }else { oled_ram_point(x + start_x, y+ start_y, OLED_DRAW_FILL - mode); } } } return 0; } int oled_ram_str(int x, int y, char *p_str, oled_ascii_size_e str_size, oled_draw_mode_e mode) { int asc_width = 0; switch (str_size) { default: case OLED_ASCII_8x6: { asc_width = 6; break; } case OLED_ASCII_12x6: { asc_width = 6; break; } case OLED_ASCII_16x8: { asc_width = 8; break; } case OLED_ASCII_24x12: { asc_width = 12; break; } } int start_x = x; while (*p_str != 0) { oled_ram_ascii(start_x, y, *p_str, str_size, mode); start_x += asc_width; p_str++; } return 0; }
内存dump
为了方便调试,我们可以使用日志方式来模拟oLED输出,代码如下:
int oled_ram_dump(void) { INFO("--- oled ram memory ---"); int i,n,k; for(i=0; i<4; i++) { for(k = 0; k<8; k++) { RAW("<%2d>: ", i*8 + k); for(n=0; n<128; n++) { unsigned char value = oled_ram[n][i]; RAW("%c", ((value>>k) & 0x1) ? '*':' '); } RAW("\r\n"); } } RAW("\r\n\r\n"); return 0; }
打印输出如下:
< 0>: < 1>: < 2>: < 3>: < 4>: * * < 5>: ** *** * ** **** < 6>: * ** ** ** *** ** * < 7>: * ** ***** *** ** < 8>: ** ** ** * ** ** < 9>: ** ** ** * ** ** <10>: **** ******* ******** ***** **** ***** ** **** **** ***** **** ** * ** ** <11>: ** ** ** ** ** ** * ** ** ** * ** ** ** * ** ** * ** ** <12>: ** ** ** * ** *** ** ** ** * ** *** ** * ** ** <13>: ** ** ** ** ** ** ** * ** ** ** ** * ** ** <14>: ** *** ** ** ** ** ** ********* ** ** * ** ** <15>: ** ***** ** ** ** ***** ** ** ** ********** ** <16>: ** *** ** ** ** *** ** ** ** ** ** ** <17>: ** * * ** ** ** ** ** ** ** ** ** ** ** <18>: ** * * ** ** * ** ** ** ** ** * ** ** ** ** <19>: ** * ** ** ** * ** ** ** ** *** * ** ** ** ** * <20>: **** ******* **** ******** ******** **** **** **** ******** ******** ****** **** <21>: <22>: <23>: <24>: * * * * * *** ***** <25>: * * ** ** * * * <26>: *** *** *** * ** ** * * *** * ** * * * * ** * <27>: * * * ** * * * * * * ** * * * * * * * * <28>: * *** * * * ** ***** * * ***** ** * * <29>: * * * * * * * * * * * * * * * * <30>: *** **** ** * *** * * *** * *** * *** * <31>:
发表评论