Arduino: code for OLED W click

2

This is a quick one: a code for using the OLED W click from MikroElektronika on Arduino boards. Of course, as the OLED W click is a 3.3V board, it will work only with those 3.3V Arduinos. I’ve tried it with chipKIT UNO32 and with my Flip & Click and it worked fine.

This article is more than two years old and might contain obsolete information; it is still kept here for informational purposes.

The code itself is inspired from an XC8 code for PIC16F18855, which can be downloaded from the examples page of MPLAB Xpress IDE. The only thing I did was to change the routines for SPI communication.

The code displays an image for a short period of time, then prints some text.Text positioning is not yet implemented, so it just prints character after character.

Advertisements

The image I used is a 96 x 39 px .png file, which I have converted in C code using the Image2GLCD tool from http://www.avrportal.com/?page=image2glcd.

Converting images to GLCD code with Image2GLCD

Converting images to GLCD code with Image2GLCD

The settings I used are: LSB to MSB, Top to Bottom, Custom size, 96 x 40. I don’t know why, but when I choose 96 x 39 it prints garbage on the last lines. With 96 x 40 it works just fine.

So, here’s my code:

/*
 * File: oled.h
*/

#define OLED_WIDTH             96
#define OLED_HEIGHT            39

#define SSD1306_DISPLAYOFF          0xAE
#define SSD1306_SETDISPLAYCLOCKDIV  0xD5
#define SSD1306_SETMULTIPLEX        0xA8
#define SSD1306_SETDISPLAYOFFSET    0xD3
#define SSD1306_SETSTARTLINE        0x40
#define SSD1306_CHARGEPUMP          0x8D

#define SSD1306_SETSEGMENTREMAP     0xA1
#define SSD1306_SEGREMAP            0xA0

#define SSD1306_COMSCANDEC          0xC8
#define SSD1306_SETCOMPINS          0xDA
#define SSD1306_SETCONTRAST         0x81
#define SSD1306_SETPRECHARGE        0xD9
#define SSD1306_SETVCOMDETECT       0xDB
#define SSD1306_DISPLAYALLON_RESUME 0xA4
#define SSD1306_NORMALDISPLAY       0xA6
#define SSD1306_DISPLAYON           0xAF

#define SSD1306_DISPLAYALLON        0xA5
#define SSD1306_INVERTDISPLAY       0xA7
#define SSD1306_SETLOWCOLUMN        0x00
#define SSD1306_SETHIGHCOLUMN       0x10
#define SSD1306_MEMORYMODE          0x20
#define SSD1306_COLUMNADDR          0x21
#define SSD1306_PAGEADDR            0x22
#define SSD1306_COMSCANINC          0xC0
#define SSD1306_SEGREMAP            0xA0
#define SSD1306_EXTERNALVCC         0x1
#define SSD1306_SWITCHCAPVCC        0x2

#define SSD1306_ACTIVATE_SCROLL                         0x2F
#define SSD1306_DEACTIVATE_SCROLL                       0x2E
#define SSD1306_SET_VERTICAL_SCROLL_AREA                0xA3
#define SSD1306_RIGHT_HORIZONTAL_SCROLL                 0x26
#define SSD1306_LEFT_HORIZONTAL_SCROLL                  0x27
#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL    0x29
#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL     0x2A

