u8g2 OLED库

OLED其实就是一个M x n 的像素点阵,想显示什么就得把具体位置的像素点亮起来。对于每一个像素点,有可能是1点亮,也有可能是0点亮;

坐标系

坐标系中,左上角是原点,向右是X轴,向下是Y轴。

U8g2是什么

U8g2是嵌入式设备的单色图形库,一句话简单明了。主要应用于嵌入式设备,包括我们常见的单片机;

U8g2支持的显示控制器

U8g2支持单色OLED和LCD,包括以下控制器:SSD1305,SSD1306,SSD1309,SSD1322,SSD1325,SSD1327,SSD1329,SSD1606,SSD1607,SH1106,SH1107,SH1108,SH1122,T6963,RA8835,LC7981,PCD8544,PCF8812,HX1230 ,UC1601,UC1604,UC1608,UC1610,UC1611,UC1701,ST7565,ST7567,ST7588,ST75256,NT7534,IST3020,ST7920,LD7032,KS0108,SED1520,SBN1661,IL3820,MAX7219

可以说,基本上主流的显示控制器都支持,比如我们常见的SSD1306 12864

U8g2支持的Arduino主板

可以说基本上所有Arduino API的主板都得到U8g2的支持。包括:

Aruino Zero,Uno,Mega,Due,101,MKR Zero以及所有其他Arduino官方主板 基于Arduino平台的STM32 基于Arduino平台的ESP8266和ESP32 甚至其他不知名的基于Arduino平台的开发板 所以说,读者完全不用担心兼容性问题,放心使用。

U8g2如何在Arduino平台上安装

Arduino库U8g2可以从Arduino IDE的库管理器安装,读者在库管理器搜索“U8g2”关键字就可以下载安装:

下载完毕,测试一下库是否安装成功:

#include <U8g2lib.h>
void setup() 
  // put your setup code here, to run once:


void loop() 
  // put your main code here, to run repeatedly:

编译成功,证明你本地已经加载了U8G2库。

U8g2的优势

为什么要运用U8g2库?也就是说U8g2库能带给我们什么样的开发便利。在博主看来,主要考虑几个方面:

U8g2库平台支持性好,基本上支持绝大部分Arduino开发板,特别也博主比较喜欢的ESP8266; U8g2库显示控制器支持性好,基本上市面上的OLED都完美支持; U8g2库 API众多,特别支持了中文,支持了不同字体,这是一个对于开发者俩说不小的福利。 ESP32 and SSD1306 OLED

U8G2_SSD1306_128X64_NONAME_1_SW_I2C u8g2(U8G2_R2, /* clock=*/ 16, /* data=*/ 17, /* reset=*/ U8X8_PIN_NONE);   // ESP32 Thing, pure SW emulated I2C
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R2, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ 16, /* data=*/ 17);   // ESP32 Thing, HW I2C with pin remapping

MAX7219 32x8 LED Matrix

U8G2_MAX7219_32X8_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 11, /* data=*/ 12, /* cs=*/ 10, /* dc=*/ U8X8_PIN_NONE, /* reset=*/ U8X8_PIN_NONE);

这只是一些常见的显示器,可以说,已经足够覆盖到我们常见的范围了。

U8g2库详解

方法可以分为四大类:

基本函数 绘制相关函数 显示配置相关函数 缓存相关函数

U8g2库函数详解

基本函数 u8g2.begin() —— 构造U8G2

u8g2.beginSimple() —— 构造U8G2 注意点:

读者可以看到和begin()函数的区别,需要用户自行控制初始化过程,给了一定的自由度,不过博主建议大家还是直接用begin函数吧。

u8g2.initDisplay() —— 初始化显示控制器 注意点:

这个方法不需要我们单独调用,会在begin函数主动调用一次,我们主要理解即可,会在里面针对具体的OLED进行配置;

u8g2.clearDisplay() —— 清除屏幕内容 注意点:

这个方法不需要我们单独调用,会在begin函数主动调用一次,我们主要理解即可; 不要在 firstPage 和 nextPage 函数之间调用该方法;

u8g2.setPowerSave() —— 是否开启省电模式 注意点:

不管是启用还是禁用,显示器需要的内存消耗是不会变的,说到底就是为了关闭屏幕,做到省电; 所以这里就可以理解为什么初始化需要 setPowerSave(0);

