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 support@ccsinfo.com

#use i2c() in hardware mode with 18F2550

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



Joined: 11 Mar 2016
Posts: 62
Location: izmir / Turkey

View user's profile Send private message

#use i2c() in hardware mode with 18F2550
PostPosted: Fri Jun 29, 2018 2:34 am     Reply with quote

Hello guys, I migrated my oled application from picbasic to ccs and used Ttelmah's codes to drive the screen.
http://www.ccsinfo.com/forum/viewtopic.php?t=54453
I'm using 18f2550 and 1.3" ssd1306. There are 3 vertical lines on screen I couldn't solve yet but works fine other than these lines. Oh by the way, Thank you for your hard work Ttelmah.

So, why I'm writing this is that I faced a situation while my code migration, I couldn't make my code work without "force_sw" option whatever I tried, then I got stubborn and wanted "hw" option but nothing worked Laughing then "noinit" option solved my stuation =) here I listed my working and not working options for people who might face same stuation later.

Not Working I2C Initialization lines
Code:

#USE I2C(MASTER,I2C1, FAST=400000, FORCE_HW)
#USE I2C(MASTER,I2C1, FAST=400000, FORCE_HW)
#USE I2C(MASTER,I2C1, FAST=400000)
#USE I2C(MASTER,I2C1, FORCE_HW)
#USE I2C(MASTER,I2C1, FAST, FORCE_HW)
#USE I2C(MASTER,I2C1, FAST)
#USE I2C(MASTER,SDA=PIN_B0,SCL=PIN_B1, FAST=400000, FORCE_HW)
#USE I2C(MASTER,SDA=PIN_B0,SCL=PIN_B1, FAST=400000)
#USE I2C(MASTER,SDA=PIN_B0,SCL=PIN_B1, FORCE_HW)
#USE I2C(MASTER,SDA=PIN_B0,SCL=PIN_B1, FAST, FORCE_HW)
#USE I2C(MASTER,SDA=PIN_B0,SCL=PIN_B1, FAST)
#USE I2C(MASTER,SDA=PIN_B0,SCL=PIN_B1)
#USE I2C(MASTER,I2C1)


"FORCE_SW" options were all working, I was using:
Code:
#USE I2C(MASTER,SDA=PIN_B0,SCL=PIN_B1, FAST=400000, FORCE_SW)


At last, NOINIT option worked for HW, Now its is working with HW option:
Code:
#USE I2C(MASTER,I2C1, FAST=400000,NOINIT, FORCE_HW)
.
.
.
void SetupMCU()
{
  .
  .
  .
   i2c_init(TRUE);
}


Note : I'm trying to figure out and solve the 3 vertical lines on screen which are not appearing with my other code and I'll post it when I found but I'm all ears to all ideas while working on this =)
_________________
There is nothing you can't do if you try
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Fri Jun 29, 2018 6:13 pm     Reply with quote

Please post your compiler version.
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Sat Jun 30, 2018 1:05 am     Reply with quote

There is a basic and very simple reason it won't work.

Signal levels.

The SSD1306, is a 3.3v device. Though a lot of the boards have an internal 5v regulator, the signalling is 3.3v.
The hardware I2C, requires the inputs to go up to at least 0.8* the supply voltage of the PIC. So on a 2550 at 5v, they need to go to 4v. The 3.3v device does not allow this to happen.
Note my comment:
Quote:

