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

Has anyone successfully used a 16F1938 as slave I2C?

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



Joined: 24 Sep 2006
Posts: 262

View user's profile Send private message AIM Address

Has anyone successfully used a 16F1938 as slave I2C?
PostPosted: Tue Sep 18, 2018 8:17 am     Reply with quote

I have been trying to migrate from WORKING software using a 16F722 as an I2C slave to a newer PIC16F1938. Has anyone successfully used a 16F1938 as slave I2C?
Can I see your ISR software please? Also the Master request software?
What was your version of CCS compiler and was it PCM?

This thread started as "16F882 used as slave i2c" which resolved the fact that a 16F882 cannot be used as a slave I2C. It also shows the problems after I discovered this and tried a 16F1938 which should work but does not.

I need to be convinced that the 16F1938 can be used as a slave I2C using the CCS compiler. It is likely that I'm doing something wrong but I was not in the case of the 16F882.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue Sep 18, 2018 11:46 am     Reply with quote

I have a 16F1937. It's in the same overall family as the 16F1938.
Do you want me to test it in hardware as an i2c slave ? I can do so
at your request. I'd prefer to test it with my own master and slave
PIC programs (in CCS code), because I know they work.
I don't have two 16F1937's so for a master I'd have to use something
else. I would prefer to run master and slave at +5v, with 4.7K pullups.
Let me know if you want this test done.
rovtech



Joined: 24 Sep 2006
Posts: 262

View user's profile Send private message AIM Address

PostPosted: Tue Sep 18, 2018 12:53 pm     Reply with quote

I appreciate the offer but hold off for a bit.
I am just starting to build a test circuit using two 16F1938 where the master will read 8 DIP switches and the slave will display on 8 LEDs. I will post the code here.
Could you see anything wrong with my code in the old posting? Particularly how I set the PIC frequency and my ISR code?
You could post an example of what has worked for you, just the relevant code for send and receive including your Interrupt Service Routine, ISR.
The Master does not matter, I have migrated all the way from 16C74A to 16F886 and now the 16F1938, all with no problems using various chips as slave until I tried the 16F882 and 16F1938 which don't work..

Update: I have a circuit built using two 16F1938 and the master is passing 3 bytes successfully to the slave. I will try returning data later today (car service). Also studying I2C basics and Ttelmah's comments in my original post.
Ttelmah



Joined: 11 Mar 2010
Posts: 19516

View user's profile Send private message

PostPosted: Wed Sep 19, 2018 7:15 am     Reply with quote

Good. Smile

It is the 'turn round' that gives issues when reading data from a slave.
Remember the master sends a new 'address' with the bottom bit changed to say 'I am going to read', and then as it's next transaction starts reading. The slave has to read the address (still holding the clcok), and then 'preload' the byte ready for the subsequent read, and release the clock.

Now there is an issue with some PIC's not releasing the clock on the write (there are about three generations of I2C peripheral on the PIC - the oldest doesn't have this issue and the newest also doesn't, but the middle one does). On these you have to explicitly release the clock:
Code:

#bit CKP=getenv("BIT:CKP")

{
   unsigned int8 state, incoming;

   state = i2c_isr_state();
   //Note <= here
   if(state <= 0x80)                      //Master is sending data
   {
      if(state == 0x80) //Now note == here
         incoming = i2c_read(2);          //Passing 2 as parameter, causes the function to read the SSPBUF without releasing the clock
      else
         incoming = i2c_read();

      if(state == 1)                      //First received byte is address
         address = incoming;
      else if(state >= 2 && state != 0x80)   //Received byte is data
         buffer[address++] = incoming;
   }

   if(state >= 0x80)                      //Master is requesting data
   {
      i2c_write(buffer[address++]);
      CKP=TRUE; //Add this for chips that don't release the clock....
   }
}