u8g2.clear() —— 清除操作 u8g2.clearBuffer() —— 清除缓冲区 一般这个函数是与sendBuffer函数配对使用,通常用法如下:

void loop(void) 
  u8g2.clearBuffer();
  // ... write something to the buffer 
  u8g2.sendBuffer();
  delay(1000);

u8g2.disableUTF8Print() —— 禁用 UTF8打印 u8g2.enableUTF8Print() —— 启用 UTF8打印 注意点:

我们的中文字符就是UTF8; 常见例子

void setup(void) 
  u8g2.begin();
  u8g2.enableUTF8Print();       // enable UTF8 support for the Arduino print() function

void loop(void) 
  u8g2.setFont(u8g2_font_unifont_t_chinese2);  // use chinese2 for all the glyphs of "你好世界"
  u8g2.firstPage();
  do 
    u8g2.setCursor(0, 40);
    u8g2.print("你好世界");     // Chinese "Hello World" 
   while ( u8g2.nextPage() );
  delay(1000);

u8g2.home() —— 重置显示光标的位置

绘制相关函数

u8g2.drawBox() —— 画实心方形 注意点:

如果支持绘制颜色(也就是不是单色显示器),那么由setDrawColor设置; 示例:

u8g2.drawBox(3,7,25,15);

u8g2.drawCircle() —— 画空心圆

注意点:

如果支持绘制颜色(也就是不是单色显示器),那么由setDrawColor设置; 直径等于2rad + 1; 示例:

u8g2.drawCircle(20, 25, 10, U8G2_DRAW_ALL);

u8g2.drawDisc() —— 画实心圆 注意点:

如果支持绘制颜色(也就是不是单色显示器),那么由setDrawColor设置; 直径等于2rad + 1;

u8g2.drawEllipse() —— 画空心椭圆 注意点:

rx*ry 在8位模式的u8g2必须小于512(博主暂且没有理解); 示例:

u8g2.drawEllipse(20, 25, 15, 10, U8G2_DRAW_ALL);

u8g2.drawFilledEllipse() —— 画实心椭圆 注意点:

rx*ry 在8位模式的u8g2必须小于512(博主暂且没有理解);

u8g2.drawFrame() —— 画空心方形 注意点:

如果支持绘制颜色(也就是不是单色显示器),那么由setDrawColor设置; 示例:

u8g2.drawFrame(3,7,25,15);

u8g2.drawGlyph() —— 绘制字体字集的符号 注意点:

U8g2支持16位以内的unicode字符集,也就是说encoding的范围为0-65535,drawGlyph方法只能绘制存在于所使用的字体字集中的unicode值; 这个绘制方法依赖于当前的字体模式和绘制颜色;

u8g2.drawHLine() —— 绘制水平线

注意点:

如果支持绘制颜色(也就是不是单色显示器),那么由setDrawColor设置; u8g2.drawLine() —— 两点之间绘制线 注意点:

如果支持绘制颜色(也就是不是单色显示器),那么由setDrawColor设置; 示例:

u8g2.drawLine(20, 5, 5, 32);

u8g2.drawPixel() —— 绘制像素点 注意点:

如果支持绘制颜色(也就是不是单色显示器),那么由setDrawColor设置; 你会发现很多绘制方法的底层都是调用drawPixel,毕竟像素属于最小颗粒度; 我们可以利用这个绘制方法自定义自己的图形显示;

u8g2.drawRBox() —— 绘制圆角实心方形 注意点:

如果支持绘制颜色(也就是不是单色显示器),那么由setDrawColor设置; 要求,w >= 2(r+1) 并且 h >= 2(r+1),这是显而易见的限制;

u8g2.drawRFrame() —— 绘制圆角空心方形 注意点:

如果支持绘制颜色(也就是不是单色显示器),那么由setDrawColor设置; 要求,w >= 2(r+1) 并且 h >= 2(r+1),这是显而易见的限制 示例:

u8g2.drawRFrame(20,15,30,22,7);

u8g2.drawStr() —— 绘制字符串 注意点:

需要先设置字体,调用setFont方法; 这个方法不能绘制encoding超过256的,超过256需要用drawUTF8或者drawGlyph;说白了就是一般用来显示英文字符; x,y属于字符串左下角的坐标; 示例:

u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.drawStr(0,15,"Hello World!");

u8g2.drawTriangle() —— 绘制实心三角形 示例:

u8g2.drawTriangle(20,5, 27,50, 5,32);

u8g2.drawUTF8() —— 绘制UTF8编码的字符 注意点:

使用该方法,有两个前提。首先是你的编译器需要支持UTF-8编码,对于绝大部分Arduino板子已经支持;其次,显示的字符串需要存为“UTF-8”编码,Arduino IDE上默认支持; 该方法需要依赖于fontMode(setFont)以及drawing Color,也就是说如果你传进来的字符串编码必须在font定义里面; 示例:

u8g2.setFont(u8g2_font_unifont_t_symbols);
u8g2.drawUTF8(5, 20, "Snowman: ?");

u8g2.drawVLine() —— 绘制竖直线

u8g2.drawXBM()/drawXBMP() —— 绘制图像 注意点:

drawXBM和drawXBMP区别在于 XBMP支持PROGMEM;

u8g2.firstPage()/nextPage() —— 绘制命令 注意点:

firstPage方法会把当前页码位置变成0; 修改内容处于firstPage和nextPage之间,每次都是重新渲染所有内容; 优势点:

该方法消耗的ram空间,比sendBuffer消耗的ram空间要少; 示例:

  u8g2.firstPage();
  do 
    /* all graphics commands have to appear within the loop body. */    
    u8g2.setFont(u8g2_font_ncenB14_tr);
    u8g2.drawStr(0,20,"Hello World!");
   while ( u8g2.nextPage() );

u8g2.print() —— 绘制内容

u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.setCursor(0, 15);
u8g2.print("Hello World!");

u8g2.sendBuffer() —— 绘制缓冲区的内容 注意点:

sendBuffer的RAM占用空间大,需要结合构造器的buffer选项(请继续往下看,先有个概念)使用; 不管是fistPage、nextPage还是sendBuffer,都涉及到一个叫做 current page position的概念; 示例:

void loop(void) 
  u8g2.clearBuffer();
  // ... write something to the buffer 
  u8g2.sendBuffer();
  delay(1000);

显示配置相关函数

u8g2.getAscent() —— 获取基准线以上的高度 注意点:

跟字体有关(setFont); 示例: 下面例子,ascent是18

u8g2.getDescent() —— 获取基准线以下的高度 注意点:

跟字体有关(setFont); 示例: 下面例子,descent是-5

u8g2.getDisplayHeight() —— 获取显示器的高度

u8g2.getDisplayWidth() —— 获取显示器的宽度

u8g2.getMaxCharHeight() —— 获取当前字体里的最大字符的高度 注意点:

每一个字符在font字集中都是一个位图,位图有高度和宽度; 3.2.3.6 u8g2.getMaxCharWidth() —— 获取当前字体里的最大字符的宽度 注意点:

每一个字符在font字集中都是一个位图,位图有高度和宽度;

u8g2.getStrWidth() —— 获取字符串的像素宽度 注意点:

像素宽度和当前font字体有关;

u8g2.getUTF8Width() —— 获取UTF-8字符串的像素宽度 注意点:

像素宽度和当前font字体有关;

u8g2.setAutoPageClear() —— 设置自动清除缓冲区 注意点:

该方法用于 firstPage 和 nextPage(看上面的源码解析); 建议该方法保持默认就好,如果用户禁止了,那么需要自己维护缓冲区的状态或者手动调用clearBuffer;

u8g2.setBitmapMode() —— 设置位图模式 示例:

u8g2.setDrawColor(1);
u8g2.setBitmapMode(0);
u8g2.drawXBM(4,3, u8g2_logo_97x51_width, u8g2_logo_97x51_height,  u8g2_logo_97x51_bits);
u8g2.drawXBM(12,11, u8g2_logo_97x51_width, u8g2_logo_97x51_height,  u8g2_logo_97x51_bits);

u8g2.setDrawColor(1);
u8g2.setBitmapMode(1);
u8g2.drawXBM(4,3, u8g2_logo_97x51_width, u8g2_logo_97x51_height,  u8g2_logo_97x51_bits);
u8g2.drawXBM(12,11, u8g2_logo_97x51_width, u8g2_logo_97x51_height,  u8g2_logo_97x51_bits);