Have had it going successfully off a 5v device, by setting this to use SMBUS levels (many PIC's support this), and having the pull-ups to 3.3v. Or if you pull-up to 3.3v, and use _software_ I2C, on a port that supports TTL levels it also works OK.


You need to enable SMBUS mode to use the hardware port:

#USE I2C(MASTER,I2C1, FAST=400000, FORCE_HW, SMBUS)

Why it works with the bodge syntax, is 'interesting'. May accidentally be selecting SMBUS mode by not setting the bit controlling this when it is configured in software.
temtronic



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

View user's profile Send private message

PostPosted: Sat Jun 30, 2018 6:00 am     Reply with quote

I see MR. T posted what I was thinking !! 5 volt PIC, 3 volt peripheral...quite common these days as most peripherals are low voltage devices.
As for the 3 vertical lines...does the display powerup properly with no lines? If so, I suspect a code problem, say a miscast variable or wrong counter value. I don't use that display or driver, just 'thinking out loud' here.
Is the power supply good for say 1 amp or more? Perhaps the LCD module draws more power than you think(or listed), the Vdd drops a bit and corrupts the LCD data ? Adding say a 470 or 1000 mfd cap on VDD may help.
Are the 3 lines always the same ones on the display, if so which x values ?? that may be a clue as to what's happening.
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Sat Jun 30, 2018 9:00 am     Reply with quote

I must admit a number of bits offset is quite common if you are not resetting the display correctly. I had one of 4 bits on one particular display board, if I didn't have a capacitor on the reset line ensuring the OLED chip reset correctly on power on (and then also delayed long enough for the module to have finished waking, before sending anything to the display).
elcrcp



Joined: 11 Mar 2016
Posts: 62
Location: izmir / Turkey

View user's profile Send private message

PostPosted: Thu Jul 05, 2018 2:52 am     Reply with quote

Uh, I thought I did post a reply to Ttelmah's topic, I didn't intend to post a new topic sorry about that.
PCM programmer wrote:
Please post your compiler version.

my compiler version is: 5.070
Ttelmah wrote:
You need to enable SMBUS mode to use the hardware port:

#USE I2C(MASTER,I2C1, FAST=400000, FORCE_HW, SMBUS)

Thanks Ttelmah, I actually totally forgot about signal levels' difference a simple but big mistake of me. However, SMBUS option isn't working either. I tried quickly after reading your answer, I also tried bit banging registers directly for smbus (I don't trust built in functions sometimes) but still not working without noinit option. I'll upgrade my hardware later and use 18LF2550 so there won't be any signal lvl problem but thing is; I didn't use smbus with my previous code which is written with picbasic and I didn't get any problem. I also applied same oled and pic initialization lines with previous one since I'm migrating the code, but results are not same Rolling Eyes

temtronic wrote:
I see MR. T posted what I was thinking !! 5 volt PIC, 3 volt peripheral...quite common these days as most peripherals are low voltage devices.

As I said, I use same hardware with my old code without any problem, I don't think this is something about hardware. But can't see the difference between codes either. I'll post my init lines.
_________________
There is nothing you can't do if you try
elcrcp



Joined: 11 Mar 2016
Posts: 62
Location: izmir / Turkey

View user's profile Send private message

PostPosted: Thu Jul 05, 2018 3:19 am     Reply with quote

This is my schematics, https://ibb.co/eyppxJ, these are my main code;
Code:
#include <18F2550.h>
#fuses HSPLL          //High Speed Crystal/Resonator with PLL enabled
#fuses PLL5           //Divide By 5(20MHz oscillator input)
#fuses CPUDIV3        //System Clock by 3
#fuses USBDIV         //USB clock source comes from PLL divide by 2
#fuses NOFCMEN        //Fail-safe clock monitor disabled
#fuses NOIESO         //Internal External Switch Over mode disabled
#fuses PUT            //Power Up Timer
#fuses NOBROWNOUT     //No brownout reset
#fuses VREGEN         //USB voltage regulator enabled
#fuses NOWDT            //Watch Dog Timer
#fuses WDT512         //Watch Dog Timer uses 1:512 Postscale
#fuses CCP2C1         //CCP2 input/output multiplexed with RC1
#fuses NOPBADEN       //PORTB pins are configured as digital I/O on RESET
#fuses NOLPT1OSC      //Timer1 configured for higher power operation
#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)

#use delay(clock=32MHz,crystal=20MHz) //INTERNAL 32MHZ CLOCK
#USE I2C(MASTER,I2C1, FAST=400000,NOINIT, FORCE_HW) //SETTING UP I2C

//DEFINING SOME OF REGISTERS FOR DIRECT ACCESS
#BYTE UCFG=0xF6F
#BYTE ADCON0=0xFC2
#BYTE ADCON1=0xFC1
#BYTE CMCON=0xFB4
#BYTE CVRCON=0xFB5
#BYTE T0CON=0xFD5
#BYTE INTCON2=0xFF1
#BYTE T2CON=0xFCA
#BYTE TRISA=0xF92
#BYTE TRISB=0xF93

//display dimensions - the physical LCD
#define S_LCDWIDTH               128
#define S_LCDHEIGHT              64
//If you want to use the SH1106, add this #define
//#define SH1106

#define TEXT_ONLY //If this is defined, gives a smaller text driver only
#define SSDADDR 0x78 //address for the chip - usually 0x7C or 0x78.
#include <string.h>
#include "ssd1306.h" //The OLED driver

//RTC variables
#define XTAL_FREQUENCY  32000000
#define TIMER1_FREQUENCY (XTAL_FREQUENCY / 4)      // 1 clock tick = 1 instr. cycle = crystal frequency / 4
int32 Ticker;
int1 ScreenUpdate;
unsigned int8 mSeconds=0,intTimes=0,Seconds=0,Minutes=0,Hours=0,Days=0,Month=0,Year=0;

//optional:
// int8 Year=11, Month=1, Days=1, Hours=0, Minutes=0;  // 2011-01-01 00:00

////////////////////////////////////////////////////////////////////////////////
// Test whether a given year is a leap year.
// This optimized version only works for the period 1901 - 2099
////////////////////////////////////////////////////////////////////////////////
#define IS_LEAP(year) (year%4 == 0)


////////////////////////////////////////////////////////////////////////////////
//    Initialize RTC
////////////////////////////////////////////////////////////////////////////////
void Initialize_RTC(void)
{
  Ticker = TIMER1_FREQUENCY;                  // initialize clock counter to number of clocks per second
  setup_timer_1( T1_INTERNAL | T1_DIV_BY_1 ); // initialize 16-bit Timer1 to interrupt
                                              // exactly every 65536 clock cycles
                                              // (about 76 times per second)
  enable_interrupts( INT_TIMER1 );            // Start RTC
}
////////////////////////////////////////////////////////////////////////////////
//  -=Process Zero Drift Real Time Clock Information=-
//
// Most algorithms configure the timer to generate an interrupt every 100ms, and
// then count the number of interrupts. The problem in that approach is that most
// clock frequencies can't be divided by 256 and you don't get an exact 100ms.
// The small errors will add up to an error of several seconds a day.
//
// The algorithm presented here is exact in the long run because it doesn't
// count the number of interrupts but counts the number of clock cycles.
////////////////////////////////////////////////////////////////////////////////
#int_TIMER1                               
void TIMER1_isr()                         
{
  Ticker -= 65536;   // Decrement ticker by clocks per interrupt
  intTimes++;
   mSeconds=(int8)((intTimes/122.0703125)*100);
  ScreenUpdate=1;
  if ( Ticker < 65536 )                   // If second has expired
  {  Ticker += TIMER1_FREQUENCY;          //   Increment ticker by clocks per second
     Seconds++;                           //   Increment number of seconds
  }

   //--- Optional part start ---
  if (Seconds == 60) {Minutes++; Seconds=0;
    if (Minutes == 60) {Hours++; Minutes=0;
      if (Hours == 24) {Days++; Hours=0;
        if (  (Days == 29 && Month==2 && !IS_LEAP(Year))                        // February in leap year
           || (Days == 30 && Month==2)                                          // February in normal years
           || (Days == 31 && (Month==4 || Month==6 || Month==9 || Month==11 ))  // All months with 30 days
           || (Days == 32)                                                      // All months with 31 days
           ) {Month++; Days=1;}
        if (Month == 13) {Year++; Month=1;}
  }}}
   //--- Optional part end --- 
}

void SetupMCU()
{
   bit_set(UCFG,2);
   bit_clear(ADCON0,0);
   ADCON1=0b00001111;
   CMCON=0b00000111;
   bit_clear(CVRCON,7);
   T0CON=0b01010111;
   bit_clear(INTCON2,4);
   T2CON=0b11111011;
   TRISA=0b00000000;
   TRISB=0b00000101;
   Initialize_RTC();
   enable_interrupts( GLOBAL );
   i2c_init(TRUE);
}
void main()
{
   SetupMCU();
   //OLED_commands(init_sequence,sizeof(init_sequence)); //initialise the OLED
   ssd_init();
   unsigned int8 prev_second;
   char text[15]; //temporary text buffer
   OLED_CLS(); //clear the physical screen

   while(TRUE)
   {
      if(ScreenUpdate==1)
      {
         ScreenUpdate=0;
         OLED_gotoxy(1,0);
         printf(OLED_putc,"%02u",Hours);
         OLED_gotoxy(3,0);
         strcpy(text,":");
         OLED_text(text,strlen(text));
         OLED_gotoxy(4,0);
         printf(OLED_putc,"%02u",Minutes);
         OLED_gotoxy(6,0);
         OLED_text(text,strlen(text));
         OLED_gotoxy(7,0);
         printf(OLED_putc,"%02u",Seconds);
         OLED_gotoxy(9,0);
         OLED_text(text,strlen(text));
         OLED_gotoxy(10,0);
         printf(OLED_putc,"%02u",mSeconds);
      }
      if(prev_second!=Seconds)
      {
         prev_second=Seconds;
         intTimes=0;
         
      }
   }
}


and these are my OLED init codes (added to Ttelmah's h file, Ttelmah's original init lines are also working same, no big difference between )
Code:
void ssd_init()
{
   i2c_start ();
   i2c_write (SSDADDR); //select the display
   i2c_write (COMMAND_ONLY); //we are sending a command         
   i2c_write(S_DISPLAYOFF);
   i2c_stop();
   i2c_start ();
   i2c_write (SSDADDR); //select the display
   i2c_write (COMMAND_ONLY); //we are sending a command         
   i2c_write(S_CHARGEPUMP);
   i2c_write(0x14);
   i2c_stop();
   i2c_start ();
   i2c_write (SSDADDR); //select the display
   i2c_write (COMMAND_ONLY); //we are sending a command         
   i2c_write(S_MEMORYMODE);
   i2c_write(0x10);
   i2c_stop();
   i2c_start ();
   i2c_write (SSDADDR); //select the display
   i2c_write (COMMAND_ONLY); //we are sending a command         
   i2c_write(0xA1);
   i2c_stop();
   i2c_start ();
   i2c_write (SSDADDR); //select the display
   i2c_write (COMMAND_ONLY); //we are sending a command         
   i2c_write(S_COMSCANDEC);
   i2c_stop();
   i2c_start ();
   i2c_write (SSDADDR); //select the display
   i2c_write (COMMAND_ONLY); //we are sending a command         
   i2c_write(S_SETCOMPINS);
   i2c_write(0x12);
   i2c_stop();
   i2c_start ();
   i2c_write (SSDADDR); //select the display
   i2c_write (COMMAND_ONLY); //we are sending a command         
   i2c_write(S_SETCONTRAST);
   i2c_write(0x1F);
   i2c_stop();
   i2c_start ();
   i2c_write (SSDADDR); //select the display
   i2c_write (COMMAND_ONLY); //we are sending a command         
   i2c_write(S_DISPLAYALLON_RESUME);
   i2c_stop();
   i2c_start ();
   i2c_write (SSDADDR); //select the display
   i2c_write (COMMAND_ONLY); //we are sending a command         
   i2c_write(S_NORMALDISPLAY);
   i2c_stop();
   i2c_start ();
   i2c_write (SSDADDR); //select the display
   i2c_write (COMMAND_ONLY); //we are sending a command         
   i2c_write(S_DISPLAYON);
   i2c_stop();
}

and here this is my screen with old code; https://ibb.co/fcZa4y
and this is the screen with new code https://ibb.co/jnQnHJ[/img]
_________________
There is nothing you can't do if you try
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Thu Jul 05, 2018 4:27 am     Reply with quote

The standard PICBASIC I2C code uses software I2C, not hardware.
The pin thresholds are fine for software I2C (which is why it works for you with this selected in CCS). It's only hardware I2C that introduces the 0.8*Vdd threshold.
Why worry?. Software I2C will be almost as fast (in fact may actually match the speed required), and works....
The SMBUS options do not work on all chips, so this may be the reason for yor problem.
temtronic



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

View user's profile Send private message

PostPosted: Thu Jul 05, 2018 5:39 am     Reply with quote

As Mr. T says 'why worrry'. Most LCD modules are snail slow in operation compared to any PIC, so I doubt you really can benefit from using the internal I2C peripheral. As you've got a SW RTC, I'll assume you're updating the LCD module no more than once per second, so again the SW I2C will be fine.
Jay
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Thu Jul 05, 2018 7:01 am     Reply with quote

Ignoring the other stuff, there is another 'screaming' thing:
Code:

void main()
{
   SetupMCU();
   //OLED_commands(init_sequence,sizeof(init_sequence)); //initialise the OLED
   ssd_init();


At 32mHz, you have just a few uSec between power being applied and sending the init to the display. The displays take some time to wake up. Typically quite a few mSec. In my example code I have:
Code:


void main()
{
   int8 temp=1;
   delay_ms(250);                //OLED takes time to wake


I suspect the start of the initialisation is being sent before the LED is ready to take it. Add a delay before trying to initialise the chip.

Update.
Just checked the Densitron data sheet for their displays based on this chip and they require 100mSec after Vcc.
elcrcp



Joined: 11 Mar 2016
Posts: 62
Location: izmir / Turkey

View user's profile Send private message

PostPosted: Tue Jul 10, 2018 4:32 pm     Reply with quote

Ttelmah wrote:
The standard PICBASIC I2C code uses software I2C, not hardware.
The pin thresholds are fine for software I2C (which is why it works for you with this selected in CCS). It's only hardware I2C that introduces the 0.8*Vdd threshold.
Why worry?. Software I2C will be almost as fast (in fact may actually match the speed required), and works....
The SMBUS options do not work on all chips, so this may be the reason for yor problem.

temtronic wrote:
As Mr. T says 'why worrry'. Most LCD modules are snail slow in operation compared to any PIC, so I doubt you really can benefit from using the internal I2C peripheral. As you've got a SW RTC, I'll assume you're updating the LCD module no more than once per second, so again the SW I2C will be fine.

Thank you for your concern guys, it's nice to have your opinions as I see you very knowledgable. I was already ok with sw IIC , also noinit option already helped HW IIC, I was just curious and wanted to share my curiosity that why I wasn't able to run HW IIC any other way =)
I found the problem with 3 vertical lines and it's a bit embrassing Embarassed I bought my oled from aliex. and it was said ssd1306 on the sellers' page after inspecting my old code I figure out that I altered driver codes to work it then I realised that these alterations are already defined as sh1106 in Ttelmah's driver code Embarassed so my oled wasn't a ssd1306 but a sh1106 Rolling Eyes
So, declaring sh1106 in my code solved vertical lines problem
_________________
There is nothing you can't do if you try
temtronic



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

View user's profile Send private message

PostPosted: Tue Jul 10, 2018 5:54 pm     Reply with quote

I'm happy you got to the bottom of them 3 lines !!! It'd be nice if these itty, bitty PCBs had BIG readable info on them, might have saved you a few sleepless nights, 5-6 coffee pots....... we've all been there, so welcome to the 'club' !
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Tue Jul 10, 2018 11:24 pm     Reply with quote

I think the phrase 'aaargh' applies....
The two pixel offset sounded familiar for the SH1106, but you seemed certain you had SSD1306's, so it didn't occur to me to suggest trying this!.
Glad you have it sorted.

Normally the SSD1306, is used for the smallest 0.9" displays. The SH1106 for the slightly larger ones (about 1.2"), then there is a higher power version of the SSD1306 the SSD1309 used for the really large displays (2.4" etc.).
2xhp



Joined: 14 Jan 2019
Posts: 39

View user's profile Send private message

PostPosted: Tue Aug 20, 2019 10:38 pm     Reply with quote

Ttelmah wrote:
There is a basic and very simple reason it won't work.

Signal levels.

The SSD1306, is a 3.3v device. Though a lot of the boards have an internal 5v regulator, the signalling is 3.3v.
The hardware I2C, requires the inputs to go up to at least 0.8* the supply voltage of the PIC. So on a 2550 at 5v, they need to go to 4v. The 3.3v device does not allow this to happen.


Thank you Ttelmah and the original poster for providing this information. It helped end frustration I was having with a 18F25K80 that I couldn't get working with FORCE_HW and without having FORCE_SW. What's odd is that it worked for a while without either. Then, it's like I changed/added/removed something in the code and then i2c stopped working. Adding FORCE_SW got it working again. BTW, I'm running the device at 5 V.

Thanks again. This forum is the reason why I steer people to use the CCS compiler and what often keeps me happy.
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Wed Aug 21, 2019 12:42 am     Reply with quote

Quite a few of the hardware peripherals offer the option 'SMBUS'. This lowers
the input thresholds (amongst other things), allowing the hardware to
support a chip like this at 3.3v.
So try adding 'SMBUS' to your #use I2C setup. I do mention this in the
driver thread.
By 'the device' I presume you mean the PIC?. If you run the SSD1306 at
5v. Bang... 5v SSD1306 'modules', have a LDO voltage regulator on them and
run the chip at 3.3v.
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