It's also vital that the master device, when reading from the slave does not send ACK on the last byte transferred.
So if transferring three bytes:
Code:

    //start the read

    b0=i2c_read();
    b1=i2c_read();
    b2=i2c_read(0); //this tells the slave this is the last byte
    I2c_stop();


Keeping the two internal state machines in the I2C peripheral 'happy' is a matter of ensuring all phases are done at the right point!...

(I managed to post '1' not '0' above - having a bad morning! - corrected now).
rovtech



Joined: 24 Sep 2006
Posts: 262

View user's profile Send private message AIM Address

PostPosted: Wed Sep 19, 2018 12:12 pm     Reply with quote

You are absolutely right Ttelmah.
I had to simplify my Master PIC hardware and software as it was complicated by RS488 communications with a control console and all sorts of other tasks with multiple I2C to the ROV and robotic arm. Now I have a simple hardware with two PICs talking to each other. The Master has 8 LEDs on the A port to display what is happening. Two 2k pullups on I2C, a 10k pullup on MCLR, a programming ICSP connector, and 5v and ground.
Note to others; This software is almost identical to other posts under various titles and will work with, including others, a PIC16F722 as slave with the comments // removed. It will not work with a 16F1938 which is what this post is about. I suggest reading a description of how I2C works and also pay particular attention to "i2c_isr_state()" in the CCS manual under "Built-in Functions" and other references to i2c in the manual.
My resources for understanding I2C are Circuit Cellar #228 July 2009 and #233 Dec 2009, and Signetics Linear Data Manual (old), and of course this forum.
Note that this is where I'm starting from in getting this software working. I will post the correct working version later so bear with me. I am no expert.
The Master I2C software at present is below. It works as long as the reply lines are commented out as shown.
Code:
/* Pre-processor directives */
 #include <16F1938.H>
 #fuses INTRC_IO, NOWDT, PUT, NOPROTECT
 #use delay (clock=8000000)
 #use I2C (master, SCL=PIN_C3, SDA=PIN_C4)
 #byte porta = getenv ("SFR:PORTA")
 #byte portb = getenv ("SFR:PORTB")
 #byte portc = getenv ("SFR:PORTC")

// define I2C addresses
 #define SLAVE_WRT_ADDR     0X14      // slave PIC
 #define SLAVE_READ_ADDR    0x15

/* The main function */
void main(void)
 {
// declare variables
  int count, reply = 0;
  int ii = 3;
  int jj = 5;

// Initialize port directions
  set_tris_a(0xff);               // Port A all inputs
  set_tris_b(0b11001001);         // B1, B2, B4, B5 outputs, the rest inputs
  set_tris_c(0b10011000);         // set Port C3, C4, C7 inputs, the rest outputs

  while (1)
  {
count = count + 1;
// send data
   I2C_START ();                  // start I2C
   I2C_WRITE (SLAVE_WRT_ADDR);    // addr of Slave PIC
   I2C_WRITE (count);             // send count
   I2C_WRITE (ii);                // send ii
   I2C_WRITE (jj);                // send jj
   I2C_STOP ();                   // stop I2C
// read side thruster status
//   I2C_START ();                 // start I2C
//   I2C_WRITE (SIDE_READ_ADDR);   // addr of slave PIC
//   reply = I2C_READ (0);         // get reply (no ack)
//   I2C_STOP ();                  // stop I2C

  output_toggle (PIN_B2);          // flash LED each pass
  delay_ms(1000);                  // allow time to see results

  }                                     // end of while loop
 }                                      // end of main function
// end


The Slave PIC software is below and works as long as the Master is as above. Next I will try Ttelmah's suggestions and post the results.
Code:
/* Pre-processor directives */
#include <16F1938.H>
#fuses INTRC_IO, NOWDT, PUT, NOPROTECT
#use delay (clock=32 MHZ)
// #use fast_io (B)
#use i2c (SLAVE, SCL=PIN_C3, SDA=PIN_C4, address=0x14)

