2

I have a Raspberry Pi 3B+ running Raspbian Stretch Lite. I also have an SSD1306 0.96" OLED display. Sometime ago, I had written a simple lightweight library for using this OLED with arduino over i2c. The code can be found at: https://github.com/shivangsgangadia/ssd1306_ardino

What is happening ?
I tried porting the library over to Raspberry pi, to run with WiringPi using C++. Most of the time for writing to the OLED, I have used C's write(fd, buffer, length) function.
However, no string is being displayed on the OLED. i2cdetect is able to detect the display at the correct address, and the bus is accesible (File descripter != -1).

What have I tried ?
I tried comparing all code line by line to the Arduino library, and nothing seems to be different. I know, that write() completes the whole function of Wire.beginTransmission() to Wire.endTranmission()
I also put printf() statements wherever write() is called to see if it is giving any errors, but everything seems to work fine.
However, when there were too many printf statements, the OLED did display some parts of the intended string in the correct places, which lead me to believe that maybe the bus speed was too much, so I set the bus speed in /boot/config.txt to 100khz, but there is still no effect. The display remains blank.

The code for the cpp file is as follows:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <wiringPi.h>
#include "OLED.h"

uint8_t OLED::doubleScaleBuffer[2];
int OLED::fd = -1;

void OLED::init() {
  // Load file descriptor
  OLED::fd = wiringPiI2CSetup(OLED_I2C_ADDRESS);
  if (OLED::fd < 0) {
    printf("OLED init failed.\n");
    return;
  }

  uint8_t initWriteBuffer[26] = {
    OLED_CONTROL_BYTE_CMD_STREAM,
    OLED_CMD_DISPLAY_OFF,
    OLED_CMD_SET_MUX_RATIO, 0x3F,
    OLED_CMD_SET_DISPLAY_OFFSET, 0x00,
    OLED_CMD_SET_DISPLAY_START_LINE,
    OLED_CMD_SET_SEGMENT_REMAP, 0xC0,
    OLED_CMD_SET_COM_PIN_MAP, 0x12,
    OLED_CMD_SET_CONTRAST, 0x7F,
    OLED_CMD_DISPLAY_RAM,
    OLED_CMD_DISPLAY_NORMAL,
    OLED_CMD_SET_DISPLAY_CLK_DIV, 0x80,
    OLED_CMD_SET_CHARGE_PUMP, 0x14,
    OLED_CMD_SET_PRECHARGE, 0x22,
    OLED_CMD_SET_VCOMH_DESELCT, 0x30,
    OLED_CMD_SET_MEMORY_ADDR_MODE, 0x01,
    OLED_CMD_DISPLAY_ON
  };
  printf("%d\n", write(OLED::fd, initWriteBuffer, 26));
  delay(1);
  OLED::clearDisplay();
}

void OLED::resetByteBuffer() {
  for(uint8_t i = 0; i < 2; i++) {
    OLED::doubleScaleBuffer[i] = 0;
  }
}

int stringLength, charIndex;
uint8_t rowMax, columnMax, currentByte;

void OLED::writeString(char* str, int scaleFactor, int row, int column) {
  stringLength = strlen(str);
  rowMax = row + scaleFactor - 1;
  columnMax = column + (stringLength * scaleFactor * 8);

  OLED::clearDisplayAt(row, column, columnMax);

  for (int i = 0; i < stringLength; i++) {
    charIndex = str[i] * 8;
    printf("Printing: %c\n", str[i]);

    // we have 8 byte fonts
    for (int j = 0; j < 8; j++) {

      // Keep the wire interaction inside this loop, i.e. 1 transaction for 1 character otherwise display gets messed up
      currentByte = font[j + charIndex];
      scale(currentByte, scaleFactor);

      OLED::setCursor(row, rowMax, (column + (j * scaleFactor) + (i * 8 * scaleFactor)), columnMax);

      // Working with verticle addressing mode
      for (uint8_t y = 0; y < scaleFactor; y++) {
    uint8_t writeBuffer[scaleFactor + 1];
    int n = 1;
    writeBuffer[0] = OLED_CONTROL_BYTE_DATA_STREAM;

    for (int x = scaleFactor - 1; x >= 0; x--) {
      writeBuffer[n] = OLED::doubleScaleBuffer[x];
      n++;
      // wiringPiI2CWrite(OLED::fd, OLED::doubleScaleBuffer[x]);
    }

    printf("%d\n", write(OLED::fd, OLED::doubleScaleBuffer, scaleFactor + 1));

    delay(1);
      }
    }
  }
}

void OLED::writeDisplayByte(char* str, int scaleFactor, int row, int column) {
  stringLength = strlen(str);
  rowMax = row + scaleFactor - 1;
  columnMax = column + (stringLength * scaleFactor * 8);

  for (int i = 0; i < stringLength; i++) {
    // Keep the wire interaction inside this loop, i.e. 1 transaction for 1 character otherwise display gets messed up
    scale(str[i], scaleFactor);

    setCursor(row, rowMax, (column + (i * scaleFactor)), columnMax);

    // Working with verticle addressing mode
      for (uint8_t y = 0; y < scaleFactor; y++) {
    uint8_t writeBuffer[scaleFactor + 1];
    int n = 1;
    writeBuffer[0] = OLED_CONTROL_BYTE_DATA_STREAM;

    for (int x = scaleFactor - 1; x >= 0; x--) {
      writeBuffer[n] = OLED::doubleScaleBuffer[x];
      n++;
      // wiringPiI2CWrite(OLED::fd, OLED::doubleScaleBuffer[x]);
    }

    printf("%d\n", write(OLED::fd, OLED::doubleScaleBuffer, scaleFactor + 1));
      }
  }
}

