P_20190810_175544 (2)

0.96インチ、128X64ドットのOLEDディスプレイの描画テストをArduinoを用いてテストしました。2cm程度の非常に小さいものなので、もともと数字等を静的に表示するための製品かとは思います。
配線は簡単で、電源とI2Cをつなぐだけです。ソフトのほうも通常はAdafruit社のライブラリを使えばよいでしょう。今回はデータシートを見て自前で書いてみました。

データシート

SSD1306イニシャライズ

I2CはArduinoのWireを使います。

void init_SSD1306(){
  
  Wire.beginTransmission(OLED_I2C_ADDRES);
    Wire.write(0x80); Wire.write(0xAE); //set display off
    Wire.write(0x00); Wire.write(0xA8); Wire.write(0x3F); //set multiplex ratio
    Wire.write(0x00); Wire.write(0xD3); Wire.write(0x00); //set display offset
    Wire.write(0x80); Wire.write(0x40); //set display start line
    Wire.write(0x80); Wire.write(0xA0); //set segment re-map
    Wire.write(0x80); Wire.write(0xC0); //set com output scan direction
    Wire.write(0x00); Wire.write(0xDA); Wire.write(0x12); //set com pins hardware configuration
    Wire.write(0x00); Wire.write(0x81); Wire.write(0x7F); //set contrast control
    Wire.write(0x80); Wire.write(0xA4); //entire display on
    Wire.write(0x80); Wire.write(0xA6); //set normal/inverse display
    Wire.write(0x00); Wire.write(0xD5); Wire.write(0x80); //set display clock
  Wire.endTransmission();
  Wire.beginTransmission(OLED_I2C_ADDRES);
    Wire.write(0x00); Wire.write(0x20); Wire.write(0b00); //horizontal addressing mode
    Wire.write(0x00); Wire.write(0x22); Wire.write(0);  Wire.write(7);  //page 0-7
    Wire.write(0x00); Wire.write(0x21); Wire.write(0);  Wire.write(127);  //colum 0-127
    Wire.write(0x00); Wire.write(0x8D); Wire.write(0x14);
    Wire.write(0x80); Wire.write(0xAF); //set display on
  Wire.endTransmission();
}

I2Cの通信の仕様に関してはデータシートのP20 I2C-bus Write dataに説明があります。デバイスのアドレスは基板の裏側の抵抗の位置によって2通りあるようです。このアドレスを1bit左にずらして末尾にR/Wの0をつけて(私の場合は0x3C)を最初に送信します。これに続けて諸々を送信するのですが、これはデータシートの最後についているApplication noteのP5に沿ってやっています。
各コマンドはデータシートのP28~に記載されていて、上のコードでは2列目(Wire.write(0xAE)
など)となります。1列目の0x80、0x00はコマンドに先立って送るControl byteといわれているもので、P20に説明がありますがわかりにくいです。0x80なら続けて1個0x00なら続けて複数個が送られる、と解釈しました。
Application noteにないコマンドで追加で送っているのは17-19行目の0x20,0x21,0x22です。この辺の説明はP34-36にあります。Horizontal addressing modeというものに設定していて、全画面の画素データを連続で送れるというものです。画面の成り立ちはP25に図があります。縦方向がpage、横方向がsegmentで分割されていて、1pageの縦は8bit(1byte)でこれを単位にアクセスします。

画素へのアクセス

#define HEIGHT 64
#define WIDTH 128
#define WIRE_MAX 30
#define NEGATIVE 0
#define POSITIVE 1

uint8_t buf[WIDTH * HEIGHT/8];

buf[]はすべての画素のデータを保持しておくための配列です。描画の計算はいつもこのbufに対して行い、最後にまとめてデバイスに転送します。

void draw_Pixel(uint8_t x, uint8_t y, uint8_t pos_neg){
  if(x<0 || x>127 || y<0 || y>63)return;
  if(pos_neg) buf[x + (y/8)*WIDTH] |=  (1 << (y&7));  //set 1 to segment after bit shift
  else buf[x + (y/8)*WIDTH] &= ~(1 << (y&7));  //set 0 to segment after bit shift
}

一つの点(ピクセル)を描く関数です。直線や円を描くときもここにやってきます。pos_negはその画素を点灯するか消灯するかを表します。bufの該当部分(1 byte)にORで書き込みます。縦方向の座標yがsegmentの中の何ビット目なのかを計算しているのが(1 << (y&7))の部分です。

