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

16F88 i2c SCL gets low

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



Joined: 19 Oct 2013
Posts: 6

View user's profile Send private message

16F88 i2c SCL gets low
PostPosted: Sat Dec 28, 2013 1:26 pm     Reply with quote

Hello all,

some issues with i2c SCL line on 16F88 slave mode using int_SSP interrupt.

Objective: 16F88 board for controlling light dimmer from 0 to 100% using i2c. Other i2c slave devices connected to this line.

Problem: After doing some rounds correctly, SCL line gets low and everything stops.

Tests: When the problem occurs, disconnecting SCL line on the 16F88 the system restores itself, so the problem is on the 16F88.

At this point I've done some searches on the forum but after implemented on the code - as you will see - none of them seems to solve this.

Any solution to this problem ?

Best regards

Code:
/////////////////////////////////////////////////////////////////////////
///
///   Fuses: MCLR,PUT,NOPUT,RC,EC,RC_IO,INTRC_IO,BROWNOUT,NOBROWNOUT
///   
/////////////////////////////////////////////////////////////////////////

/************************************************************/
/*               P R E P R O C E S S O R                    */
/************************************************************/

#include <16F88.h>

/************************************************************/
/*                      F U S E S                           */
/************************************************************/

#fuses NOWDT,NOPROTECT,NOLVP,NOMCLR,INTRC_IO, NOFCMEN, NOIESO, CCPB3

/************************************************************/
/*           O S C I L A T O R   &   D E L A Y              */
/************************************************************/

#use delay (clock=4000000)

/************************************************************/
/*                         A D C                            */
/************************************************************/

//

/************************************************************/
/*                S E T U P   R S - 2 3 2                   */
/************************************************************/

#use RS232(baud=9600, xmit=PIN_A4,rcv=PIN_A5)

/************************************************************/
/*                  S E T U P   I 2 C                       */
/************************************************************/

#use i2c(SLAVE,FORCE_HW, SDA=PIN_B1,SCL=PIN_B4,address=0xA0)

/************************************************************/
/*                   D E F I N I T I O N S                  */
/************************************************************/

#define LED PIN_A0
#define SCL PIN_B4
#define SDA PIN_B1

#define LED_GROC_ON()    output_high(PIN_A0)
#define LED_GROC_OFF()   output_low(PIN_A0)

/************************************************************/
/*           G L O B A L   V A R I A B L E S                */
/************************************************************/

char i;
char duty;
char direction;
char rep;
char RB0_value;
char fade;
char start_dimmer;
char state;
char incoming;

BYTE address, buffer[0x10];

#byte SSPCON = 0x14
#bit CKP = SSPCON.4  // Some posts suggest to handle this manually

/************************************************************/
/*                 F U N C T I O N S                        */
/************************************************************/
void light_dimmer (void)
{
   if (start_dimmer==1)
   {
      output_low (PIN_B2);
      rep++;

      for (i=0;i<=duty;i++)
      {
         delay_us (85);
      }

      output_high (PIN_B2);
      start_dimmer = FALSE;
   }
}


/******************************************************************/
/*             I N T E R R U P T   V E C T O R S                  */
/******************************************************************/

#INT_EXT
void int_ext_isr()
{
   RB0_value = input_state(PIN_B0);

   if (RB0_value == 1)
   {
      start_dimmer = TRUE;
   }
   clear_interrupt (INT_EXT);
}

#int_SSP
void ssp_ISR (void)
{
   BYTE incoming, state;
   disable_interrupts(GLOBAL); // Enabling Global Interrupts
   
   state = i2c_isr_state();
   
   if(state <= 0x80)                     //Master is sending data
   {
      incoming = i2c_read();
      if(state == 1)                     //First received byte is address
      {
         address = incoming;
      }
      if(state == 2)
      {                     //Second received byte is data
         output_toggle (PIN_A0);
         buffer[address] = incoming;
         duty = incoming;
      }
   }
   if(state == 0x80)                     //Master is requesting data
   {
      //i2c_write(buffer[address]);
   }
   CKP = 1;
   clear_interrupt (INT_SSP);
   enable_interrupts(GLOBAL); // Enabling Global Interrupts
}