void OLED::clearDisplay() {
  // Set the GDDRAM to (Row0, Col0), ie: top-left and establish range as the whole screen - 128x64
  uint8_t writeBuffer[7] = {
    OLED_CONTROL_BYTE_CMD_STREAM,
    OLED_CMD_SET_COLUMN_RANGE,
    0,
    127,
    OLED_CMD_SET_PAGE_RANGE,
    0,
    7
  };

  printf("%d\n", write(OLED::fd, writeBuffer, 7));
  delay(1);

  uint8_t blankWriteBuffer[16+1];
  for (uint8_t x = 0; x < 64; x++)
  {
    blankWriteBuffer[0] = OLED_CONTROL_BYTE_DATA_STREAM;

    for (uint8_t i = 0; i < 16; i++) {
      blankWriteBuffer[1+i] = 0;
    }
    printf("%d\n", write(OLED::fd, blankWriteBuffer, 16+1));
    delay(1);
  }
}

void OLED::clearDisplayAt(uint8_t row, uint8_t column, uint8_t count) {
  // Set the GDDRAM to (Row0, Col0), ie: top-left and establish range as the whole screen - 128x64
  uint8_t writeBuffer[7] = {
    OLED_CONTROL_BYTE_CMD_STREAM,
    OLED_CMD_SET_COLUMN_RANGE,
    column,
    column + count * 8,
    OLED_CMD_SET_PAGE_RANGE,
    7 - row,
    7 - row
  };
  printf("%d\n", write(OLED::fd, writeBuffer, 7));
  delay(1);

  uint8_t blankWriteBuffer[16+1];
  for (uint8_t x = 0; x < count; x++)
  {
    blankWriteBuffer[0] = OLED_CONTROL_BYTE_DATA_STREAM;

    for (uint8_t i = 0; i < 16; i++) {
      blankWriteBuffer[1+i] = 0;
    }
    printf("%d\n", write(OLED::fd, blankWriteBuffer, 16+1));
    delay(1);
  }

}

void OLED::setCursor(int rowStart, int rowEnd, int columnStart, int columnEnd) {
  uint8_t writeBuffer[7] = {
    OLED_CONTROL_BYTE_CMD_STREAM,
    OLED_CMD_SET_COLUMN_RANGE,
    columnStart,
    columnEnd,
    OLED_CMD_SET_PAGE_RANGE,
    7 - rowStart - (rowEnd - rowStart),
    7 - rowStart
  };
  printf("%d\n", write(OLED::fd, writeBuffer, 7));
  delay(1);
}


uint8_t byteToScale, bitPoint, temp;

void OLED::scale(uint8_t inp, uint8_t scale) {
  OLED::resetByteBuffer();
  byteToScale = 0, bitPoint = 0, temp = 0;

  for (int i = 0; i < 8; i++) {
    temp = inp & ANDER;
    temp = temp >> bitPoint;
    for (uint8_t j = 0; j < scale; j++) {
      OLED::doubleScaleBuffer[byteToScale] |= temp;
      temp = temp >> 1;
      bitPoint ++;

      if (bitPoint > 7) {
    bitPoint = 0;
    byteToScale++;
    temp = inp & ANDER;
      }
    }
    inp = inp << 1;
  }
}
tlfong01
  • 4,665
  • 3
  • 10
  • 24
  • 2
    Not sure how we can help. There are plenty of Python modules to write to these OLEDs. Try one. If it works you know it is your code. If it doesn't work it may be faulty connections. – joan May 18 '20 at 20:13
  • For Rpi3B you *cannot* set I2C speed: "What is Rpi's I2C Maximum Speed?": https://raspberrypi.stackexchange.com/questions/108896/what-is-rpi4b-i2c-maximum-speed. But if you use Rpi4B, you can lower speed to 50kHz or even 10kHz to try your luck. SSD1306 is a mature chip, so worth more trials and errors: I have made a couple answers on I2C/SPI OLED. You might like to search this forum for "OLED". *One successful example*: "Rpi3B SSD1306 OLED I2C Interface Problem": https://raspberrypi.stackexchange.com/questions/106482/ssd1306-oled-i2c-detection-problem. Good luck and cheers.. – tlfong01 May 19 '20 at 02:05
  • 1
    I rechecked the code, turns out it was an extremely silly mistake. The wrong array was being printed. Should I delete the question or answer it ? – Shivang Gangadia May 19 '20 at 04:44
  • How nice to hear the good news. I think if you give your own answer with the debugged listing, other OLED newbies should find it useful to learn how to write OLED drivers. Cheers. – tlfong01 May 19 '20 at 05:26

1 Answers1

1

The problem is that I was sending the wrong data array to the OLED for printing.
In functions writeString() and writeDisplayByte() , the last printf statement is printing OLED::doubleScaleBuffer whereas the correct array, containing the BYTE_STREAM command is writeBuffer.
So the correct statement should be:

printf("%d\n", write(OLED::fd, writeBuffer, scaleFactor + 1));

OR

write(OLED::fd, writeBuffer, scaleFactor + 1);
  • Please accept your own answer with a click on the tick on its left side. Only this will finish the question and it will not pop up again year for year. – Ingo May 24 '20 at 08:33