u8g2.setBusClock() —— 设置总线时钟 注意点:

仅仅Arduino平台支持; 必须在u8g2.begin() 或者 u8g2.initDisplay()之前调用;

u8g2.setClipWindow() —— 设置采集窗口大小 注意点:

可以通过 setMaxClipWindow 去掉该限制

void U8G2::setMaxClipWindow(void)

示例:

u8g2.setClipWindow(10, 10, 85, 30);
u8g2.setDrawColor(1);
u8g2.drawStr(3, 32, "U8g2");

u8g2.setCursor() —— 设置绘制光标位置 示例:

u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.setCursor(0, 15);
u8g2.print("Hello World!");

u8g2.setDisplayRotation() —— 设置显示器的旋转角度

u8g2.setDrawColor() —— 设置绘制颜色

u8g2.setFont() —— 设置字体集 这是一个非常重要的方法,非常重要!!! Font会根据像素点高度做了很多区分,具体font请参考 wiki。 如果我们需要用到中文字符,可以在wiki里面搜索一下chinese,你就会发现很多中文font,比如:

//支持UTF-8或者GB2312编码
u8g2_font_wqy15_t_chinese1
u8g2_font_wqy15_t_chinese2
u8g2_font_wqy15_t_chinese3
u8g2_font_wqy12_t_gb2312
u8g2_font_wqy12_t_gb2312a

...... 注意点:

中文字符集消耗内存大,请谨慎使用,可以用在Arduino 101等ram空间比较大的板子上;

至于用哪一个,看自己的需求了。

我们看看Font的命名规则:

<prefix> '_' <name> '_' <purpose> <char set>

其中:

prefix基本上都是 u8g2; name 一般会挂钩上字符像素使用量,比如5X7 purpose

注意点:

U8G2库提供的font非常多,博主也暂时消化不了太多。如果我们使用中文的话,就去看看中文font就好; 示例:Fonts u8g2font5x7tr and u8g2fontpressstart2p8u

u8g2.setFontDirection() —— 设置字体方向 示例:

u8g2.setFont(u8g2_font_ncenB14_tf);
u8g2.setFontDirection(0);
u8g2.drawStr(15, 20, "Abc");
u8g2.setFontDirection(1);
u8g2.drawStr(15, 20, "Abc");

缓存相关函数

缓存相关函数,一般不会去操作,了解即可;

u8g2.getBufferPtr() —— 获取缓存空间的地址 注意点:

缓存大小等于 8 * u8g2.getBufferTileHeight() * u8g2.getBufferTileWidth().

u8g2.getBufferTileHeight() —— 获取缓冲区的Tile高度 注意点:

一个tile等于8个像素点.

u8g2.getBufferTileWidth() —— 获取缓冲区的Tile宽度

注意点:

一个tile等于8个像素点.

u8g2.getBufferCurrTileRow() —— 获取缓冲区的当前Tile row 注意点:

这个方法跟我们上面说到的page position相关.

u8g2.setBufferCurrTileRow() —— 设置缓冲区的当前Tile row 注意点:

在 firstPage/nextPage 循环时,由于底层调用了setBufferCurrTileRow,所以尽量不要自己手动调用该方法; 示例:

u8g2.setBufferCurrTileRow(0);       // let y=0 be the topmost row of the buffer
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_helvB08_tr);
u8g2.drawStr(2, 8, "abcdefg");

u8g2.setBufferCurrTileRow(2);   // write the buffer to tile row 2 (y=16) on the display
u8g2.sendBuffer();
u8g2.setBufferCurrTileRow(4);   // write the same buffer to tile row 4 (y=32) on the display
u8g2.sendBuffer();

利用好该方法,我们可以实现部分更新;

如何运用U8G2库

前面博主介绍到U8G2适配了绝大部分的OLED,那么我们如何构建具体的OLED驱动呢?可分为以下几个顺序步骤:

区分显示器 选择物理总线方式 区分数字连线 U8g2初始化 U8g2绘制模式

区分显示器

首先,你需要知道OLED显示器的控制器型号以及屏幕大小。举个例子,博主手上有一块SSD1306 128X64的OLED,那么它的控制器就是SSD1306,屏幕大小是128X64。 其次,你所选择的OLED必须在U8g2库所支持的OLED列表中,具体可参考 链接地址。

