授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力。希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石。。。

QQ技术互动交流群:ESP8266&32 物联网开发 群号622368884,不喜勿喷

一、你如果想学基于Arduino的ESP8266开发技术

一、基础篇

  1. ESP8266开发之旅 基础篇① 走进ESP8266的世界
  2. ESP8266开发之旅 基础篇② 如何安装ESP8266的Arduino开发环境
  3. ESP8266开发之旅 基础篇③ ESP8266与Arduino的开发说明
  4. ESP8266开发之旅 基础篇④ ESP8266与EEPROM
  5. ESP8266开发之旅 基础篇⑤ ESP8266 SPI通信和I2C通信
  6. ESP8266开发之旅 基础篇⑥ Ticker——ESP8266定时库

二、网络篇

  1. ESP8266开发之旅 网络篇① 认识一下Arduino Core For ESP8266
  2. ESP8266开发之旅 网络篇② ESP8266 工作模式与ESP8266WiFi库
  3. ESP8266开发之旅 网络篇③ Soft-AP——ESP8266WiFiAP库的使用
  4. ESP8266开发之旅 网络篇④ Station——ESP8266WiFiSTA库的使用
  5. ESP8266开发之旅 网络篇⑤ Scan WiFi——ESP8266WiFiScan库的使用
  6. ESP8266开发之旅 网络篇⑥ ESP8266WiFiGeneric——基础库
  7. ESP8266开发之旅 网络篇⑦ TCP Server & TCP Client
  8. ESP8266开发之旅 网络篇⑧ SmartConfig——一键配网
  9. ESP8266开发之旅 网络篇⑨ HttpClient——ESP8266HTTPClient库的使用
  10. ESP8266开发之旅 网络篇⑩ UDP服务
  11. ESP8266开发之旅 网络篇⑪ WebServer——ESP8266WebServer库的使用
  12. ESP8266开发之旅 网络篇⑫ 域名服务——ESP8266mDNS库
  13. ESP8266开发之旅 网络篇⑬ SPIFFS——ESP8266 Flash文件系统
  14. ESP8266开发之旅 网络篇⑭ web配网
  15. ESP8266开发之旅 网络篇⑮ 真正的域名服务——DNSServer
  16. ESP8266开发之旅 网络篇⑯ 无线更新——OTA固件更新

三、应用篇

  1. ESP8266开发之旅 应用篇① 局域网应用 ——炫酷RGB彩灯
  2. ESP8266开发之旅 应用篇② OLED显示天气屏
  3. ESP8266开发之旅 应用篇③ 简易版WiFi小车

四、高级篇

  1. ESP8266开发之旅 进阶篇① 代码优化 —— ESP8266内存管理
  2. ESP8266开发之旅 进阶篇② 闲聊Arduino IDE For ESP8266配置
  3. ESP8266开发之旅 进阶篇③ 闲聊 ESP8266 Flash
  4. ESP8266开发之旅 进阶篇④ 常见问题 —— 解决困扰
  5. ESP8266开发之旅 进阶篇⑤ 代码规范 —— 像写文章一样优美
  6. ESP8266开发之旅 进阶篇⑥ ESP-specific APIs说明

1.前言

    最近博主听到QQ群里面问得比较多的问题:
    “博哥,有玩过OLED吗?”
    “博哥,有试过在ESP8266上调成功过SSD1306吗?”
    “博哥,OLED上显示天气信息怎么弄?”
    ........
    诸如此类的问题,在博主看来,无非都是对OLED库用法的不熟悉甚至不了解。Arduino OLED库众多,博主也曾经介绍过一款 《博哥OLED系列》- 玩转SSD1306-12864 OLED Adafruit_GFX 和 Adafruit_SSD1306库。但是,博主今天要介绍的是目前Arduino平台上使用最广泛的OLED库 —— U8G2库,也是很多初学者容易懵逼的一个第三方库。目前在github上超过1K star,1800次commit,可以说维护热度很高,读者大可放心使用。
    至于这个库为什么这么火爆,请读者认真看完本博文自行评价,博主只能说功能真的很好很强大!!!!
    那么,博主首先立个flag,阅读完本篇博文的最终效果——读者以后在Arduino平台上开发OLED项目的时候,可以考虑U8G2库,并且知道U8G2库的具体用法,甚至可以能够当做工具书来翻阅。
    进入正文之前,博主要给读者灌输两个个非常重要的知识点:

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

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

    希望读者能仔细理解这两个知识点。

2.U8g2简介

2.1 U8g2是什么

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

2.2 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,读者在使用该库之前请查阅自己的OLED显示控制器是否处于支持列表中

2.2 U8g2支持的Arduino主板

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

  • Aruino Zero,Uno,Mega,Due,101,MKR Zero以及所有其他Arduino官方主板
  • 基于Arduino平台的STM32
  • 基于Arduino平台的ESP8266和ESP32
  • 甚至其他不知名的基于Arduino平台的开发板

    所以说,读者完全不用担心兼容性问题,放心使用。

2.3 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库。

2.4 U8g2的优势

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

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

    以下是官方提供的一些U8G2库测试图,博主挑选了几张常见的以及附上U8G2库的配置(读者可以先不用理会配置,等看完博文再次阅读就会了解含义):

  • 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);
  • SSD1305 128x32
U8G2_SSD1305_128X32_NONAME_1_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);
  • SSD1306 128x32