void flash(){
  uint16_t count = 0;
  uint8_t bytesOut = 1;
  
  Wire.beginTransmission(OLED_I2C_ADDRES);
  Wire.write(0b01000000);
  //send byte data for all segments
  while(count<128*8) {
    if(bytesOut >= WIRE_MAX) {
      Wire.endTransmission();
      Wire.beginTransmission(OLED_I2C_ADDRES);
      Wire.write(0b01000000);
      bytesOut = 1;
    }
    Wire.write(buf[count]);
    count++;
    bytesOut++;
  }
  Wire.endTransmission();
}

flash()はbuf[]に画素の設定をすべて終わった段階で呼び出します。左上の画素(0,0)から右下の画素(127,64)までデータシートP35の図の要領でデバイスに送信します。少し妙な形になっているのは、Wire.beginTransmissionとWire.endTransmissionの間で最高32byteしか送れないためで、途中にWire.endTransmissionをはさむためです。

デモ動画とソースコード

*今回スクロールのテストは実施しませんでした。

#include "Wire.h"

#define HEIGHT 64
#define WIDTH 128
#define WIRE_MAX 30
#define NEGATIVE 0
#define POSITIVE 1
const char Font[192][5] PROGMEM =
{
    { 0x00, 0x00, 0x00, 0x00, 0x00 }, // " " 0x20
    { 0x00, 0x00, 0x4f, 0x00, 0x00 }, // !   0x21
    { 0x00, 0x07, 0x00, 0x07, 0x00 }, // "   0x22
    { 0x14, 0x7f, 0x14, 0x7f, 0x14 }, // #   0x23
    { 0x24, 0x2a, 0x7f, 0x2a, 0x12 }, // $   0x24
    { 0x23, 0x13, 0x08, 0x64, 0x62 }, // %   0x25
    { 0x36, 0x49, 0x55, 0x22, 0x50 }, // &   0x26
    { 0x00, 0x05, 0x03, 0x00, 0x00 }, // '   0x27
    { 0x00, 0x1c, 0x22, 0x41, 0x00 }, // (   0x28
    { 0x00, 0x41, 0x22, 0x1c, 0x00 }, // )   0x29
    { 0x14, 0x08, 0x3e, 0x08, 0x14 }, // *   0x2A
    { 0x08, 0x08, 0x3e, 0x08, 0x08 }, // +   0x2B
    { 0x00, 0x50, 0x30, 0x00, 0x00 }, // ,   0x2C
    { 0x08, 0x08, 0x08, 0x08, 0x08 }, // -   0x2D
    { 0x00, 0x60, 0x60, 0x00, 0x00 }, // .   0x2E
    { 0x20, 0x10, 0x08, 0x04, 0x02 }, // /   0x2F
    { 0x3e, 0x51, 0x49, 0x45, 0x3e }, // 0   0x30
    { 0x00, 0x42, 0x7f, 0x40, 0x00 }, // 1   0x31
    { 0x42, 0x61, 0x51, 0x49, 0x46 }, // 2   0x32
    { 0x21, 0x41, 0x45, 0x4b, 0x31 }, // 3   0x33
    { 0x18, 0x14, 0x12, 0x7f, 0x10 }, // 4   0x34
    { 0x27, 0x45, 0x45, 0x45, 0x39 }, // 5   0x35
    { 0x3c, 0x4a, 0x49, 0x49, 0x30 }, // 6   0x36
    { 0x01, 0x71, 0x09, 0x05, 0x03 }, // 7   0x37
    { 0x36, 0x49, 0x49, 0x49, 0x36 }, // 8   0x38
    { 0x06, 0x49, 0x49, 0x29, 0x1e }, // 9   0x39
    { 0x00, 0x36, 0x36, 0x00, 0x00 }, // :   0x3A
    { 0x00, 0x56, 0x36, 0x00, 0x00 }, // ;   0x3B
    { 0x08, 0x14, 0x22, 0x41, 0x00 }, // <   0x3C
    { 0x14, 0x14, 0x14, 0x14, 0x14 }, // =   0x3D
    { 0x00, 0x41, 0x22, 0x14, 0x08 }, // >   0x3E
    { 0x02, 0x01, 0x51, 0x09, 0x06 }, // ?   0x3F
    { 0x32, 0x49, 0x79, 0x41, 0x3e }, // @   0x40
    { 0x7e, 0x11, 0x11, 0x11, 0x7e }, // A   0x41
    { 0x7f, 0x49, 0x49, 0x49, 0x36 }, // B   0x42
    { 0x3e, 0x41, 0x41, 0x41, 0x22 }, // C   0x43
    { 0x7f, 0x41, 0x41, 0x22, 0x1c }, // D   0x44
    { 0x7f, 0x49, 0x49, 0x49, 0x41 }, // E   0x45
    { 0x7f, 0x09, 0x09, 0x09, 0x01 }, // F   0x46
    { 0x3e, 0x41, 0x49, 0x49, 0x7a }, // G   0x47
    { 0x7f, 0x08, 0x08, 0x08, 0x7f }, // H   0x48
    { 0x00, 0x41, 0x7f, 0x41, 0x00 }, // I   0x49
    { 0x20, 0x40, 0x41, 0x3f, 0x01 }, // J   0x4A
    { 0x7f, 0x08, 0x14, 0x22, 0x41 }, // K   0x4B
    { 0x7f, 0x40, 0x40, 0x40, 0x40 }, // L   0x4C
    { 0x7f, 0x02, 0x0c, 0x02, 0x7f }, // M   0x4D
    { 0x7f, 0x04, 0x08, 0x10, 0x7f }, // N   0x4E
    { 0x3e, 0x41, 0x41, 0x41, 0x3e }, // O   0x4F
    { 0x7f, 0x09, 0x09, 0x09, 0x06 }, // P   0X50
    { 0x3e, 0x41, 0x51, 0x21, 0x5e }, // Q   0X51
    { 0x7f, 0x09, 0x19, 0x29, 0x46 }, // R   0X52
    { 0x46, 0x49, 0x49, 0x49, 0x31 }, // S   0X53
    { 0x01, 0x01, 0x7f, 0x01, 0x01 }, // T   0X54
    { 0x3f, 0x40, 0x40, 0x40, 0x3f }, // U   0X55
    { 0x1f, 0x20, 0x40, 0x20, 0x1f }, // V   0X56
    { 0x3f, 0x40, 0x38, 0x40, 0x3f }, // W   0X57
    { 0x63, 0x14, 0x08, 0x14, 0x63 }, // X   0X58
    { 0x07, 0x08, 0x70, 0x08, 0x07 }, // Y   0X59
    { 0x61, 0x51, 0x49, 0x45, 0x43 }, // Z   0X5A
    { 0x00, 0x7f, 0x41, 0x41, 0x00 }, // [   0X5B
    { 0x02, 0x04, 0x08, 0x10, 0x20 }, // "\" 0X5C
    { 0x00, 0x41, 0x41, 0x7f, 0x00 }, // ]   0X5D
    { 0x04, 0x02, 0x01, 0x02, 0x04 }, // ^   0X5E
    { 0x40, 0x40, 0x40, 0x40, 0x40 }, // _   0X5F
    { 0x00, 0x01, 0x02, 0x04, 0x00 }, // `   0X60
    { 0x20, 0x54, 0x54, 0x54, 0x78 }, // a   0X61
    { 0x7f, 0x48, 0x44, 0x44, 0x38 }, // b   0X62
    { 0x38, 0x44, 0x44, 0x44, 0x20 }, // c   0X63
    { 0x38, 0x44, 0x44, 0x48, 0x7f }, // d   0X64
    { 0x38, 0x54, 0x54, 0x54, 0x18 }, // e   0X65
    { 0x08, 0x7e, 0x09, 0x01, 0x02 }, // f   0X66
    { 0x0c, 0x52, 0x52, 0x52, 0x3e }, // g   0X67
    { 0x7f, 0x08, 0x04, 0x04, 0x78 }, // h   0X68
    { 0x00, 0x44, 0x7d, 0x40, 0x00 }, // i   0X69
    { 0x20, 0x40, 0x44, 0x3d, 0x00 }, // j   0X6A
    { 0x7f, 0x10, 0x28, 0x44, 0x00 }, // k   0X6B
    { 0x00, 0x41, 0x7f, 0x40, 0x00 }, // l   0X6C
    { 0x7c, 0x04, 0x18, 0x04, 0x78 }, // m   0X6D
    { 0x7c, 0x08, 0x04, 0x04, 0x78 }, // n   0X6E
    { 0x38, 0x44, 0x44, 0x44, 0x38 }, // o   0X6F
    { 0x7c, 0x14, 0x14, 0x14, 0x08 }, // p   0X70
    { 0x08, 0x14, 0x14, 0x18, 0x7c }, // q   0X71
    { 0x7c, 0x08, 0x04, 0x04, 0x08 }, // r   0X72
    { 0x48, 0x54, 0x54, 0x54, 0x20 }, // s   0X73
    { 0x04, 0x3f, 0x44, 0x40, 0x20 }, // t   0X74
    { 0x3c, 0x40, 0x40, 0x20, 0x7c }, // u   0X75
    { 0x1c, 0x20, 0x40, 0x20, 0x1c }, // v   0X76
    { 0x3c, 0x40, 0x30, 0x40, 0x3c }, // w   0X77
    { 0x44, 0x28, 0x10, 0x28, 0x44 }, // x   0X78
    { 0x0c, 0x50, 0x50, 0x50, 0x3c }, // y   0X79
    { 0x44, 0x64, 0x54, 0x4c, 0x44 }, // z   0X7A
    { 0x00, 0x08, 0x36, 0x41, 0x00 }, // {   0X7B
    { 0x00, 0x00, 0x7f, 0x00, 0x00 }, // |   0X7C
    { 0x00, 0x41, 0x36, 0x08, 0x00 }, // }   0X7D
    { 0x08, 0x08, 0x2a, 0x1c, 0x08 }, // ->  0X7E
    { 0x08, 0x1c, 0x2a, 0x08, 0x08 }, // <-  0X7F
  { 0x00, 0x00, 0x0f, 0x08, 0x08 }, //     0x80
  { 0x08, 0x08, 0x0f, 0x00, 0x00 }, //     0x81    
  { 0x2c, 0x32, 0x02, 0x32, 0x2c }, // ohm 0x82
  { 0x44, 0x3c, 0x04, 0x7c, 0x44 }, // pi  0x83     
  { 0x63, 0x55, 0x49, 0x41, 0x41 }, // siguma0x84
  { 0x14, 0x14, 0x7c, 0x14, 0x12 }, // sec 0x85    
  { 0x44, 0x3c, 0x14, 0x14, 0x74 }, // man 0x86
  { 0x7c, 0x14, 0x1c, 0x14, 0x7c }, // en  0x87     
  { 0x10, 0x10, 0x54, 0x10, 0x10 }, // waru0x88
  { 0x7f, 0x7f, 0x7f, 0x7f, 0x7f }, //     0x89    
  { 0x0f, 0x0f, 0x0f, 0x0f, 0x0f }, //     0x8A
  { 0x00, 0x00, 0x00, 0x00, 0x00 }, //     0x8B     
  { 0x00, 0x00, 0x00, 0x00, 0x00 }, //     0x8C
  { 0x00, 0x00, 0x00, 0x00, 0x00 }, //     0x8D    
  { 0x00, 0x00, 0x00, 0x00, 0x00 }, //     0x8E
  { 0x00, 0x00, 0x00, 0x00, 0x00 }, //     0x8F     
  { 0x00, 0x00, 0x00, 0x00, 0x00 }, //     0x90
  { 0x00, 0x00, 0x00, 0x00, 0x00 }, //     0x91    
  { 0x00, 0x00, 0x00, 0x00, 0x00 }, //     0x92
  { 0x00, 0x00, 0x00, 0x00, 0x00 }, //     0x93     
  { 0x00, 0x00, 0x00, 0x00, 0x00 }, //     0x94
  { 0x00, 0x00, 0x00, 0x00, 0x00 }, //     0x95    
  { 0x00, 0x00, 0x00, 0x00, 0x00 }, //     0x96
  { 0x00, 0x00, 0x00, 0x00, 0x00 }, //     0x97     
  { 0x00, 0x00, 0x00, 0x00, 0x00 }, //     0x98
  { 0x00, 0x00, 0x00, 0x00, 0x00 }, //     0x99    
  { 0x00, 0x00, 0x00, 0x00, 0x00 }, //     0x9A
  { 0x00, 0x00, 0x00, 0x00, 0x00 }, //     0x9B     
  { 0x00, 0x00, 0x00, 0x00, 0x00 }, //     0x9C
  { 0x00, 0x00, 0x00, 0x00, 0x00 }, //     0x9D    
  { 0x00, 0x00, 0x00, 0x00, 0x00 }, //     0x9E
  { 0x00, 0x00, 0x00, 0x00, 0x00 }, //     0x9F
  { 0x00, 0x00, 0x00, 0x00, 0x00 }, //     0xA0
  { 0x70, 0x50, 0x70, 0x00, 0x00 }, //  .  0xA1    
  { 0x00, 0x00, 0x0f, 0x01, 0x01 }, //  [  0xA2
  { 0x40, 0x40, 0x78, 0x00, 0x00 }, //  ]  0xA3     
  { 0x10, 0x20, 0x40, 0x00, 0x00 }, //  ,  0xA4
  { 0x00, 0x18, 0x18, 0x00, 0x00 }, //  .  0xA5    
  { 0x0a, 0x0a, 0x4a, 0x2a, 0x1e }, // wo  0xA6
  { 0x04, 0x24, 0x34, 0x14, 0x0c }, // a   0xA7     
  { 0x20, 0x10, 0x78, 0x04, 0x00 }, // i   0xA8
  { 0x18, 0x08, 0x4c, 0x48, 0x38 }, // u   0xA9    
  { 0x48, 0x48, 0x78, 0x48, 0x48 }, // e   0xAA
  { 0x48, 0x28, 0x18, 0x7c, 0x08 }, // o   0xAB     
  { 0x08, 0x7c, 0x08, 0x28, 0x18 }, // ya  0xAC
  { 0x40, 0x48, 0x48, 0x78, 0x40 }, // yu  0xAD    
  { 0x54, 0x54, 0x54, 0x7c, 0x00 }, // yo  0xAE
  { 0x18, 0x00, 0x58, 0x40, 0x38 }, // tu  0xAF   
  { 0x08, 0x08, 0x08, 0x08, 0x08 }, //  -  0xB0
  { 0x01, 0x41, 0x3d, 0x09, 0x07 }, //  a  0xB1    
  { 0x20, 0x10, 0x7c, 0x02, 0x01 }, //  i  0xB2
  { 0x0e, 0x02, 0x43, 0x22, 0x1e }, //  u  0xB3     
  { 0x42, 0x42, 0x7e, 0x42, 0x42 }, //  e  0xB4
  { 0x22, 0x12, 0x0a, 0x7f, 0x02 }, //  o  0xB5    
  { 0x42, 0x3f, 0x02, 0x42, 0x3e }, // ka  0xB6
  { 0x0a, 0x0a, 0x7f, 0x0a, 0x0a }, // ki  0xB7     
  { 0x08, 0x46, 0x42, 0x22, 0x1e }, // ku  0xB8
  { 0x04, 0x03, 0x42, 0x3e, 0x04 }, // ke  0xB9    
  { 0x42, 0x42, 0x42, 0x42, 0x7e }, // ko  0xBA
  { 0x02, 0x4f, 0x22, 0x1f, 0x02 }, // sa  0xBB     
  { 0x4a, 0x4a, 0x40, 0x20, 0x1c }, // si  0xBC
  { 0x42, 0x22, 0x12, 0x2a, 0x46 }, // su  0xBD    
  { 0x02, 0x3f, 0x42, 0x4a, 0x46 }, // se  0xBE
  { 0x06, 0x48, 0x40, 0x20, 0x1e }, // so  0xBF
  { 0x08, 0x46, 0x4a, 0x32, 0x1e }, // ta  0xC0
  { 0x0a, 0x4a, 0x3e, 0x09, 0x08 }, // ti  0xC1    
  { 0x0e, 0x00, 0x4e, 0x20, 0x1e }, // tu  0xC2
  { 0x04, 0x45, 0x3d, 0x05, 0x04 }, // te  0xC3     
  { 0x00, 0x7f, 0x08, 0x10, 0x00 }, // to  0xC4
  { 0x44, 0x24, 0x1f, 0x04, 0x04 }, // na  0xC5    
  { 0x40, 0x42, 0x42, 0x42, 0x40 }, // ni  0xC6
  { 0x42, 0x2a, 0x12, 0x2a, 0x06 }, // nu  0xC7     
  { 0x22, 0x12, 0x7b, 0x16, 0x22 }, // ne  0xC8
  { 0x00, 0x40, 0x20, 0x1f, 0x00 }, // no  0xC9    
  { 0x78, 0x00, 0x02, 0x04, 0x78 }, // ha  0xCA
  { 0x3f, 0x44, 0x44, 0x44, 0x44 }, // hi  0xCB     
  { 0x02, 0x42, 0x42, 0x22, 0x1e }, // hu  0xCC
  { 0x04, 0x02, 0x04, 0x08, 0x30 }, // he  0xCD    
  { 0x32, 0x02, 0x7f, 0x02, 0x32 }, // ho  0xCE
  { 0x02, 0x12, 0x22, 0x52, 0x0e }, // ma  0xCF   
  { 0x00, 0x2a, 0x2a, 0x2a, 0x40 }, // mi  0xD0
  { 0x38, 0x24, 0x22, 0x20, 0x70 }, // mu  0xD1    
  { 0x40, 0x28, 0x10, 0x28, 0x06 }, // me  0xD2
  { 0x0a, 0x3e, 0x4a, 0x4a, 0x4a }, // mo  0xD3     
  { 0x04, 0x7f, 0x04, 0x14, 0x0c }, // ya  0xD4
  { 0x40, 0x42, 0x42, 0x7e, 0x40 }, // yu  0xD5    
  { 0x4a, 0x4a, 0x4a, 0x4a, 0x7e }, // yo  0xD6
  { 0x04, 0x05, 0x45, 0x25, 0x1c }, // ra  0xD7     
  { 0x0f, 0x40, 0x20, 0x1f, 0x00 }, // ri  0xD8
  { 0x7c, 0x00, 0x7e, 0x80, 0x30 }, // ru  0xD9    
  { 0x7e, 0x40, 0x20, 0x10, 0x08 }, // re  0xDA
  { 0x7e, 0x42, 0x42, 0x42, 0x7e }, // ro  0xDB     
  { 0x0e, 0x02, 0x42, 0x22, 0x1e }, // wa  0xDC
  { 0x42, 0x42, 0x40, 0x20, 0x18 }, // n   0xDD    
  { 0x02, 0x04, 0x01, 0x02, 0x00 }, // "   0xDE
  { 0x07, 0x05, 0x07, 0x00, 0x00 } // .   0xDF
              
};
const uint8_t OLED_I2C_ADDRES = 0x3C;

