
ESP8266驱动0.96寸oled显示屏
ESP8266驱动0.96寸oled显示屏
- 概述: ESP8266是一款集成了32位Tensilica微控制器、Wi-Fi MAC/BB/RF/PA/LNA的片上系统(SoC)。 它支持2.4 GHz Wi-Fi(802.11 b/g/n),并具备WPA/WPA2安全特性。 ESP8266适用于各种物联网设备,如智能家居、环境监测、工业自动化等。
- 硬件特性: 处理器:基于Tensilica's Diamond Standard 106Micro 32位处理器控制内核,工作频率可达80 MHz(或超频至160 MHz)。 内存:64 KiB引导ROM、32 KiB指令RAM、80 KiB用户数据RAM(另有32 KiB指令高速缓存RAM和16 KiB ETS系统数据RAM)。 封装:5 mm×5 mm Quad Flat No-Leads封装,带有33个连接焊盘。 接口:16个GPIO引脚、I²C、SPI、UART等,支持DMA的I²S接口(与GPIO共用引脚)。
- 软件特性: ESP8266支持多种开发方式,包括AT指令集开发、Arduino开发、Lua脚本开发等。 开发者可以利用ESP8266的Wi-Fi功能,轻松连接到现有的Wi-Fi网络,或将其设置为Wi-Fi接入点(AP)或站点(Station)。 供电电压范围为2.5V至3.6V,具有低功耗特性,适合电池供电的应用场景。
- 应用示例: ESP8266可以作为独立的网络控制器,为现有设备添加联网功能。 它可以通过串口与单片机进行通讯,访问API获取天气信息、完成网络授时等任务。 ESP8266支持多种型号和模块,如ESP-01、ESP-01S、ESP-12F等,开发者可以根据项目需求选择合适的型号。 开发环境: ESP8266的开发环境通常包括IDE(如Arduino IDE、PlatformIO等)和相应的开发板支持包(Board Support Package, BSP)。 开发者可以使用这些环境编写和上传代码到ESP8266芯片中,实现各种物联网应用。
ESP8266
首先来看一下ESP8266的引脚定义,因为本篇需要外接OLED,就要先看看ESP8266具有哪些功能的引脚。
ESP8266的引脚定义如下:

可以看出,ESP8266的功能引脚包括:
- 3个串口:TXD、RXD
- 2个SPI接口:MOSI、MISO、SCLK、CS
- 1个IIC接口:SDA、SCL
- 多个数字输入/输出接口:D1~D8
- 1个模拟输入/输出接口:A0
2 OLED简介
OLED模块的尺寸多种多样,比较常用的是0.96寸的矩形的,也有其它尺寸的OLED。
此外,屏幕的接口,一般有IIC接口和SPI接口两种。加上电源,IIC接口需要4根线,而SPI接口需要6根线,IIC的通信比SPI通信慢,但4线接线更方便。

本篇使用最为常用的0.96寸的OLED,分辨率128x64,黄蓝双色。
注意这里的双色,不是值一个像素点可以显示两种颜色,而是屏幕的上部1/4只能显示黄色,下部的3/4只能显示蓝色,并且黄色和蓝色之间,不是紧密靠在一起的,而是有约一个像素点的间隙。

3 U8g2库简介与安装
3.1 U8g2库简介
U8g2 是一个用于嵌入式设备的单色图形库。U8g2支持单色OLED和LCD,并支持如SSD1306等多种类型的OLED驱动。
U8g2源码的开源库地址:https://github.com/olikraus/u8g2
U8g2专为Arduino提供的方便安装的库地址:https://github.com/olikraus/U8g2_Arduino
想要研究U8g2源码的可以看看这里的源代码,C和C++写的。
比如画直线这个函数和具体实现如下:

3.2 U8g2库安装
和上篇介绍ESP8266库的安装类似, U8g2库的安装也有两种方式:
- 在线安装
在线安装,在Arduino IDE的菜单的“项目->加载库->管理库”中搜索u8g2后安装即可,对网络环境要求较高

