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

I2C Slave will not work on PIC16F690

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



Joined: 01 Jul 2004
Posts: 14

View user's profile Send private message

I2C Slave will not work on PIC16F690
PostPosted: Sun Nov 04, 2007 10:23 pm     Reply with quote

Hi

In my project I need to communicate with other hardware via I2C.

Currently I can't get I2C working. Interrupt naver happen.

Here is my code
Code:

#include <16F690.h>
#device *=16 ICD=TRUE
#fuses INTRC, NOWDT, NOPROTECT
#use delay(clock=8000000)
#use rs232(baud=9600, xmit=PIN_B7, rcv=PIN_B5, ERRORS)
#LIST
#use i2c(SLAVE, SDA=PIN_B4, SCL=PIN_B6, address= 0x60,  FORCE_HW)

#include <input.c>
#include <stdlib.h>
#include <string.h>


#byte SSPSTAT = 0x014
#byte SSPADD = 0x013
#byte SSPCON = 0x014
#byte STATUS = 0x003
#byte SSPBUF = 0x013
#byte PIE1 = 0x8c
#bit SSPOV1 = 0x014.6

#bit SSPSTART = 0x014.3

#define  MY_I2C_ADDR  0x60


void I2C_Reset_Hardware();
//------------------- Synchronous Serial Port I2C ---------------------------

                                   
#define  I2C_MASTER_MASK 0x0B // SSPCON.SSPM3-0 = 1011 firmware master mode, slave idle

#define  I2C_SLAVE_MASK  0x0E // SSPCON.SSPM3-0 = 1110 - slave mode, 7-bit addr, S & P
                              // interrupts enabled

#define  I2C_CLK_ENABLE          0x10
#define  I2C_MODE_SETUP          0x20

// this is our current i2c address
static int8 I2C_Address = MY_I2C_ADDR;


//I2C Interrupt Service Routine
#INT_SSP
void ssp_interupt ()
  {
   BYTE incoming, state ;

 // change the bank to 1
  bit_set(STATUS, 6);
  bit_clear(STATUS, 5);

  output_low(PIN_A5);     
  output_high(PIN_A5);
  output_low(PIN_A5);     
   
    state = i2c_isr_state();

    if(state == 0x80)                     //address Master is sending data
      {
    //  output_low(PIN_A5);     
    //  output_high(PIN_A5);
   //   output_low(PIN_A5);
        incoming = i2c_read();
   // printf("Address %x\n\r", incoming );
     }

    else if(state < 0x80)   
     {
     // output_low(PIN_A5);     
    //  output_high(PIN_A5);
    //  output_low(PIN_A5);   
   //     incoming = i2c_read();
    //   printf("i2c read %x\n\r", incoming );
     }

  // change the bank back to 0
  bit_clear(STATUS, 6);
  bit_clear(STATUS, 5);
}

 
// ***************************
// Main star here
// ***************************
void main ()
 { 
 int addr1;
 
 disable_interrupts(GLOBAL);

  // ************** Intialise All Veriables ********************
  setup_adc_ports(NO_ANALOGS); 
  setup_comparator(NC_NC_NC_NC);

  // 1 = Input,0 = Output
  set_tris_A ( 0x1B );      // Port A 00011011
  set_tris_B ( 0x30 );      // Port B 00110000 ( Bit5/7 - RS232- Rx/tx)     
  set_tris_C ( 0x87 );        // Port C 00011111

  printf( "\n\r\n\rInitialising \n\r");
  setup_wdt(WDT_OFF);

  output_float(PIN_B6);
  output_float(PIN_B4);
   
  I2C_Reset_Hardware();
 
// Change the Bank to set the address
  bit_set(STATUS, 6);
  bit_clear(STATUS, 5);
  SSPADD = 0x60;
  addr1 = SSPADD;
   bit_set(PIE1,3);
  bit_clear(STATUS, 6);
  bit_clear(STATUS, 5);

  printf(" get Address %X \n\r",addr1);
   enable_interrupts(GLOBAL);
  enable_interrupts(INT_SSP);
  disable_interrupts(INT_RB);

   bit_set(STATUS, 6);
  bit_clear(STATUS, 5);
  SSPADD = 0x60;
  addr1 = SSPADD;
   bit_set(PIE1,3);
  bit_clear(STATUS, 6);
  bit_clear(STATUS, 5);

  while (TRUE)
   {   
   ;
   }
}