// global variables
    int count, ii, jj;              // data from master
    int reply;                      // data returned from slave         

// Interrupt on I2C (Interrupt Service Routine)
 #INT_SSP
 void ssp_interrupt ()                        // have an interrupt
   {
    int incoming, state;                     // variables
    state = i2c_isr_state ();                // get state
     if (state < 0x80)                       // master is sending data
      {
       incoming = i2c_read ();               // throw away device address if state = 0
       if (state == 1)                       // first data received is count
         count = incoming;
       if (state == 2)                       // second data received is ii
         ii = incoming;
       if (state == 3)                       // third data received is jj
         jj = incoming;
       }
     if (state >= 0x80)                      // master is requesting data from slave
      {
       i2c_write (reply);                    // send reply
      }
   }

/* The main function */
void main(void)
 {
// declare variables

// initialize port directions
    set_tris_a (0x00);                       // set Port RA3 output, the rest inputs
    set_tris_b (0xC9);                       // set Port B7, B6, B3, B0 inputs, B5, B4, B2, B1 outputs
    set_tris_c (0xff);                       // set Port C3 & C4 inputs, the rest outputs

enable_interrupts (INT_SSP);
enable_interrupts (GLOBAL);

// main loop
  while (1)                                  // endless loop
  {
output_a(count);
delay_ms (1000);
output_a(ii);
delay_ms (1000);
output_a(jj);
delay_ms (1000);
  }                           // end of endless while loop
 }                            // end of main function


Last edited by rovtech on Thu Sep 20, 2018 6:57 am; edited 1 time in total
rovtech



Joined: 24 Sep 2006
Posts: 262

View user's profile Send private message AIM Address

PostPosted: Wed Sep 19, 2018 6:40 pm     Reply with quote

Thank you Ttelmah and PCM Programmer for help in this and other problems.
The program is now working. I think you meant state 1 is first data byte and I have adjusted the program to reflect that. I also added displaying the addr(ess). The 16F1938 needs the forced release of CKP.
The Master sends count which is my switch settings, ii which is set to 3, and jj which is what is read back from the Slave.
The Slave displays on 8 LEDs in order: addr, count, ii, jj (which is what it returned to the Master). With my switches set to 0x81 (count), and reply set to 0xc8 as in the program I get LEDs showing: 0x15 (Slave read address), 0x81 (switches), 0x03 (ii), and 0xc8 (reply). Sorry if this is confusing but, along with an LED on B2 of the Master, these displays made sure everything was being sent and received properly.
This is the Master software
Code:
/// Mster I2C PIC FIRMWARE      Main PIC v1.1.c     Last modified:   19 Sept 2018            ///
/* Pre-processor directives */
 #include <16F1938.H>
 #fuses INTRC_IO, NOWDT, PUT, NOPROTECT
 #use delay (clock=8000000)
 #use I2C (master, SCL=PIN_C3, SDA=PIN_C4)
 #byte porta = getenv ("SFR:PORTA")
 #byte portb = getenv ("SFR:PORTB")
 #byte portc = getenv ("SFR:PORTC")

// define I2C addresses
 #define SLAVE_WRT_ADDR     0X14      // slave PIC
 #define SLAVE_READ_ADDR    0x15

/* The main function */
void main(void)
 {
// declare variables
  int count, reply = 0;
  int ii = 3;
  int jj = 5;

// Initialize port directions
  set_tris_a(0xff);               // Port A all inputs
  set_tris_b(0b11001001);         // B1, B2, B4, B5 outputs, the rest inputs
  set_tris_c(0b10011000);         // set Port C3, C4, C7 inputs, the rest outputs

  while (1)
  {
count = input_A();                // switches on port A
// send data
   I2C_START ();                  // start I2C
   I2C_WRITE (SLAVE_WRT_ADDR);    // addr of Slave PIC
   I2C_WRITE (count);             // send count
   I2C_WRITE (ii);                // send ii
   I2C_WRITE (jj);                // send jj
   I2C_STOP ();                   // stop I2C
// read reply
   I2C_START ();                  // start I2C
   I2C_WRITE (SLAVE_READ_ADDR);   // addr of slave PIC
   reply = I2C_READ (0);          // get reply (no ack)
   I2C_STOP ();                   // stop I2C

  jj = reply;                     // programmed in slave
  output_toggle (PIN_B2);         // flash LED each pass
  delay_ms(1000);                 // allow time to see results

  }                                     // end of while loop
 }                                      // end of main function