U8X8_SSD1306_128X32_UNIVISION_SW_I2C u8x8(/* clock=A5*/ 19, /* data=A4*/ 18);
  • PCD8544 84x48 (Nokia 5110) LCD
U8G2_PCD8544_84X48_1_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);    // Nokia 5110 Display
  • ST7920 128x64 LCD in 8080 parallel mode
U8G2_ST7920_128X64_1_8080 u8g2(U8G2_R0, 8, 9, 10, 11, 4, 5, 6, 7, /*enable=*/ 18 /* A4 */, /*cs=*/ U8X8_PIN_NONE, /*dc/rs=*/ 17 /* A3 */, /*reset=*/ 15 /* A1 */);  // Remember to set R/W to 0 

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

3.U8g2库详解

3.1 U8g2库百度脑图

老规则,先上一个图:

方法可以分为四大类:

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

3.2 U8g2库函数详解

3.2.1 基本函数

3.2.1.1 u8g2.begin() —— 构造U8G2

函数说明:

/**
 * 初始化U8g2库
 * @Note 关联方法 initDisplay clearDisplay setPowerSave
 */
bool U8G2::begin(void)

源码说明:

bool begin(void) {
/* note: call to u8x8_utf8_init is not required here, this is done in the setup procedures before */
   initDisplay(); //初始化显示器
   clearDisplay();  // 重置清屏
   setPowerSave(0); //唤醒屏幕
   return 1;
}

3.2.1.2 u8g2.beginSimple() —— 构造U8G2

函数说明:

/**
 * 简单初始化U8g2库
 * @Note 关联方法 beginSimple
 */
void U8G2::beginSimple(void);

源码说明:

void beginSimple(void) {
/* does not clear the display and does not wake up the display */
/* user is responsible for calling clearDisplay() and setPowerSave(0) */
   initDisplay();//初始化显示器
}

注意点:

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

3.2.1.3 u8g2.initDisplay() —— 初始化显示控制器

函数说明:

/**
 * 初始化显示控制器
 */
void U8G2::initDisplay(void)

注意点:

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

3.2.1.4 u8g2.clearDisplay() —— 清除屏幕内容

函数说明:

/**
 * 清除屏幕
 */
void U8G2::clearDisplay(void)

注意点:

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

3.2.1.5 u8g2.setPowerSave() —— 是否开启省电模式

函数说明:

/**
 * 清除显示缓冲区
 * @param is_enable
 *        1 表示启用显示器的省电模式,屏幕上看不到任何东西
 *        0 表示禁用省电模式
 */
void U8G2::setPowerSave(uint8_t is_enable)

注意点:

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

3.2.1.6 u8g2.clear() —— 清除操作

函数说明:

/**
 * 清除屏幕显示,清除缓冲区,光标回到左上角原点位置(0,0)
 * @Note 关联方法 home clearDisplay clearBuffer
 */
void U8G2::clear(void)

源码说明:

void clear(void) {
   home(); //回到原点
   clearDisplay(); //清除屏幕上的显示
   clearBuffer();  //清除缓冲区
}

3.2.1.7 u8g2.clearBuffer() —— 清除缓冲区

函数说明:

/**
 * 清除内存中数据缓冲区
 */
void U8G2::clearBuffer(void)

注意点:

  • 一般这个函数是与sendBuffer函数配对使用,通常用法如下:
void loop(void) {
  u8g2.clearBuffer();
  // ... write something to the buffer
  u8g2.sendBuffer();
  delay(1000);
}

3.2.1.8 u8g2.disableUTF8Print() —— 禁用 UTF8打印

函数说明:

/**
 * 禁用Arduino平台下支持输出UTF8字符集,默认是开启
 */
void U8G2::disableUTF8Print(void)

3.2.1.9 u8g2.enableUTF8Print() —— 启用 UTF8打印

函数说明:

/**
 * 开启Arduino平台下支持输出UTF8字符集
 */
void U8G2::enableUTF8Print(void)

注意点:

  • 我们的中文字符就是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);
}

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

函数说明:

/**
 * 重置显示光标的位置,回到原点(0,0)
 * @Note 关联方法 print clear
 */
void U8G2::home(void)

3.2.2 绘制相关函数

3.2.2.1 u8g2.drawBox() —— 画实心方形

函数说明:

/**
 * 画实心方形,左上角坐标为(x,y),宽度为w,高度为h
 * @param x 左上角的x坐标
 * @param y 左上角的y坐标
 * @param w 方形的宽度
 * @param h 方形的高度
 * @Note 关联方法 drawFrame setDrawColor
 */
void U8G2::drawBox(u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h)

注意点:

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

示例:

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

3.2.2.2 u8g2.drawCircle() —— 画空心圆

函数说明:

/**
 * 画空心圆,圆心坐标为(x0,y0),半径为rad
 * @param x0 圆点的x坐标
 * @param y0 圆点的y坐标
 * @param rad 圆形的半径
 * @param opt 圆形选项
 *        U8G_DRAW_ALL 整个圆
 *        U8G2_DRAW_UPPER_RIGHT 右上部分的圆弧
 *        U8G2_DRAW_UPPER_LEFT  左上部分的圆弧
 *        U8G2_DRAW_LOWER_LEFT  左下部分的圆弧
 *        U8G2_DRAW_LOWER_RIGHT 右下部分的圆弧
 *        选项可以通过 | 操作符来组合
 * @Note 关联方法 drawDisc setDrawColor
 */
