单片机菜鸟 发表于 2017-9-24 21:07

《博哥OLED系列》- 玩转SSD1306-12864 OLED

本帖最后由 单片机菜鸟 于 2019-7-4 09:45 编辑

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

一、前言      
      之前博哥在写ESP8266系列的时候,基本上都是通过Debug模式来获取8266 IP地址。这样就会存在一个问题,在正式环境下我们一般都会关掉debug,那么我们又如何能直观地观察当前8266的工作状态呢?既然这样,不如我们给它配上一个小的显示屏吧,然后博哥就去tb弄了一块SSD1306-12864的OLED显示屏。刚好今天又是周末,不用上班的感觉真好,果断折腾一下这个模块。
      模块就长这个样子:
      
      简洁点,有以下基本步骤:接下来,要分别烧写两个端的代码,
      这款屏幕尺寸约为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;
      

   SSD1306屏幕驱动库,最出名应该就是u8g2。但是博哥这里并不用它,改为用Adafruit_GFX 和 Adafruit_SSD1306。
   那么这两个库有什么关系呢?博哥去大概看了一下里面的源码,大家可以看看这两个图:
   
      

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

   请先添加这两个库到arduino:
      
         
      注意,原生库中并没有ShowCN_16这个方法,是我后面加的,具体有什么用直接看下面的代码。


二、撸代码



      实验前提:博哥这里是用Mega2560来测试。
      最重要的一个意识点,SSD1306-12864其实就是一个128X64像素点阵。想显示什么就把具体位置的像素点亮起来。
      首先,请烧录以下代码,我们直接在代码中讲解上面的库怎么用。
      /**
* 日期:2017/09/24
* 功能:OLED12864SSD1306测试
* 作者:单片机菜鸟
* 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_WIDTH16

#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);
}

   先不理代码,烧录出来的效果应该是顺序显示以下图案(从左到右,从上到下):
   
   
   


       在讲解方法之前,先来建立一个坐标系的概念:
   
      在讲解方法之前这其实就是一个128(width)X64(height)点阵。在坐标系中,左上角是原点,向右是X轴,向下是Y轴。
    有了坐标系概念之后,我们就开始讲解方法使用:
    1.begin()
      初始化I2C地址,在Arduino setup调用。

    2.clearDisplay()
      把Buffer清零,其实就是把OLED对应的缓存(缓存对应点阵数据)清掉,缓存就是这个:
   

    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


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


我这里用到了下面的字模取模软件:

我这里用到了下面的字模取模软件:注意以下取模方式(阴码+逐行式+顺向):


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




单片机菜鸟 发表于 2017-9-24 21:07

楼主做好冷板凳

leisd 发表于 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
博哥第一粉来了,先评再看哈哈

感谢美女支持

ynqjwfb 发表于 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总线
页: [1] 2 3 4 5 6 7 8
查看完整版本: 《博哥OLED系列》- 玩转SSD1306-12864 OLED