I'm trying to use an OLED-screen with I2C, but nothing happens (literally) so I come here to see if anyone can shed some lights on what I've done wrong.

My first approach was to write a software I2C-driver, after I read up about the protocol from an appnote by Texas Instruments, but somehow my signals from SDA and SCL occured completely separate (clock pulse came after data). 4h of bug searching I found out that it was 2 out of 5 GPIO-pins on my PIC12F683 that was damaged.

At that point I was fed up, so I grabbed a PIC16F886 with hardware-I2C instead, because my main point was to use the OLED-screen instead of writing a protocol handler.

However, not even that does work. In the main loop I start/stop an LED just to see if the loop is running, and it is in indeed. This means, I think, that the I2C itself is working (since I have a while-loop waiting for the ACK, and it comes), but the screen somehow stays off.

The screen is brand new and unused, so it should work, but it is ofcourse a possibility it's broken. The main suspect here is my code though, it's most likely me who's forgotten something.

So, here it is, written for the PIC16F886 using XC8-compiler:

#include <xc.h>

#pragma config FOSC = INTRC_NOCLKOUT// Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled and can be enabled by SWDTEN bit of the WDTCON register)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON       // RE3/MCLR pin function select bit (RE3/MCLR pin function is MCLR)
#pragma config CP = OFF         // Code Protection bit (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Code Protection bit (Data memory code protection is disabled)
#pragma config BOREN = OFF      // Brown Out Reset Selection bits (BOR disabled)
#pragma config IESO = OFF       // Internal External Switchover bit (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is disabled)
#pragma config LVP = OFF        // Low Voltage Programming Enable bit (RB3 pin has digital I/O, HV on MCLR must be used for programming)

#pragma config BOR4V = BOR40V   // Brown-out Reset Selection bit (Brown-out Reset set to 4.0V)
#pragma config WRT = OFF        // Flash Program Memory Self Write Enable bits (Write protection off)

#define OLED_CONTRAST 0x81
#define OLED_DISPLAY_ON 0xaf
#define OLED_ADDRESS 0b01111000
#define OLED_WRITE   0b01111000
#define OLED_READ    0b01111001

#define LED RC7

void delay() {
    volatile unsigned long korv;

    for (korv = 0; korv < 2*65535; korv++) {

void init_mcu() {
    OSCCON = 0b01110001;        // 8MHz intosc

    ANSEL  = 0;                 // Digital I/O
    ANSELH = 0;

    CM1CON0 = 0;                // Disable comparators
    CM2CON0 = 0;

    TRISA = 0;
    TRISB = 0;
    TRISC = 0b00011000;         // SDA/SCL - inputs

    SSPCONbits.SSPEN = 1;       // Enable serial module
    SSPCONbits.SSPM = 0b1000;   // I2C-master

    SSPCON2bits.RCEN = 0;       // Disable receive mode
    SSPADD = 4;                 // 8MHz Fosc -> 400kbps I2C
    SSPSTATbits.SMP = 0;        // Enable slew rate control for 400kHz

void i2c_wait() {
    while (!SSPIF) {}
    SSPIF = 0;

void i2c_start() {
    SSPCON2bits.SEN = 1;

void i2c_stop() {
    SSPCON2bits.PEN = 1;

void i2c_write_byte(unsigned char b) {
    SSPBUF = b;

void oled_command(unsigned char c) {
    i2c_write_byte(0x00);                   // D/C

void main() {

    while (1) {

        LED = 1;

        LED = 0;

Are there any obvious errors, regarding the I2C-usage, I've done?


Schematics, photo of oscilloscope and breadboard as follows.

Scoping SDA/SCL yields more or less constant 5V, no pulses at all. Surmising the chip was broken, I replaced it with a brand new, factory new, one. Same error.

I'm at a complete loss here.

enter image description here enter image description here enter image description here

  • 3
    \$\begingroup\$ The problem might be either on the software or on the hardware. Or both. Can you update the question with the schematics of your system and the datasheeet of your OLED display? By the way, did you try also to use a simpler I2C device (e.g. a port expander) ? Or did you try to grab the I2C SDA and SCL with a scope, to see if you're actually sending the correct data? \$\endgroup\$
    – next-hack
    Sep 17 '17 at 17:55

You seem to be using an SSD1306 oled. (worth mentioning!)

To get such a display to life, you must initialize it. The sequence I use is

  static constexpr const uint8_t init_sequence[] = {
     CMD_MODE,  DISPLAYOFF,                   
     CMD_MODE,  SETMULTIPLEX,         0x3F,   
     CMD_MODE,  SETDISPLAYOFFSET,     0x00,   
     CMD_MODE,  SETSTARTLINE        | 0x00,  
     CMD_MODE,  CHARGEPUMP,           0x14,   
     CMD_MODE,  MEMORYMODE,           0x00,   
     CMD_MODE,  SEGREMAP            | 0x01,
     CMD_MODE,  SETCOMPINS,           0x12,   
     CMD_MODE,  SETCONTRAST,          0xCF,   
     CMD_MODE,  SETPRECHARGE,         0xF1,  
     CMD_MODE,  SETVCOMDETECT,        0x40,   
     CMD_MODE,  NORMALDISPLAY,                
     CMD_MODE,  DISPLAYON                     

After that you can start writing to the pixel RAM.

I am not sure the on/off switching you use will show anything at all (even with correct initialization) when the pixel RAM happens to be all zeros.

But as next-hack hinted in his comment, there might be lots of problems. Start with a simple I2C chip, like a PCF8574A. Once you get that to work, copy teh code from a known-working driver for your display and get it to work in your context. My driver can be found at https://github.com/wovo/hwlib/blob/master/library/hwlib-glcd-oled.hpp It is in C++, but you should be able to adapt it to C.

(Or better: switch to a modern chip that supports C++! Cortex modules or the Arduino Due are very cheap from Aliexpress. Or take an LPC1114 if you want a DIP chip.)

  • \$\begingroup\$ You are correct it's an SSD1306. According to the datasheet I have, the only two steps required to bring it to life is: 1. Power on Vcc and wait 6us, 2. When Vcc is stable, send command 0xAF for "display ON". SEG/COM will be ON after 100ms. And that's it. \$\endgroup\$
    – bos
    Sep 18 '17 at 11:11
  • \$\begingroup\$ I'm accepting this answer, because it turns out you are right and the datasheet is wrong(!); an initialization sequence /has/ to be done. All now works as expected. \$\endgroup\$
    – bos
    Sep 18 '17 at 20:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.