/******************************************************************/
/*                      M A I N   P R O G R A M                   */
/******************************************************************/

void main()
{
   setup_oscillator(OSC_4MHZ);
   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   setup_vref(FALSE);
   setup_timer_1(T1_DISABLED);

   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
   
   output_float(PIN_B0);      // Make PIN_B0 into an input
   ext_int_edge(L_TO_H);      // Configure for Raising Edge Interrupt
   enable_interrupts(INT_EXT);// Enabling External Interrupt
   enable_interrupts(INT_SSP);// Enabling i2c Interrupt
   enable_interrupts(GLOBAL); // Enabling Global Interrupts
 
   duty = 50;
   direction = 1;
   i=0;
   rep = 0;
 
   fade = FALSE;
   
   output_low(PIN_A1);  // Some posts suggests to put unused lines to low
   output_low(PIN_A2);
   output_low(PIN_A4);
   output_low(PIN_A5); 
   output_low(PIN_A6);
   output_low(PIN_A7);
 
   output_low(PIN_B3);
   output_low(PIN_B5);
   output_low(PIN_B6);
   output_low(PIN_B7);
 
   while(1)
   {
      light_dimmer();
   }
}


Master dimmer control routine (0<val<100)


Code:
void dimmer (char val)
{
   i2c_start();
   i2c_write(0xA0);
   i2c_write(0x00);
   i2c_write(val);
   delay_ms(50);
   i2c_stop();
   delay_ms(50);
}
Ttelmah



Joined: 11 Mar 2010
Posts: 19537

View user's profile Send private message

PostPosted: Sat Dec 28, 2013 1:39 pm     Reply with quote

You must _never_, _ever_ enable_interrupts(GLOBAL), inside a PIC interrupt handler.

The PIC _hardware_ disables the global bit when interrupts are called. It also re-enables the interrupts on the next instruction _after_ the interrupt exits. This way the interrupt handler cannot be called inside itself (recursion), which the PIC does not basically support. By enabling the global bit inside the handler you make it possible for this to happen, and the code will then crash....

You can also get rid of the clear_interrupt lines. The compiler does this for you, unless you specify 'noclear', so you are wasting an instruction in each case.

Best Wishes
Zilog750



Joined: 19 Oct 2013
Posts: 6

View user's profile Send private message

PostPosted: Sat Dec 28, 2013 5:04 pm     Reply with quote

Hello ttelmah,

only corrected code