void U8G2::drawCircle(u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, uint8_t opt = U8G2_DRAW_ALL)

注意点:

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

示例:

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

3.2.2.3 u8g2.drawDisc() —— 画实心圆

函数说明:

/**
 * 画实心圆,圆心坐标为(x0,y0),半径为rad
 * @param x0 圆点的x坐标
 * @param y0 圆点的y坐标
 * @param rad 圆形的半径
 * @param opt 圆形选项
 *        U8G_DRAW_ALL 整个圆
 *        U8G2_DRAW_UPPER_RIGHT 右上部分的圆弧
 *        U8G2_DRAW_UPPER_LEFT  左上部分的圆弧
 *        U8G2_DRAW_LOWER_LEFT  左下部分的圆弧
 *        U8G2_DRAW_LOWER_RIGHT 右下部分的圆弧
 *       选项可以通过 | 操作符来组合
 * @Note 关联方法 drawCircle setDrawColor
 */
void U8G2::drawDisc(u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, uint8_t opt = U8G_DRAW_ALL)

注意点:

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

3.2.2.4 u8g2.drawEllipse() —— 画空心椭圆

函数说明:

/**
 * 画空心椭圆,圆心坐标为(x0,y0),半径为rad
 * @param x0 圆点的x坐标
 * @param y0 圆点的y坐标
 * @param rx 椭圆形水平x方向的半径
 * @param ry 椭圆形竖直y方向的半径
 * @param opt 圆形选项
 *        U8G_DRAW_ALL 整个椭圆
 *        U8G2_DRAW_UPPER_RIGHT 右上部分的圆弧
 *        U8G2_DRAW_UPPER_LEFT  左上部分的圆弧
 *        U8G2_DRAW_LOWER_LEFT  左下部分的圆弧
 *        U8G2_DRAW_LOWER_RIGHT 右下部分的圆弧
 *        选项可以通过 | 操作符来组合
 * @Note 关联方法 drawCircle
 */
void U8G2::drawEllipse(u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rx, u8g2_uint_t ry, uint8_t opt)

注意点:

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

示例:

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

3.2.2.5 u8g2.drawFilledEllipse() —— 画实心椭圆

函数说明:

/**
 * 画实心椭圆,圆心坐标为(x0,y0),半径为rad
 * @param x0 圆点的x坐标
 * @param y0 圆点的y坐标
 * @param rx 椭圆形水平x方向的半径
 * @param ry 椭圆形竖直y方向的半径
 * @param opt 圆形选项
 *        U8G_DRAW_ALL 整个椭圆
 *        U8G2_DRAW_UPPER_RIGHT 右上部分的圆弧
 *        U8G2_DRAW_UPPER_LEFT  左上部分的圆弧
 *        U8G2_DRAW_LOWER_LEFT  左下部分的圆弧
 *        U8G2_DRAW_LOWER_RIGHT 右下部分的圆弧
 *        选项可以通过 | 操作符来组合
 * @Note 关联方法 drawCircle
 */
void U8G2::drawFilledEllipse(u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rx, u8g2_uint_t ry, uint8_t opt)

注意点:

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

3.2.2.6 u8g2.drawFrame() —— 画空心方形

函数说明:

/**
 * 画空心方形,左上角坐标为(x,y),宽度为w,高度为h
 * @param x 左上角的x坐标
 * @param y 左上角的y坐标
 * @param w 方形的宽度
 * @param h 方形的高度
 * @Note 关联方法 setDrawColor
 */
void U8G2::drawFrame(u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h)

注意点:

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

示例:

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

3.2.2.7 u8g2.drawGlyph() —— 绘制字体字集的符号

函数说明:

/**
 * 绘制字体字集里面定义的符号
 * @param x 左上角的x坐标
 * @param y 左上角的y坐标
 * @param encoding 字符的unicode值
 * @Note 关联方法 setFont
 */
void U8G2::drawGlyph(u8g2_uint_t x, u8g2_uint_t y, uint16_t encoding)

注意点:

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

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

函数说明:

/**
 * 绘制水平线
 * @param x 左上角的x坐标
 * @param y 左上角的y坐标
 * @param w 水平线的长度
 * @Note 关联方法 setDrawColor
 */
 void U8G2::drawHLine(u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w)

注意点:

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

3.2.2.9 u8g2.drawLine() —— 两点之间绘制线

函数说明:

/**
 * 绘制线,从坐标(x0,y0) 到(x1,y1)
 * @param x0 端点0的x坐标
 * @param y0 端点0的y坐标
 * @param x1 端点1的x坐标
 * @param y1 端点1的y坐标
 * @Note 关联方法 setDrawColor
 */
 void U8G2::drawLine(u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t x1, u8g2_uint_t y1)

注意点:

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

示例:

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

3.2.2.10 u8g2.drawPixel() —— 绘制像素点

函数说明:

/**
 * 绘制像素点,坐标(x,y)
 * @param x 像素点的x坐标
 * @param y 像素点的y坐标
 * @Note 关联方法 setDrawColor
 */
void U8G2::drawPixel(u8g2_uint_t x, u8g2_uint_t y)

注意点:

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

3.2.2.11 u8g2.drawRBox() —— 绘制圆角实心方形

函数说明:

/**
 * 绘制圆角实心方形,左上角坐标为(x,y),宽度为w,高度为h,圆角半径为r
 * @param x 左上角的x坐标
 * @param y 左上角的y坐标
 * @param w 方形的宽度
 * @param h 方形的高度
 * @param r 圆角半径
 */
void U8G2::drawRBox(u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h, u8g2_uint_t r)

注意点:

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

3.2.2.12 u8g2.drawRFrame() —— 绘制圆角空心方形

函数说明:

/**
 * 绘制圆角空心方形,左上角坐标为(x,y),宽度为w,高度为h,圆角半径为r
 * @param x 左上角的x坐标
 * @param y 左上角的y坐标
 * @param w 方形的宽度
 * @param h 方形的高度
 * @param r 圆角半径
 */
void U8G2::drawRFrame(u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h, u8g2_uint_t r)

注意点:

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

示例:

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

3.2.2.13 u8g2.drawStr() —— 绘制字符串

函数说明:

/**
 * 绘制字符串
 * @param x 左上角的x坐标
 * @param y 左上角的y坐标
 * @param s 绘制字符串内容
 * @return 字符串的长度
 */
u8g2_uint_t U8g2::drawStr(u8g2_uint_t x, u8g2_uint_t y, const char *s) 

注意点:

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

示例:

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

3.2.2.14 u8g2.drawTriangle() —— 绘制实心三角形

函数说明:

/**
 * 绘制实心三角形,定点坐标分别为(x0,y0),(x1,y1),(x2,y2)
 */
void U8G2::drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2) 

示例:

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

3.2.2.15 u8g2.drawUTF8() —— 绘制UTF8编码的字符

函数说明:

/**
 * 绘制UTF8编码的字符串
 * @param x 字符串在屏幕上的左下角x坐标
 * @param y 字符串在屏幕上的左下角y坐标
 * @param s 需要绘制的UTF-8编码字符串
 * @return 返回字符串的长度
 */
u8g2_uint_t U8g2::drawUTF8(u8g2_uint_t x, u8g2_uint_t y, const char *s)

注意点:

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

示例:

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

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

函数说明:

/**
 * 绘制竖直线
 * @param x 左上角坐标x
 * @param y 左上角坐标y
 * @param h 高度
 */
void U8G2::drawVLine(u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t h) 

3.2.2.17 u8g2.drawXBM()/drawXBMP() —— 绘制图像

函数说明:

/**
 * 绘制图像
 * @param x 左上角坐标x
 * @param y 左上角坐标y
 * @param w 图形宽度
 * @param h 图形高度
 * @param bitmap 图形内容
 * @Note 关联方法 setBitmapMode
 */
void U8G2::drawXBM(u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h, const uint8_t *bitmap)
void U8G2::drawXBMP(u8g2_uint_t x, u8g2_uint_t y, u8g2_uint_t w, u8g2_uint_t h, const uint8_t *bitmap) 

注意点:

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

3.2.2.18 u8g2.firstPage()/nextPage() —— 绘制命令

函数说明:

/**
 * 绘制图像
 */
void U8G2::firstPage(void)
uint8_t U8G2::nextPage(void)

注意点:

  • 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() );

库源码解析:

void u8g2_FirstPage(u8g2_t *u8g2)
{
  if ( u8g2->is_auto_page_clear )
  {
    //清除缓冲区
    u8g2_ClearBuffer(u8g2);
  }
  //设置当前缓冲区的Tile Row 一个Tile等于8个像素点的高度
  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 )
  {
    //如果row已经到达最后一行,触发refreshDisplay调用,表示整个页面已经刷完了
    u8x8_RefreshDisplay( u8g2_GetU8x8(u8g2) );
    return 0;
  }
  if ( u8g2->is_auto_page_clear )
  {
     //清除缓冲区
    u8g2_ClearBuffer(u8g2);
  }
  //不断更新TileRow 这是非常关键的一步
  u8g2_SetBufferCurrTileRow(u8g2, row);
  return 1;
}

3.2.2.19 u8g2.print() —— 绘制内容

函数说明:

/**
 * 绘制内容
 * @Note 关联方法  setFont setCursor enableUTF8Print
 */
void U8G2::print(...)

示例:

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

3.2.2.20 u8g2.sendBuffer() —— 绘制缓冲区的内容

函数说明:

/**
 * 绘制缓冲区的内容
 * @Note 关联方法  clearBuffer
 */
void U8G2::sendBuffer(void)

注意点:

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

库源码解析:

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);
}

/*
  write the buffer to the display RAM.
  For most displays, this will make the content visible to the user.
  Some displays (like the SSD1606) require a u8x8_RefreshDisplay()
*/
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 );
}

示例:

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

3.2.3 显示配置相关函数

3.2.3.1 u8g2.getAscent() —— 获取基准线以上的高度

函数说明:

/**
 * 获取基准线以上的高度
 * @return 返回高度值
 * @Note 关联方法  setFont getDescent setFontRefHeightAll
 */
int8_t U8G2::getAscent(void)

注意点:

  • 跟字体有关(setFont);

示例:
下面例子,ascent是18

3.2.3.2 u8g2.getDescent() —— 获取基准线以下的高度

函数说明:

/**
 * 获取基准线以下的高度
 * @return 返回高度值
 * @Note 关联方法  setFont setFontRefHeightAll
 */