// end


The Slave software is:
Code:
/// Slave PIC TEST 16F1938     Last modified:   19 Sept 2018                        ///
/* Pre-processor directives */
#include <16F1938.H>
#fuses INTRC_IO, NOWDT, PUT, NOPROTECT
#use delay (clock=32 MHZ)
// #use fast_io (B)
#use i2c (SLAVE, SCL=PIN_C3, SDA=PIN_C4, address=0x14)
#bit CKP=getenv("BIT:CKP")
// global variables
    int addr, count, ii, jj;     // data from master
    int reply = 0xc8;            // data returned from slave         

// Interrupt on I2C (Interrupt Service Routine)
 #INT_SSP
 void ssp_interrupt ()                  // have an interrupt
   {
    int incoming, state;                // variables
    state = i2c_isr_state ();           // get state
     if (state <= 0x80)                 // master is sending data
      {
        if(state == 0x80)               // address match
         {
          incoming = i2c_read(2);       // read address without releasing clock
          addr = incoming;
         }
        else
         incoming = i2c_read();
       if (state == 1)                  // first byte received is count
         count = incoming;
       if (state == 2)                  // second data received is ii
         ii = incoming;
       if (state == 3)                  // third data received is jj
         jj = incoming;                 // the reply from this sent back
       }
     if (state >= 0x80)                 // master is requesting data from slave
      {
       i2c_write (reply);               // send reply, set in this program
       CKP=TRUE;                        // release clock
      }
   }

/* The main function */
void main(void)
 {
// declare variables

// initialize port directions
    set_tris_a (0x00);                       // set Port RA3 output, the rest inputs
    set_tris_b (0xC9);                       // set Port B7, B6, B3, B0 inputs, B5, B4, B2, B1 outputs
    set_tris_c (0xff);                       // set Port C3 & C4 inputs, the rest outputs

enable_interrupts (INT_SSP);
enable_interrupts (GLOBAL);

// main loop
  while (1)                                  // endless loop
  {
output_a(addr);
delay_ms (1000);
output_a(count);
delay_ms (1000);
output_a(ii);
delay_ms (1000);
output_a(jj);
delay_ms (1000);
  }                           // end of endless while loop
 }                            // end of main function


Last edited by rovtech on Fri Sep 21, 2018 8:40 am; edited 1 time in total
rovtech



Joined: 24 Sep 2006
Posts: 262

View user's profile Send private message AIM Address

PostPosted: Thu Sep 20, 2018 10:19 am     Reply with quote

The software in my ROV is now working using a 16F1938 as a slave.
It is very important to check the Silicon version of the PIC and look at the Errata for it.
The Device ID Revision = 00000003 for the 16F1938 that I am using. The earlier revisions have issues described in the Errata. They may not work with this software.
The Errata for the 16F882 that I initially tried indicates that none of them will work as an I2C Slave! It is important to check your PIC. The revision shows up right after "Target Detected", on my ICD3 anyway.
My PCM compiler version 5.064 uses software I2C by default so that is what is being used in the above software and initially on my ROV. I forced the hardware I2C with
Code:
 #use i2c (SLAVE, FORCE_HW, SCL=PIN_C3, SDA=PIN_C4, address=0x14)

in my ROV and it works fine.
I believe newer compilers use hardware I2C by default so if it is important it is best to force your choice.
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