uint8_t buf[WIDTH * HEIGHT/8];

void init_SSD1306(){
  
  Wire.beginTransmission(OLED_I2C_ADDRES);
    Wire.write(0x80); Wire.write(0xAE); //set display off
    Wire.write(0x00); Wire.write(0xA8); Wire.write(0x3F); //set multiplex ratio
    Wire.write(0x00); Wire.write(0xD3); Wire.write(0x00); //set display offset
    Wire.write(0x80); Wire.write(0x40); //set display start line
    Wire.write(0x80); Wire.write(0xA0); //set segment re-map
    Wire.write(0x80); Wire.write(0xC0); //set com output scan direction
    Wire.write(0x00); Wire.write(0xDA); Wire.write(0x12); //set com pins hardware configuration
    Wire.write(0x00); Wire.write(0x81); Wire.write(0x7F); //set contrast control
    Wire.write(0x80); Wire.write(0xA4); //entire display on
    Wire.write(0x80); Wire.write(0xA6); //set normal/inverse display
    Wire.write(0x00); Wire.write(0xD5); Wire.write(0x80); //set display clock
  Wire.endTransmission();
  Wire.beginTransmission(OLED_I2C_ADDRES);
    Wire.write(0x00); Wire.write(0x20); Wire.write(0b00); //horizontal addressing mode
    Wire.write(0x00); Wire.write(0x22); Wire.write(0);  Wire.write(7);  //page 0-7
    Wire.write(0x00); Wire.write(0x21); Wire.write(0);  Wire.write(127);  //colum 0-127
    Wire.write(0x00); Wire.write(0x8D); Wire.write(0x14);
    Wire.write(0x80); Wire.write(0xAF); //set display on
  Wire.endTransmission();
}

