Search code examples
cdebianlinux-device-driveri2cioctl

Debian I2C driver for OLED screen not working. [ssd1306]


I have some driver code that I am testing out for use with an SSD1306 driven OLED screen which is 128x32 (same as the OLED adafruit model). I need this to run in debian (I am using Linario-4.4.9)

I have followed the Debian guides on how to start creating a file handler for the device, this can be seen as follows below. The only thing in oled.h is the device adress (0x3C) and the proto types. I followed the initialization approach taken on the adafruit github (as I tried their code out first on an Ardunio to ensure the screen does in fact work). I believe I may be doing something wrong but I'm not entirely sure what I am doing wrong. I have also listed my init process below.

#include <errno.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include <linux/i2c-dev.h>

#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>

#include "oled.h"

int oled;

int  lcd_driver_init(void)
{
        ///< Begin the init proc.
        int dloc = open("/dev/i2c-1", O_RDWR);
        if (dloc < 0 )
        {
                fprintf(stderr, "Error opening i2c device\n");
                return -1;
        }


        if(ioctl(dloc, I2C_SLAVE, SCR_ADDR) < 0)
        {
                fprintf(stderr, "Error in ioctl. Errno :%i\n",errno);
                return -2;
        }

        oled = dloc;
        fprintf(stderr, "init success, device open and local\n");   
        return EXIT_SUCCESS;
}

int oled_command( uint8_t cmd)
{
        char command[2]= {0};
        command[1] = cmd;
        int check = (write(oled, command, 2));

        return check;
}

void oled_cmd_start()
{
        int check = (write(oled, 0x00, sizeof(uint8_t)));
        if(check<0)
                fprintf(stderr, "Errno set:: %i\n", errno);
        return;
}
void oled_data_start()
{
        uint8_t _data_start_[1] ={ 0x40 };
        int check = (write(oled, _data_start_, sizeof(uint8_t)));
        if(check<0)
                fprintf(stderr, "Errno set oled_data_start:: %i\n", errno);
        return;
}

int oled_data (uint8_t xmit)
{

        int check = (write(oled, &xmit, (sizeof(uint8_t))));
        if(check<0)
                fprintf(stderr, "Errno set oled_data:: %i\n", errno);
        return check;
}

INIT PROCESS

void sendcommand(unsigned char payload)
{
    oled_data(0x00);        //Control Byte - Command
    oled_data(payload);     //payload
}
void lcd_init(void)
{

    sendcommand(0xAE);//--Set Display off

    sendcommand(0x00);//--set low column address

    sendcommand(0x10);//--set high column address

    sendcommand(0x81);//--set contrast control register
    sendcommand(0x7f);

    sendcommand(0xa1);//--set segment re-map 95 to 0

    sendcommand(0xA6);//--set normal display

    sendcommand(0xa8);//--set multiplex ratio(1 to 16)
    sendcommand(0x1f);//--duty 1/32

    sendcommand(0xd3);//--set display offset
    sendcommand(0x00);//--not offset

    sendcommand(0xd5);//--set display clock divide ratio/oscillator frequency
    sendcommand(0xf0);//--set divide ratio

    sendcommand(0xd9);//--set pre-charge period
    sendcommand(0x22);

    sendcommand(0xda);//--set com pins hardware configuration
    sendcommand(0x02);//disable left/right remap and set for sequential

    sendcommand(0xdb);//--set vcomh
    sendcommand(0x49);//--0.83*vref

    sendcommand(0x8d);//--set DC-DC enable
    sendcommand(0x14);//

    sendcommand(0xAF);//--turn on oled panel

    sendcommand(0xA4);//--Entire Display ON

}

Following this, I send alternating 0xFF to try and make stripes on the screen. The only thing that shows up is random pixels. Nothing coherent.

I have connected a logic analyzer to sniff the I2C lines, and it appears that when I have the LA connected, the I2C lines no longer function and ERRNO returns an IO fault (#5). There doesn't ever seem to be an issue opening up the device to get the file pointer however.

I do get ERRNO as timeout sometimes, but I have read that this is just an issue with I2C devices using the protocal as write expects a quicker response than I2C might give.

I am also compiling with -std=c99 -O0 to ensure all of the inline functions are there as well as ensuring that loop variables are available.

If anyone can point me in the right direction of can point out some flaw in my approach it would be much appreciated. Thank you.

EDIT

I've checked the device tree and the i2c device is correctly enabled. However none of the i2c_freq speeds seem to be enabled. Could this be causing the timeouts and the garbage data transfer?


Solution

  • It turns out the SOM has an internal regulator for the I2C lines to be 1V8 where as the SSD1306 chip is running at 3V3 causing information to be mishandled. This behavior wasn't documented on the SOM.

    Applying a level shifting chip to the design allowed for proper communication.

    If someone has this same problem, check your schematics for voltage mismatch levels.