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 PROBLEM - 18F46K22

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



Joined: 28 Mar 2021
Posts: 12

View user's profile Send private message

I2C PROBLEM - 18F46K22
PostPosted: Fri Sep 09, 2022 12:25 am     Reply with quote

Hello All,

I have been using PIC18F4620 as an I2C SLAVE so far with no issues at all. However, when I migrated to PIC18F46K22 I am facing quite a lot of issues. Sometimes it works and in most cases, it gets struck and I am unable to identify the reason. Fuses and settings for slave are as follow.

PIC18F46K22 - Operating Voltage 3.3V.
I also tried switching over to external crystal but still have the same issues. What am I missing? Kindly please advise.

Code:

//==================================================================
#include<18F46K22.h>
#fuses INTRC_IO,NOPLLEN,NOWDT,NOPUT,NOBROWNOUT,NOLVP,MCLR
#use delay(clock=4M)


///////////////// i2C DECLARATION
#byte SSPADD = 0xFC8
#use i2c(SLAVE, SDA=PIN_C4, SCL=PIN_C3)



/*******************************************************************/
#INT_SSP
void ssp_interrupt()
{
   int8 incoming, state,address;

   state = i2c_isr_state();
 


   if(state <= 0x80)                      //Master is sending data
   {
      if(state == 0x80)
         incoming = i2c_read(2);         
      else
         incoming = i2c_read();

   if(state!=0)
      {
      i2c_read_data[icount++]=incoming;
         if(icount==6){icount=0;i2c_read_complete=1;}
   }


     
   }

 
   
  if(state >= 0x80)   // Master is requesting data from slave
  {
   
   SLED=0; 
   i2c_write(i2c_write_data[0]);
   i2c_write(i2c_write_data[1]);
   i2c_write(i2c_write_data[2]);
   i2c_write(i2c_write_data[3]);
   i2c_write(i2c_write_data[4]);
   i2c_write(i2c_write_data[5]);
   i2c_write(i2c_write_data[6]);
   i2c_write(i2c_write_data[7]);
   i2c_send_complete=1;
   SLED=1;

  }
clear_interrupt(INT_SSP);
}

/*******************************************************************/
/*******************************************************************/
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Fri Sep 09, 2022 5:41 am     Reply with quote

I'm not sure your chip is clocking at the speed you expect. Change your setup
to:

Code:

//==================================================================
#include<18F46K22.h>
#fuses NOWDT,NOPUT,NOBROWNOUT,NOLVP,MCLR
#use delay(INTERNAL=4MHz)


I think you may be clocking at 8MHz. This is the default for the internal
oscillator on this chip. Using the 'internal' keyword sets both no clock out,
and the PLL.

Also what are you using for pullups?. Remember at 3.3v, the resistors
need to be 33% smaller than at 5v for the same current.

Your interrupt is not really correct. The handler should only ever write
one byte. Look at the example for I2C slave handlers.