void clear_All(){
  memset(buf, 0, sizeof(buf));
}

void draw_Char(uint8_t line, uint8_t colum, uint16_t letter, uint8_t c_size,
               uint8_t pos_neg ){
  uint8_t orig;
  uint8_t orig_d;
  uint8_t page;
  uint8_t data[3];
  int i;
  page = line/8;
  line = line%8;
  if(c_size==1){
    for(i=0; i<5; i++){
      orig = pgm_read_byte(&Font[letter][i]);
      data[0] = orig << line; 
      draw_Byte(data[0], page, colum+i, pos_neg);
      data[0] = orig >> (8-line); 
      draw_Byte(data[0], page+1, colum+i, pos_neg);
    }
  }else{
    for(i=0; i<5; i++){
      orig = pgm_read_byte(&Font[letter][i]);
      data[0] = (orig & 0b00000001)+((orig & 0b00000001)<<1)+
              ((orig & 0b00000010)<<1)+((orig & 0b00000010)<<2)+
              ((orig & 0b00000100)<<2)+((orig & 0b00000100)<<3)+
              ((orig & 0b00001000)<<3)+((orig & 0b00001000)<<4);
      data[1] = ((orig & 0b00010000)>>4)+((orig & 0b00010000)>>3)+
              ((orig & 0b00100000)>>3)+((orig & 0b00100000)>>2)+
              ((orig & 0b01000000)>>2)+((orig & 0b01000000)>>1)+
              ((orig & 0b10000000)>>1)+((orig & 0b10000000));
      data[2] = data[1] >> (8-line);
      data[1] = (data[1] << line) + (data[0] >> (8-line));
      data[0] = data[0] << line;
      draw_Byte(data[0], page, colum+2*i, pos_neg);
      draw_Byte(data[0], page, colum+2*i+1, pos_neg);
      draw_Byte(data[1], page+1, colum+2*i, pos_neg);
      draw_Byte(data[1], page+1, colum+2*i+1, pos_neg);
      draw_Byte(data[2], page+2, colum+2*i, pos_neg);
      draw_Byte(data[2], page+2, colum+2*i+1, pos_neg);
    }
  }
}

