友情提示:380元/半年,儿童学编程,就上码丁实验室。
我是潘,曾经是个工程师。这是 “Arduino 公开课” 系列的入门教程。上一课介绍了I2C 协议连接1602 LCD。现在我们将屏幕升级到更强大的12864 OLED(也称“1306”),让交互界面更加丰富。有任何疑问请在评论区提出,我会逐一回答。
1602 LCD 限制很多,只能显示字符不能绘图,而且每个字符的像素是分隔的,一般用在简单的交互设备上,比如显示电压、温度等。而 12864 OLED,则是一块功能完整屏幕,想象一下早期的诺基亚手机,利用这块屏幕还可以设计一些像贪吃蛇等简单游戏。
首先,12864 OLED 屏幕有 I2C 和 SPI 两种通信协议的模组,由于协议不同,所以完全不兼容。这次使用的是 I2C 协议的模组,SPI 协议后面会介绍。不过,可以提前剧透一下,I2C 和 SPI 性能和扩展性有很大差异,在产品设计开发过程中,选用哪一种将是一场艰难的选择。
回到正题,与 1602 LCD 的命名不同(“16”代表16个字符,“2”代表2行),12863 的含义是 128 X 64 个像素,这些像素都是连续的,可以构成不同的字符或者图形。OLED 意味着面板的显示方式是 发光二极管。但 12864 也是一款单色的屏幕,所以每个像素就是一个二极管,而不是彩色的 3~4 个(一些OLED 屏幕会在 RGB 之外增加 W 白色二极管,从而提升对比度和亮度)。
一般屏幕是横向使用的,128 个像素横向排列在 X 轴上,分别以 0~127 来代表,64个像素垂直排列在 Y 轴上,分别以 0~63 来代表。
按照 I2C 方式接线即可:
SCL 接到 A5
SDA 接到 A4
VCC、GND 分别接 VCC 和 GND
驱动这块屏幕要用到 U8g2 库,可以直接从 IDE 的库管理器中下载。这个库功能非常强大,除了能驱动 12864 外,还能驱动市面上大部分常用的LCD/OLED。而且能方便地调节字体的大小、间距,还可以显示比较复杂的动态图形。
安装好后,打开示例程序,找到 “U8g2 -> full_buffer -> HelloWorld” :
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
47
48
|
/*
作者:Ardui.Co
效果:1306 OLED 显示 Hello World
版本:1.0
更新时间:2017年4月12日
*/
#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>
// Please UNCOMMENT one of the contructor lines below
// U8g2 Contructor List (Frame Buffer)
// The complete list is available here: https://github.com/olikraus/u8g2/wiki/u8g2setupcpp
// Please update the pin numbers according to your setup. Use U8X8_PIN_NONE if the reset pin is not connected
//U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);
//U8G2_SSD1306_128X64_NONAME_F_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 12, /* dc=*/ 4, /* reset=*/ 6); // Arduboy (Production, Kickstarter Edition)
//U8G2_SSD1306_128X64_NONAME_F_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);
//U8G2_SSD1306_128X64_NONAME_F_3W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* cs=*/ 10, /* reset=*/ 8);
//U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
//U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* reset=*/ 8);
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0,/* clock=*/SCL,/* data=*/SDA,/* reset=*/U8X8_PIN_NONE); // All Boards without Reset of the Display
//U8G2_SSD1306_128X64_NONAME_F_6800 u8g2(U8G2_R0, 13, 11, 2, 3, 4, 5, 6, A4, /*enable=*/ 7, /*cs=*/ 10, /*dc=*/ 9, /*reset=*/ 8);
//U8G2_SSD1306_128X64_NONAME_F_8080 u8g2(U8G2_R0, 13, 11, 2, 3, 4, 5, 6, A4, /*enable=*/ 7, /*cs=*/ 10, /*dc=*/ 9, /*reset=*/ 8);
//U8G2_SSD1306_128X64_VCOMH0_F_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8); // same as the NONAME variant, but maximizes setContrast() range
//U8G2_SH1106_128X64_NONAME_F_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);
//U8G2_SH1106_128X64_VCOMH0_F_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8); // same as the NONAME variant, but maximizes setContrast() range
//U8G2_SSD1306_128X32_UNIVISION_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ 21, /* data=*/ 20, /* reset=*/ U8X8_PIN_NONE); // Adafruit Feather M0 Basic Proto + FeatherWing OLED
//U8G2_SSD1306_128X32_UNIVISION_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE); // Adafruit Feather ESP8266/32u4 Boards + FeatherWing OLED
//U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); // Adafruit ESP8266/32u4/ARM Boards + FeatherWing OLED
//U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ SCL, /* data=*/ SDA); // pin remapping with ESP8266 HW I2C
//U8G2_SSD1306_64X48_ER_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); // EastRising 0.66″ OLED breakout board, Uno: A4=SDA, A5=SCL, 5V powered
//U8G2_SSD1322_NHD_256X64_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8); // Enable U8G2_16BIT in u8g2.h
//U8G2_SSD1322_NHD_256X64_F_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8); // Enable U8G2_16BIT in u8g2.h
//U8G2_SSD1325_NHD_128X64_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);
//U8G2_SSD1325_NHD_128X64_F_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);
//……
voidsetup(void){
u8g2.begin();
}
voidloop(void){
u8g2.clearBuffer();//清除模组的缓存
u8g2.setFont(u8g2_font_ncenB14_tr);// 设置字体
u8g2.drawStr(0,20,“Hello World!”);// 设置坐标 x=0,y=20 输出内容,0表示最左端
u8g2.sendBuffer();// 将缓存输出到屏幕
delay(1000);
}
|
让人崩溃的注释,但只要找到对应的模块:U8G2_SSD1306_128X64_NONAME_F_SW_I2C,对于 Arduino UNO 选择标注有 SCL、SDA 即可(第21行),把注释去掉,其他注释内容就能删除掉。现在,上传程序到 Arduino 就可以工作了:
但我们并不满足静态的文字,现在要让文字动起来,让Hello World 自上而下循环滚动,此时就要用到坐标系:
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
|
/*
作者:Ardui.Co
效果:1306 OLED 自上而下滚动显示 Hello World
版本:1.0
更新时间:2017年4月12日
*/
#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0,/* clock=*/SCL,/* data=*/SDA,/* reset=*/U8X8_PIN_NONE); // All Boards without Reset of the Display
intyPos=0;//初始化y轴的坐标系
voidsetup(void){
u8g2.begin();
u8g2.setFont(u8g2_font_ncenB14_tr); // 设置字体
}
voidloop(){
u8g2.firstPage();
do{
draw();
}while(u8g2.nextPage());
if(yPos<83){//y纵轴的坐标系最大值
yPos++;//从0开始,每次循环+1,向下滚动
}
else{
yPos=0;//滚到底部时yPos归零,重新循环
}
}
voiddraw(){
u8g2.drawStr(0,yPos,“Hello World”);
}
|
程序里面涉及了很多参数和方法,简单介绍一下:
U8G2_R0 (在指定显示屏型号的注释中)是一个参数,指定了整体显示布局:
布局 | 描述 |
U8G2_R0 | 正常显示 |
U8G2_R1 | 90度顺时针旋转 |
U8G2_R2 | 180度顺时针旋转 |
U8G2_R3 | 270度顺时针旋转 |
U8G2_MIRROR | 显示镜像内容,需与setFlipMode()配搭使用. |
u8g2.clear() // 清空缓冲区内的所有像素点;
u8g2.clearBuffer() //清空缓冲区内的所有像素,接着用 sendBuffer() 方法来把缓冲区内容显示出来;
u8g2.Print() // 在当前光标位置输出文字。光标位置可以用 setCursor() 函数设定。字体可以用setFont() 函数。
u8g2.drawStr(x, y, str) // 绘制字符串,它能输出什么在屏上。取决于它的setFont被设置的字体集。
u8g2.drawBox (x, y, w, h) //画一个实心方形,w 为宽,h 为高;
u8g2.firstPage() 和 u8g2.nextPage() // 绘图库的图片循环需要放在这两个函数内部
u8g2.drawCircle(x, y, rad, opt) // 画个空心圆,可选4个方向的半圆
rad是圆的四分之一弧度。opt是选项:
U8G2_DRAW_UPPER_RIGHT //左上角弧度
U8G2_DRAW_UPPER_LEFT //右上角弧度
U8G2_DRAW_LOWER_LEFT //左下角幅度
U8G2_DRAW_LOWER_RIGHT //右下角幅度
U8G2_DRAW_ALL //全圆
u8g2.drawDisc(x, y, rad, opt) //画个实心圆,参数用法同上
u8g.drawLine(x0, y0, x1, y1) //画直线,x0,y0是直线起始位置, x1,y1是直线终止位置。
还有很多方法,这里不再逐一说明,举一个具体的应用例子,我们画一个正方形:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
/*
作者:Ardui.Co
效果:1306 OLED 画一个正方形
版本:1.0
更新时间:2017年4月12日
*/
#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0,/* clock=*/SCL,/* data=*/SDA,/* reset=*/U8X8_PIN_NONE); // All Boards without Reset of the Display
voidsetup(void){
u8g2.begin();
}
voidloop(void){
u8g2.firstPage();
do{
u8g2.drawBox(48,20,25,15);//(起始X,起始Y,方形的宽W,方形的高H)
}while(u8g2.nextPage());
delay(1000);
}
|
要提醒的是,U8G2 是有中文字库的,但使用中文字库会占用极大的空间,Arduino UNO 那区区的32KB ROM 也仅仅能放下字库和很简单的程序。如果要显示中文字库,建议使用2560、 Arduino 101 这类空间充裕开发板。