模块熟悉

第 19 课:OLED 显示屏——把数字画出来

唯一会用 I²C 的模块,也是唯一能把信息"说出来"的模块

我是吴老师。这是「模块熟悉」单元的最后一节,也是这里面最"像样"的一个模块。

先说它最特别的地方:

前面所有的模块,要么只会"读",要么只会"亮/响/转"。

光敏只会告诉你亮不亮,按钮只会告诉你按没按,舵机只会转——它们都不会"说话"。 你想知道板子在想什么,只能打开电脑看串口监视器。一拔掉电脑,你就什么也看不见了。

OLED 是套件里唯一能把信息显示出来的模块。

这一下就不一样了。你的作品从"会闪的灯",变成了**"能说话的东西"**——它能告诉你现在几度、现在是几点、还剩多少电。

看视频时注意的三件事

这节课有视频,先看。视频里讲的是接线和跑通的全过程。

视频教程OLED 显示屏:I²C 接线、显示文字和数字下载 ↗

看的时候别光看热闹,盯这三件事:

第一,它是唯一一个用 I²C 的模块,所以它有两个信号线。

针脚 作用
GND
VDD 电源(就是正极,和 VCC 一个意思)
SCK 时钟线(就是 SCL
SDA 数据线

以前的模块,信号线要么就一根(OUTAOI/O),要么各用各的(超声波那两根 Trig/Echo 是它专用的)。而 OLED 这两根线是"总线"——以后所有 I²C 设备都并到这两根线上。 为什么这么设计?下一节讲。

第二,注意你板子上印的字。

教程里普遍写 VCCSCL但你手上这块印的是 VDDSCK

是同一个东西,接的位置完全一样。 别被字母差异唬住——这事儿第 4 课讲过,今天你第一次真的用上。

第三,注意显示的位置。

视频里我调了几次坐标,你会看到字写歪了、挤在一起。这都是正常的,多试两次就有手感了。

一、I²C:两根线挂一串设备

先回答"为什么它要两根线"。

以前那种接法是"一个设备一对线"。 你有 3 个传感器,就得占 3 根信号线,加上电源和地,一堆线缠在一起。

I²C 换了个思路:两根线,可以挂一长串设备。

I2C 总线:两根线可以挂很多设备,靠地址区分谁是谁

打比方:I²C 就是大家一起用的一个对讲机频道。

  • 一根线是时钟SCK/SCL)——相当于节拍,所有人跟着这个节奏说话
  • 一根线是数据SDA)——相当于频道里说的话
  • 每个设备有个地址——相当于门牌号 / 呼号

主控在数据里带上地址,发一句"0x3C,把这段话显示出来"。所有设备都听见了,但只有地址是 0x3C 的那个才应答,别的都装没听见。

(这玩意儿正式叫总线,你以后查资料会用到这个词。)

这是 I²C 最爽的地方:加一个新设备,不用再占新引脚。 只要把它并在那两根线上就行,SCKSDA共用的。

你后面做第 31 课(光敏 + 舵机 + OLED)的时候会发现:引脚不够用的时候,I²C 模块是最省心的那个。

还记得第 4 课我说"引脚要提前规划"吗?I²C 就是工程师为了解决这个麻烦想出来的办法。

但有个坑:地址冲突

如果两个设备的地址一样,它们会同时应答,谁也没法正常工作。

对讲机里两个人共用一个呼号,你喊一声,两个人一起开口——谁也听不清。 这就是地址冲突

好消息:你套件里只有一个 I²C 模块,撞不上。

但以后你买第二个 I²C 传感器的时候,第一件事就是查它的地址。 有些模块背面有可以改地址的焊盘/跳线,买之前先看清楚。

你现在记着有这么回事就行。

二、接线:四根线

OLED 接到
GND GND
VDD 3.3V
SCK GPIO9(板子默认的 SCL
SDA GPIO8(板子默认的 SDA

注意最后两行:SCKSDA 是一对,不能接反。 接反了屏幕不亮,但不会烧,换过来就行。

GPIO8 / GPIO9 是板子默认的 I²C 脚(图形化平台里也是这两个)。

但第 4 课我提醒过:GPIO8GPIO9 是启动时的特殊引脚,建议避开。 它们能用,你心里有个数就行——如果屏幕时不时不亮,或者板子偶尔启动不了,先怀疑这两个脚。

换脚很简单:I²C 用哪两个 GPIO 是代码里指定的,不像有些接口是焊死的。换成 GPIO6/GPIO7,代码里同步改一句就行(下一节给代码)。

接之前还有个动作:先断电。 插好再上电,这是第 8 课就立下的规矩。

三、动手第一步:先显示一行字

代码路线要先确认库装好了。第 2 课你已经装过 Adafruit SSD1306Adafruit GFX Library——当时我说"现在装好,省得上到这一课手忙脚乱",就是为今天准备的。

C++
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

Adafruit_SSD1306 oled(128, 64, &Wire, -1);   // 128×64 的屏

void setup() {
  Wire.begin(8, 9);                          // 先 SDA,后 SCL(想换脚就改这里)
  oled.begin(SSD1306_SWITCHCAPVCC, 0x3C);    // 0x3C 是它的 I²C 地址
  oled.clearDisplay();
  oled.setTextSize(2);                       // 字的大小
  oled.setTextColor(SSD1306_WHITE);
  oled.setCursor(0, 0);                      // 从左上角开始写
  oled.println("Hello OLED");
  oled.display();                            // 这一句才真的画到屏上
}

void loop() {
}

烧进去。屏幕上应该出现一行 Hello OLED。

如果没亮,别慌,这是本课最正常的一件事,下面的"不亮怎么查"就是给你准备的。

display() 那一句,是新手最容易漏的

你注意到 oled.display() 了吗?前面画的、写的,一句都不落地。

因为那些 printsetCursor 都只是在"草稿纸"上画——画在板子的一块内存里,屏幕上一个像素都没变。只有 display() 这一句,才把草稿纸整个推到屏幕上。

忘了它,程序一点错都不报,屏幕就是不动。 这个坑坑过所有人,包括我。

屏不亮,按这个顺序查

先别改代码,按第 5 课那套顺序来:电 → 信号 → 代码。

  1. 供电:电压档,黑笔搭 GND、红笔点 VDD,是不是 3.3V?没电,后面都免谈
  2. 接线:通断档,四根线一根一根量通断;顺便看看 SCK/SDA 有没有接反
  3. 地址:这一步是 OLED 专属的——地址不对,屏幕就是死的。 大多数模块是 0x3C,但也有印 0x3D 的。搜一个 "I2C scanner"(I²C 扫描)的小程序烧进去,它会把你总线上所有能应答的地址打印出来。照它报的地址改代码。
  4. 代码:前面三步都排除了,再回头看代码

第 3 步那句话你记住:与其猜地址,不如让板子自己报出来。 这又是"先测,再猜"。

四、动手第二步:显示一个会变的数字

只会显示固定文字,那跟贴张纸条没区别。 我们来显示一个真正有用的东西——第 10 课那个电位器的读数

C++
void loop() {
  int v = analogRead(0);      // 读电位器(接在 GPIO0 上)

  oled.clearDisplay();        // 先擦干净
  oled.setCursor(0, 0);
  oled.print("ADC: ");
  oled.println(v);
  oled.display();             // 再推上屏

  delay(200);
}

拧旋钮,屏上的数字跟着变。 到这一步,你的板子就真的"能说话"了。

那个 clearDisplay() 也别忘

它是这一节最值得体会的东西。

你把 clearDisplay() 那一行注释掉,再跑一遍——会看到数字叠在一起,一会儿就糊成一团黑。

为什么?因为屏幕不会自己"擦黑板"。你的每一次 print 都是"往上面画",新数字画在旧数字上面,两层叠一起就是一团。

所以画动态内容的标准套路是三步:

擦干净(clearDisplay)→ 重新画(print / setCursor)→ 推上屏(display

这三步你记牢,以后做第 30 课"坐姿提醒器"、第 34 课"联网天气站",全都要用。

五、动手第三步:坐标

最后玩一下位置。屏幕是 128 列 × 64 行(单位是像素),你要告诉它"从哪儿开始画":

C++
oled.setCursor(0, 0);     // 左上角
oled.setCursor(60, 30);   // 偏中间
oled.setTextSize(1);      // 字变小,能写更多
  • setCursor(x, y) 里的 x 是从左往右数的像素,y 是从上往下数的像素
  • setTextSize(2) 就是字放大到 2 倍。放大了会占更多地方,所以字号变了,坐标也得跟着调

你多试几次,把字放到屏幕中间、右下角。 这个过程有点像在纸上写字之前先想好落笔的位置——试两次就有手感了。

y 是从上往下数的,这个跟数学课上的坐标系是反的。别问为什么,屏幕行业一直就这么定。 你记住就行。

自测

单选题OLED 为什么需要两根信号线,而别的模块只要一根?
单选题你板子上印的是 VDDSCK,但教程里写的是 VCCSCL,怎么办?
单选题屏幕上要显示一个不断变化的数字,为什么每次都得先 clearDisplay()
问答题线接好了、程序也烧进去了,屏幕就是不亮。说说你会怎么查?
拓展I²C 一共能挂多少个设备?为什么两个设备地址一样就会打架?

这个问题课上不讲,留给你自己找答案——**问 AI 是最好的办法**。

提示:搜"I2C 地址 7 位 112 个 保留地址"、"I2C 地址冲突 怎么办"。想一想:如果实在买不到两个地址不同的模块,还有别的办法吗?(提示:另一种叫 SPI 的总线,是"每个设备单独一根片选线",代价是什么?)

拓展屏幕上的一个字,是怎么被"画"出来的?

这个问题课上不讲,留给你自己找答案——**问 AI 是最好的办法**。

提示:搜"点阵字库 取模"、"OLED 显示原理 像素"。想一想:一个 16×16 的汉字,需要多少个小点来决定哪儿亮哪儿不亮?那这一整屏 128×64,又是多少个点?

本节你要动手做的事

  • [ ] 认清丝印GND VDD SCK SDA——VDD 就是正极,SCK 就是时钟,别被字母唬住
  • [ ] 接好四根线(记得先断电再插):VDD3.3VGNDGNDSCK/SDA→ 板子的两个 I²C 脚
  • [ ] 先显示一行字:确认屏幕能亮。记得最后那句 display()
  • [ ] 不亮就用万用表查一遍:先量 VDD 的电压,再量四根线的通断。先测,再猜
  • [ ] 显示一个会变的数字:把第 10 课那个电位器接上,屏上显示它的读数
  • [ ] 故意注释掉 clearDisplay():亲眼看看数字叠成一团糊的样子——这个现象比任何解释都记得住
  • [ ] 调调位置和字号setCursor(x, y)setTextSize(1/2/3),把字放到屏幕正中间

这个单元,你走完了

回头看看你这一路摸过的东西。

模块 你学会了什么
读世界 电位器、光敏、热敏、麦克风 模拟输入、ADC、标定
按钮、红外 数字输入、抖动、上拉
超声波、DHT11 按协议跟模块"对话"
做动作 LED、蜂鸣器 数字输出——只有开和关
舵机 模拟输出——用 PWM 说出"37 度"
说出来 OLED 把数字变成人能看懂的信息

你发现没有——从第 10 课到现在,你其实只学了四件事:读一个值、判断一下、做一个动作、把它显示出来。

零件一直在换,骨架一次都没变过。

这就是「模块熟悉」这个单元的终点。 你现在手上有的不是十个模块的用法,而是一套"拿到新模块该怎么上手"的方法:看丝印、接三根线、先测再猜、跑个最小程序看现象。

说句实在的:这个单元是整套课里最"碎"的一段——十个模块,个个都要接一遍线、调一遍代码,重复得让人有点烦。

但你是走完了的那一个。 多数人是在第三个模块就开始"回头看视频跟着抄"的;而你现在拿到一个没见过的小板子,大概知道从哪儿下手了。

这个本事,比记住十个库的用法值钱。

下一课:让板子上网

下一节是「联网与通信」单元的第一课:第 20 课 WiFi 联网。

前面这 19 节课,你的板子一直活在自己的小世界里——它只知道接在它身上的那几根线。下一课开始,它能连上路由器,能去外面的世界取数据。

不过我要提前给你打个预防针:

联网出问题的时候,你几乎看不见任何东西。

接错一根线,你能看见灯不亮;连不上网,你只能看见"没反应"。所以第 20 课我会专门讲**"连不上网的时候按什么顺序查"**——那套顺序比代码本身值钱。

从"能看见"的世界,跨到"看不见"的世界。这是这块板子真正区别于普通开发板的地方。