int8_t U8G2::getDescent(void)

注意点:

  • 跟字体有关(setFont);

示例:
下面例子,descent是-5

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

函数说明:

/**
 * 获取显示器的高度
 * @return 返回高度值
 */
u8g2_uint_t getDisplayHeight(void)

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

函数说明:

/**
 * 获取显示器的宽度
 * @return 返回宽度值
 */
u8g2_uint_t getDisplayWidth(void)

3.2.3.5 u8g2.getMaxCharHeight() —— 获取当前字体里的最大字符的高度

函数说明:

/**
 * 获取当前字体里的最大字符的高度
 * @return 返回高度值
 * @Note 关联方法 setFont
 */
u8g2_uint_t getMaxCharHeight(void)

注意点:

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

3.2.3.6 u8g2.getMaxCharWidth() —— 获取当前字体里的最大字符的宽度

函数说明:

/**
 * 获取当前字体里的最大字符的宽度
 * @return 返回宽度值
 * @Note 关联方法 setFont
 */
u8g2_uint_t getMaxCharWidth(void)

注意点:

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

3.2.3.7 u8g2.getStrWidth() —— 获取字符串的像素宽度

函数说明:

/**
 * 获取字符串的像素宽度
 * @param s 绘制字符串
 * @return 返回字符串的像素宽度值
 * @Note 关联方法 setFont drawStr
 */
u8g2_uint_t U8G2::getStrWidth(const char *s)

注意点:

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

3.2.3.8 u8g2.getUTF8Width() —— 获取UTF-8字符串的像素宽度

函数说明:

/**
 * 获取UTF-8字符串的像素宽度
 * @param s 绘制字符串
 * @return 返回字符串的像素宽度值
 * @Note 关联方法 setFont drawStr
 */
u8g2_uint_t U8G2::getUTF8Width(const char *s)

注意点:

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

3.2.3.9 u8g2.setAutoPageClear() —— 设置自动清除缓冲区

函数说明:

/**
 * 是否自动清除缓冲区
 * @param mode 0 表示关闭
 *             1 表示开启,默认是开启
 */
void U8G2::setAutoPageClear(uint8_t mode)

注意点:

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

3.2.3.10 u8g2.setBitmapMode() —— 设置位图模式

函数说明:

/**
 * 设置位图模式(定义drawXBM方法是否绘制背景颜色)
 * @param is_transparent
 *         0 绘制背景颜色,不透明,默认是该值
 *         1 不绘制背景颜色,透明
 * @Note 关联方法 drawXBM
 */
void U8G2::setBitmapMode(uint8_t is_transparent)

示例:

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);

3.2.3.11 u8g2.setBusClock() —— 设置总线时钟

函数说明:

/**
 * 设置总线时钟(I2C SPI)
 * @param mode clock_speed 总线时钟频率(Hz)
 * @Note 关联方法 begin
 */
void U8G2::setBusClock(uint32_t clock_speed);

注意点:

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

3.2.3.12 u8g2.setClipWindow() —— 设置采集窗口大小

函数说明:

/**
 * 设置采集窗口,窗口范围从左上角(x0,y0)到右下角(x1,y1)
 * 也就是我们绘制的内容只能在规范范围内显示
 * @param x0 左上角x坐标
 * @param y0 左上角y坐标
 * @param x1 右上角x坐标
 * @param y1 右上角y坐标
 * @Note 关联方法 begin
 */
void U8G2::setClipWindow(u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t x1, u8g2_uint_t y1 );

注意点:

  • 可以通过 setMaxClipWindow 去掉该限制
void U8G2::setMaxClipWindow(void)

示例:

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

3.2.3.13 u8g2.setCursor() —— 设置绘制光标位置

函数说明:

/**
 * 设置绘制光标位置(x,y)
 * @Note 关联方法 print
 */
void U8G2::setCursor(u8g2_uint_t x, u8g2_uint_t y)

示例:

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

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

函数说明:

/**
 * 设置显示器的旋转角度
 * @param u8g2_cb 旋转选项
 *        U8G2_R0 不做旋转 水平
 *        U8G2_R1 旋转90度
 *        U8G2_R2 旋转180度
 *        U8G2_R3 旋转270度
 *        U8G2_MIRROR 不做旋转 水平,显示内容是镜像的,暂时不理解
 */
void setDisplayRotation(const u8g2_cb_t *u8g2_cb)

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

函数说明:

/**
 * 设置绘制颜色(暂时还没有具体去了解用法)
 */
void U8G2::setDrawColor(uint8_t color)

3.2.3.16 u8g2.setFont() —— 设置字体集

这是一个非常重要的方法,非常重要!!!

函数说明:

/**
 * 设置字体集(字体集用于字符串绘制方法或者glyph绘制方法)
 * @param font 具体的字体集
 * @Note 关联方法  drawUTF8 drawStr drawGlyph print
 */
void U8G2::setFont(const uint8_t *font)

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
描述
t Transparent font, Do not use a background color.
h All glyphs have common height(所有的图形有通用的高度).
m All glyphs have common height and width (monospace).
8 All glyphs fit into a 8x8 pixel box.
  • char set
描述
f The font includes up to 256 glyphs.
r Only glyphs on the range of the ASCII codes 32 to 127 are included in the font.
u Only glyphs on the range of the ASCII codes 32 to 95 (uppercase chars) are included in the font.
n Only numbers and extra glyphs for writing date and time strings are included in the font.
... Other custom character list.

