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

16F882 used as slave i2c
Goto page 1, 2  Next
 
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

16F882 used as slave i2c
PostPosted: Mon Sep 10, 2018 10:17 am     Reply with quote

I have a program that works with a 16F722.
When I replaced the PIC with a 16F882 and changed only the one line to #include<16F882.H> the program still runs OK but the I2C does not communicate. I get no compile errors.
I assume the problem lies in this line which works with the 16F722
Code:
#use i2c (SLAVE, SCL=PIN_C3, SDA=PIN_C4, address=0x14)

and I tried variations, after reading the 16F882.h file, such as below
Code:
#use i2c (SLAVE, SCL=PIN_C3, SDA=PIN_C4)
#use i2c_slaveaddr(0x14)

I get a compile error: Library not found "I2C_SLAVEADDR"
Any suggestions on how to correctly specify this as a slave i2c including its address?
temtronic



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

View user's profile Send private message

PostPosted: Mon Sep 10, 2018 10:41 am     Reply with quote

hmm..
this..
#use i2c_slaveaddr(0x14)

I've never seen before, must be a much newer version of the compiler than I have.

The error indicates that the library (file) that is called i2c_slaveaddr has not been found.
Without seeing your working code and what changes you made, it's hard to say exactly what's not correct. I don't use those PICs.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon Sep 10, 2018 11:12 am     Reply with quote

Quote:

I assume the problem lies in this line which works with the 16F722:
#use i2c (SLAVE, SCL=PIN_C3, SDA=PIN_C4, address=0x14)

1. Ideally, you would post the complete slave program that fails.
2. What PIC you are using for the master ?
3. What are the pullup resistor values and the pullup's voltage ?
4. What are the Vdd voltages of the two PICs ?
5. What is the oscillator frequency of the master PIC ?
6. What's the distance between the two PICs (i2c bus length).
7. And what compiler version ?
rovtech



Joined: 24 Sep 2006
Posts: 262

View user's profile Send private message AIM Address

PostPosted: Mon Sep 10, 2018 12:01 pm     Reply with quote

Complete program below. As I said the only change is the #include<16F882.h>
The master is a 16F886. It works with a 16F722 as slave in this program
Nothing wrong with the hardware and pullups. The 16F722 works fine.
I repeat; the only change was #include <16F882.H> from #include <16F722.H>
The two PICs are on a common +5v
Both PICs at 8MHz
About 2" apart
MPLAB IDE v8.92, PCM 5.064
#use i2c_slaveaddr(0x14) that I tried came directly from the 16F882.h file which I have trouble understanding hence my question, and where I think the problem is. How do I correctly specify a slave PIC and its address?

Code:
/* Pre-processor directives */
#include <16F882.H>
#fuses INTRC, NOWDT, PUT, NOPROTECT
#use delay (clock=8MHZ)                       // osc defaults to 8 MHz
#use fast_io (B)
#use i2c (SLAVE, SCL=PIN_C3, SDA=PIN_C4)
#use i2c (SLAVE, SCL=PIN_C3, SDA=PIN_C4, address=0x14)

// Function Prototypes
    void p_fwd (void);
    void p_rev (void);
    void p_stop (void);
    void s_fwd (void);
    void s_rev (void);
    void s_stop (void);

// global variables
    short p_dir, s_dir = true;               // direction flags, true=fwd
    int p_speed, s_speed;                    // port & stbd speeds, speed offset
    int side_thr_status = 0x00;              // initialize to no trips
    int status_reset = 0xff;                 // initialize to no resets
    int p_count = 0;                         // port jam count
    int s_count = 0;                         // stbd jam count

// INTERRUPT ROUTINES
// Interrupt on I2C
#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 port speed
         p_speed = incoming;
       if (state == 2)                       // second data received is stbd speed
         s_speed = incoming;
       if (state == 3)                       // third data received is status reset
       status_reset = incoming;
       }
     if (state >= 0x80)                      // master is requesting data from slave
      {
       i2c_write (side_thr_status);          // send side thruster status
      }
   }