- 源码安装
将U8g2专为Arduino提供的库(https://github.com/olikraus/U8g2_Arduino)整个下载下来,然后还是在Arduino IDE的菜单的“项目->加载库”中选择“添加.ZIP库...”,然后选到你刚下载的U8g2_Arduino源码文件夹后即可安装,也十分的方便。

3.3 U8g2库的基础使用
使用U8g2库进行OLED的显示十分简单,首先要包含两个库,U8g2lib和Wire,后者是IIC通信需要用。
对于IIC接口的OLED,需要在程序中指定一下引脚的接口定义,如果是SPI接口,可以参考U8g2库自带例程中SPI接口是使用方法。
然后在Ardunio的setup中进行u8g2的初始化。
最后在Ardunio的loop中就可以编写自己的逻辑了。
另外,U8g2库在loop中的通用写法是使用do{}while()的形式:
u8g2.firstPage();
do
{
//自己的的逻辑
} while (u8g2.nextPage());
delay(1000);
一个简单的HelloWord在OLED中的显示如下:
#include <U8g2lib.h>
#include <Wire.h>
#define SCL 5
#define SDA 4
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /*clock=*/SCL, /*data=*/SDA, /*reset=*/U8X8_PIN_NONE);
void setup()
{
u8g2.begin();
u8g2.enableUTF8Print(); // enable UTF8 support for the Arduino print() function
}
void loop()
{
u8g2.setFont(u8g2_font_unifont_t_symbols);
u8g2.firstPage();
do
{
u8g2.setCursor(0, 15); //指定显示位置
u8g2.print("Hello World!"); //使用print来显示字符串
} while (u8g2.nextPage());
delay(1000);
}
注意,setCursor(0, 15),是将画图位置移动到x=0,y=15处,然后以这个点的右上区域进行字符串的显示,这样看起来就是显示在OLED的第一行,如果你设置setCursor(0, 0),字符串实际是到屏幕外面了,不会显示!

4U8g2常用API函数
4.1 基础设置
setFont(font) 设置字体
font
:u8g2的字体,比较常用的有u8g2_font_unifont_t_symbols(通常使用这个)和u8g2_font_wqy12_t_gb2312b(用于显示汉字)等setFontMode(num) 设置字体背景颜色模式
num
:启用(1
)透明模式num
:禁用(0
)透明模式setDrawColor(color) 设置所有绘图函数的位值
color
:0
(显示RAM中的清晰像素值)color
:1
(设置像素值)color
:2
(异或模式)
4.2 画像素点
- drawPixel(x,y)
只有指定位置即可显示像素点,比如把所有的点都显示出来:
//画像素点-填充屏幕
void testDrawPixelToFillScreen()
{
int t = 1000;
u8g2.clearBuffer();
for (int j = 0; j < 64; j++)
{
for (int i = 0; i < 128; i++)
{
u8g2.drawPixel(i, j);
}
}
SEND_BUFFER_DISPLAY_MS(t);
}
效果如下面的右图:

注意测试程序中,我定义了一个宏定义,用于延时显示每一次的画图,方便观察OLED的显示过程:
#define SEND_BUFFER_DISPLAY_MS(ms)\
do {\
u8g2.sendBuffer(); \
delay(ms);\
}while(0);
可以指定延时时间,如500毫秒或1000毫秒等。
4.3 画直线
drawLine(x0,y0,x1,y1) 画一条线
x0,y0
线的起点x1,y1
线的终点drawHLine(x,y,w) 画一条水平线
x,y
线的起点w
水平线的长度(宽度)drawVLine(x,y,h) 画一条竖直线
x,y
线的起点h
竖直线的长度(高度)
测试函数:
//画直线
void testDrawLine()
{
int t = 500;
u8g2.clearBuffer();
u8g2.drawStr(33, 14, "drawLine");
u8g2.drawLine(0, 0, 127, 63);
SEND_BUFFER_DISPLAY_MS(t);
u8g2.drawLine(0, 0, 127, 0);
SEND_BUFFER_DISPLAY_MS(t);
u8g2.drawLine(32, 15, 127, 15);
SEND_BUFFER_DISPLAY_MS(t);
u8g2.drawLine(33, 16, 127, 16);
SEND_BUFFER_DISPLAY_MS(t);
u8g2.drawLine(127, 0, 127, 15);
SEND_BUFFER_DISPLAY_MS(t);
u8g2.drawLine(127, 16, 127, 63);
SEND_BUFFER_DISPLAY_MS(t);
}
显示效果如下面的左上图:

4.4 画空心/实心(圆角)矩形
drawFrame(x,y,w,h) 绘制一个空心框
drawBox(x,y,w,h) 绘制一个实心矩形
drawRFrame(x,y,w,h,r) 绘制一个空心框(圆角)
drawRBox(x,y,w,h,r) 绘制一个实心矩形 (圆角)
x,y
起点坐标w,h
框的宽度和高度r
圆角的半径
测试函数:
//画空心圆角矩形
void testDrawRFrame()
{
int t = 500;
int x = 16;
int y = 32;
int w = 50;
int h = 20;
int r = 3;
u8g2.clearBuffer();
u8g2.drawStr(0, 15, "drawRFrame");
u8g2.drawRFrame(x, y, w, h, r);
SEND_BUFFER_DISPLAY_MS(t);
u8g2.drawRFrame(x+w+5, y-10, w-20, h+20, r);
SEND_BUFFER_DISPLAY_MS(t);
}
显示效果如下面的右下图:

4.5 画空心/实心圆
drawCircle(x,y,rad,opt) 绘制一个空心圆
drawDisc(x,y,rad,opt) 绘制一个实心圆
x,y
为圆心坐标rad
为圆的半径opt
为选择画的部分,分为:U8G2_DRAW_UPPER_RIGHT
(右上)U8G2_DRAW_UPPER_LEFT
(左上)U8G2_DRAW_LOWER_LEFT
(左下)U8G2_DRAW_LOWER_RIGHT
(右下)U8G2_DRAW_ALL
(全部)
空心圆
//画空心圆
void testDrawCircle()
{
int t = 500;
int stx = 0; //画图起始x
int sty = 16; //画图起始y
int with = 16;//一个图块的间隔
int r = 15; //圆的半径
u8g2.clearBuffer();
u8g2.drawStr(0, 15, "drawCircle");
u8g2.drawCircle(stx, sty - 1 + with, r, U8G2_DRAW_UPPER_RIGHT); //右上
SEND_BUFFER_DISPLAY_MS(t);
u8g2.drawCircle(stx + with, sty, r, U8G2_DRAW_LOWER_RIGHT); //右下
SEND_BUFFER_DISPLAY_MS(t);
u8g2.drawCircle(stx - 1 + with * 3, sty - 1 + with, r, U8G2_DRAW_UPPER_LEFT); //左上
SEND_BUFFER_DISPLAY_MS(t);
u8g2.drawCircle(stx - 1 + with * 4, sty, r, U8G2_DRAW_LOWER_LEFT); //左下
SEND_BUFFER_DISPLAY_MS(t);
u8g2.drawCircle(stx - 1 + with * 2, sty - 1 + with * 2, r, U8G2_DRAW_ALL);//整个圆
SEND_BUFFER_DISPLAY_MS(t);
}
显示效果如下面的左图:

注意,U8g2库画出的圆,因像素点的显示原理,圆的直径占用的宽度不是半径的2倍,而是2倍再加一个像素点。
4.6 画空心/实心椭圆
drawEllipse(x,y,rx,ry,opt) 绘制一个空心椭圆
drawFilledEllipse(x,y,rx,ry,opt) 绘制一个实心椭圆
x,y
为圆心坐标rx,ry
为与椭圆x和y方向的半径opt
与画圆时的作用一致
椭圆的显示与圆的显示类似,只是椭圆可以分别指定x和y方向的半径