void I2C_Reset_Hardware(void)
{
 int tmp;
  // Reset the I2C if we timeout, this should prevent us from ever
  // locking up the I2C
 
  SSPADD = I2C_Address;
  SSPCON = (I2C_SLAVE_MASK);
  SSPCON = (I2C_SLAVE_MASK | I2C_CLK_ENABLE | I2C_MODE_SETUP);
  // Clear the buffer
  tmp =SSPBUF;
  SSPOV1 = 0;   
  clear_interrupt(INT_SSP);
  enable_interrupts(INT_SSP);

  return;
}



Did anyone had any issue with I2c using PIC16F690.

Can someone help me out here
Compiler Ver PCM 4.058


PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sun Nov 04, 2007 10:59 pm     Reply with quote

Quote:
#byte SSPSTAT = 0x014
#byte SSPADD = 0x013
#byte SSPCON = 0x014
#byte STATUS = 0x003
#byte SSPBUF = 0x013
#byte PIE1 = 0x8c
#bit SSPOV1 = 0x014.6

#bit SSPSTART = 0x014.3


// Change the Bank to set the address
bit_set(STATUS, 6);
bit_clear(STATUS, 5);
SSPADD = 0x60;
addr1 = SSPADD;
bit_set(PIE1,3);
bit_clear(STATUS, 6);
bit_clear(STATUS, 5);

CCS automatically selects the correct bank if you specify the correct
address of the SSP registers. You don't need to manually switch banks
with CCS.
tanyamahen



Joined: 01 Jul 2004
Posts: 14

View user's profile Send private message

PostPosted: Mon Nov 05, 2007 2:21 am     Reply with quote

I have chaged as you said but still problem
rnielsen



Joined: 23 Sep 2003
Posts: 852
Location: Utah

View user's profile Send private message

PostPosted: Mon Nov 05, 2007 10:03 am     Reply with quote

I haven't compiled this code so I'm not 100% that it works but, try something like this:

Code:
//I2C Interrupt Service Routine
#INT_SSP
void ssp_interupt ()
{
unsigned int8 state;

  state = i2c_isr_state();

  output_toggle(PIN_A0);// indicate that the ISR has at least been entered

  if(!state)
  {
    output_toggle(PIN_A1)// toggle output, assign whatever pin you want here
  }
  else if(state < 0x80)
  {
    output_toggle(PIN_A2)
  }
  else if(state == 0x80)
  {
    output_toggle(PIN_A3)
  }
  else if(state > 0x80)
  {
    output_toggle(PIN_A4)
  }
}// end of ISR


If you get my drift you'll see that we're simply trying to see if the PIC is responding to I2C commands. Assign what ever pins you want to toggle. I would suggest using LEDs for visual indication on what the slave is seeing. If none of the outputs toggle then there might be a hardware issue between the master and slave.

Make your master code very simple. Maybe something like:

Code:
i2c_start();
i2c_write(0x60);
i2c_stop();


This should cause the slave to ACK and toggle one of the outputs. Use this to start with and then expand the master code to write and read data. I've found it's best to start from the most simplest code, doing only one thing, and then adding to it.

Best of luck.

Ronald
tanyamahen



Joined: 01 Jul 2004
Posts: 14

View user's profile Send private message

PostPosted: Mon Nov 05, 2007 11:59 pm     Reply with quote

Thanks Ronald

Today holiday in melbourne.... I will go to work tomorrow to check this out... I have CRO to check the toggled PIN (In my code PIN_A5).

Thanks for your help
Tanya
Guest








PostPosted: Wed Nov 14, 2007 6:52 pm     Reply with quote

Thank you all

I have fix the problem. (Two Problem)

1) In PIC16f690 when you set "enable_interrupts(INT_SSP) " This interrupt setting goes and set the "RB interrupt" I have now set the interrupt in ASM and I get I2C interrupt.

2) Master send wrong address (0x10) My address is 0x60.

Once I finish coding I will post my code
ymoona