/* The main function */
void main(void)
 {
// declare variables
    int speed;
    int leak = 255;                          // voltage on leak pins
// initialize port directions
    set_tris_a (0xF7);                       // 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 (0xB9);                       // set Port C3 & C4 inputs, the rest outputs
    output_low (PIN_A3);                     // initialize LED off
// setup for PWM
    setup_CCP1 (CCP_PWM);                    // PORT SPEED & DIRECTION
    setup_CCP2 (CCP_PWM);                    // STBD SPEED & DIRECTION
    setup_timer_2 (T2_DIV_BY_16, 255, 2);    // freq of 488 Hz
// setup ADCs
    setup_adc (ADC_CLOCK_DIV_32);            // configures ADC, 2-6 us reqd in .h
    setup_adc_ports (sAN0|sAN1|sAN9|sAN12);  // stbd leak, port leak, port current, stbd current
// initialize motors
    p_stop();                                // initialize thruster stop
    s_stop();
    p_speed = s_speed = 128;                 // both thrusters min fwd
// setup interrupts
    enable_interrupts (INT_SSP);             // enable I2C interrupt
    enable_interrupts (GLOBAL);

// main loop                                   ********************************************
  while (1)                                  // endless loop
  {
// set port thruster speed & direction
   if (!bit_test(side_thr_status, 3))        // set speed if no jam
   {
    if (p_speed <= 127)                      // below halfway set reverse
     {
      p_rev ();
      speed = (127 - p_speed) * 2;           // stop at 2.5v, max at 0v, make 8 bits
     }
    else                                     // >127, set forward
     {
      p_fwd ();
      speed = (p_speed - 128) * 2;           // stop at 2.5, max at 5v
     }
    set_pwm1_duty (speed);                   // set port speed
   }

// set stbd thruster speed & direction
   if (!bit_test (side_thr_status, 2))        // set speed if no jam
   {
// s_speed = 170;                             // test PWM
    if (s_speed <= 127)                      // below halfway set reverse
     {
      s_rev ();
      speed = (127 - s_speed) * 2;           // stop at 2.5v, max at 0v, make 8 bits
     }
    else                                     // >127, set forward
     {
      s_fwd ();
      speed = (s_speed - 128) * 2;           // stop at 2.5, max at 5v
     }
    set_pwm2_duty (speed);                 // set stbd speed
   }

// apply any status resets, a 0 to reset, a 1 to remain unchanged   ***********************
   side_thr_status = (side_thr_status & status_reset);   // AND with 0 to selectively reset
    if (status_reset == 0)
     p_count = s_count = 0;                 // reset jam counts
    output_toggle (PIN_A3);

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

// functions        ***********************************************************************
void p_fwd (void)
     {
       if (!p_dir)                     // if dir = 0 (rev)
        p_stop();                        // stop first
      output_high (PIN_B4);            // then
      output_low (PIN_B5);             // Set direction forward
      p_dir = true;                    // dir true for fwd
     }

void p_rev (void)
     {
       if (p_dir)                      // if dir = 1 (fwd)
        p_stop();                        // stop first
      output_low (PIN_B4);             // then
      output_high (PIN_B5);            // Set direction reverse
      p_dir = false;                   // dir false for reverse
     }

void p_stop (void)
     {
      output_low (PIN_B4);
      output_low (PIN_B5);            // stop port motor
      delay_ms(10);                   // wait for stop
     }

void s_fwd (void)
     {
       if (!s_dir)                     // if dir = 0 (rev)
        s_stop();                      // stop first
      output_high (PIN_B2);            // then
      output_low (PIN_B1);             // Set direction forward
      s_dir = true;                      // dir true for fwd
     }

void s_rev (void)
     {
       if (s_dir)                      // if dir = 1 (fwd)
        s_stop();                      // stop first
      output_low (PIN_B2);             // then
      output_high (PIN_B1);            // Set direction reverse
      s_dir = false;                   // dir false for reverse
     }

void s_stop (void)
     {
      output_low (PIN_B2);
      output_low (PIN_B1);            // stop stbd motor
      delay_ms(10);                   // wait for stop
     }

// end


If I uncomment the last line below (from the last block in main()) the motor turns showing there is nothing wrong with the PWM code. The I2C is not communicating.
Code:
// set stbd thruster speed & direction
   if (!bit_test (side_thr_status, 2))        // set speed if no jam
   {
// s_speed = 170;                             // test PWM
temtronic



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

View user's profile Send private message

PostPosted: Mon Sep 10, 2018 12:15 pm     Reply with quote

hmm, any chance there's an internal peripheral in the new PIC that's not in the old one? If so, you should disable anything attached to the I/O pins used for I2C.
HMMMM ! Perhaps it's created a software I2C and not using the Hardware I2C ?? Looking at the listing will confirm/deny this. If those pins are HW I2C, you may need to add 'force_hw' or whatever the option is to tell the compiler to use the hardware peripheral.

Jay
rovtech



Joined: 24 Sep 2006
Posts: 262

View user's profile Send private message AIM Address

PostPosted: Mon Sep 10, 2018 1:07 pm     Reply with quote

There is a problem mentioned in the Silicon Errata about i2c slave mode for the 16F882.
I sort of understand it but the workaround is a PITA so it is easier to just go back to the 16F722.
Like windows everything seems to be getting "improved" until totally useless and burning up valuable time. I have been looking at C++ in 21 days and it is the same with all sorts of features like nameplaces:: and returning zero from main(). It sure lessens my interest in using a Raspberry Pi which seems to run on C++.
Getting old and grumpy.
There is also a problem with the 16F886 as a master i2c which is also in my circuit so perhaps I should go back to an older chip for that one that works as it should.
temtronic



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

View user's profile Send private message

PostPosted: Mon Sep 10, 2018 2:37 pm     Reply with quote

'new' is NOT always better, so WHY change ? Sounds like you had a good,working product so it begs the question WHY did you decide to change PICs? If the new ones are cheaper, well, you've blown a big pile of cash on R&D wasted on the new PICs. It's one of my many mantras NEVER,ever 'upgrade' unless you absolutely have to. HArdware or software, if it ain't broke, does the job, do NOT upgrade.
Maybe cause I'm now getting the 'old age ' pension, I'm crankier (sp) ?? Rolling Eyes
rovtech



Joined: 24 Sep 2006
Posts: 262

View user's profile Send private message AIM Address

PostPosted: Mon Sep 10, 2018 3:21 pm     Reply with quote

The 16F722 cannot be used as a master. The ancient 16C74A that I started with needed a UV erase and did not have EEPROM so as things evolved I had to change and am now using the 16F886 to replace the 16F874A. I try to stick to as few chips as possible and the 16F882 seemed a good replacement after a re-design that replaced many obsolete parts (digital compass, clinometers, depth gauge). Apparently my latest choices are not good so I will contact Microchip to see if they have versions that work. One nice thing is that the pinouts of this family of chips has not changed even though new features are added so minor harware additions can be incorporated with simple software changes. I use i2c between chips so some can be working on time consuming chores while the main PIC can run fast receiving and sending data between the ROV and surface. A few seconds time delay while trying to control a robotic arm remotely while watching a video display would be very frustrating I imagine, so I avoid designing in such a feature.
rovtech



Joined: 24 Sep 2006
Posts: 262

View user's profile Send private message AIM Address

PostPosted: Fri Sep 14, 2018 8:29 am     Reply with quote

Microchip support was not much help:

"Regarding the PIC16F88x, the errata item #6 states that the clock stretching is not working as expected. The workaround is to slow down the I2C communication speed to match the speed of the slave. It means that the developer must consider the amount of time the slave needs to fetch the data needed by the master and that process must fall under 1 BRG period to avoid clock stretching.
For example, the PIC16F88x is talking to a slave MCU, the time to parse the data from the master, fetch it, write and shift the data out to the SDA should take less than 10us(100KHz baud rate, 1 BRG Period). If the slave MCU is running at 8MHz, an instruction would take 0.5us, the slave process must not take more than 20 instructions(at Disassembly)." That sounds like asking for problems with six slaves on the I2C lines.

Microchip Forum was helpful and suggested the 16F1938 that I have, but I believe is 2011 vintage (from the 2011-2013 date on the bottom of the data sheet).
Another suggestion was the 16F15356 which is not listed in my two year old version of PCM compiler.
Could someone check if it is listed in the newer compiler, then I will buy it and switch to these newer chips.
alan



Joined: 12 Nov 2012
Posts: 357
Location: South Africa

View user's profile Send private message

PostPosted: Fri Sep 14, 2018 8:42 am     Reply with quote

This link lists the supported devices http://www.ccsinfo.com/devices.php?page=devices.
But yes it is supported, don't know from what version.
rovtech



Joined: 24 Sep 2006
Posts: 262

View user's profile Send private message AIM Address

PostPosted: Mon Sep 17, 2018 10:11 am     Reply with quote

I decided to use the 16F1938 for now but ran into problems.
My program works with 16F886 as Master and 16F722 as slave.
Does not work with 16F886 as Master and 16F882 as slave.
Does not work with 16F886 as Master and a 16F1938 as slave. The I2C bus is hung and the Master freezes in the loop.
Works with 16F1938 as Master and 16F722 as Slave.
Does not work with 16F1938 as Master and a 16F1938 as slave. The I2C bus is hung with SDA high and SCL low (should be high most of the time).

Here is the relevant part of my Master PIC software, it has worked reliably for years and is unchanged except for the type of PIC and changing INTRC to INTRC_IO in the #fuses.
Code:
/* Pre-processor directives */
 #include <16F1938.H>
 #fuses INTRC_IO, NOWDT, PUT, NOPROTECT
 #use delay (clock=8000000)
 #use rs232 (baud=9600, parity=N, bits=8, enable = pin_C5, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
 #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 SERVO_WRT_ADDR    0X12      // Rud & Elev servo PIC
 #define SERVO_READ_ADDR   0x13
 #define SIDE_WRT_ADDR     0X14      // Side thrusters PIC
 #define SIDE_READ_ADDR    0x15
 #define ROTATE_WRT_ADDR   0X16      // Rotate PIC
 #define ROTATE_READ_ADDR  0x17

// send p_speed and s_speed to SIDE THRUSTERS
   I2C_START ();                        // start I2C
   I2C_WRITE (SIDE_WRT_ADDR);           // addr of side thrusters PIC
   I2C_WRITE (p_motor);                 // send port speed
   I2C_WRITE (s_motor);                 // send stbd speed
   I2C_WRITE (side_thr_reset);          // send any resets
   I2C_STOP ();                         // stop I2C
// read side thruster status
   I2C_START ();                        // start I2C
   I2C_WRITE (SIDE_READ_ADDR);          // addr of side thrusters
   side_thr_status = I2C_READ (0);      // get status (no ack)
   I2C_STOP ();                         // stop I2C


Here is the full code for the Slave PIC (stripped to bare minimum).
Code:
/// SIDE THRUSTERS TEST 16F1938     Last modified:   10 Sept 2018                        ///

/* Pre-processor directives */
#include <16F1938.H>
#fuses INTRC_IO, NOWDT, PUT, NOPROTECT
#use delay (clock=8MHZ)
#use fast_io (B)
#use i2c (SLAVE, SCL=PIN_C3, SDA=PIN_C4, address=0x14)

// global variables
    int p_speed, s_speed;                    // port & stbd speeds, speed offset
    int side_thr_status = 0x00;              // initialize to no trips
    int status_reset = 0xff;                 // initialize to no resets

// Interrupt on I2C
 #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 port speed
         p_speed = incoming;
       if (state == 2)                       // second data received is stbd speed
         s_speed = incoming;
       if (state == 3)                       // third data received is status reset
       status_reset = incoming;
       }
     if (state >= 0x80)                      // master is requesting data from slave
      {
       i2c_write (side_thr_status);          // send side thruster status
      }
   }

/* The main function */
void main(void)
 {
// declare variables
// initialize port directions
    set_tris_a (0xF7);                       // 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
    output_low (PIN_A3);                     // initialize LED off

// main loop
  while (1)                                  // endless loop
  {

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


Is there something I'm not seeing? Any suggestions?
If I pull the I2C connector the Master is freed and my indicator LED blinks each loop. Remember the same software works with 16F722 as slave.
I tried editing out the #use fast_IO (B) which did not help.
temtronic



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

View user's profile Send private message

PostPosted: Mon Sep 17, 2018 10:29 am     Reply with quote

what value are the I2C pullups?
what speed is the I2C running ?
Is the clock a real xtal and 2 caps ?

OK, I see you're using the internal RC OSC. That might be a problem. If the 2 PICs are not running at the same speed...could mess up communications.

Jay
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Mon Sep 17, 2018 11:06 am     Reply with quote

rovtech wrote:
I decided to use the 16F1938 for now but ran into problems.
Is there something I'm not seeing? Any suggestions?

I'm not at my regular desk so I can't do my usual hardware tests.
But assuming everything else is correct, the first thing I'd do is run the
slave at 32 MHz while running the master at 8 MHz or less. See what
happens.
rovtech



Joined: 24 Sep 2006
Posts: 262

View user's profile Send private message AIM Address

PostPosted: Mon Sep 17, 2018 11:19 am     Reply with quote

The pullups are 2k (one board only)
I have never defined the I2c speed which may be the probelm. I always assumed (from examples) that the compiler used a default.
Both Master and slave are 8MHz if I have defined them properly. I was not sure about the 16F1938 as the .h file is not clear what is being selected with INTRC_IO. This has always been a bit of a mystery and I have depended on #use delay (clock=xxx) for setting the clock. Where can I get info on what the compiler needs? The data sheet and .h do not help. I just looked at my CCS manual but not much help there either, it's too brief.
My compiler is PCM Ver 5.064 and I was wondering if it could be too old for the 16F1938 but I'm reluctant to make too many major changes at once.
Remember the software works fine with a 16F722 as slave.

I just ran with the slave at 32 MHz and master at 8 MHz but still hangs the master.

I added
Code:
enable_interrupts (INT_SSP);
enable_interrupts (GLOBAL);

in main, no change.
If I comment out only the ISR then the Master runs so it seems the ISR is holding SCL low at some point.

If I change the ISR as below it still hangs the Master
Code:
// Interrupt on I2C
 #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 port speed
//         p_speed = incoming;
//       if (state == 2)                       // second data received is stbd speed
//         s_speed = incoming;
//       if (state == 3)                       // third data received is status reset
//       status_reset = incoming;
//       }
//     if (state >= 0x80)                      // master is requesting data from slave
//      {
//       i2c_write (side_thr_status);          // send side thruster status
//      }
   }
rovtech



Joined: 24 Sep 2006
Posts: 262

View user's profile Send private message AIM Address

PostPosted: Mon Sep 17, 2018 11:52 am     Reply with quote

When I use
Code:
#use i2c (SLAVE, FORCE_HW, NO_STRETCH, SCL=PIN_C3, SDA=PIN_C4, address=0x14)

the master now works. It seems the slave is stretching indefinitely. The force is not needed but probably safer.
However since I really don't now what I'm doing can someone help?

Edit: BUT THERE IS NO i2c COMMUNICATIONS if I go back to my full program.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
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