选择物理总线方式

图像信息是通过物理总线方式发给OLED显示器。通常,我们的总线包括:

3SPI,3-wire SPI:串行外围接口,依靠三个控制信号,Clock、Data、CS; 4SPI, 4-Wire SPI,跟3SPI一样,只是额外多了一条数据命令线,经常叫做D/C; I2C, IIC or TWI: SCL SDA; 8080:A 8-Bit bus which requires 8 data lines, chip select and a write strobe signa 6800: Another 8-Bit bus, but with a different protocol. 具体的OLED使用什么物理总线,我们需要查阅各自的数据手册。比如,博主的SSD1306就是IIC。

区分数字连线

知道了物理连线模式之后,我们一般都是把OLED连接到Arduino Board的输出引脚,也就是软件模拟具体总线协议。当然,如果有现成的物理总线端口那就更好了。

U8g2初始化

经历以上三步之后,我们就可以开始初始化出具体的OLED驱动了。比如,博主的IIC SSD1306 128X64 的OLED,就可以用以下初始化构造器(Builder设计模式,有空可以去了解一下):

U8G2_SSD1306_128X64_NONAME_1_SW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE);   // All Boards without Reset of the Display

当然SSD1306还有其他构造器(具体可以参考 wiki): Controller "ssd1306", Display "128x64noname" Descirption U8G2SSD1306128X64NONAME14WSWSPI(rotation, clock, data, cs, dc [, reset]) page buffer, size = 128 bytes U8G2SSD1306128X64NONAME24WSWSPI(rotation, clock, data, cs, dc [, reset]) page buffer, size = 256 bytes U8G2SSD1306128X64NONAMEF4WSWSPI(rotation, clock, data, cs, dc [, reset]) full framebuffer, size = 1024 bytes U8G2SSD1306128X64NONAME14WHW_SPI(rotation, cs, dc [, reset]) page buffer, size = 128 bytes ????那么,我们这里就需要重点讲述一下构造器的规则。

U8g2绘制模式

U8g2支持三种绘制模式:

Full screen buffer mode,全屏缓存模式 Page mode (This is the U8glib picture loop) 分页模式 U8x8, character only mode 仅仅支持普通字符

Full screen buffer mode 特点:

绘制速度快 所有的绘制方法都可以使用 需要大量的ram空间 构造器:

构造器必须带有F,比如:

U8G2_ST7920_128X64_F_SW_SPI(rotation, clock, data, cs [, reset])

用法:

清除缓冲区 u8g2.clearBuffer() 操作一些绘制方法 发送缓冲区的内容到显示器 u8g2.sendBuffer(). 示例代码:

void setup(void) 
  u8g2.begin();


void loop(void) 
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_ncenB14_tr);
  u8g2.drawStr(0,20,"Hello World!");
  u8g2.sendBuffer();

Page mode 特点:

绘制速度慢 所有的绘制方法都可以使用 需要少量的ram空间 构造器:

构造器必须带有“1”或者2,比如:

U8G2_ST7920_128X64_ 1 _SW_SPI(rotation, clock, data, cs [, reset])

用法:

调用 u8g2.firstPage() 开始一个 do while 循环 在循环内部 操作一些绘制方法 不断判断 u8g2.nextPage() 示例代码:

void setup(void) 
  u8g2.begin();


void loop(void) 
  u8g2.firstPage();
  do 
    u8g2.setFont(u8g2_font_ncenB14_tr);
    u8g2.drawStr(0,24,"Hello World!");
   while ( u8g2.nextPage() );

U8x8 character mode 特点:

绘制速度快 并不是对所有的显示器都有效 图形绘制不可用 不需要ram空间 构造器:

使用U8X8构造器,比如:

U8X8_ST7565_EA_DOGM128_4W_SW_SPI(clock, data, cs, dc [, reset])

用法:

所有绘制命令是直接把数据写到显示器 示例代码:

void setup(void) 
  u8x8.begin();


void loop(void) 
  u8x8.setFont(u8x8_font_chroma48medium8_r);
  u8x8.drawString(0,1,"Hello World!");

以上是关于玩转u8g2 OLED库,一篇就够的主要内容

文档信息

版权声明:可自由转载(请注明转载出处)-非商用-非衍生

发表时间:2023年4月11日 15:57