In fact there are then other issues. For the high states >=0x80.
it'll still execute your read code. Wrong. This code:
Code:

   if(state!=0)
      {
      i2c_read_data[icount++]=incoming;
         if(icount==6){icount=0;i2c_read_complete=1;}

Will be executed for these states.
The whole logic is wrong.

Also have a look. Some newer chips require you to manually reset
the CKP bit. Older chips did not. This has been covered here.
avjarun



Joined: 28 Mar 2021
Posts: 12

View user's profile Send private message

PostPosted: Sun Sep 11, 2022 9:46 am     Reply with quote

Thank you for your valuable time. I have made the changes as follows and I am having 1.2K as pull up. However, I still have some difficulties in sorting out a few issues.

Code:
#fuses NOWDT,NOPLLEN,NOPUT,NOBROWNOUT,NOLVP,NOIESO,MCLR
#FUSES NOHFOFST,NOSTVREN,NOXINST
#use delay(INTERNAL=4MHz)


/*******************************************************************/
/*******************************************************************/
#INT_SSP
void ssp_interrupt()
{
int8 incoming, state,address;

state = i2c_isr_state();
 


  if(state <= 0x80)     // Master is sending data
  {

     if(state == 0x80)
         incoming = i2c_read(2);         
      else
         incoming = i2c_read();

   if(state!=0 && state != 0x80)
   {
   i2c_read_data[icount++]=incoming;
      if(icount==6){icount=0;i2c_read_complete=1;}
   }
 }




  if(state >= 0x80)   // Master is requesting data from slave
  {       
   i2c_write(i2c_write_data[i2c_count++]);
   //if(i2c_count==9){SDATA=0;i2c_count=0;i2c_send_complete=1;} // WRITING AN IF STATEMENT HERE FREEZES THE OPERATION
  }


}

/*******************************************************************/
/*******************************************************************/



void sendStatus(void)
{
   int1 statCheck=0;
   i2c_count=0;
   i2c_send_complete=0;

   RLED=TLED=1;
   SDATA=1;
   while(!statCheck)
   {

               
      if(i2c_send_complete)
      {
         statCheck=1;
      }

   
   }
   RLED=TLED=0;
   SDATA=0; delay_ms(100);

}






My Code works in the following manner.

1. MASTER will SEND CONFIG VALUES etc to the SLAVE whenever required.

2. I have a function in the slave to monitor the status of ANALOG PINS and when the SLAVE wants to send the Data to the Master, it TURNS on a PIN (SDATA) to HIGH.

3. Master detects the HIGH State of SDATA input and will read the data from the slave.


PROBLEM-1
Writing an if statement within the interrupt freezes the operations / the controller simply gets struck in a loop


PROBLEM-2

I am reading 8 bytes of data (0-7), but to terminate the write sequence I have to wait until the counter reaches 9. I am not sure why. And.... I have to bring down the IF statement to sendStatus function to make it work.

Code:
while(!statCheck)
   {
               if(i2c_count==9){SDATA=0;i2c_count=0;i2c_send_complete=1;}
               
      if(i2c_send_complete)
      {
         statCheck=1;
      }

   
   }
       



PROBLEM-3

Above all, with these changes in IF statement and counter variable wait until 9, I am able to send the data from slave to master. But once the MASTER receives the data from the SLAVE, the SLAVE stops READING CONFIGURATION DATA from the MASTER.

General Call enabled in Slave and Master uses 0 as the address to send config data to Slave so that all the slaves receive the config data at once.


MASTER CODE as FOLLOWS
Code:

///// Declaration

#use i2c(stream=LC, master, sda=PIN_C4, scl=PIN_C3,FAST=90000)

//// Write Config to Slave
boolean write_to_slave(int sID) {
   i2c_start(LC);    delay_us(100);
   i2c_write(LC,(sID<<1));
    i2c_write(LC,i2c_write_data[0]); // Command Byte
   i2c_write(LC,i2c_write_data[1]);
   i2c_write(LC,i2c_write_data[2]);
   i2c_write(LC,i2c_write_data[3]);
   i2c_write(LC,i2c_write_data[4]);
   i2c_write(LC,i2c_write_data[5]);
   delay_ms(10);
   i2c_stop(LC);
}




/// Read from Slave
/*************************************************************/
/* READ SLAVE DATA                                   */
/*************************************************************/
void readData(int16 sID)
{
   RLED=1;
   for(i2c_x=0;i2c_x<=7;i2c_x++) i2c_read_data[i2c_x]=0x00;
   
   i2c_start(LC);
   i2c_write(LC,sID<<1 | 0x01);
   for(i2c_x=0;i2c_x<=6;i2c_x++) i2c_read_data[i2c_x]=i2c_read(LC);
   i2c_read_data[i2c_x]=i2c_read(LC,0);
   i2c_stop(LC);
   delay_ms(5);RLED=0;
}

The whole process was working well with PIC18F4620 without any issues. But with PIC18F46K22, I really cannot get thru it. Kindly point me in the right direction.

Again. Thank you so much.
avjarun



Joined: 28 Mar 2021
Posts: 12

View user's profile Send private message

Update
PostPosted: Sun Sep 11, 2022 9:57 am     Reply with quote

I have an update. In slave the declaration I have written for GEN Call and I2C is

Code:
#byte INTCON = 0xFF2
#byte SSPCON2 = 0xFC5
#bit GCEN = SSPCON2.7
#bit ACKDT=SSPCON2.5

#byte SSPADD = 0xFC8
#use i2c(SLAVE, SDA=PIN_C4, SCL=PIN_C3)



SSPADD=SLACE_ADD<<1;
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
GCEN=1;




I just understood that If I send Data to the respective Slave ID from the Master instead of 0 (General Call) the slave receives the data from the Master.

What is the error with the GEN CALL?

I am still confused about reading up to counter 9 and the IF statement in interrupt.
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Sun Sep 11, 2022 10:09 am     Reply with quote

If you are enabling general call addressing, then your interrupt handler will
need to be modified to read the address byte, and check if this matches
the general call address or the normal device address.
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Sun Sep 11, 2022 6:06 pm     Reply with quote

AS an aside - re power -unless you are doing a pull up on some big leaky power transistor or operating at some very high switch rate -1.2K is pretty darned stiff in my experience. And a possible waste of current in a battery app.
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Mon Sep 12, 2022 1:22 am     Reply with quote

Remember though he is on 3.3v

1.2K is 'low', The extra power from this is only when the bus is being
driven, so tiny. Rp(min) for a standard puil-up at 3.3v, is 1100R. So this is
getting near this, but is still theoretically OK.
I must admit on a five device 3.3v bus I'll commonly use 1.5K, but if the
capacitance was so high that a drive as low as 1.2K was needed, I'd be looking
at changing to using active pull-ups.....

5V short bus 100K 4.7K
5V long bus 400K 3.3k
3.3v short bus 100K 3.3K
3.3v long bus 400K 1.8K

Are sensible 'likely to work' values.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon Sep 12, 2022 1:34 am     Reply with quote

avjarun wrote:

The whole process was working well with PIC18F4620 without any issues. But with PIC18F46K22, I really cannot get thru it.

What were the oscillator frequencies for the 18F4620 and it's Master PIC ?

What is your Master PIC ? Is it a PIC ? What is it's oscillator frequency ?

What's your compiler version ?

Were you using 3.3v for the 18F4620 ?

You said it was working before. Then it failed when you moved to a
new PIC. I'm trying to find out what's changed.

Where is your declaration statement for SDATA ? You never posted it.
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Mon Sep 12, 2022 5:12 am     Reply with quote

I'm sure his problem is the CKP bit.

On 90% of PIC's, this automatically releases when data is loaded to the
buffer register. On the rest, it has to be manually released. The K22 is one
of the chips I know has this issue.

Code:

/*******************************************************************/
#BIT CKP=getenv("BIT:CKP")

//I2C interrupt handler showing handling GCA, and handling CKP

#INT_SSP
void ssp_interrupt()
{
   int8 incoming, state, address;
   static int In_Gca = FALSE; //flag for GCA handling

   state = i2c_isr_state();
 
   //First we have to handle General call
   if (state==0)
   {
      icount=0; //synchronise data counter
      address=i2c_read();
      if (address==0) //If this is true we have received address 0
         In_Gca=TRUE;
      else
         In_GCA=FALSE; //else normal address 
      return; //immediate exit
   }
   if(state <= 0x80)   // Master is sending data or a transmission has to start
   {
      if(state == 0x80)
         incoming = i2c_read(2); //read the byte but don't release the clock       
      else
         incoming = i2c_read(); //normal read
      if(state != 0x80)
      {
         if (In_Gca)
            config[icount++]=incoming; //save config data
         else
            i2c_read_data[icount++]=incoming; //otherwise save normal data
         if(icount==6)
         {
            i2c_read_complete=1; //flag to say six bytes have been received
         }
      }
   }
   if(state >= 0x80)   // Master is requesting data from slave
   {       
      i2c_write(i2c_write_data[i2c_count++]);
      CKP=TRUE; //release CKP.
      //Some chips do this automatically others don't
   }
}


I've rewritten this to show how a normal data read is distinguished from
a GCA read, and add the CKP release.

You will see in the data sheet that on this chip these lines are added to
the CKP entry:
Quote:

In I2 C Slave mode:
SCLx release control
1 = Enable clock
0 = Holds clock low (clock stretch). (Used to ensure data setup time.)
avjarun



Joined: 28 Mar 2021
Posts: 12

View user's profile Send private message

PostPosted: Tue Sep 13, 2022 9:20 pm     Reply with quote

Dear PCM

In 18F4620, it was clocked @ 4 Mhz, Internal Crystal
The same is done in PIC18F46K22

As you pointed out, the code shared in the beginning was used in 18F4620. Although the code was wrong, it still worked.

The points that I have mentioned is still buzzing me...

1. Adding an IF statement halts the controller (I am not sure why it is getting struck)

2. I am not sure why I should check i2c_count==9 when I am actually reading 8 bytes of data in the master.

Master is clocked @ 4 Mhz - PIC18F4620 (Internal)

I will make changes to the CKP bit and will update you again the process.
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Wed Sep 14, 2022 2:21 am     Reply with quote

Your problem with the number of bytes is what you are doing.
The _master_ has to terminate the transmission, not the slave.
The slave loads the bytes 'in advance' of the master reading them.
The master on the last legitimate read, needs to send a NACK. This
is the signal to the slave ISR that this transfer has finished.
The last read on the master needs to send NACK.

So to read a two bytes you use:
Code:

    val1=i2c_read();
    val2=i2c_read(0);


It is the '0' here that sends the signal to say that the master has finished
reading.
This tells the slave that it doesn't need another byte loaded. The transaction
for this never has a read at the master (since it effectively aborts the
transfer with a stop).

The master must signal that it has read the last byte of the transaction.

Now you show this as being done.
If so, then a ninth write should never happen. However would when you
start the next transaction (since you are not basing the count on the
state byte). Resetting the transaction at this point is simply wrong, and will
result in the wrong bytes being sent.
Understand that this routine will not be called when the write has completed.
To detect this would requite you to wait for the eighth write, and then wait
for a byte time after this.
Why do this though?. The point is it is the state that shows when a new
write begins, and it is this that should reset the count if you want to use
a separate count. Use the state value to show which byte should be sent,
not a separate count, or reset the count on state==0x80, which is saying
a new transaction is starting.
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