Joined: 20 Mar 2005
Posts: 32
Location: Heiloo, the Netherlands

View user's profile Send private message Visit poster's website

PostPosted: Sun Dec 30, 2007 12:26 pm     Reply with quote

Anonymous wrote:
Thank you all

I have fix the problem. (Two Problem)

1) In PIC16f690 when you set "enable_interrupts(INT_SSP) " This interrupt setting goes and set the "RB interrupt" I have now set the interrupt in ASM and I get I2C interrupt.

2) Master send wrong address (0x10) My address is 0x60.

Once I finish coding I will post my code


currently I face the same problem. I've connected to 16F690 via I2C. Both line have 10k pullups to the +5V. The master source is:
Code:

#include <16F690.h>
#device adc=8

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES INTRC_IO                 //Internal RC Osc, no CLKOUT
#FUSES NOPROTECT                //Code not protected from reading
#FUSES BROWNOUT                 //Reset when brownout detected
#FUSES MCLR                     //Master Clear pin enabled
#FUSES NOCPD                    //No EE protection
#FUSES NOPUT                    //No Power Up Timer
#FUSES IESO                     //Internal External Switch Over mode enabled
#FUSES FCMEN                    //Fail-safe clock monitor enabled
#use delay(clock=8000000)
#use rs232(baud=19200, xmit=PIN_B7, rcv=PIN_A5)
#use i2c(Master, Slow, sda=PIN_B4, scl=PIN_B6)

/*
Pin defines
*/
#define led PIN_A5

#int_SSP // i2c activty
SSP_isr()
{
   return;
}



void main(){
   setup_adc_ports(NO_ANALOGS|VSS_VDD);
   setup_adc(ADC_OFF);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_DISABLED);
   //setup_timer_2(T2_DIV_BY_1,255,1);
   setup_timer_2(T2_DIV_BY_16, 124, 1);  // 1000 Hz, the pwm frequention
   setup_comparator(NC_NC_NC_NC);
   enable_interrupts(INT_SSP);
   enable_interrupts(GLOBAL);
   setup_oscillator(OSC_8MHZ);


   printf("I2C engine driver\n\r");

   while(1){
      output_high(led);
      // Write the letter 'B' to the slave board.
      i2c_start();
      i2c_write(0xA0);
      i2c_write(0x00);
      i2c_write(0b01001111);
      i2c_stop();
      delay_ms(500);
      output_low(led);
      // Write the letter 'B' to the slave board.
      i2c_start();
      i2c_write(0xA0);
      i2c_write(0x00);
      i2c_write(0b10001111);
      i2c_stop();
      delay_ms(500);
   }
}

This is the slave code:
Code:

#include <16F690.h>
#device adc=8

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES INTRC_IO                 //Internal RC Osc, no CLKOUT
#FUSES NOPROTECT                //Code not protected from reading
#FUSES BROWNOUT                 //Reset when brownout detected
#FUSES MCLR                     //Master Clear pin enabled
#FUSES NOCPD                    //No EE protection
#FUSES NOPUT                    //No Power Up Timer
#FUSES IESO                     //Internal External Switch Over mode enabled
#FUSES FCMEN                    //Fail-safe clock monitor enabled
#use delay(clock=8000000)
#use rs232(baud=19200, xmit=PIN_A5, rcv=PIN_A4)
#use i2c(Slave, Slow, sda=PIN_B4, scl=PIN_B6, address=0xa0)
/*
Pin defines
*/
#define driver_a PIN_C0
#define driver_b PIN_C1

/*
Variables
*/

/*
Function prototypes
*/
// description: This funtion enables or disables the LM298 both H-bridges.
// arguments:   Boolean enable
// return:      None
void EnableDrivers( int1 enable );

// description: This funtion sets both the h-bridges to the desired value
// arguments:   int8 config
//              The four MBS configure the side and direction.
//              The four LSB configure the duty cycle.
// return:      None
void SetDrivers( int8 config );

// There are some issues with the PWM module, before changing pinout please clear.
void clear( void );

#int_SSP // i2c activty
SSP_isr()
{
   printf("interrupt!\n\r");
   return;
}