Code:
.................... #INT_EXT
.................... void int_ext_isr()
.................... {
....................    RB0_value = input_state(PIN_B0);
*
0039:  CLRF   2C
003A:  BTFSC  06.0
003B:  INCF   2C,F
.................... 
....................    if (RB0_value == 1)
003C:  DECFSZ 2C,W
003D:  GOTO   040
....................    {
....................       start_dimmer = TRUE;
003E:  MOVLW  01
003F:  MOVWF  2E
....................    }
.................... }
.................... 
0040:  BCF    0B.1
0041:  BCF    0A.3
0042:  GOTO   024
.................... #int_SSP
.................... void ssp_ISR (void)
.................... {
....................    BYTE incoming, state;
....................     
....................    state = i2c_isr_state();
0043:  BSF    03.5
0044:  BTFSC  14.5
0045:  GOTO   04E
0046:  BCF    03.5
0047:  CLRF   42
0048:  BSF    03.5
0049:  BTFSS  14.2
004A:  GOTO   04E
004B:  BCF    03.5
004C:  BSF    42.7
004D:  BSF    03.5
004E:  BCF    03.5
004F:  MOVF   42,W
0050:  INCF   42,F
0051:  MOVWF  44
....................     
....................    if(state <= 0x80)                     //Master is sending data
0052:  MOVF   44,W
0053:  SUBLW  80
0054:  BTFSS  03.0
0055:  GOTO   071
....................    {
....................       incoming = i2c_read();
0056:  BCF    14.6
0057:  BTFSS  0C.3
0058:  GOTO   057
0059:  MOVF   13,W
005A:  BSF    14.4
005B:  MOVWF  43
....................       if(state == 1)                     //First received byte is address
005C:  DECFSZ 44,W
005D:  GOTO   060
....................       {
....................          address = incoming;
005E:  MOVF   43,W
005F:  MOVWF  31
....................       }
....................       if(state == 2)
0060:  MOVF   44,W
0061:  SUBLW  02
0062:  BTFSS  03.2
0063:  GOTO   071
....................       {                     //Second received byte is data
....................          output_toggle (PIN_A0);
0064:  BSF    03.5
0065:  BCF    05.0
0066:  MOVLW  01
0067:  BCF    03.5
0068:  XORWF  05,F
....................          buffer[address] = incoming;
0069:  MOVLW  32
006A:  ADDWF  31,W
006B:  MOVWF  04
006C:  BCF    03.7
006D:  MOVF   43,W
006E:  MOVWF  00
....................          duty = incoming;
006F:  MOVF   43,W
0070:  MOVWF  29
....................       }
....................    }
....................    if(state == 0x80)                     //Master is requesting data
0071:  MOVF   44,W
0072:  SUBLW  80
0073:  BTFSS  03.2
0074:  GOTO   075
....................    {
....................       //i2c_write(buffer[address]);
....................    }
....................    CKP = 1;
0075:  BSF    14.4
.................... }


Results: After changing (erasing) enable/disable interrupts inside ISR, SCL still gets low. System blocked by 16F88 board after 20 or 30 rounds. In this situation, PIN_A0 still toggles, so SSP interrupt is running and the most strangest thing is that the INT is still running even with the SCL line disconnected. Using a scope, there's no relevant disturbance on the Power Supply, nor on the i2c lines during com transactions.

It's very confusing, obviously there's something that don't let SCL line go high.

Regards
temtronic



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

View user's profile Send private message

PostPosted: Sat Dec 28, 2013 5:43 pm     Reply with quote

um maybe a dumb question but Do you have good pullups on the I2C lines ?

jay
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sat Dec 28, 2013 10:46 pm     Reply with quote

Post your CCS compiler version. It could be the problem.
Ttelmah



Joined: 11 Mar 2010
Posts: 19537

View user's profile Send private message

PostPosted: Sun Dec 29, 2013 1:56 am     Reply with quote

Agree with PCM programmer, give your compiler version.
There was an odd problem some time ago, where the I2C_ISR_STATE routine, would not return the status correctly till after a byte had been read.

However a couple of questions.

How fast is your master CPU?.
How fast are you clocking the I2C?.

The slave holds the SCL line low, when it hasn't yet completed 'handling' a transaction. Suggests that the code is entering the ISR, and something is not being done that needs to be done. I'd wonder if the slave is seeing a condition on the bus that is not handled by the code, which then suggests possibly inadequate pull-ups, or a noise problem.

I would write 'something' (just a dummy byte), when the state is >=0x80. With the master code you show, this should never be called, bur if the chip does get to this routine, something has to be loaded.

I have to ask 'why' you put the data both into a buffer, and the duty cycle register?. A waste to store it twice.

So something like:
Code:

#bit WCOL=getenv("BIT:WCOL")
#bit SSPOV=getenv("BIT:SSPOV")

#INT_SSP
void i2c_isr(void)
{
    int8 state, incoming;
    state = i2c_isr_state();

    if(state== 0 )
       i2c_read();
    if(state == 0x80)
       i2c_read(2); //holds SCL low, till the write is performed
    if(state >= 0x80)
       i2c_write(0); //load the output buffer or the interrupt won't clear
    else if(state > 0)
    {
       duty=i2c_read();
    }
    if (WCOL)
       WCOL=FALSE; //brute force error handling.....
    if (SSPOV)
       SSPOV=FALSE;
}