void fill_Circle(uint8_t x, uint8_t y, uint8_t rad,
                  uint8_t pos_neg){
  int8_t r = (int8_t)rad;
  int8_t d = 1 - r;
  int8_t dH = 3;
  int8_t dD = 5 - 2 * r;
  int8_t cy = r;
  int8_t cx;
  for(cx = 0; cx < cy; cx++){
    if(d < 0){
      d += dH;
      dH += 2;
      dD += 2;
    }else{
      d += dD;
      dH += 2;
      dD += 4;
      --cy;
    }
    draw_Line(cy + x, cx + y, -cy + x, cx + y,pos_neg);
    draw_Line(cx + x, cy + y, -cx + x, cy + y,pos_neg);
    draw_Line(-cy + x, -cx + y, cy + x, -cx + y,pos_neg);
    draw_Line(-cx + x, -cy + y, cx + x, -cy + y,pos_neg);
  }
}

void draw_Circle(uint8_t x, uint8_t y, uint8_t rad,
                  uint8_t pos_neg){
  int8_t r = (int8_t)rad;
  int8_t d = 1 - r;
  int8_t dH = 3;
  int8_t dD = 5 - 2 * r;
  int8_t cy = r;
  int8_t cx;
  for(cx = 0; cx < cy; cx++){
    if(d < 0){
      d += dH;
      dH += 2;
      dD += 2;
    }else{
      d += dD;
      dH += 2;
      dD += 4;
      cy--;
    }
    draw_Pixel(cy + x, cx + y,pos_neg);
    draw_Pixel(cx + x, cy + y,pos_neg);
    draw_Pixel(-cy + x, -cx + y,pos_neg);
    draw_Pixel(-cx + x, -cy + y,pos_neg);
    draw_Pixel(cx + x, -cy + y,pos_neg);
    draw_Pixel(cy + x, -cx + y,pos_neg);
    draw_Pixel(-cx + x, cy + y,pos_neg);
    draw_Pixel(-cy + x, cx + y,pos_neg);
  }
}