void main(){
   setup_adc_ports(NO_ANALOGS|VSS_VDD);
   setup_adc(ADC_OFF);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_DISABLED);
   //setup_timer_2(T2_DIV_BY_1,255,1);
   setup_timer_2(T2_DIV_BY_16, 124, 1);  // 1000 Hz, the pwm frequention
   setup_comparator(NC_NC_NC_NC);
   enable_interrupts(INT_SSP);
   //enable_interrupts(INT_RB);
   enable_interrupts(GLOBAL);
   setup_oscillator(OSC_8MHZ);


   printf("I2C engine driver Updated with I2C\n\r");

   EnableDrivers( true );

   while(1){
      // recht vooruit
      SetDrivers( 0b10000111 );
      delay_ms( 2000 );

      // recht achteruit
      SetDrivers( 0b01000111 );
      delay_ms( 2000 );

      // rechtsom
      SetDrivers( 0b00100111 );
      delay_ms( 2000 );

      // linksom

      SetDrivers( 0b00010111 );
      delay_ms( 2000 );

      // stop
      SetDrivers( 0b00000111 );
      delay_ms( 2000 );
   }
}

/*
Funtion boddies
*/

void EnableDrivers( values ){
   if( values ){
      output_high( driver_a );
      output_high( driver_b );
   }
   else{
      output_low( driver_a );
      output_low( driver_b );
   }
}

void SetDrivers( int8 config ){
   int8 settings = 0;
   int8 duty = 0;

   duty = config & 0b00001111;
   duty = duty * 8;
   set_pwm1_duty( duty );
   settings = config & 0b11110000;
   settings = settings >> 4;
   if((settings & 0b1000) >  0 &&  // forward
      (settings & 0b0100) == 0 &&
      (settings & 0b0010) == 0 &&
      (settings & 0b0001) == 0 ){
       clear();
       setup_ccp1(CCP_PWM | CCP_PWM_H_H | CCP_PULSE_STEERING_A | CCP_PULSE_STEERING_C );
       output_low( PIN_C4);
       output_low( PIN_C3);
   }
   else if((settings & 0b1000) == 0 &&
           (settings & 0b0100) >  0 &&  // backward
           (settings & 0b0010) == 0 &&
           (settings & 0b0001) == 0 ){
            clear();
            setup_ccp1(CCP_PWM | CCP_PWM_H_H | CCP_PULSE_STEERING_B | CCP_PULSE_STEERING_D );
            output_low( PIN_C5);
            output_low( PIN_C2);
   }
   else if((settings & 0b1000) == 0 &&
           (settings & 0b0100) == 0 &&
           (settings & 0b0010) >  1 &&  // turn right
           (settings & 0b0001) == 0 ){
            clear();
            setup_ccp1(CCP_PWM | CCP_PWM_H_H | CCP_PULSE_STEERING_A | CCP_PULSE_STEERING_D );
            output_low( PIN_C4);
            output_low( PIN_C2);
   }
   else if((settings & 0b1000) == 0 &&
           (settings & 0b0100) == 0 &&
           (settings & 0b0010) == 0 &&
           (settings & 0b0001) > 0 ){  // turn
            clear();
            setup_ccp1(CCP_PWM | CCP_PWM_H_H | CCP_PULSE_STEERING_B | CCP_PULSE_STEERING_C );
            output_low( PIN_C3);
            output_low( PIN_C5);
   }
   else{
      clear();
      setup_ccp1(CCP_PWM | CCP_PWM_H_H );
      output_low( PIN_C2);
      output_low( PIN_C3);
      output_low( PIN_C4);
      output_low( PIN_C5);   }
}

void clear( void ){
   setup_ccp1(CCP_PWM | CCP_PWM_H_H | CCP_PULSE_STEERING_A | CCP_PULSE_STEERING_B | CCP_PULSE_STEERING_C | CCP_PULSE_STEERING_D );
   output_low( PIN_C2);
   output_low( PIN_C3);
   output_low( PIN_C4);
   output_low( PIN_C5);
}


The slave code is a bit big, but otherwise it doesn't compile.
The problem is that I get no interrupt.

I get this code on the slave in ASM
Code:

....................    enable_interrupts(INT_SSP);
0226:  BSF    03.5
0227:  BSF    0C.3
....................    enable_interrupts(GLOBAL);
0228:  MOVLW  C0
0229:  BCF    03.5
022A:  IORWF  0B,F

Is this correct?
_________________
checkout my site: www.ymoona.com/wiki
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sun Dec 30, 2007 2:26 pm     Reply with quote

It worked for me. Here's the output of the 16F690:
Quote:

I2C engine driver Updated with I2C
interrupt!
interrupt!
interrupt!
interrupt!

I don't have two 16F690's, so I used a 16F877 for the master, but with
similar code. I tested this with vs. 4.064 of the compiler.

What is your compiler version ?

Also, you don't need SSP interrupts in the Master code. Remove the
#int_ssp routine, and remove the enable interrupts for INT_SSP and
GLOBAL. You only need SSP interrupts in the slave code.
Another thing is you don't need a 'return' statement in an interrupt
routine. The compiler puts in code to handle exiting from the routine.
Delete the 'return' statements.
ymoona



Joined: 20 Mar 2005
Posts: 32
Location: Heiloo, the Netherlands

View user's profile Send private message Visit poster's website

PostPosted: Sun Dec 30, 2007 2:40 pm     Reply with quote

PCM programmer wrote:
It worked for me. Here's the output of the 16F690:
Quote:

I2C engine driver Updated with I2C
interrupt!
interrupt!
interrupt!
interrupt!

I don't have two 16F690's, so I used a 16F877 for the master, but with
similar code. I tested this with vs. 4.064 of the compiler.

What is your compiler version ?

Also, you don't need SSP interrupts in the Master code. Remove the
#int_ssp routine, and remove the enable interrupts for INT_SSP and
GLOBAL. You only need SSP interrupts in the slave code.
Another thing is you don't need a 'return' statement in an interrupt
routine. The compiler puts in code to handle exiting from the routine.
Delete the 'return' statements.


I had a little break, an suddenly interrupts seem to work. The return statement in a function is a habbit, a function always returns.
_________________
checkout my site: www.ymoona.com/wiki
ymoona



Joined: 20 Mar 2005
Posts: 32
Location: Heiloo, the Netherlands

View user's profile Send private message Visit poster's website

PostPosted: Sun Dec 30, 2007 4:49 pm     Reply with quote

I thought the problem was fixed, but still it isn't working as I expect.

This is my master code:
Code:

   while(1){
      output_high(led);
      i2c_start();
      delay_ms(10);
      i2c_write(0xA0);
      delay_ms(10);
      i2c_write(0b01001111);
      delay_ms(10);
      i2c_stop();
      delay_ms(10);
      delay_ms(500);
      output_low(led);
      i2c_start();
      delay_ms(10);
      i2c_write(0xA0);
      delay_ms(10);
      i2c_write(0b10001111);
      delay_ms(10);
      i2c_stop();
      delay_ms(10);
      delay_ms(500);
   }


So I send a single byte (plus of course the address)

This is my slave code:
Code:

#int_SSP // i2c activty
SSP_isr(){
   output_high(led);

   // you will only be interrupted if the address is correct.
   // first check the mode: read or write

   state = i2c_isr_state();
   if(state < 0x80){                     //Master is sending data
      incoming = i2c_read();
      if(state == 1){                     //First received byte is byte I want
          value = incoming;
      }

   }
   output_low(led);
}


I expected the led to flash 2 times quick (I probably cannot see two pulses) per half second. But this is not what happens. The led flahes very irregularly, and it stays on for way too long sometimes. Up to 500 to 1500 ms. How does this happen?

Compiler version: 4.013

Edit:
I've done some tests, and this is the result: working code.
Code:

#int_SSP // i2c activty
SSP_isr(){
   output_high(led);
   state = i2c_isr_state();
   if(state < 0x80){                     //Master is sending data
      if(state == 0){
      }
      if(state == 1){                     //First received byte is byte I want
         incoming = i2c_read();
         value = incoming;
      }
   }
   output_low(led);
}


As you can see I ignore state 0 (the address byte) and then I don't read. Only the first data byte is read. Why is this working?
_________________
checkout my site: www.ymoona.com/wiki
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