//change your master to
void dimmer (char val)
{
   i2c_start();
   i2c_write(0xA0); //device address
   i2c_write(val); //value to send
   i2c_stop();
}

Since you are not addressing separate registers in the slave, there is no point in sending a register address...

I've added 'brute force' clearing of the possible error status bits.

Both a data read, or a data write, _should_ automatically release the CKP. This is where compiler version matters.

Best Wishes
Zilog750



Joined: 19 Oct 2013
Posts: 6

View user's profile Send private message

PostPosted: Sun Dec 29, 2013 7:32 am     Reply with quote

Hi all,

thanks for the reply. As requested:

Compiler Version: 4.108
Master Board: PICDEM2 PLUS 16F877 XTAL @4MHz with i2c 4k7 Pull-Up resistors
Slave Board: 16F88 Internal Oscillator @4MHz

Master Code

Code:
////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
 
/************************************************************/
/*               P R E P R O C E S S O R                    */
/************************************************************/

#include <16F877.h>
#device ADC=10

/************************************************************/
/*                      F U S E S                           */
/************************************************************/

#fuses XT,NOWDT,NOPROTECT,NOLVP,BROWNOUT

/************************************************************/
/*           O S C I L A T O R   &   D E L A Y              */
/************************************************************/

#use delay(clock=4000000) /* one instruction=1us? */

/************************************************************/
/*                S E T U P   R S - 2 3 2                   */
/************************************************************/

// NONE

/************************************************************/
/*                  S E T U P   I 2 C                       */
/************************************************************/

#use I2C(master, SCL=PIN_C3, SDA=PIN_C4, restart_wdt, SLOW)


/************************************************************/
/*           G L O B A L   V A R I A B L E S                */
/************************************************************/

int duty;

/************************************************************/
/*                 F U N C T I O N S                        */
/************************************************************/

void dimmer (char val)
{
   i2c_start();
   i2c_write(0xA0); //device address
   i2c_write(val); //value to send
   i2c_stop();
}


/******************************************************************/
/*                      M A I N   P R O G R A M                   */
/******************************************************************/

void main()
{   
   duty = 0;
   
   while(TRUE)
   {     
      dimmer (duty);

      if (duty >= 100)
      {
         duty = 0;
      }
      else
      {
         duty = duty+1;
      }
     
      delay_ms (250);
   }
}


Slave code

Code:
/////////////////////////////////////////////////////////////////////////
///
///   
///   
/////////////////////////////////////////////////////////////////////////

/************************************************************/
/*               P R E P R O C E S S O R                    */
/************************************************************/

#include <16F88.h>

/************************************************************/
/*                      F U S E S                           */
/************************************************************/

#fuses NOWDT,NOPROTECT,NOLVP,NOMCLR,INTRC_IO, NOFCMEN, NOIESO, CCPB3

/************************************************************/
/*           O S C I L A T O R   &   D E L A Y              */
/************************************************************/

#use delay (clock=4000000)

/************************************************************/
/*                S E T U P   R S - 2 3 2                   */
/************************************************************/

#use RS232(baud=9600, xmit=PIN_A4,rcv=PIN_A5)

/************************************************************/
/*                  S E T U P   I 2 C                       */
/************************************************************/

#use i2c(SLAVE,FORCE_HW, SDA=PIN_B1,SCL=PIN_B4,restart_wdt, address=0xA0)

/************************************************************/
/*                   D E F I N I T I O N S                  */
/************************************************************/

#define LED PIN_A0
#define SCL PIN_B4
#define SDA PIN_B1

/************************************************************/
/*           G L O B A L   V A R I A B L E S                */
/************************************************************/

char i;
char duty;
char direction;
char rep;
char RB0_value;
char fade;
char start_dimmer;
char state;
char incoming;

BYTE address, buffer[0x10];

#byte SSPCON = 0x14
#bit CKP = SSPCON.4
#bit WCOL=getenv("BIT:WCOL")
#bit SSPOV=getenv("BIT:SSPOV")

/************************************************************/
/*                 F U N C T I O N S                        */
/************************************************************/
void light_dimmer (void)
{
   if (start_dimmer==1)
   {
      output_low (PIN_B2);
      rep++;

      for (i=0;i<=duty;i++)
      {
         delay_us (85);
      }
     
      if (fade==1)
      {
         if (rep>=5)
         {
            if (direction == 1)
            {
               duty++;
               if (duty == 100)
               {
                  direction =0;
               }
            }
            if (direction == 0)
            {
               duty--;
               if (duty == 0)
               {
                  direction =1;
               }
            }
           
            rep=0;
         }
      }
      output_high (PIN_B2);
      start_dimmer = FALSE;
   }
}


/******************************************************************/
/*             I N T E R R U P T   V E C T O R S                  */
/******************************************************************/

#INT_EXT
void int_ext_isr()
{
   RB0_value = input_state(PIN_B0);

   if (RB0_value == 1)
   {
      start_dimmer = TRUE;
   }
}

#INT_SSP
void i2c_isr(void)
{
    int8 state, incoming;
    state = i2c_isr_state();

    if(state== 0 )
       i2c_read();
    if(state == 0x80)
       i2c_read(2);     //holds SCL low, till the write is performed
    if(state >= 0x80)
       i2c_write(0);    //load the output buffer or the interrupt won't clear
    else if(state > 0)
    {
       duty=i2c_read();
    }
    if (WCOL)
       WCOL=FALSE;      //brute force error handling.....
    if (SSPOV)
       SSPOV=FALSE;
}


/******************************************************************/
/*                      M A I N   P R O G R A M                   */
/******************************************************************/

void main()
{
   setup_oscillator(OSC_4MHZ);
   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   setup_vref(FALSE);
   setup_timer_1(T1_DISABLED);

   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
   
   output_float(PIN_B0);      // Make PIN_B0 into an input
   ext_int_edge(L_TO_H);      // Configure for Raising Edge Interrupt
   enable_interrupts(INT_EXT);// Enabling External Interrupt
   enable_interrupts(INT_SSP);// Enabling i2c Interrupt
   enable_interrupts(GLOBAL); // Enabling Global Interrupts
 
   duty = 50;
   direction = 1;
   i=0;
   rep = 0;
 
   fade = FALSE;
   
   output_low(PIN_A1);
   output_low(PIN_A2);
   output_low(PIN_A4);
   output_low(PIN_A5); 
   output_low(PIN_A6);
   output_low(PIN_A7);
 
   output_low(PIN_B3);
   output_low(PIN_B5);
   output_low(PIN_B6);
   output_low(PIN_B7);   

   while(1)
   {
      light_dimmer();
   }
}


Results: In this case SDA line is low instead SCL, after 50 or 60 rounds. As you can see there's only one slave on the i2c bus and the codes on both sides are quite simple.

Any ideas?

Best Regards
Ttelmah



Joined: 11 Mar 2010
Posts: 19537

View user's profile Send private message

PostPosted: Sun Dec 29, 2013 8:10 am     Reply with quote

I think you are seeing noise on one or more of the lines.

It'd hold SDA low, if the slave thinks it has been asked to send a byte (so loads '0' into the data register), and then the master doesn't clock this data.
In the older code, since the register is not loaded, it'd hold the clock low instead.

I'd start by lowering the pull-up resistors being used. 4K7 is usually an acceptable value in 'low noise' environments, for a single slave, with reasonably short wires, but something lower like 1K8, will decrease the rise time, and lower the impedance when the lines are not driven.
I'd also add a short delay after sending each command in the master. Shouldn't be needed, but worth trying. Say 50uSec each.

Look into making sure you have a really good ground connection between the chips, and good suppression.

Best Wishes
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