1 前言 OLED 图形库众多,如Adafruit_GFX 和 Adafruit_SSD1306库。但是,今天要使用的是 U8g2图形库。
参考文章:深入学习Arduino u8g2 OLED库,一篇就够 参考文章:GitHub–>u8g2reference
1.1 U8g2库百度脑图
可以分为四大类:
基本函数
绘制相关函数
显示配置相关函数
缓存相关函数
2 U8g2库函数详解 2.1 基本函数(干货从这里开始) 2.1.1 u8g2_t u8g2;
2.1.2 u8g2Init(u8g2_t *u8g2) 1 2 u8g2_t u8g2; u8g2Init(&u8g2);
2.1.3 u8g2_InitDisplay(u8g2) 1 2 3 4 5 6 void u8g2Init (u8g2_t *u8g2) { u8g2_Setup_ssd1306_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_4wire_hw_spi, u8x8_stm32_gpio_and_delay); u8g2_InitDisplay(u8g2); u8g2_SetPowerSave(u8g2, 0 ); }
2.1.4 u8g2_SetPowerSave(u8g2, is_enable); 1 u8g2_SetPowerSave(&u8g2, 0 );
不管是启用还是禁用,显示器需要的内存消耗是不变的,说到底就是为了关闭屏幕,做到省电;
所以这里就可以理解为什么初始化需要 u8g2_SetPowerSave(u8g2, 0); 来开启显示
2.1.5 u8g2_ClearDisplay(u8g2_t *u8g2) 1 u8g2_ClearDisplay(&u8g2);
不要在 firstPage 和 nextPage 函数之间调用该方法。
2.1.6 u8g2_ClearBuffer(u8g2_t *u8g2) —— 清除缓冲区
一般这个函数是与u8g2_SendBuffer函数配对使用,通常用法如下:
1 2 3 4 5 void Buffer (u8g2_t *u8g2) { u8g2_ClearBuffer(u8g2); u8g2_SendBuffer(u8g2); }
2.2 绘制相关函数(进阶) 2.2.1 u8g2_DrawBox() —— 画实心矩形 函数说明:
1 2 3 4 5 6 7 8 void u8g2_DrawBox (u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h)
如果支持绘制颜色(也就是不是单色显示器),那么由**u8g2_SetDrawColor(u8g2_t *u8g2, uint8_t color)**设置;
示例:
1 u8g2_DrawBox(&u8g2,3 ,7 ,25 ,15 );
1 2 3 4 5 6 u8g2_FirstPage(&u8g2); do { u8g2_DrawBox(&u8g2,0 ,32 ,i++,15 ); HAL_Delay(50 ); }while (u8g2_NextPage(&u8g2));
2.2.2 u8g2_DrawCircle() —— 画空心圆 函数说明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void u8g2_DrawCircle (u8g2_t *u8g2, u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, uint8_t option)
1 u8g2_DrawCircle(&u8g2,20 ,25 ,10 ,U8G2_DRAW_ALL);
1 2 3 4 5 6 7 u8g2_FirstPage(&u8g2); do { u8g2_DrawCircle(&u8g2,63 ,31 ,i++,U8G2_DRAW_ALL ); HAL_Delay(50 ); }while (u8g2_NextPage(&u8g2));
2.2.3 u8g2_DrawDisc() —— 画实心圆 1 2 3 4 5 6 7 8 9 10 11 12 13 14 void u8g2_DrawDisc (u8g2_t *u8g2, u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, uint8_t option)
1 2 3 4 5 6 u8g2_FirstPage(&u8g2); do { u8g2_DrawDisc(&u8g2,63 ,31 ,i++,U8G2_DRAW_UPPER_RIGHT|U8G2_DRAW_LOWER_LEFT); HAL_Delay(50 ); }while (u8g2_NextPage(&u8g2));
2.2.4 u8g2_DrawEllipse() —— 画空心椭圆 函数说明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void u8g2_DrawEllipse (u8g2_t *u8g2, u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rx, u8g2_uint_t ry, uint8_t option)
rx*ry 在8位模式的u8g2必须小于512(博主也没有理解);
示例:
1 u8g2_DrawEllipse(&u8g2,20 ,25 ,15 ,10 ,U8G2_DRAW_ALL);
1 2 3 4 5 6 u8g2_FirstPage(&u8g2); do { u8g2_DrawEllipse(&u8g2,63 ,31 ,i++,30 ,U8G2_DRAW_UPPER_RIGHT|U8G2_DRAW_LOWER_LEFT); HAL_Delay(50 ); }while (u8g2_NextPage(&u8g2));
2.2.5 u8g2_DrawFilledEllipse() —— 画实心椭圆 2.2.6 u8g2_DrawFrame() —— 画空心矩形 函数说明:
1 2 3 4 5 6 7 8 void u8g2_DrawFrame (u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h)
如果支持绘制颜色(也就是不是单色显示器),那么由**u8g2_SetDrawColor(u8g2_t *u8g2, uint8_t color)**设置;
示例
1 u8g2_DrawFrame(&u8g2,3 ,7 ,25 ,15 );
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 u8g2_FirstPage(&u8g2); do { for (uint8_t i=0 ;i<=99 ;i=i+1 ) { u8g2_ClearBuffer(&u8g2); char buff[20 ]; u8g2_SetFont(&u8g2,u8g2_font_ncenB08_tf); sprintf (buff,"%d%%" ,(int )(i/100.0 *100 )); u8g2_DrawStr(&u8g2,105 ,49 ,buff); u8g2_DrawBox(&u8g2,2 ,40 ,i,10 ); u8g2_DrawFrame(&u8g2,0 ,38 ,103 ,14 ); HAL_Delay(200 ); u8g2_SendBuffer(&u8g2); } }while (u8g2_NextPage(&u8g2));
2.2.7 u8g2_DrawGlyph() —— 绘制字体字集的符号 函数说明:
1 2 3 4 5 6 7 8 u8g2_uint_t u8g2_DrawGlyph (u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, uint16_t encoding)
U8g2支持16位以内的unicode字符集,也就是说encoding的范围为0-65535,DrawGlyph方法只能绘制存在于所使用的字体字集中的unicode值;
这个绘制方法依赖于当前的字体模式和绘制颜色;
2.2.8 u8g2_DrawHLine() —— 绘制水平线 函数说明:
1 2 3 4 5 6 7 void u8g2_DrawHLine (u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t len)
如果支持绘制颜色(也就是不是单色显示器),那么由**u8g2_SetDrawColor(u8g2_t *u8g2, uint8_t color)**设置;
2.2.9 u8g2_DrawLine() —— 两点之间绘制线 函数说明:
1 2 3 4 5 6 7 8 void u8g2_DrawLine (u8g2_t *u8g2, u8g2_uint_t x1, u8g2_uint_t y1, u8g2_uint_t x2, u8g2_uint_t y2)
如果支持绘制颜色(也就是不是单色显示器),那么由**u8g2_SetDrawColor(u8g2_t *u8g2, uint8_t color)**设置;
示例:
1 u8g2_DrawLine(&u8g2,20 , 5 , 5 , 32 );
2.2.10 u8g2_DrawPixel() —— 绘制像素点 函数说明:
1 2 3 4 5 6 7 void u8g2_DrawPixel (u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y)
如果支持绘制颜色(也就是不是单色显示器),那么由**u8g2_SetDrawColor(u8g2_t *u8g2, uint8_t color)**设置;
你会发现很多绘制方法的底层都是调用**u8g2_DrawPixel()**,毕竟像素属于最小颗粒度;
我们可以利用这个绘制方法自定义自己的图形显示;
2.2.11 u8g2_DrawRBox() —— 绘制圆角实心方形 函数说明:
1 2 3 4 5 6 7 8 9 void u8g2_DrawRBox (u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h, u8g2_uint_t r)
如果支持绘制颜色(也就是不是单色显示器),那么由**u8g2_SetDrawColor(u8g2_t *u8g2, uint8_t color)**设置;
要求,w >= 2(r+1) 并且 h >= 2(r+1),这是显而易见的限制;
2.2.12 u8g2_DrawRFrame() —— 绘制圆角空心方形 函数说明:
1 2 3 4 5 6 7 8 9 void u8g2_DrawRFrame (u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h, u8g2_uint_t r)
如果支持绘制颜色(也就是不是单色显示器),那么由**u8g2_SetDrawColor(u8g2_t *u8g2, uint8_t color)**设置;
要求,w >= 2(r+1) 并且 h >= 2(r+1),这是显而易见的限制
示例:
1 u8g2_DrawRFrame(&u8g2,20 ,15 ,30 ,22 ,7 );
2.2.13 u8g2_DrawStr() —— 绘制字符串 函数说明:
1 2 3 4 5 6 7 8 u8g2_uint_t u8g2_DrawStr (u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, const char *str)
1 2 u8g2_SetFont(&u8g2,u8g2_font_ncenB14_tr); u8g2_DrawStr(&u8g2,0 ,15 ,"Hello World!" );
1 2 3 4 5 6 7 8 9 10 11 for (uint8_t i=0 ;i<100 ;i++){ u8g2_ClearBuffer(&u8g2); char buff[20 ]; sprintf (buff,"%d" ,(int )(i/100.0 *100 )); u8g2_SetFont(&u8g2,u8g2_font_inb24_mf); u8g2_DrawStr(&u8g2,127 -41 ,24 ,buff); u8g2_SendBuffer(&u8g2); HAL_Delay(100 ); }
2.2.14 u8g2_DrawTriangle() —— 绘制实心三角形 函数说明:
1 2 3 4 void u8g2_DrawTriangle (u8g2_t *u8g2, int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2)
1 2 3 4 5 6 7 8 for (uint8_t i=0 ;i<100 ;i++){ u8g2_ClearBuffer(&u8g2); u8g2_DrawTriangle(&u8g2,20 ,5 , 27 ,50 , i,32 ); HAL_Delay(100 ); u8g2_SendBuffer(&u8g2); }
2.2.15 u8g2_DrawUTF8() —— 绘制UTF8编码的字符 函数说明:
1 2 3 4 5 6 7 8 u8g2_uint_t u8g2_DrawUTF8 (u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, const char *str)
使用该方法,有两个前提。首先是你的编译器需要支持UTF-8编码,对于绝大部分Arduino板子已经支持;其次,显示的字符串需要存为“UTF-8”编码,Arduino IDE上默认支持;
该方法需要依赖于fontMode(setFont)以及drawing Color,也就是说如果你传进来的字符串编码必须在font定义里面;
Keil v5 mdk 编译UTF8字符串报错的解决办法–no-multibyte-chars
1 2 u8g2_SetFont(&u8g2,u8g2_font_unifont_t_symbols); u8g2_DrawUTF8(&u8g2,5 , 20 , "Snowman: ☃" );
2.2.16 u8g2_DrawVLine() —— 绘制竖直线 函数说明:
1 2 3 4 5 6 7 void u8g2_DrawVLine (u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t len)
2.2.17 u8g2_DrawXBM()/u8g2_DrawXBMP() —— 绘制图像 函数说明:
1 2 3 4 5 6 7 8 9 10 11 void u8g2_DrawXBM (u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h, const uint8_t *bitmap) void u8g2_DrawXBMP (u8g2_t *u8g2, u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h, const uint8_t *bitmap)
u8g2_DrawXBM 和u8g2_DrawXBMP 区别在于 XBMP 支持PROGMEM ;
2.2.18 u8g2_FirstPage()/u8g2_NextPage() —— 绘制命令 函数说明:
1 2 3 4 5 void u8g2_FirstPage (u8g2_t *u8g2) uint8_t u8g2_NextPage (u8g2_t *u8g2)
u8g2_FirstPage 方法会把当前页码位置变成0;
修改内容处于u8g2_FirstPage 和u8g2_NextPage 之间,每次都是重新渲染所有内容;
该方法消耗的ram空间,比u8g2_SendBuffer 消耗的RAM空间要少;
示例:
1 2 3 4 5 u8g2_FirstPage(&u8g2); do { u8g2_SetFont(&u8g2,u8g2_font_ncenB14_tr); u8g2_DrawStr(&u8g2,0 ,15 ,"Hello World!" ); } while (u8g2_NextPage(&u8g2));
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 32 void u8g2_FirstPage (u8g2_t *u8g2) { if ( u8g2->is_auto_page_clear ) { u8g2_ClearBuffer(u8g2); } u8g2_SetBufferCurrTileRow(u8g2, 0 ); } uint8_t u8g2_NextPage (u8g2_t *u8g2) { uint8_t row; u8g2_send_buffer(u8g2); row = u8g2->tile_curr_row; row += u8g2->tile_buf_height; if ( row >= u8g2_GetU8x8(u8g2)->display_info->tile_height ) { u8x8_RefreshDisplay( u8g2_GetU8x8(u8g2) ); return 0 ; } if ( u8g2->is_auto_page_clear ) { u8g2_ClearBuffer(u8g2); } u8g2_SetBufferCurrTileRow(u8g2, row); return 1 ; }
2.2.19 u8g2_SendBuffer() —— 绘制缓冲区的内容 函数说明:
1 2 3 4 5 void u8g2_SendBuffer (u8g2_t *u8g2)
u8g2_SendBuffer 的RAM 占用空间大,需要结合构造器的buffer选项使用;
不管是u8g2_FirstPage 、u8g2_NextPage 还是u8g2_SendBuffer ,都涉及到一个叫做 current page position 的概念;
库源码解析:
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 void u8g2_SendBuffer (u8g2_t *u8g2) { u8g2_send_buffer(u8g2); u8x8_RefreshDisplay( u8g2_GetU8x8(u8g2) ); } static void u8g2_send_tile_row (u8g2_t *u8g2, uint8_t src_tile_row, uint8_t dest_tile_row) { uint8_t *ptr; uint16_t offset; uint8_t w; w = u8g2_GetU8x8(u8g2)->display_info->tile_width; offset = src_tile_row; ptr = u8g2->tile_buf_ptr; offset *= w; offset *= 8 ; ptr += offset; u8x8_DrawTile(u8g2_GetU8x8(u8g2), 0 , dest_tile_row, w, ptr); } static void u8g2_send_buffer (u8g2_t *u8g2) U8X8_NOINLINE ;static void u8g2_send_buffer (u8g2_t *u8g2) { uint8_t src_row; uint8_t src_max; uint8_t dest_row; uint8_t dest_max; src_row = 0 ; src_max = u8g2->tile_buf_height; dest_row = u8g2->tile_curr_row; dest_max = u8g2_GetU8x8(u8g2)->display_info->tile_height; do { u8g2_send_tile_row(u8g2, src_row, dest_row); src_row++; dest_row++; } while ( src_row < src_max && dest_row < dest_max ); }
1 2 3 4 5 6 void Buffer (u8g2_t *u8g2) { u8g2_ClearBuffer(u8g2); u8g2_SendBuffer(u8g2); HAL_Delay(1000 ); }
2.3 显示配置相关函数(并不是很有用,再进阶) 2.3.1 u8g2_GetAscent() —— 获取基准线以上的高度 函数说明:
1 2 3 4 5 6 int8_t u8g2_GetAscent (&u8g2)
跟字体有关(u8g2_SetFont);
示例: 下面例子,ascent是18
2.3.2 u8g2_GetDescent() —— 获取基准线以下的高度 函数说明:
1 2 3 4 5 6 int8_t u8g2_GetDescent (&u8g2) ;
跟字体有关(setFont);
示例: 下面例子,descent是-5
2.3.3 u8g2_GetDisplayHeight() —— 获取显示器的高度 函数说明:
1 2 3 4 5 u8g2_uint_t u8g2_GetDisplayHeight (void )
2.3.4 u8g2_GetDisplayWidth() —— 获取显示器的宽度 函数说明:
1 2 3 4 5 u8g2_uint_t u8g2_GetDisplayWidth (void )
2.3.5 u8g2_GetMaxCharHeight() —— 获取当前字体里的最大字符的高度 函数说明:
1 2 3 4 5 6 u8g2_uint_t u8g2_GetMaxCharHeight ()
2.3.6 u8g2_GetMaxCharWidth() —— 获取当前字体里的最大字符的宽度 函数说明:
1 2 3 4 5 6 u8g2_uint_t u8g2_GetMaxCharWidth
2.3.7 u8g2_GetStrWidth() —— 获取字符串的像素宽度 函数说明:
1 2 3 4 5 6 7 u8g2_uint_t u8g2_GetStrWidth (u8g2_t *u8g2, const char *s)
2.3.8 u8g2_GetUTF8Width() —— 获取UTF-8字符串的像素宽度 函数说明:
1 2 3 4 5 6 7 u8g2_uint_t u8g2_GetUTF8Width (u8g2_t *u8g2, const char *str)
2.3.9 u8g2_SetAutoPageClear() —— 设置自动清除缓冲区 函数说明:
1 2 3 4 5 6 u8g2_uint_t u8g2_SetAutoPageClear ()
2.3.10 u8g2_SetBitmapMode() —— 设置位图模式 函数说明:
1 2 3 4 5 6 7 8 void u8g2_SetBitmapMode (u8g2_t *u8g2, uint8_t is_transparent)
1 2 3 4 u8g2_SetDrawColor(&u8g2,1 ); u8g2_SetBitmapMode(&u8g2,0 ); u8g2_DrawXBM(&u8g2,4 ,3 , u8g2_logo_97x51_width, u8g2_logo_97x51_height, u8g2_logo_97x51_bits); u8g2_DrawXBM(&u8g2,12 ,11 , u8g2_logo_97x51_width, u8g2_logo_97x51_height, u8g2_logo_97x51_bits);
1 2 3 4 u8g2_SetDrawColor(&u8g2,1 ); u8g2_SetBitmapMode(&u8g2,1 ); u8g2_DrawXBM(&u8g2,4 ,3 , u8g2_logo_97x51_width, u8g2_logo_97x51_height, u8g2_logo_97x51_bits); u8g2_DrawXBM(&u8g2,12 ,11 , u8g2_logo_97x51_width, u8g2_logo_97x51_height, u8g2_logo_97x51_bits);
2.3.11 u8g2_SetClipWindow() —— 设置采集窗口大小 函数说明:
1 2 3 4 5 6 7 8 9 10 void u8g2_SetClipWindow (u8g2_t *u8g2, u8g2_uint_t clip_x0, u8g2_uint_t clip_y0, u8g2_uint_t clip_x1, u8g2_uint_t clip_y1 )
可以通过 u8g2_SetMaxClipWindow去掉该限制
1 void u8g2_SetMaxClipWindow (u8g2_t *u8g2)
1 2 3 u8g2_SetClipWindow(&u8g2,10 , 10 , 85 , 30 ); u8g2_SetDrawColor(&u8g2,1 ); u8g2_DrawStr(&u8g2,3 , 32 , "U8g2" );
2.3.14 u8g2_SetDisplayRotation() —— 设置显示器的旋转角度 函数说明:
1 2 3 4 5 6 7 8 9 10 void u8g2_SetDisplayRotation (u8g2_t *u8g2, const u8g2_cb_t *u8g2_cb)
2.3.15 u8g2_SetDrawColor() —— 设置绘制颜色(反色) 函数说明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void u8g2_SetDrawColor (u8g2_t *u8g2, uint8_t color)
2.3.16 u8g2_SetFont() —— 设置字体集 这是一个非常重要的方法,非常重要!!! 函数说明:
1 2 3 4 5 6 void u8g2_SetFont (u8g2_t *u8g2, const uint8_t *font)
Font会根据像素点高度做了很多区分,具体font请参考 Fntlistall iki 。
如果我们需要用到中文字符,可以在wiki里面搜索一下chinese,你就会发现很多中文font,比如:
2.3.17 u8g2_SetFontDirection() —— 设置字体方向 函数说明:
1 2 3 4 5 6 void u8g2_SetFontDirection (u8g2_t *u8g2, uint8_t dir)
Description: The arguments defines the drawing direction of all strings or glyphs.
Argument
String Rotation
Description
0
0 degree
Left to right
1
90 degree
Top to down
2
180 degree
Right to left
3
270 degree
Down to top
1 2 3 4 5 u8g2_SetFont(u8g2_font_ncenB14_tf); u8g2_SetFontDirection(0 ); u8g2_DrawStr(15 , 20 , "Abc" ); u8g2_SetFontDirection(1 ); u8g2_DrawStr(15 , 20 , "Abc" );
2.4 缓存相关函数(了解了解) 2.4.1 u8g2_GetBufferPtr() —— 获取缓存空间的地址 函数说明:
1 2 3 4 5 6 uint8_t u8g2_GetBufferPtr ()
2.4.2 u8g2_GetBufferTileHeight() —— 获取缓冲区的Tile高度 函数说明:
1 2 3 4 5 uint8_t u8g2_GetBufferTileHeight ()
2.4.3 u8g2_GetBufferTileWidth() —— 获取缓冲区的Tile宽度 函数说明:
1 2 3 4 5 uint8_t u8g2_GetBufferTileWidth ()
2.4.4 u8g2_GetBufferCurrTileRow() —— 获取缓冲区的当前Tile row 函数说明:
1 2 3 4 5 uint8_t u8g2_GetBufferCurrTileRow ()
2.4.5 u8g2_SetBufferCurrTileRow() —— 设置缓冲区的当前Tile row 函数说明:
1 2 3 4 5 void u8g2_SetBufferCurrTileRow (u8g2_t *u8g2, uint8_t row)
1 2 3 4 5 6 7 8 9 u8g2_SetBufferCurrTileRow(&u8g2,0 ); u8g2_ClearBuffer(&u8g2); u8g2_SetFont(&u8g2,u8g2_font_helvB08_tr); u8g2_DrawStr(&u8g2,2 , 8 , "abcdefg" ); u8g2_SetBufferCurrTileRow(&u8g2,2 ); u8g2_SendBuffer(&u8g2); u8g2_SetBufferCurrTileRow(&u8g2,4 ); u8g2_SendBuffer(&u8g2);
在 u8g2_FirstPage/u8g2_NextPage 循环时,由于底层调用了**u8g2_SetBufferCurrTileRow(u8g2, row);**,所以尽量不要自己手动调用该方法;
3.如何运用U8G2库
相关链接
玩转u8g2 OLED库,一篇就够——基于SMT32、HAL
=================我是分割线=================
欢迎到公众号来唠嗑: