CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

I2C Driver for SSD1306 Graphics Chip [Solved]

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
soonc



Joined: 03 Dec 2013
Posts: 215

View user's profile Send private message

I2C Driver for SSD1306 Graphics Chip [Solved]
PostPosted: Mon Sep 01, 2014 10:58 pm     Reply with quote

I'm trying to develop a I2C driver for the SSD1306 OLED graphics controller chip. If I ever get it working I'll be happy to post it .

Progress so far:
Hardware is from Adafruit it's the 128x64 OLED . Configured for I2C.
They have code examples and I'm trying to port to CCS PIC which Adafruit claims should be easy.

Lots of the code is straight forward and the initialization comes from the SSD1306 data sheet.

However they (AdaFruit) and others are using I2C address 0X3C and the data sheet clearly states 0X78 ! 0X3C shifted left become 0x78! Perhaps someone read it wrong but the mistake appears to be copied a lot, if it's not a mistake it may have something to do with Arduino platform.

I decided to use the PIC18LF26K22 at 48MHz. It has enough RAM and ROM for my application.

What my code does not seem to be able to do it transfer the PIC buffer to the SSD1306 RAM.

Logic analyzer tells me IC2 is working.

Display does not light up I guess I forgot something !
Any suggestions please.

The code so far:

// Using PCWHD 4.134

Code:

#include <18LF26K22.h>
#device ADC=10

#FUSES PRIMARY     //Primary clock is system clock when scs=00
#FUSES HSM
#FUSES PLLEN             
#FUSES FCMEN                    //Fail-safe clock monitor enabled
#FUSES IESO                     //Internal External Switch Over mode enabled
#FUSES NOPUT                    //No Power Up Timer
#FUSES NOBROWNOUT         //No brownout reset
#FUSES BORV19                   //Brownout reset at 1.9V
#FUSES MCLR                     //Master Clear pin enabled
#FUSES STVREN                   //Stack full/underflow will cause reset
#FUSES NOLVP  //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOXINST                  //Extended set extension and Indexed Addressing mode disabled (Legacy mode)
#FUSES NOPROTECT                //Code not protected from reading
#FUSES NOCPB                    //No Boot Block code protection
#FUSES NOCPD                    //No EE protection
#FUSES NOWRT                    //Program memory not write protected
#FUSES NOWRTC                   //Configuration registers not write protected
#FUSES NOWRTB                   //Boot block not write protected
#FUSES NOWRTD                   //Data EEPROM not write protected
#FUSES NOEBTR                   //Memory not protected from table reads
#FUSES NOEBTRB                  //Boot block not protected from table reads

#use delay(clock=48000000,crystal=12MHz)

#use FIXED_IO( B_outputs=PIN_B5,PIN_B2 )
#use FIXED_IO( C_outputs=PIN_C5,PIN_C1,PIN_C0 )

#define DISPLAY_RESET   PIN_C5

#use i2c(Master,Fast,sda=PIN_C4,scl=PIN_C3,force_hw)


#define SSD1306_LCDWIDTH      128
#define SSD1306_LCDHEIGHT      64
#define SSD1306_SETCONTRAST   0x81
#define SSD1306_DISPLAYALLON_RESUME 0xA4
#define SSD1306_DISPLAYALLON 0xA5
#define SSD1306_NORMALDISPLAY 0xA6
#define SSD1306_INVERTDISPLAY 0xA7
#define SSD1306_DISPLAYOFF 0xAE
#define SSD1306_DISPLAYON 0xAF
#define SSD1306_SETDISPLAYOFFSET 0xD3
#define SSD1306_SETCOMPINS 0xDA
#define SSD1306_SETVCOMDETECT 0xDB
#define SSD1306_SETDISPLAYCLOCKDIV 0xD5
#define SSD1306_SETPRECHARGE 0xD9
#define SSD1306_SETMULTIPLEX 0xA8
#define SSD1306_SETLOWCOLUMN 0x00
#define SSD1306_SETHIGHCOLUMN 0x10
#define SSD1306_SETSTARTLINE 0x40
#define SSD1306_MEMORYMODE 0x20
#define SSD1306_COLUMNADDR 0x21
#define SSD1306_PAGEADDR   0x22
#define SSD1306_COMSCANINC 0xC0
#define SSD1306_COMSCANDEC 0xC8
#define SSD1306_SEGREMAP 0xA0
#define SSD1306_CHARGEPUMP 0x8D
#define SSD1306_EXTERNALVCC 0x1
#define SSD1306_SWITCHCAPVCC 0x2


int8 _i2c_address;
int8 display_buffer[1024];

///////////////////////////////////////
//
void  ssd1306_command(int8 c)
{
    int8 control = 0x00; // some use 0X00 other examples use 0X80. I tried both 
    i2c_start();
    i2c_write(_i2c_address);
    i2c_write(control); // This is Command
    i2c_write(c);
    i2c_stop();
}
////////////////////////////////////////////
//
void  ssd1306_data(int8 c)
{
    i2c_start();
    i2c_write(_i2c_address);
    i2c_write(0X40); // This byte is DATA
    i2c_write(c);
    i2c_stop();
}
///////////////////////////////////////////////////
// Used when doing Horizontal or Vertical Addressing
void setColAddress()
{
  ssd1306_command(SSD1306_COLUMNADDR); // 0x21 COMMAND
  ssd1306_command(0); // Column start address
  ssd1306_command(SSD1306_LCDWIDTH-1); // Column end address
}
/////////////////////////////////////////////////////
// Used when doing Horizontal or Vertical Addressing
void setPageAddress()
{
  ssd1306_command(SSD1306_PAGEADDR); // 0x22 COMMAND
  ssd1306_command(0); // Start Page address
  ssd1306_command((SSD1306_LCDHEIGHT/8)-1);// End Page address
}
///////////////////////////////////////////////////////////
// Transfers the local buffer to the CGRAM in the SSD1306
void TransferBuffer()
{
  int16 j=0;
 
      // set the Column and Page addresses to 0,0
      setColAddress();
      setPageAddress();
       
      i2c_start();
      i2c_write(_i2c_address);
      i2c_write(0X40); // data not command
      for(j=0;j<1024;j++)
      {
        i2c_write(display_buffer[j]);
      }

      i2c_stop();
}
///////////////////////////////////////////////////////////////////
// init according to SSD1306 data sheet and many places on the web
void  InitializeDisplay()
{
    // reset
    output_high(DISPLAY_RESET);
    delay_ms(1);
    output_low(DISPLAY_RESET);
    delay_ms(10);
    output_high(DISPLAY_RESET); // Reset Pin High for normal operation
   
   
    // Init sequence for 128x64 OLED module
    ssd1306_command(SSD1306_DISPLAYOFF);                    // 0xAE

    ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV);            // 0xD5
    ssd1306_command(0x80);                 // the suggested ratio 0x80
   
    ssd1306_command(SSD1306_SETMULTIPLEX);                  // 0xA8
    ssd1306_command(0x3F);
   
    ssd1306_command(SSD1306_SETDISPLAYOFFSET);              // 0xD3
    ssd1306_command(0x0);                                   // no offset
   
    ssd1306_command(SSD1306_SETSTARTLINE);// | 0x0);        // line #0
   
    ssd1306_command(SSD1306_CHARGEPUMP);                    // 0x8D
    ssd1306_command(0x14);  // using internal VCC
   
    ssd1306_command(SSD1306_MEMORYMODE);                    // 0x20
    ssd1306_command(0x00);          // 0x00 horizontal addressing
   
    ssd1306_command(SSD1306_SEGREMAP | 0x1); // rotate screen 180
   
    ssd1306_command(SSD1306_COMSCANDEC); // rotate screen 180
   
    ssd1306_command(SSD1306_SETCOMPINS);                    // 0xDA
    ssd1306_command(0x12);
   
    ssd1306_command(SSD1306_SETCONTRAST);                   // 0x81
    ssd1306_command(0xCF);
   
    ssd1306_command(SSD1306_SETPRECHARGE);                  // 0xd9
    ssd1306_command(0xF1);
   
    ssd1306_command(SSD1306_SETVCOMDETECT);                 // 0xDB
    ssd1306_command(0x40);
   
    ssd1306_command(SSD1306_DISPLAYALLON_RESUME);           // 0xA4
   
    ssd1306_command(SSD1306_NORMALDISPLAY);                 // 0xA6
 
    ssd1306_command(SSD1306_DISPLAYON);                     //switch on OLED
}
//////////////////////////////
//
void main()
{
   output_high(PIN_C1);// keep power ON

   output_float(PIN_C3);
   output_float(PIN_C4);

   // fill buffer with something for test
   memset( display_buffer, 0X02, 1024); // tried other values
   
   _i2c_address = 0X78; // this works 0X3C or 0X3D does not
   
   InitializeDisplay();
 
   TransferBuffer(); // try sending buffer

   while(1)
   {
     ; // keyboard code here
   }
}






Ttelmah



Joined: 11 Mar 2010
Posts: 19498

View user's profile Send private message

PostPosted: Tue Sep 02, 2014 12:36 am     Reply with quote

You need to read up about I2C. It's an I2C thing about how addresses are actually handled.

On I2C, the first byte sent, is the 7bit address, plus the single bit 'flag' for the transaction direction.

So a device with an I2C address of 0x3C, requires bytes of 0x78 or 0x79, for write/read respectively. The original I2C documentation uses the 7bit address, while a lot of modern documentation instead uses the 8bit byte value.

Now on the Arduino code, they keep the address, and the direction flag separate, so you call the function with an address of 0x3C, and the direction flag. On the CCS functions, you send the full 8bit value as one item.
soonc



Joined: 03 Dec 2013
Posts: 215

View user's profile Send private message

PostPosted: Tue Sep 02, 2014 6:43 am     Reply with quote

Ttelmah wrote:
....
Now on the Arduino code, they keep the address, and the direction flag separate, so you call the function with an address of 0x3C, and the direction flag. On the CCS functions, you send the full 8bit value as one item.


Thanks for the clarification. I'm not using Arduino.
I'm doing my own hardware and using the Adafruit OLED module as it's a convenient format and saves me the extra effort of making it myself.

In I2C mode this device cannot be read so I'm using 0X78 .

Now to the results so far:

The way I read it after calling the InitializeDisplay() I expected the display should be lighting up random pixels as the GCRAM has not been cleared.

Most code examples I've looked at clear the buffer or transfer a graphic right after the InitializeDisplay().

I think I most have missed something.

The chip is initialized.

The addressing mode is set to horizontal.

The column and page addresses are set to with start and end values.

Vdd is 3.3V, and I2C has 3K3 pull up resistors. I2C is outputting the values according to the logic analyzer. I tried another display but as always is the case it's never the chip !
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue Sep 02, 2014 10:15 am     Reply with quote

Quote:
They have code examples and I'm trying to port to CCS PIC which
Adafruit claims should be easy.

Post a link to this driver so we can compare it to your code.
soonc



Joined: 03 Dec 2013
Posts: 215

View user's profile Send private message

PostPosted: Tue Sep 02, 2014 12:28 pm     Reply with quote

PCM programmer wrote:
Quote:
They have code examples and I'm trying to port to CCS PIC which
Adafruit claims should be easy.

Post a link to this driver so we can compare it to your code.


Here the a link to Adafruit Arduino driver:
https://github.com/adafruit/Adafruit_SSD1306

This link is a version using i2c, but not CCS C. This version there are extra items in the Init routine. I've tried all of them.
https://sites.google.com/site/0miker0/oled-displays

This link refers to the Arduino code using i2C and a similar issue.
https://code.google.com/p/u8glib/issues/detail?id=204

This is using the Squirrel language, and is easy to read.
https://gist.github.com/smittytone/20ed962d9f06072bd1c0
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue Sep 02, 2014 2:23 pm     Reply with quote

Quote:
Logic analyzer tells me IC2 is working.

This means you see activity on SCL and SDA, but it doesn't mean you
are getting a response from the OLED chip.

Did you run the i2c bus scanner program to see if it says the OLED
slave address is 0x7c:
http://www.ccsinfo.com/forum/viewtopic.php?t=49713
soonc



Joined: 03 Dec 2013
Posts: 215

View user's profile Send private message

I2C address confirmed
PostPosted: Tue Sep 02, 2014 4:24 pm     Reply with quote

PCM programmer wrote:
Quote:
Logic analyzer tells me IC2 is working.

This means you see activity on SCL and SDA, but it doesn't mean you
are getting a response from the OLED chip.

Did you run the i2c bus scanner program to see if it says the OLED
slave address is 0x7c:
http://www.ccsinfo.com/forum/viewtopic.php?t=49713


OK I tried your suggestion in my code here is the test routine which is essentially the same as your test routine except I don't have a display to do the printf() so I used the display_buffer[] and dump the results in there.
Thanks for the idea it confirms my use of 0X7A as the correct address.
I'll fine comb my code I feel sure I forgot something.

Code:

 // in main()
 int8 ack_status=0;
 int8 i;

   for( j=0; j<1024; j++)
   {
       display_buffer[j]=0; // clean array
   }


   for( i=0x10; i<0XF0; i+=2)
   {
      i2c_start();
      ack_status = i2c_write(i);
      i2c_stop();

      if(  !ack_status )
      {
        display_buffer[i] = i;
      }
   }
   
   i=0; // break point.
   // Using PCHWD look for a non zero in the array
   // location 88 and 122  are non-zero. 88 is a digital pot, and 122=0X7A
   // which is the OLED display.

 
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue Sep 02, 2014 4:35 pm     Reply with quote

Quote:
Thanks for the idea it confirms my use of 0X7A as the correct address.

Were you using 0x7A ?

In your original progam you had this:
Quote:
_i2c_address = 0X78; // this works 0X3C or 0X3D does not

So if you put 0x7A in the above line, does your OLED driver now work ?
soonc



Joined: 03 Dec 2013
Posts: 215

View user's profile Send private message

Problem Solved
PostPosted: Tue Sep 02, 2014 7:32 pm     Reply with quote

PCM programmer wrote:
Quote:
Thanks for the idea it confirms my use of 0X7A as the correct address.

Were you using 0x7A ?

In your original progam you had this:
Quote:
_i2c_address = 0X78; // this works 0X3C or 0X3D does not

So if you put 0x7A in the above line, does your OLED driver now work ?


That's it.... The I2C Address is 0X7A and I had it set at 0X78

0b01111010 = 0X7A the SA0 line is set high in the OLED module.

I just tested the original code with the correct address and it works.

How I miss these small details ! This morning I found out I have cataracts in both eyes ! I'm removing the cataracts in 14 days, but I doubt I can blame it on that.

Anyway your modified test routine found the problem.

Thanks.
Ttelmah



Joined: 11 Mar 2010
Posts: 19498

View user's profile Send private message

PostPosted: Wed Sep 03, 2014 12:42 am     Reply with quote

PCM_Programmers I2C test routine is one of those 'core' bits of code here, that all the 'old hands' will use, when trying to get an I2C project working. It is simple, and proves that you are successfully talking to the chip (so several parts of the wiring are OK), and at what address it sits.
Now you can move forward. Smile
superfranz



Joined: 05 Sep 2014
Posts: 10
Location: Trieste, Italy

View user's profile Send private message Send e-mail

PostPosted: Fri Sep 05, 2014 6:56 am     Reply with quote

Hi!
I'm working on ssd1306 i2c driver.
I'm using pic 16f1877, and tested on 18f4525

It's working!
pixel(x,y), line, circle, rect, text like glcd lib and large number fullscreen 99.9 style (from ttf font calibri),for my real needs.

...using little more than 1k RAM

If you want i can post my code. It's newbie, no RAM optimized (full of vars Smile ),confused and uncommented (yet) but good-working.
(maybe you can help me in WHY hardware i2c doesn't work!)

Let me know if you're interested and i can post it when back home.

bye bye
Superfranz
temtronic



Joined: 01 Jul 2010
Posts: 9221
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Fri Sep 05, 2014 6:59 am     Reply with quote

nice to hear it's 'up and running'

yes, you should post your code into the 'code library', the 'other ' forum on this site !!





jay
superfranz



Joined: 05 Sep 2014
Posts: 10
Location: Trieste, Italy

View user's profile Send private message Send e-mail

PostPosted: Sat Sep 06, 2014 5:20 pm     Reply with quote

done

SuperFranz
miskol



Joined: 01 Feb 2011
Posts: 16

View user's profile Send private message

PostPosted: Mon Sep 22, 2014 8:28 pm     Reply with quote

interested on this project, i'm currently trying to port/build the SSD1306 driver for use with CCS too but i'm using the SPI OLED.

hope you could share your ported library.

best of all, i'm using the same PIC too!! hehe

please help, TQ Smile
akis_t



Joined: 20 Dec 2014
Posts: 1

View user's profile Send private message

PostPosted: Sat Dec 20, 2014 3:15 am     Reply with quote

If anyone is still reading this.

I am using the 128 x 64 SSD1306 OLED communicating over I2C.

It is all working fine, except it is slow.

It takes 6.5 ms to simply write a line of text to the display, as each character has to be plotted pixel by pixel. This operation simply manipulates the local memory buffers.

Then it takes another 30ms to send those buffers over to the OLED.

The code uses the Adafruit SSD1306 libraries which work by maintaining a local memory buffer representing the OLED display and sending the whole buffer across every time you need to show something.

Can you please tell me if there is a better way of doing this.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group