void fill_Rect(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1,
               uint8_t pos_neg){
  uint8_t i,j;
  uint8_t temp;
  if(x0 > x1){temp = x0;x0=x1;x1=temp;}
  if(y0 > y1){temp = y0;y0=y1;y1=temp;}
  for(i=x0; i<=x1; i++){
    draw_Line(i,y0,i,y1,pos_neg);
  }
}

void draw_Rect(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1,
               uint8_t pos_neg){
  draw_Line(x0, y0, x1, y0, pos_neg);
  draw_Line(x1, y1, x1, y0, pos_neg);
  draw_Line(x1, y1, x0, y1, pos_neg);
  draw_Line(x0, y0, x0, y1, pos_neg);
}

void draw_Line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1,
              uint8_t pos_neg){
  uint8_t temp;
  uint8_t x, y;
  uint8_t ystep;
  
  uint8_t inclination = (abs(y1 - y0) > abs(x1 - x0));
  if(inclination){
    temp = x0; x0 = y0; y0= temp;
    temp = x1; x1 = y1; y1= temp;
  }
  if(x0 > x1){
    temp = x0; x0 = x1; x1= temp;
    temp = y0; y0 = y1; y1= temp;
  }
  uint8_t deltaX = x1 - x0;
  uint8_t deltaY = abs(y1 - y0);
  int dummyY = deltaX/2;
  y = y0;
  if(y0 < y1)ystep = 1; else ystep = -1;
  for(x = x0; x < x1; x++){
    if(inclination) draw_Pixel(y, x, pos_neg); else draw_Pixel(x, y, pos_neg);
    dummyY -= deltaY;
    if(dummyY < 0){
      y += ystep;
      dummyY += deltaX;
    }
  }
}