注意点:

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

示例:Fonts u8g2_font_5x7_tr and u8g2_font_pressstart2p_8u

3.2.3.17 u8g2.setFontDirection() —— 设置字体方向

函数说明:

/**
 * 定义字符串绘制或者图形绘制的方向
 * @param dir 方向
 * @param 关联方法 drawStr
 */
void U8G2::setFontDirection(uint8_t dir)

注意点:

  • dir参数
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

示例:

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

3.2.4 缓存相关函数

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

3.2.4.1 u8g2.getBufferPtr() —— 获取缓存空间的地址

函数说明

/**
 * 获取缓存空间的地址
 * @return 返回缓存空间起始地址
 * @Note 关联方法 getBufferTileHeight, getBufferTileWidth, clearBuffer
 */
uint8_t *U8G2::getBufferPtr(void)

注意点:

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

3.2.4.2 u8g2.getBufferTileHeight() —— 获取缓冲区的Tile高度

函数说明

/**
 * 获取缓冲区的Tile高度
 * @return 返回高度值
 */
uint8_t U8G2::getBufferTileHeight(void)

注意点:

  • 一个tile等于8个像素点.

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

函数说明

/**
 * 获取缓冲区的Tile宽度
 * @return 返回宽度值
 */
uint8_t U8G2::getBufferTileWidth(void)

注意点:

  • 一个tile等于8个像素点.

3.2.4.4 u8g2.getBufferCurrTileRow() —— 获取缓冲区的当前Tile row

函数说明

/**
 * 获取缓冲区的当前Tile row行数
 * @return 返回当前的tilerow
 */
uint8_t U8G2::getBufferCurrTileRow(void)

注意点:

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

3.2.4.5 u8g2.setBufferCurrTileRow() —— 设置缓冲区的当前Tile row

函数说明

/**
 * 设置缓冲区的当前Tile row
 * @param 当前的tilerow
 */
void U8G2::setBufferCurrTileRow(uint8_t  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();

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

4.如何运用U8G2库

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

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

4.1 区分显示器

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

4.2 选择物理总线方式

    图像信息是通过物理总线方式发给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。

4.3 区分数字连线

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

4.4 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 "128x64_noname" Descirption
U8G2_SSD1306_128X64_NONAME_1_4W_SW_SPI(rotation, clock, data, cs, dc [, reset]) page buffer, size = 128 bytes
U8G2_SSD1306_128X64_NONAME_2_4W_SW_SPI(rotation, clock, data, cs, dc [, reset]) page buffer, size = 256 bytes
U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI(rotation, clock, data, cs, dc [, reset]) full framebuffer, size = 1024 bytes
U8G2_SSD1306_128X64_NONAME_1_4W_HW_SPI(rotation, cs, dc [, reset]) page buffer, size = 128 bytes

    那么,我们这里就需要重点讲述一下构造器的规则。
    构造器的名字包括以下几方面:

No Description Example
1 Prefix U8G2
2 Display Controller SSD1306
3 Display Name 128X64_NONAME
4 Buffer Size 1, 2 or F (full frame buffer)
5 Communication 4W_SW_SPI

    它们之间使用"_"连接起来。其中:

  • BufferSize,缓存大小
BufferSize Description
1 保持一页的缓冲区,用于firstPage/nextPage的PageMode.
2 保持两页的缓冲区,用于firstPage/nextPage的PageMode..
F 获取整个屏幕的缓冲区,ram消耗大,一般用在ram空间比较大的arduino板子.
  • Communication,通信协议
Communication Description
4W_SW_SPI 4-wire (clock, data, cs and dc) software emulated SPI
4W_HW_SPI 4-wire (clock, data, cs and dc) hardware SPI (based on Arduino SPI library)
2ND_4W_HW_SPI If supported, second 4-wire hardware SPI (based on Arduino SPI library)
3W_SW_SPI 3-wire (clock, data and cs) software emulated SPI
SW_I2C Software emulated I2C/TWI
HW_I2C Hardware I2C based on the Arduino Wire library
2ND_HW_I2C If supported, use second hardware I2C (Arduino Wire lib)
6800 8-bit parallel interface, 6800 protocol
8080 8-bit parallel interface, 8080 protocol
  • Rotation (软件模拟总线前提下的构造器的第一个参数)
Rotation/Mirror Description
U8G2_R0 No rotation, landscape
U8G2_R1 90 degree clockwise rotation
U8G2_R2 180 degree clockwise rotation
U8G2_R3 270 degree clockwise rotation
U8G2_MIRROR No rotation, landscape, display content is mirrored (v2.6.x)

所以,一个完整的例子为:

#include <Arduino.h>
#include <U8g2lib.h>
#include <SPI.h>
#include <Wire.h>

U8G2_ST7920_128X64_1_SW_SPI u8g2(U8G2_R0, 13, 11, 10, 8);

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() );
}

    那么,我们来看看到底构造器里面做了什么操作?还是以博主使用的SSD1306 128X64为例子:

/**
 * SSD1306 4线软件模拟SPI
 */
U8G2_SSD1306_128X64_NONAME_1_4W_SW_SPI(.....)

    类定义:

/**
 * SSD1306构造器,继承U8G2
 */