const uint8_t font[] = { // compact 5x8 font
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5F,0x00,0x00,0x00,0x07,0x00,0x07,0x00, //  'sp,!,"
    0x14,0x7F,0x14,0x7F,0x14, // #
    0x24,0x2A,0x7F,0x2A,0x12,0x23,0x13,0x08,0x64,0x62,0x36,0x49,0x56,0x20,0x50, //  '$,%,&
    0x00,0x08,0x07,0x03,0x00,0x00,0x1C,0x22,0x41,0x00,0x00,0x41,0x22,0x1C,0x00, //  '',(,)
    0x2A,0x1C,0x7F,0x1C,0x2A,0x08,0x08,0x3E,0x08,0x08,0x00,0x00,0x70,0x30,0x00, //  '*,+,,
    0x08,0x08,0x08,0x08,0x08,0x00,0x00,0x60,0x60,0x00,0x20,0x10,0x08,0x04,0x02, //  '-,.,/
    0x3E,0x51,0x49,0x45,0x3E,0x00,0x42,0x7F,0x40,0x00,0x72,0x49,0x49,0x49,0x46, //  '0,1,2
    0x21,0x41,0x49,0x4D,0x33,0x18,0x14,0x12,0x7F,0x10,0x27,0x45,0x45,0x45,0x39, //  '3,4,5
    0x3C,0x4A,0x49,0x49,0x31,0x41,0x21,0x11,0x09,0x07,0x36,0x49,0x49,0x49,0x36, //  '6,7,8
    0x46,0x49,0x49,0x29,0x1E,0x00,0x00,0x14,0x00,0x00,0x00,0x40,0x34,0x00,0x00, //  '9,:,;
    0x00,0x08,0x14,0x22,0x41,0x14,0x14,0x14,0x14,0x14,0x00,0x41,0x22,0x14,0x08, //  '<,=,>
    0x02,0x01,0x59,0x09,0x06,0x3E,0x41,0x5D,0x59,0x4E,                          //  '?,@
    0x7C,0x12,0x11,0x12,0x7C,                                                   //  'A
    0x7F,0x49,0x49,0x49,0x36,0x3E,0x41,0x41,0x41,0x22,0x7F,0x41,0x41,0x41,0x3E, //  'B,C,D
    0x7F,0x49,0x49,0x49,0x41,0x7F,0x09,0x09,0x09,0x01,0x3E,0x41,0x41,0x51,0x73, //  'E,F,G
    0x7F,0x08,0x08,0x08,0x7F,0x00,0x41,0x7F,0x41,0x00,0x20,0x40,0x41,0x3F,0x01, //  'H,I,J
    0x7F,0x08,0x14,0x22,0x41,0x7F,0x40,0x40,0x40,0x40,0x7F,0x02,0x1C,0x02,0x7F, //  'K,L,M
    0x7F,0x04,0x08,0x10,0x7F,0x3E,0x41,0x41,0x41,0x3E,0x7F,0x09,0x09,0x09,0x06, //  'N,O,P
    0x3E,0x41,0x51,0x21,0x5E,0x7F,0x09,0x19,0x29,0x46,0x26,0x49,0x49,0x49,0x32, //  'Q,R,S
    0x03,0x01,0x7F,0x01,0x03,0x3F,0x40,0x40,0x40,0x3F,0x1F,0x20,0x40,0x20,0x1F, //  'T,U,V
    0x3F,0x40,0x38,0x40,0x3F,0x63,0x14,0x08,0x14,0x63,0x03,0x04,0x78,0x04,0x03, //  'W,X,Y
    0x61,0x59,0x49,0x4D,0x43,                                                   //  'Z
    0x00,0x7F,0x41,0x41,0x41,0x02,0x04,0x08,0x10,0x20,                          //  '[,\
    0x00,0x41,0x41,0x41,0x7F,0x04,0x02,0x01,0x02,0x04,0x40,0x40,0x40,0x40,0x40, //  '],^,_
    0x00,0x03,0x07,0x08,0x00,0x20,0x54,0x54,0x38,0x40,0x7F,0x28,0x44,0x44,0x38, //  '`,a,b
    0x38,0x44,0x44,0x44,0x28,0x38,0x44,0x44,0x28,0x7F,0x38,0x54,0x54,0x54,0x18, //  'c,d,e
    0x00,0x08,0x7E,0x09,0x02,0x0C,0x52,0x52,0x4A,0x3C,0x7F,0x08,0x04,0x04,0x78, //  'f,g,h
    0x00,0x44,0x7D,0x40,0x00,0x20,0x40,0x40,0x3D,0x00,0x7F,0x10,0x28,0x44,0x00, //  'i,j,k
    0x00,0x41,0x7F,0x40,0x00,0x7C,0x04,0x78,0x04,0x78,0x7C,0x08,0x04,0x04,0x78, //  'l,m,n
    0x38,0x44,0x44,0x44,0x38,0x7C,0x18,0x24,0x24,0x18,0x18,0x24,0x24,0x18,0x7C, //  'o,p,q
    0x7C,0x08,0x04,0x04,0x08,0x48,0x54,0x54,0x54,0x24,0x04,0x04,0x3F,0x44,0x24, //  'r,s,t
    0x3C,0x40,0x40,0x20,0x7C,0x1C,0x20,0x40,0x20,0x1C,0x3C,0x40,0x30,0x40,0x3C, //  'u,v,w
    0x44,0x28,0x10,0x28,0x44,0x4C,0x50,0x50,0x50,0x3C,0x44,0x64,0x54,0x4C,0x44, //  'x,y,z
    0x00,0x08,0x36,0x41,0x00,0x00,0x00,0x77,0x00,0x00,0x00,0x41,0x36,0x08,0x00, //  '{,|,}
    0x02,0x01,0x02,0x04,0x02                                                    //  '~
    };

The main Arduino code is:

#include <SPI.h>
#include "oled.h"

// Bitmap created with Image2GLCD
// http://www.avrportal.com/?page=image2glcd
// Set to LSB to MSB, Top to Bottom
// Custom, 96 x 40 size
// Yes, 96 x 40! It prints garbage when setting 96 x 39

const uint8_t bitmap[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x10, 0xD0, 0x70, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xE0,
0x70, 0xD0, 0xF0, 0x30, 0x10, 0x10, 0x00, 0x10, 0x90, 0xF0, 0x70, 0x10, 0x10, 0x10, 0x10, 0x30,
0xE0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x80, 0xC0, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0x3F, 0xFF, 0xC0, 0xE0, 0x70, 0x1C, 0x0F, 0x03, 0x01,
0xF8, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 0x0F, 0x08, 0x08, 0x08, 0x08, 0x0C, 0x06,
0x07, 0x01, 0x01, 0xF9, 0xFF, 0x0F, 0x02, 0x01, 0x01, 0x03, 0xF0, 0xFC, 0x06, 0x03, 0x01, 0x01,
0xC3, 0xFE, 0x3C, 0x00, 0x00, 0x00, 0x01, 0xF9, 0xFF, 0x07, 0x00, 0xF0, 0xFC, 0x2E, 0x22, 0x23,
0x31, 0x11, 0x99, 0x9F, 0x0E, 0x00, 0xF0, 0xFC, 0x0E, 0x02, 0x01, 0x01, 0x01, 0x8F, 0x8E, 0x00,
0x01, 0xF9, 0xFF, 0x07, 0x01, 0x81, 0x80, 0x80, 0x0E, 0x1E, 0x19, 0x19, 0x31, 0x31, 0xE3, 0xE6,
0x02, 0x02, 0x03, 0x02, 0x02, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x02, 0x02, 0x03,
0x03, 0x02, 0x02, 0x00, 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x02, 0x02, 0x03,
0x01, 0x20, 0x60, 0x40, 0x40, 0x60, 0x3F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x02, 0x02,
0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00,
0x01, 0x03, 0x03, 0x02, 0x01, 0x00, 0x01, 0x01, 0x03, 0x02, 0x02, 0x02, 0x02, 0x03, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

// Oled W click mapping

// chipKIT UNO32, socket #1
#define OLED_CS         10
#define OLED_DC         6
#define OLED_RST        A3

// Flip & Click, socket #1
//#define OLED_CS         77
//#define OLED_DC         6
//#define OLED_RST        33

void setup() {
  // put your setup code here, to run once:
  SPI.begin();

  // Set OLED pins
  pinMode(OLED_CS, OUTPUT);
  pinMode(OLED_DC, OUTPUT);
  pinMode(OLED_RST, OUTPUT);
  OLED_Initialize();
  delay(100);
  OLED_PutPicture(bitmap);
  delay(8000);
  OLED_Clear();
  OLED_SetScale(1, 2);
  OLED_Putchar('T');
  OLED_Putchar('H');
  OLED_Putchar('I');
  OLED_Putchar('S');
  OLED_Putchar(' ');
  OLED_Putchar('I');
  OLED_Putchar('S');
  OLED_Putchar(' ');
  OLED_Putchar('A');
  OLED_Putchar(' ');
  OLED_Putchar('T');
  OLED_Putchar('E');
  OLED_Putchar('X');
  OLED_Putchar('T');
}

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

}


// OLED W functions

void OLED_Command(uint8_t temp){
  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
  digitalWrite(OLED_CS,LOW);
  digitalWrite(OLED_DC,LOW);
  SPI.transfer(temp);
  digitalWrite(OLED_CS,HIGH);
  SPI.endTransaction();
}

void OLED_Data(uint8_t temp){
  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
  digitalWrite(OLED_CS,LOW);
  digitalWrite(OLED_DC,HIGH);
  SPI.transfer(temp);
  digitalWrite(OLED_CS,HIGH);
  SPI.endTransaction();
}

void OLED_Initialize(void)
{
    digitalWrite(OLED_RST,LOW);
    delay(1000);
    digitalWrite(OLED_RST,HIGH);
    delay(1000);
    OLED_Command(SSD1306_DISPLAYOFF);             //0xAE  Set OLED Display Off
    OLED_Command(SSD1306_SETDISPLAYCLOCKDIV);     //0xD5  Set Display Clock Divide Ratio/Oscillator Frequency
    OLED_Command(0x80);
    OLED_Command(SSD1306_SETMULTIPLEX);           //0xA8  Set Multiplex Ratio
    OLED_Command(39);

    OLED_Command(SSD1306_SETSEGMENTREMAP);        //0xA1  Set Segment Remap Inv
    OLED_Command(SSD1306_COMSCANDEC);             //0xC8  Set COM Output Scan Inv

    OLED_Command(SSD1306_SETDISPLAYOFFSET);       //0xD3  Set Display Offset
    OLED_Command(0x00);
    OLED_Command(SSD1306_CHARGEPUMP);             //0x8D  Set Charge Pump
    OLED_Command(0x14);                           //0x14  Enable Charge Pump
    OLED_Command(SSD1306_SETSTARTLINE);           //0x40  Set Display Start Line
    OLED_Command(SSD1306_SETCOMPINS);             //0xDA  Set COM Pins Hardware Configuration
    OLED_Command(0x12);
    OLED_Command(SSD1306_SETCONTRAST);            //0x81   Set Contrast Control
    OLED_Command(0xAF);
    OLED_Command(SSD1306_SETPRECHARGE);           //0xD9   Set Pre-Charge Period
    OLED_Command(0x25);
    OLED_Command(SSD1306_SETVCOMDETECT);          //0xDB   Set VCOMH Deselect Level
    OLED_Command(0x20);
    OLED_Command(SSD1306_DISPLAYALLON_RESUME);    //0xA4   Set Entire Display On/Off
    OLED_Command(SSD1306_NORMALDISPLAY);          //0xA6   Set Normal/Inverse Display
    OLED_Command(SSD1306_DISPLAYON);              //0xAF   Set OLED Display On
} // OLED_Initialize

void OLED_SetRow(uint8_t add)
{
    add = 0xB0 | add;
    OLED_Command(add);
}

void OLED_SetColumn(uint8_t add)
{
    add += 32;
    OLED_Command((SSD1306_SETHIGHCOLUMN | (add >> 4))); // SET_HIGH_COLUMN
    OLED_Command((0x0f & add));        // SET LOW_COLUMN
}

void OLED_PutPicture(const uint8_t *pic)
{
    unsigned char i,j;
    for(i=0; i<5; i++) // 5*8=40 pixel rows (actually 39)
    {
        OLED_SetRow(i);
        OLED_SetColumn(0);
        for(j=0; j<96; j++)  // 96 pixel columns
        {
            OLED_Data(*pic++);
        }
    }
}

void OLED_SetContrast(uint8_t temp)
{
    OLED_Command(SSD1306_SETCONTRAST);
    OLED_Command(temp);                  // contrast step 1 to 256
}



uint8_t _x, _y;
uint8_t _sx=1, _sy=1; // scaling factors

void OLED_SetScale(uint8_t sx, uint8_t sy){
    _sx = sx; _sy = sy;
}

void OLED_Clear(void)
{
    unsigned char i,j;
    for(i=0; i<5; i++) // 5*8=40 pixel rows (actually 39)
    {
        OLED_SetRow(i);
        OLED_SetColumn(0);
        for(j=0; j<96; j++)  OLED_Data(0);
    }
    _x = 0; _y = 0;
    OLED_SetRow(0);
    OLED_SetColumn(0);
}

/**  scalable horizontally and vertically
 */
void OLED_Putchar(char ch)
{
    uint8_t i, j, k, byte;
    const uint8_t *f = &font[(ch-' ')*5];
    const uint8_t mask[]={1, 3, 7, 0xf };

    for(i=0; i<6; i++) {
        uint32_t word;
        byte = *f++ << 1;
        if (i==5) byte = 0;
        for(j=0; j<8; j++) { // expand byte to word
            word <<= _sy;
            if (byte & 0x80) word |= mask[_sy-1];
            byte <<= 1;
        }
        for(j=0; j<_sy; j++){ // exp vert
            OLED_SetRow(_y+j) ;
            OLED_SetColumn(_x+i*_sx);
            for(k=0; k<_sx; k++){ // exp horiz
                OLED_Data(word);
            }
            word >>= 8;
        }
    }

    _x+= 6 * _sx;
    if (_x >= OLED_WIDTH) { // wrap x
        _x = 0; OLED_SetColumn(0);
        _y += _sy;
        if (_y >= 5-_sy) { // wrap y
            _y = 0;
        }
        OLED_SetRow(_y);
    }
}

void OLED_Puts(char x, char y, char *s)
{
    _y = y; _x = x;
    OLED_SetRow(_y);
    OLED_SetColumn(_x);
    while(*s) {
        OLED_Putchar(*s++);
        _x++;
    }
}

Wishlist?

This is still a work in progress, so I have to ask: what other features would you like to have implemented? I’m waiting for your comments…

 

Advertisements
Share.

2 Comments

  1. Avatar

    This code worked perfectly on my Arduino but it also worked when I ported it to my STM32F7xx after just changing OLED_Command and OLED_Data functions.

    Great job, keep it up. 🙂

Leave A Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

error: