原创文章,未经许可,禁止转载!

5 点亮屏幕1(ssd1306)

视频教程:https://www.bilibili.com/video/BV1cA411E76c/

既然给女神做礼物,那么高逼格道具自然不能少,下面就是我们要用到的道具,ssd1306,0.96寸oled,分辨率128×64

图1 ssd1306

整个模块的尺寸大概是 NodeMCU 的一半,当然也有其他的尺寸。模块上有4个引脚,分别是GND(接地),VDD(也叫VCC,供电),SCK(也叫SCL,时钟信号),SDA(数据)。真正和ESP8266通讯的其实只有SCK和SDA,这就是I2C协议。

I2C协议

简单介绍下I2C协议,纯属扫盲,不想看的可以跳过,编码不涉及该内容。

I2C是的传输仅需要2跟数据线,一个是时钟信号,一个传输数据,可以双向传输,但是每次传输都只能是一个方向,也就是半双工模式。协议里必须要由主设备(master,也就是ESP8266)发起,从设备(slave)根据master的指令进行操作,请看下图2:

图2 I2C时序图

规则1:在闲置状态下,SDA和SCL都会是高电平,时钟信号不会产生脉冲。
规则2:当需要传输数据时,由master拉低SDA,表示开始,此时时钟产生脉冲信号。
规则3:请看上图2左边的位置,在时钟为高电平时,进行数据的读操作。
规则4:请看上图2右边的位置,在时钟为低电平时,设备可以进行写操作,这样不会互相打架。
规则5:当master启动开始标志后会传送8个bit,前7个bit表示选择哪一个slave设备,第8个bit表示master是要读还是写,如果是读则发送高电平,写发送低电平。
规则6:当前面8个bit发送完后,对应地址的slave发送ack(acknowledge回应),低电平表示准备好了,高电平表示忙,无法接收信息。
规则7:如果slave表示准备好了,如果R/W标志为master收,则slave发送8bit的数据给master接收,如果master接收成功则发送ack。
规则8:如果slave表示准备好了,如果R/W标志为master发,则master发送8bit的数据给slave接收,如果slave接收成功则发送ack。
规则9:如果要继续传输数据,则重复2-5的操作,如果要结束传输,master会在时钟为高电平的时候拉低数据,传输结束,时钟脉冲停止,SDA和SCL回到高电平位置。

接线

图3 接线图

看上图3,VCC接在3.3v上,随便哪个3.3v都可以,GND也接在8266的GND上。SCL时钟信号接在D5上,也就是HSCLK引脚,SDA我这里接在了D3上。然后就可以开始编码了。

添加库

首先,我们要下载驱动ssd1306的库,请看下图4:

图4 下载ssd1306库

编码

添加完成库以后就可以开始编码了,对应的代码如下,这里有几个概念,一个是display就是对应的ssd1306的设备,OLEDDisplayUi可以帮我们绘制画面,它可以同时设置多个需要绘制的画面并且可以来回的切换,每一个屏幕的画面是一个frame,下面的代码只有一个frame。这里的frame绘制的是英文,因为英文字库比较小可以装入8266,而中文的绘制就比较麻烦了,8266的内存无法放入整个中文字库,因此中文会转换成图片的形式绘制。

// 引入头文件
#include "SSD1306Wire.h" 
#include "OLEDDisplayUi.h"

// 定义一个display设备,并且设置它的SDA为D3引脚,SCL为D5引脚
SSD1306Wire display(0x3c, D3, D5);

// 创建一个ui,并且把display传递给ui
OLEDDisplayUi ui(&display);

// 绘制Frame,这个函数最后由ui来调用,参数格式是给定的
void DrawFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
  // 设置绘制文字的方式为左对齐
  display->setTextAlignment(TEXT_ALIGN_LEFT);

  // 设置字体为ArialMT_Plain_10,并在屏幕(0,10)的位置绘制Hello World!
  display->setFont(ArialMT_Plain_10);
  display->drawString(0 + x, 10 + y, "Hello World!");

  // 设置字体为ArialMT_Plain_16,并在屏幕(0,20)的位置绘制Hello World!
  display->setFont(ArialMT_Plain_16);
  display->drawString(0 + x, 20 + y, "Hello World!");
  
  // 设置字体为ArialMT_Plain_16,并在屏幕(0,34)的位置绘制Hello World!
  display->setFont(ArialMT_Plain_24);
  display->drawString(0 + x, 34 + y, "Hello World!");
}

// 创建FrameCallback给ui使用,这里只有一个Frame,Frame的作用可以让ui来切换不同的frame绘制不同的画面
FrameCallback frames[] = {DrawFrame};
// 只有一个frame,所以frame count是1
int frameCount = 1;

void setup() {
  // 设置刷新率为30帧1秒
  ui.setTargetFPS(30);
  
  // 给ui传递frame信息
  ui.setFrames(frames, frameCount);

  // 关闭frame标签
  ui.disableAllIndicators();

  // 关闭自动切换frame功能
  ui.disableAutoTransition();

  // 初始化ui
  ui.init();

  // 翻转屏幕180度
  display.flipScreenVertically();

  // 设置ui绘制的当前frame为frames的第0个frame
  ui.switchToFrame(0);
}

void loop() {
  // 帧速率由ui控制,更新完ui后会返回下一帧需要等待的时间
  int remainingTimeBudget = ui.update();

  // 延迟对应的时间后可以再次更新屏幕
  if (remainingTimeBudget > 0) {
    delay(remainingTimeBudget);
  }
}
图5 HelloWorld

这一切搞定以后,就可以在屏幕上看到3行大小不一的Hello World!了,下一节将讲如何绘制图案,中文字以及如何切换frame。

发表评论