void draw_Byte(uint8_t ptn, uint8_t page, uint8_t colum, uint8_t pos_neg){
  if(colum<0 || colum>127 || page<0 || page>7)return;
  if(pos_neg) buf[colum + page*WIDTH] |=  ptn;
  else buf[colum + page*WIDTH] &= ~ptn;
}

void draw_Pixel(uint8_t x, uint8_t y, uint8_t pos_neg){
  if(x<0 || x>127 || y<0 || y>63)return;
  if(pos_neg) buf[x + (y/8)*WIDTH] |=  (1 << (y&7));  //set 1 to segment after bit shift
  else buf[x + (y/8)*WIDTH] &= ~(1 << (y&7));  //set 0 to segment after bit shift
}

void flash(){
  uint16_t count = 0;
  uint8_t bytesOut = 1;
  
  Wire.beginTransmission(OLED_I2C_ADDRES);
  Wire.write(0b01000000);
  //send byte data for all segments
  while(count<128*8) {
    if(bytesOut >= WIRE_MAX) {
      Wire.endTransmission();
      Wire.beginTransmission(OLED_I2C_ADDRES);
      Wire.write(0b01000000);
      bytesOut = 1;
    }
    Wire.write(buf[count]);
    count++;
    bytesOut++;
  }
  Wire.endTransmission();
}