4.7 字符串、汉字和变量显示
字符串的显示,可以使用drawStr函数,也可以使用通用风格的print函数。
drawStr(x,y,string) 绘制一个字符串
x,y
起点坐标string
字符串
如果想要使用print显示汉字,需要先设置如下两句:
u8g2.enableUTF8Print();//enable UTF8
u8g2.setFont(u8g2_font_wqy12_t_gb2312b);//设置中文字符集
如果想要显示变量,使用print函数即可:
字符串、汉字、变量的测试函数如下:
//字符串/文字/变量显示测试
void testDrawStr()
{
int t = 1000;
u8g2.clearBuffer();
u8g2.drawStr(0, 14, "drawStr / print");
SEND_BUFFER_DISPLAY_MS(t);
u8g2.drawStr(0, 32, "~!@#$%^&*()_+");
SEND_BUFFER_DISPLAY_MS(t);
u8g2.enableUTF8Print();//enable UTF8
u8g2.setFont(u8g2_font_wqy12_t_gb2312b);//设置中文字符集
u8g2.setCursor(0, 48);
u8g2.print("码农爱学习");
SEND_BUFFER_DISPLAY_MS(t);
int a = 234;
u8g2.setCursor(0, 64);
u8g2.print("int a = ");
u8g2.setCursor(40, 64);
u8g2.print(a);//显示变量
SEND_BUFFER_DISPLAY_MS(t);
}
显示效果:

4.8 画内置图标
drawGlyph(x,y,addr) 绘制U8g2内置的图标
x,y
起点坐标addr
内置图标的地址
U8g2库内置了需要预先定义的图形,通过drawGlyp函数以及指定的地址,即可看OLED上显示对应的图标:
各个图形的地址定义如下:

编写一个测试程序:
void testGlyph()
{
int t = 1000;
u8g2.clearBuffer();
u8g2.drawStr(0, 14, "drawGlyph");
u8g2.drawGlyph(0, 32, 0x23f0);
SEND_BUFFER_DISPLAY_MS(t);
u8g2.drawGlyph(16, 32, 0x23f3);
SEND_BUFFER_DISPLAY_MS(t);
u8g2.drawGlyph(32, 32, 0x2603);
SEND_BUFFER_DISPLAY_MS(t);
u8g2.drawGlyph(48, 32, 0x2615);
SEND_BUFFER_DISPLAY_MS(t);
u8g2.drawGlyph(64, 32, 0x2618);
SEND_BUFFER_DISPLAY_MS(t);
}
测试效果如下:

4.9 画自定义图片
drawXBM(x,y,w,h,addr) 绘制一个实心矩形 (圆角)
x,y
起点坐标w,h
图片的宽度和高度`addr
图片(数组)的地址
自定义图片的显示,需要先将图形转换为数组,可以使用如下工具进行图片到数组的转换:
https://tools.clz.me/image-to-bitmap-array

编写测试程序:
// width: 128, height: 48
const unsigned char bilibili[] U8X8_PROGMEM = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ... 省略若干行 };
void testDrawXBM()
{
int t = 1000;
u8g2.clearBuffer();
u8g2.drawStr(0, 14, "drawXBM");
u8g2.drawXBM(0, 16, 128, 48, bilibili);
SEND_BUFFER_DISPLAY_MS(t);
}
效果如下:

5 总结
笔记来自大佬https://gitee.com/xxpcb
本篇介绍了ESP8266的引脚定义以及U8g2库在OLED的使用基础,并重点介绍了U8g2库的各种画图函数,这个函数总结下来如下下表所示:

借助U8g2库,可以十分方便的在OLED上进行图形的显示。
本篇的测试代码已开源于我的gitee仓库:https://gitee.com/xxpcb/esp8266-arduino-instance,感觉不错的,欢迎star~