Arduino爱好者

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 75690|回复: 71

[教程] 《博哥OLED系列》- 玩转SSD1306-12864 OLED

  [复制链接]
发表于 2017-9-24 21:07 | 显示全部楼层 |阅读模式
本帖最后由 单片机菜鸟 于 2019-7-4 09:45 编辑

个人免费交流群:ESP物联网开发之旅 622368884

一、前言
      
      之前博哥在写ESP8266系列的时候,基本上都是通过Debug模式来获取8266 IP地址。这样就会存在一个问题,在正式环境下我们一般都会关掉debug,那么我们又如何能直观地观察当前8266的工作状态呢?既然这样,不如我们给它配上一个小的显示屏吧,然后博哥就去tb弄了一块SSD1306-12864的OLED显示屏。刚好今天又是周末,不用上班的感觉真好,果断折腾一下这个模块。
      模块就长这个样子:
       LC}T9YI9~AO[(2XJPOJAFMT.png
      简洁点,有以下基本步骤:接下来,要分别烧写两个端的代码,
      这款屏幕尺寸约为0.96英寸,由SSD1306驱动,驱动接口I2C,I2C地址(0x3c 默认/0x3d)。


      注意点:
      1.ssd1306本身支持多种总线驱动,我这里是I2C,也有是SPI的
      2.使用I2C接口,SSD1306允许有最多两位7位的I2C地址,通过相应的IO口拉低拉高来切换,一般默认为0x3c,在屏幕模块的背面,可以看到I2C地址切换提示,需要改变模块I2C地址时,只需要把提示位置的电阻取下焊接到另一个端,要注意的是板上的I2C地址是经过左移一位的数值,也就是说0x78 = 0x3c<<1, 0x7A = 0x3d << 1;
       B28LS0EIL~$EUBM${(QNR13.png

     SSD1306屏幕驱动库,最出名应该就是u8g2。但是博哥这里并不用它,改为用Adafruit_GFX 和 Adafruit_SSD1306。
     那么这两个库有什么关系呢?博哥去大概看了一下里面的源码,大家可以看看这两个图:
      }F5(V3[J[_WY6FOWS}R6YEN.png
       {1PG9IV}MIS3$IL(0`)GIN1.png

      可以看出Adafruit_GFX 定义了一系列的绘画方法(线,矩形,圆....),属于基础类,并且最重要的一点,drawPixel方法由子类来实现。
    Adafruit_SSD1306 定义了一系列跟SSD1306有关的方法,并且重写了drawPixel方法,属于扩展类。

     请先添加这两个库到arduino:
       Adafruit_GFX.rar (8.04 KB, 下载次数: 1999)
            Adafruit_SSD1306.rar (16.28 KB, 下载次数: 2019)
        注意,原生库中并没有ShowCN_16这个方法,是我后面加的,具体有什么用直接看下面的代码。


二、撸代码



      实验前提:博哥这里是用Mega2560来测试。
      最重要的一个意识点,SSD1306-12864其实就是一个128X64像素点阵。想显示什么就把具体位置的像素点亮起来。
      首先,请烧录以下代码,我们直接在代码中讲解上面的库怎么用。
      [mw_shl_code=c,true]/**
* 日期:2017/09/24
* 功能:OLED12864  SSD1306测试
* 作者:单片机菜鸟
* 16X16点阵显示 取模方式 阴码+逐行式+顺向
**/
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

#define NUMFLAKES 10
#define XPOS 0
#define YPOS 1
#define DELTAY 2

#define LOGO16_GLCD_HEIGHT 16
#define LOGO16_GLCD_WIDTH  16

#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

//显示一个心形
static const uint8_t PROGMEM Heart_16x16[] = {
  0x00,0x00,0x18,0x18,0x3C,0x3C,0x7E,0x7E,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  0xFF,0xFF,0x7F,0xFE,0x3F,0xFC,0x1F,0xF8,0x0F,0xF0,0x07,0xE0,0x03,0xC0,0x00,0x00//未命名文件0
};

//"猛"
static const uint8_t PROGMEM Strong_16x16[] = {
  0x00,0x00,0x45,0xFC,0x28,0x08,0x10,0x10,0x28,0x20,0x4B,0xFE,0x88,0x20,0x08,0xA0,
  0x18,0x40,0x29,0xFC,0x49,0x54,0x89,0x54,0x09,0x54,0x09,0x54,0x57,0xFE,0x20,0x00//猛0
};

static const uint8_t PROGMEM Welcome_16x16[] ={
0x00,0x80,0x00,0x80,0xFC,0x80,0x04,0xFC,0x05,0x04,0x49,0x08,0x2A,0x40,0x14,0x40,
0x10,0x40,0x28,0xA0,0x24,0xA0,0x45,0x10,0x81,0x10,0x02,0x08,0x04,0x04,0x08,0x02,//欢0
0x00,0x00,0x20,0x80,0x13,0x3C,0x12,0x24,0x02,0x24,0x02,0x24,0xF2,0x24,0x12,0x24,
0x12,0x24,0x12,0xB4,0x13,0x28,0x12,0x20,0x10,0x20,0x28,0x20,0x47,0xFE,0x00,0x00,//迎1
0x01,0x00,0x01,0x00,0x01,0x00,0x7F,0xFC,0x01,0x00,0x11,0x10,0x09,0x10,0x09,0x20,
0xFF,0xFE,0x03,0x80,0x05,0x40,0x09,0x20,0x31,0x18,0xC1,0x06,0x01,0x00,0x01,0x00,//来2
0x00,0x04,0xFF,0x84,0x08,0x04,0x10,0x24,0x22,0x24,0x41,0x24,0xFF,0xA4,0x08,0xA4,
0x08,0x24,0x08,0x24,0x7F,0x24,0x08,0x24,0x08,0x04,0x0F,0x84,0xF8,0x14,0x40,0x08,//到3
0x00,0x00,0x7F,0xFC,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x3F,0xF8,
0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0xFF,0xFE,0x00,0x00,//王4
0x02,0x00,0x02,0x08,0x3F,0xD0,0x02,0x20,0x02,0x40,0xFF,0xFE,0x01,0x00,0x02,0x00,
0x0F,0xF0,0x18,0x10,0x28,0x10,0x4F,0xF0,0x88,0x10,0x08,0x10,0x0F,0xF0,0x08,0x10,//者5
0x08,0x20,0x08,0x20,0xFF,0xFE,0x08,0x20,0x00,0x00,0x7F,0xFE,0x40,0x02,0x81,0x04,
0x01,0x00,0x7F,0xFC,0x03,0x80,0x05,0x40,0x09,0x20,0x31,0x18,0xC1,0x06,0x01,0x00,//荣6
0x10,0x00,0x11,0xDC,0x90,0x44,0x55,0x54,0x58,0xCC,0x11,0x54,0xFC,0x00,0x28,0x48,
0x28,0xFE,0x29,0x90,0x2A,0xFC,0x28,0x90,0x2A,0xFC,0x4C,0x90,0x48,0xFE,0x80,0x80//耀7
};

static const uint8_t PROGMEM Author_16x16[] ={
0x10,0x10,0x08,0x20,0x04,0x40,0x3F,0xF8,0x21,0x08,0x21,0x08,0x3F,0xF8,0x21,0x08,
0x21,0x08,0x3F,0xF8,0x01,0x00,0x01,0x00,0xFF,0xFE,0x01,0x00,0x01,0x00,0x01,0x00,//单0
0x00,0x40,0x10,0x40,0x10,0x40,0x10,0x40,0x10,0x40,0x1F,0xFC,0x10,0x00,0x10,0x00,
0x10,0x00,0x1F,0xE0,0x10,0x20,0x10,0x20,0x10,0x20,0x20,0x20,0x20,0x20,0x40,0x20,//片1
0x10,0x00,0x11,0xF0,0x11,0x10,0x11,0x10,0xFD,0x10,0x11,0x10,0x31,0x10,0x39,0x10,
0x55,0x10,0x55,0x10,0x91,0x10,0x11,0x12,0x11,0x12,0x12,0x12,0x12,0x0E,0x14,0x00,//机2
0x08,0x20,0x08,0x20,0xFF,0xFE,0x08,0x20,0x00,0x10,0x00,0xF8,0x3F,0x00,0x11,0x10,
0x08,0x20,0x01,0x00,0x7F,0xFC,0x05,0x40,0x09,0x20,0x31,0x18,0xC1,0x06,0x01,0x00,//菜3
0x01,0x00,0x02,0x00,0x1F,0xF0,0x10,0x10,0x12,0x10,0x11,0x10,0x11,0x50,0x10,0x20,
0x10,0x00,0x1F,0xFC,0x00,0x04,0x00,0x04,0x7F,0xE4,0x00,0x04,0x00,0x28,0x00,0x10,//鸟4
};

void setup()   {               
  Serial.begin(115200);
  delay(500);
  // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3D (for the 128x64)
}

void loop() {
  test_SSD1306();
}

void test_SSD1306(void){
    //1.检测全屏显示(看看有没有大面积坏点)
  display.fillScreen(WHITE);
  display.display();
  delay(2000);

  //2.画点 点坐标(10,10)
  display.clearDisplay();   // clears the screen and buffer
  display.drawPixel(10, 10, WHITE);
  display.display();
  delay(2000);

  //3. 画线 从(0,0)到(50,50)
  display.clearDisplay();   // clears the screen and buffer
  display.drawLine(0, 0,50,50, WHITE);
  display.display();
  delay(2000);

  //4.画空心矩形  左上角坐标(x0,y0)  右下角坐标(x1,y1)
  display.clearDisplay();   // clears the screen and buffer
  display.drawRect(0,0,128,64,WHITE);
  display.display();
  delay(2000);

  //5.来画个实心矩形
  display.clearDisplay();   // clears the screen and buffer
  display.fillRect(0,0,64,64,WHITE);
  display.display();
  delay(2000);

  //6.画空心圆
  display.clearDisplay();   // clears the screen and buffer
  display.drawCircle(20,20,20,WHITE);
  display.display();
  delay(2000);
  
  //7.画实心圆
  display.clearDisplay();   // clears the screen and buffer
  display.fillCircle(20,20,20,WHITE);
  display.display();
  delay(2000);

  //8.画空心三角形
  display.clearDisplay();   // clears the screen and buffer
  display.drawTriangle(20,0,0,20,40,20,WHITE);
  display.display();
  delay(2000);

  //9.画实心三角形
  display.clearDisplay();   // clears the screen and buffer
  display.fillTriangle(20,0,0,20,40,20,WHITE);
  display.display();
  delay(2000);

  //10.画空心圆角矩形
  display.clearDisplay();   // clears the screen and buffer
  display.drawRoundRect(0,0,40,40,5,WHITE);
  display.display();
  delay(2000);

  //11.画实心圆角矩形
  display.clearDisplay();   // clears the screen and buffer
  display.fillRoundRect(0,0,40,40,5,WHITE);
  display.display();
  delay(2000);

  //12.画心形(楼主自己用取模软件画的)
  display.clearDisplay();   // clears the screen and buffer
  display.drawBitmap(16,16,Heart_16x16,16,16,WHITE);
  display.display();
  delay(2000);

  //13.显示英文 数字
  display.clearDisplay();   // clears the screen and buffer
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.println("Hello, Arduino!");
  display.setTextColor(BLACK, WHITE); // 'inverted' text
  display.println(3.141592);
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.print("0x"); display.println(0xDEADBEEF, HEX);
  display.display();
  delay(2000);

  //14.显示单个文字 有木有发现就是调用drawBitmap
  display.clearDisplay();   // clears the screen and buffer
  display.drawBitmap(16,16,Strong_16x16,16,16,WHITE);
  display.display();
  delay(2000);
  
  //15.显示多个文字(楼主在库里面加入一个ShowCN_16方法  就是为了同时显示多个中文 16X16 ,不然的话 就得一个一个drawBitmap)
  display.clearDisplay();   // clears the screen and buffer
  display.ShowCN_16(0,0, Welcome_16x16,sizeof(Welcome_16x16)/32,WHITE);
  display.ShowCN_16(48,16, Author_16x16,sizeof(Author_16x16)/32,WHITE);   
  display.display();
  delay(2000);
}[/mw_shl_code]

   先不理代码,烧录出来的效果应该是顺序显示以下图案(从左到右,从上到下):
    J)9UCJ7)F)K@B7`1C6UF71J.png 1R7H@7}E7J_Q23KY@C]5(YW.png 167GHJ3CN%(K_AQGZ%%3V9V.png
    }6LJRZD]ACHE6WO@6`EVI}Q.png U74SXB35{REDDOM02D6$${D.png 8AA5[`G1GQ_G1Y6M~AK7{2A.png
    T1Q)J{7D_K2%SD98%Q9694I.png FZC@SP77N13SDT2QMVBB.png SY_(6QIUWOG}[W]P5E7I31P.png
   4B{UUQYCYPMJ%U$_2CX02`O.png }F`_S8$(Q4R_64T~)24S320.png ~F[HC$RD[`}QHWZG5L0OVCT.png
DWWAIVE[B$~IW~{Y@B6P4_3.png Q}HO`B9{[2)E8LD9[803S{1.png
       在讲解方法之前,先来建立一个坐标系的概念:
    TVEMX}TXVNRB3WVZGN0ZZSR.png
      在讲解方法之前这其实就是一个128(width)X64(height)点阵。在坐标系中,左上角是原点,向右是X轴,向下是Y轴。
    有了坐标系概念之后,我们就开始讲解方法使用:
    1.begin()  
      初始化I2C地址,在Arduino setup调用。

    2.clearDisplay()  
      把Buffer清零,其实就是把OLED对应的缓存(缓存对应点阵数据)清掉,缓存就是这个:
    0M}9VLQDF(3N2(NT~@B78H6.png

    3.display()  
      把Buffer数据显示到屏幕上,任意一个需要显示的操作都需要调用这个方法,不然就仅仅是写入缓存而已。


    4.drawPixel(int16_t x, int16_t y, uint16_t color)   
      画点 点坐标(x,y)


    5.drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color)
      画线 从(x0,y0)到(x1,y1)


    6.drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)  
      画空心矩形  左上角坐标(x0,y0)  宽度是w 高度是h


    7.fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)  
      画实心矩形  左上角坐标(x0,y0)  宽度是w 高度是h


    8.drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color)  
      画空心圆,圆心(x0,y0),半径 r


    9.fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color)
      画实心圆,圆心(x0,y0),半径 r


    10.drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
      int16_t x2, int16_t y2, uint16_t color)  

      画空心三角形,上面第一个角坐标(x0,y0),下面左边角坐标(x1,y1),下面右边角坐标(x2,y2)


    11.fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
      int16_t x2, int16_t y2, uint16_t color)  

      画实心三角形,上面第一个角坐标(x0,y0),下面左边角坐标(x1,y1),下面右边角坐标(x2,y2)


    12.drawRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h,
      int16_t radius, uint16_t color)  

      画空心圆角矩形,左上角坐标(x0,y0)  宽度是w 高度是h,圆角半径 radius


    13.fillRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h,
      int16_t radius, uint16_t color)

      画实心圆角矩形,左上角坐标(x0,y0)  宽度是w 高度是h,圆角半径 radius


    14.drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap,
      int16_t w, int16_t h, uint16_t color)

      画任意图形,左上角坐标(x,y),图形数据 bitmap,图形高度h 宽度w
      其实,任意的图形最终都转成点阵的显示方式,bitmap可以是真正图片的数据,也可以是单个文字的字模。


    15.ShowCN_16(int16_t x, int16_t y, const uint8_t *bitmap,uint8_t size,uint16_t color)  
      显示一行文字(16X16字模),左上角坐标(x,y),字模bitmap,字数size


    至于其他的方法,请自行阅读源码。


我这里用到了下面的字模取模软件:
128x64取字软件.rar (702.08 KB, 下载次数: 958)
我这里用到了下面的字模取模软件:注意以下取模方式(阴码+逐行式+顺向):
LUN169)OU2T5DC1VRU2X(V5.png

我这里用到了下面的字模取模软件:最后,关于SSD1306-12864的基本方法大概讲了一下,剩下就是自由组合了,下一篇博哥准备给ESP8266 天气接口加入OLED显示,敬请期待了。




 楼主| 发表于 2017-9-24 21:07 | 显示全部楼层
楼主做好冷板凳
发表于 2017-9-25 08:48 | 显示全部楼层
楼主威武,膜拜中!
 楼主| 发表于 2017-9-25 13:35 | 显示全部楼层
leisd 发表于 2017-9-25 08:48
楼主威武,膜拜中!

哈哈哈  赶紧用起来
发表于 2017-9-28 10:00 | 显示全部楼层
博哥第一粉来了,先评再看哈哈
 楼主| 发表于 2017-9-28 11:06 | 显示全部楼层
汇研 发表于 2017-9-28 10:00
博哥第一粉来了,先评再看哈哈

感谢美女支持
发表于 2017-10-1 18:41 | 显示全部楼层
楼主讲得很好,名字中如果也有博字那我俩太有缘分了。顶了。
 楼主| 发表于 2017-10-1 19:04 | 显示全部楼层
ynqjwfb 发表于 2017-10-1 18:41
楼主讲得很好,名字中如果也有博字那我俩太有缘分了。顶了。

哈哈哈  我的笔名有
发表于 2017-10-19 21:06 | 显示全部楼层
楼主,请教oled 和 UN怎么接线的?
 楼主| 发表于 2017-10-19 22:51 | 显示全部楼层
阵中人 发表于 2017-10-19 21:06
楼主,请教oled 和 UN怎么接线的?

看看UN的I2C总线
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino爱好者

GMT+8, 2022-12-9 14:56 , Processed in 0.077447 second(s), 18 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表