void setup() {
  // put your setup code here, to run once:
  int i,j,k;
  Wire.setClock(400000);
  Wire.begin();
  Wire.setClock(400000);
  delay(100);
  init_SSD1306();

  /****************************************
   * DEMO
   */
  clear_All();
  flash();
  for(i=0;i<128;i+=4){
    draw_Line(i,0,0,63,POSITIVE);
    flash();
  }
  for(i=0;i<128;i+=4){
    draw_Line(127,0,i,63,POSITIVE);
    flash();
  }
  clear_All();
  flash();
  for(i=10;i<128;i+=20){
    for(j=10;j<64;j+=20){
      draw_Rect(i-8,j-8,i+8,j+8,POSITIVE);
      flash();
    }
  }
  delay(200);
  clear_All();
  flash();
  for(i=10;i<128;i+=20){
    for(j=10;j<64;j+=20){
      fill_Rect(i-8,j-8,i+8,j+8,POSITIVE);
      flash();
    }
  }
  delay(200);
  clear_All();
  flash();
  for(i=10;i<128;i+=20){
    for(j=10;j<64;j+=20){
      draw_Circle(i,j,8,POSITIVE);
      flash();
    }
  }
  delay(200);
  clear_All();
  flash();
  for(i=10;i<128;i+=20){
    for(j=10;j<64;j+=20){
      fill_Circle(i,j,8,POSITIVE);
      flash();
    }
  }
  delay(200);
  clear_All();
  flash();
  k=1;
  for(i=0;i<64;i+=8){
    for(j=0;j<128;j+=8){
      draw_Char(i,j,k++,1,POSITIVE);
    }
  }
  flash();
  delay(400);
  clear_All();
  flash();
  k=1;
  for(i=0;i<64;i+=16){
    for(j=0;j<128;j+=16){
      draw_Char(i,j,k++,2,POSITIVE);
    }
  }
  flash();
  delay(400);
  fill_Rect(0,0,127,63,POSITIVE);
fill_Circle(64,32,18,NEGATIVE);
flash();
}

void loop() {
  // put your main code here, to run repeatedly:
  
}