ESP8266 Arduino开发之路(9)— OLED的UI显示控制
一、前言
在上一节中,我们使用了esp8266-oled-ssd1306
库来实现OLED屏幕的显示,该库函数还提供了UI显示的文件库:OLEDDisplayUi.cpp
,我们可以使用其提供的库来实现一些很炫酷的UI界面。
Ui库提供了一组基本的Ui元素,称为Frames
和Overlays
;Frames被用来提供基本的显示,在设定的时间内显示一帧图像后移动到下一个图像显示,然后该库还提供了一个将相应更新的指示符。另一方面,Overlays是一个总是显示在相同位置的控件。
参考:https://github.com/ThingPulse/esp8266-oled-ssd1306/tree/4.2.0#ui-library-oleddisplayui
二、UI基本配置
1、对象定义
要使用OLEDDisplayUi库提供的UI控制功能,我们需要定义一个oled的ui对象,
/* 新建一个olde屏幕ui控制对象,参数为oled屏幕对象指针 */
OLEDDisplayUi ui(&oled);
2、帧率设置
接下来设置帧率,ESP8266在80MHz主频模式下可以达到渲染60fps的帧率图像,但是这几乎会将CPU的时间占满,你将没有太多的时间做其他事情,我们建议在160MHz的主频模式下设置60fps,或者设置为直接设置为30帧。
/* 1.设置帧率,当设置为60fps时,会几乎占满cpu,没有太多时间做其他事 */ui.setTargetFPS(60);
3、指示器栏设置
如下图所示,该指示器栏表现了页面总数和当前页面所在的位置,每个指示点的像素为8*8
,
页面总数通过setInactiveSymbol
设置,即所谓非活动符号,当前页面符号称为活动符号,通过setActiveSymbol
设置。
/* 2. 设置设置指示栏活动和非活动符号外形 */ui.setActiveSymbol(activeSymbol); // 外形数组activeSymbol[]在image.h中定义ui.setInactiveSymbol(inactiveSymbol); // 外形数组inactiveSymbol[]在image.h中定义
其参数为外形像素点数组,需要我们自己定义,为一个8字节数组,每一位表示一个像素点,共8*8
个像素点,如下所示,数组中的元素使用二进制表示
const uint8_t activeSymbol[] PROGMEM = {
B00000000,B00000000,B00011000,B00100100,B01000010,B01000010,B00100100,B00011000
};const uint8_t inactiveSymbol[] PROGMEM = {
B00000000,B00000000,B00000000,B00000000,B00011000,B00011000,B00000000,B00000000
};
然后还可以设置指示栏的位置,提供TOP
, LEFT
, BOTTOM
, RIGHT
四个位置的设置
/* 3. 指示栏位置设置: TOP, LEFT, BOTTOM, RIGHT */ui.setIndicatorPosition(BOTTOM);
如下所示为指示栏位于TOP
和LEFT
的效果
4、动画效果
该UI库提供了四个动画效果:SLIDE_LEFT
, SLIDE_RIGHT
, SLIDE_UP
,SLIDE_DOWN
,
即在上一幅图像和下一幅图像切换时的动画效果,
/* 5. 设置从上一帧过渡到下一帧的动画效果:SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN */ui.setFrameAnimation(SLIDE_LEFT);
5、添加图像
接下来就到最重要的环节了,我们需要设置我们需要显示的每一幅图像
/* 6. 添加我们需要显示的图像,第一个参数为添加的图像数组,第二个参数为图像数量 */ui.setFrames(frames, frameCount);
在这个方法调用之前,我们需要将图像数组和图像数量定义出来
/* 将要显示的每一帧图像函数定义在一个数组中 */
FrameCallback frames[] = {
drawFrame1, drawFrame2, drawFrame3, drawFrame4, drawFrame5 };
/* 图像数量 */
int frameCount = 5;
而图像数组中的每一个函数都需要我们自己编写好,例如:
/* 要显示的第1帧图像 */
void drawFrame1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
/* 绘制一个xbm图像 *//* 注意:所有坐标位置都需要相对于传入参数x和y绘制 */display->drawXbm(x + 34, y + 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits);
}
6、Overlay
Overlay
是一个总是显示在相同位置的控件,我们可以用来显示如时钟这类信息。
/* 7. 添加一个Overlays,Overlay是一个总是显示在相同位置的控件,第一个参数为添加的Overlays数组,第二个参数为Overlays数量*/ui.setOverlays(overlays, overlaysCount);
在这个方法调用之前,我们需要将Overlays数组和Overlays数量定义出来
/* 将要显示的每一个Overlays函数定义在一个数组中 */
OverlayCallback overlays[] = {
msOverlay };
/* Overlays数量 */
int overlaysCount = 5;
而Overlays数组中的每一个函数都需要我们自己编写好,例如下所示,将当前系统运行时间显示在右上角;
millis()
函数用来获取Arduino开机后运行的时间长度,该时间长度单位是毫秒,最长可记录接近50天左右的时间。如果超出记录时间上限,记录将从0重新开始。
/* 一个Overlay定义函数 */
void msOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) {
display->setTextAlignment(TEXT_ALIGN_RIGHT);display->setFont(ArialMT_Plain_10);display->drawString(128, 0, String(millis()));
}
三、实现效果
主程序代码如下所示
/** ESP8266-NodeMCU通过驱动oled显示ui界面* 需要使用Arduino-OLED第三方库:https://github.com/ThingPulse/esp8266-oled-ssd1306/tree/4.2.0* ui界面显示需要用到OLEDDisplayUi.cpp和OLEDDisplayUi.h*//* 使用0.96寸的OLED屏幕需要使用包含这个头文件 */
#include "SSD1306Wire.h"
/* OLED屏幕ui界面需要使用的头文件 */
#include "OLEDDisplayUi.h"#include "text.h"
#include "image.h"/* 设置oled屏幕的相关信息 */
const int I2C_ADDR = 0x3c; // oled屏幕的I2c地址
#define SDA_PIN 4 // SDA引脚,默认gpio4(D2)
#define SCL_PIN 5 // SCL引脚,默认gpio5(D1)/* 新建一个oled屏幕对象,需要输入IIC地址,SDA和SCL引脚号 */
SSD1306Wire oled(I2C_ADDR, SDA_PIN, SCL_PIN);/* 新建一个olde屏幕ui控制对象,参数为oled屏幕对象指针 */
OLEDDisplayUi ui(&oled);void setup() {
/* 1. 初始化串口通讯波特率为115200*/Serial.begin(115200);/* 2. oled屏幕ui显示控制初始化*/oled_ui_init();/* 3. oled屏幕初始化 */oled.init();oled.flipScreenVertically(); // 设置屏幕翻转oled.setContrast(255); // 设置屏幕亮度oled.clear(); oled.display(); // 清除屏幕
}/* 要显示的第1帧图像 */
void drawFrame1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
/* 绘制一个xbm图像 *//* 注意:所有坐标位置都需要相对于传入参数x和y绘制 */display->drawXbm(x + 34, y + 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits);
}/* 要显示的第2帧图像 */
void drawFrame2(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
/* 显示在SSD1306Fonts.h中的3中默认字体 */display->setTextAlignment(TEXT_ALIGN_LEFT);display->setFont(ArialMT_Plain_10);display->drawString(0 + x, 10 + y, "Arial 10");display->setFont(ArialMT_Plain_16);display->drawString(0 + x, 20 + y, "Arial 16");display->setFont(ArialMT_Plain_24);display->drawString(0 + x, 34 + y, "Arial 24");
}/* 要显示的第3帧图像 */
void drawFrame3(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
/* 显示不同对齐方式下的字母显示方式 */display->setFont(ArialMT_Plain_10);/* 左对齐显示,起始坐标需要设置在左起始点 */display->setTextAlignment(TEXT_ALIGN_LEFT);display->drawString(0 + x, 11 + y, "Left aligned (0,10)");/* 中心对其显示,起始坐标需要设置在中心位置 */display->setTextAlignment(TEXT_ALIGN_CENTER);// display->drawString(64 + x, 22 + y, "Center aligned (64,22)");display->drawString(64 + x, 22 + y, "Center aligned (64,22)");/* 右对齐显示,起始坐标需要设置在最右侧坐标位置 */display->setTextAlignment(TEXT_ALIGN_RIGHT);// display->drawString(128 + x, 33 + y, "Right aligned (128,33)");display->drawString(128 + x, 33 + y, "Right aligned (128,33)");
}/* 要显示的第4帧图像 */
void drawFrame4(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
/* 多行文本显示 *//* 在给定位置绘制一个最大宽度的字符串。如果给定的字符串比指定的宽度宽,文本将以空格或破折号换行到下一行*/display->setTextAlignment(TEXT_ALIGN_LEFT);display->setFont(ArialMT_Plain_10);display->drawStringMaxWidth(0 + x, 10 + y, 128, "Lorem ipsum\n dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore.");
}/* 要显示的第5帧图像 */
void drawFrame5(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
/* 空图像 */
}/* 将要显示的每一帧图像函数定义在一个数组中 */
FrameCallback frames[] = {
drawFrame1, drawFrame2, drawFrame3, drawFrame4, drawFrame5 };
/* 图像数量 */
int frameCount = 5;/* 一个Overlay定义函数 */
void msOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) {
display->setTextAlignment(TEXT_ALIGN_RIGHT);display->setFont(ArialMT_Plain_10);display->drawString(128, 0, String(millis()));
}/* 将要显示的每一个Overlays函数定义在一个数组中 */
OverlayCallback overlays[] = {
msOverlay };
/* Overlays数量 */
int overlaysCount = 1;/* oled屏幕ui显示控制初始化*/
void oled_ui_init(void)
{
/* 1.设置帧率,当设置为60fps时,会几乎占满cpu,没有太多时间做其他事 */ui.setTargetFPS(60);/* 2. 设置指示栏活动和非活动符号外形 */ui.setActiveSymbol(activeSymbol); // 外形数组activeSymbol[]在image.h中定义ui.setInactiveSymbol(inactiveSymbol); // 外形数组inactiveSymbol[]在image.h中定义/* 3. 指示栏位置设置: TOP, LEFT, BOTTOM, RIGHT */ui.setIndicatorPosition(BOTTOM);/* 4. 设置指示栏指示条移动的方向 */ui.setIndicatorDirection(LEFT_RIGHT);/* 5. 设置从上一帧过渡到下一帧的动画效果:SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN */ui.setFrameAnimation(SLIDE_LEFT);/* 6. 添加我们需要显示的图像,第一个参数为添加的图像数组,第二个参数为图像数量 */ui.setFrames(frames, frameCount);/* 7. 添加一个Overlays,Overlay是一个总是显示在相同位置的控件,第一个参数为添加的Overlays数组,第二个参数为Overlays数量*/ui.setOverlays(overlays, overlaysCount);
}void loop() {
/* 刷新OLED屏幕,返回值为在当前设置的刷新率下显示完当前图像后所剩余的时间 */int remainingTimeBudget = ui.update();if (remainingTimeBudget > 0) {
// 我们可以在这剩余的时间做一些事情,但是如果时间不够,就不要做任何事情了delay(remainingTimeBudget);Serial.println(remainingTimeBudget);}/* LED状态取反 */digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));}
编译上传到开发板后实现效果如下所示:
四、其他设置
除了上面一些基本设置之外,还有一些其他设置如下所示
/* 1. 禁止自动切换下一帧,默认是开启自动切换的 */ui.disableAutoTransition();/* 2. 使能自动切换下一帧,默认是开启自动切换的*/ui.enableAutoTransition();/* 3. 设置图像切换方向为从后往前,默认为从前往后 */ui.setAutoTransitionBackwards();/* 4. 设置图像切换方向为从前往后,默认为从前往后 */ui.setAutoTransitionForwards();/* 5. 设置每一帧图片持续时间, 单位为ms */ui.setTimePerFrame(1000);/* 6. 设置两帧图片之间的切换时间, 单位为ms */ui.setTimePerTransition(2000);/* 7. 关闭指示条栏的显示,默认开启 */ui.disableAllIndicators();/* 8. 开启指示条栏的显示,默认开启 */ui.enableAllIndicators();
五、附录
上一篇:ESP8266 Arduino开发之路(8)— 使用OLED显示文字和图片
下一篇:ESP8266 Arduino开发之路(10)— JSON基础