class U8G2_SSD1306_128X64_NONAME_1_4W_SW_SPI : public U8G2 {
  public: U8G2_SSD1306_128X64_NONAME_1_4W_SW_SPI(const u8g2_cb_t *rotation, uint8_t clock, uint8_t data, uint8_t cs, uint8_t dc, uint8_t reset = U8X8_PIN_NONE) : U8G2() {
    //配置SSD1306
    u8g2_Setup_ssd1306_128x64_noname_1(&u8g2, rotation, u8x8_byte_arduino_4wire_sw_spi, u8x8_gpio_and_delay_arduino);
    //设置通信协议
    u8x8_SetPin_4Wire_SW_SPI(getU8x8(), clock, data, cs, dc, reset);
  }
};

    从上面代码看出,默认调用了父类U8G2的构造函数,我们看看它里面做了什么:

class U8G2 : public Print
{
  protected:
    u8g2_t u8g2;
    u8x8_char_cb cpp_next_cb; /*  the cpp interface has its own decoding function for the Arduino print command */
  public:
    u8g2_uint_t tx, ty;

    U8G2(void) {
          //设置Arduino print函数的解码方法,这里是ASCII,当然也有UTF-8
          cpp_next_cb = u8x8_ascii_next;
          //屏幕初始化
          home();
    }
    .......
  • U8G2类构造函数主要是定义好解码方法以及初始化屏幕(包括重置原点);

    然后我们看看,u8g2_Setup_ssd1306_128x64_noname_1做了什么:

void u8g2_Setup_ssd1306_128x64_noname_1(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb)
{
  uint8_t tile_buf_height;
  //定义好缓存空间 记住 这里是 1 page mode
  uint8_t *buf;
  //配置屏幕
  u8g2_SetupDisplay(u8g2, u8x8_d_ssd1306_128x64_noname, u8x8_cad_001, byte_cb, gpio_and_delay_cb);
  //生成buf 这里是128 bytes
  buf = u8g2_m_16_8_1(&tile_buf_height);
  //初始化buf
  u8g2_SetupBuffer(u8g2, buf, tile_buf_height, u8g2_ll_hvline_vertical_top_lsb, rotation);
}

/*============================================*/
/*
  This procedure is called after setting up the display (u8x8 structure).
  --> This is the central init procedure for u8g2 object
*/
void u8g2_SetupBuffer(u8g2_t *u8g2, uint8_t *buf, uint8_t tile_buf_height, u8g2_draw_ll_hvline_cb ll_hvline_cb, const u8g2_cb_t *u8g2_cb)
{
  u8g2->font = NULL;
  //u8g2->kerning = NULL;
  //u8g2->get_kerning_cb = u8g2_GetNullKerning;

  //u8g2->ll_hvline = u8g2_ll_hvline_vertical_top_lsb;
  u8g2->ll_hvline = ll_hvline_cb;

  u8g2->tile_buf_ptr = buf;
  u8g2->tile_buf_height = tile_buf_height;

  u8g2->tile_curr_row = 0;//页码 这是一个很重要的参数

  u8g2->font_decode.is_transparent = 0; /* issue 443 */
  u8g2->bitmap_transparency = 0;

  u8g2->draw_color = 1;
  u8g2->is_auto_page_clear = 1;//自动清除

  u8g2->cb = u8g2_cb;
  u8g2->cb->update_dimension(u8g2);
#ifdef U8G2_WITH_CLIP_WINDOW_SUPPORT
  u8g2_SetMaxClipWindow(u8g2);      /* assign a clip window and call the update() procedure */
#else
  u8g2->cb->update_page_win(u8g2);
#endif

  u8g2_SetFontPosBaseline(u8g2);  /* issue 195 */

#ifdef U8G2_WITH_FONT_ROTATION
  u8g2->font_decode.dir = 0;
#endif
}

可以总结几点:

  • 配置初始化屏幕
  • 配置初始化缓冲区

注意点:

  • 上面讲解涉及到了绘制模式,请参考下一节;

4.5 U8g2绘制模式

    U8g2支持三种绘制模式:

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

4.5.1 Full screen buffer mode

特点:

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

构造器:

  • 构造器必须带有F,比如:
U8G2_ST7920_128X64_F_SW_SPI(rotation, clock, data, cs [, reset])

用法:

  1. 清除缓冲区 u8g2.clearBuffer()
  2. 操作一些绘制方法
  3. 发送缓冲区的内容到显示器 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();
}

4.5.2 Page mode

特点:

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

构造器:

  • 构造器必须带有“1”或者2,比如:
U8G2_ST7920_128X64_ 1 _SW_SPI(rotation, clock, data, cs [, reset])

用法:

  1. 调用 u8g2.firstPage()
  2. 开始一个 do while 循环
  3. 在循环内部 操作一些绘制方法
  4. 不断判断 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() );
}

4.5.3 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库,一篇就够的更多相关文章

  1. 玩转u8g2 OLED库 MAX7219_32X8点阵模块

    u8g2 OLED库 + MAX7219_32X8点阵模块 理论基础 玩转u8g2 OLED库,一篇就够 玩转u8g2 OLED库,一篇就够(字数太多 要分篇) 实验内容 Full screen bu ...

  2. 玩转u8g2 OLED库,一篇就够(分篇)

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  3. Python GUI之tkinter窗口视窗教程大集合(看这篇就够了) JAVA日志的前世今生 .NET MVC采用SignalR更新在线用户数 C#多线程编程系列(五)- 使用任务并行库 C#多线程编程系列(三)- 线程同步 C#多线程编程系列(二)- 线程基础 C#多线程编程系列(一)- 简介

    Python GUI之tkinter窗口视窗教程大集合(看这篇就够了) 一.前言 由于本篇文章较长,所以下面给出内容目录方便跳转阅读,当然也可以用博客页面最右侧的文章目录导航栏进行跳转查阅. 一.前言 ...

  4. 玩转 RTC时钟库 DS3231

    1.前言     接着博主的上一篇 玩转 RTC时钟库 + DS1302,这一篇我们重点讲解DS3231时钟模块.没有看过上一篇的同学,麻烦先去阅读一下,因为很多理论基础已经在上一篇做了详细讲解,这里 ...

  5. 支撑微博亿级社交平台,小白也能玩转Redis集群(实战篇)

    上篇文章<支撑微博亿级社交平台,小白也能玩转Redis集群(原理篇)>介绍了Redis集群相关原理,这篇文章将介绍Redis Cluster集群的搭建.配置,运维.扩容等具体操作 集群搭建 ...

  6. 关于 Docker 镜像的操作,看完这篇就够啦 !(下)

    紧接着上篇<关于 Docker 镜像的操作,看完这篇就够啦 !(上)>,奉上下篇 !!! 镜像作为 Docker 三大核心概念中最重要的一个关键词,它有很多操作,是您想学习容器技术不得不掌 ...

  7. .NET Core实战项目之CMS 第二章 入门篇-快速入门ASP.NET Core看这篇就够了

    作者:依乐祝 原文链接:https://www.cnblogs.com/yilezhu/p/9985451.html 本来这篇只是想简单介绍下ASP.NET Core MVC项目的(毕竟要照顾到很多新 ...

  8. 如果这样来理解HTTPS,一篇就够了!

    1.前言 可能有初学者会问,即时通讯应用的通信安全,不就是对Socket长连接进行SSL/TLS加密这些知识吗,干吗要理解HTTPS协议呢. 这其实是个误解:当今主流的移动端IM数据通信,总结下来无外 ...

  9. [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了

    [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 本文首发自:博客园 文章地址: https://www.cnblogs.com/yilezhu/p/ ...

随机推荐

  1. 工厂/Builder,桥接/策略

    1.工厂 vs 抽象工厂 工厂方法模式: 用来加工.生产对象的类.比如说我想要一个汽车类,但是我总不能现场给你造个车出来对吧?于是我找到工厂类,然后工厂帮我把发动机型号选好,轮胎装好,油漆喷好,然后把 ...

  2. Linux线程唤醒与等待

    生产者消费者模式在程序设计中出现频率非常高,经常会有线程间通过消息队列或其他共享变量进行交互的场景.而这时就会出现一个问题,消费者如何知道生产者已经生产了数据呢?有的程序会采取消费者循环判断消息队列大 ...

  3. 基于SpringBoot实现AOP+jdk/CGlib动态代理详解

    动态代理是一种设计模式.在Spring中,有俩种方式可以实现动态代理--JDK动态代理和CGLIB动态代理. JDK动态代理 首先定义一个人的接口: public interface Person { ...

  4. Django&,Flask&pyrthon原生sql语句 基本操作

    Django框架 ,Flask框架 ORM 以及pyrthon原生sql语句操作数据库 WHAT IS ORM? ORM( Object Relational Mapping) 对象关系映射 , 即通 ...

  5. 使用JSP+Servlet+Jdbc+Echatrs实现对豆瓣电影Top250的展示

    使用JSP+Servlet+Jdbc+Echatrs实现对豆瓣电影Top250的展示 写在前面: 有的小伙伴,会吐槽啦,你这个标题有点长的啊.哈哈 ,好像是的!不过,这个也是本次案例中使用到的关键技术 ...

  6. 小型APP系统开发与应用项目实训

    实训项目 :             小型APP系统开发与应用项目实训                           项目成品名称:          果乐多商城               项 ...

  7. 杭州蓝松科技---短视频SDK介绍

    蓝松短视频的口号和 更新周期: 我们的口号是:  蓝松短视频  任意个性化. 我们是杭州蓝松科技,  专业做视频短视频SDK的技术团队. 我们提供 Android/IOS平台上的 短视频编辑SDK,  ...

  8. 应用角度看kafka的术语和功能

    kafka的术语(Terminology) Topic 和Consumer Group Topic 每条发布到 Kafka 集群的消息都有一个类别,这个类别被称为 Topic.(物理上不同 Topic ...

  9. .NET进阶篇-丑话先说,Flag先立

    作为开发者,工作了几年,也总觉得技术栈和刚毕业区别不大,用的技术还都是N年前的,每每看到新东西,也只心里哇塞惊叹一下,然后就回归于忙碌.怪自己的技术池太浅,热门的令人称奇的技术也都是在其他巨人的肩膀上 ...

  10. .Net Core与Vue.js模块化前后端分离快速开发解决方案(NetModular)

    NetModular是什么? NetModular不仅仅是一个框架,它也是一整套的模块化与前后端分离的快速开发的解决方案,目标是致力于开箱即用,让开发人员完全专注于业务开发,不需要关心底层封装和实现. ...