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

18F2480 I2C problem

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



Joined: 17 Aug 2005
Posts: 316
Location: Perth, Western Australia

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

18F2480 I2C problem
PostPosted: Mon Mar 06, 2006 7:45 am     Reply with quote

I'm having a problem where it seems my I2C ISR doesn't exit properly, causing my mainline to stop working. I've trimmed the slave code down, so here it is:

First "Test.c":
Code:
#define CLOCK_FREQ 8000000

#define I2C_SDA  PIN_C4
#define I2C_SCL  PIN_C3

#define PIN_LED1 PIN_A3
#define PIN_LED2 PIN_A4
#define PIN_LED3 PIN_A5

#define PIN_I2C_ADDR_BIT0 PIN_C1
#define PIN_I2C_ADDR_BIT1 PIN_C6
#define PIN_I2C_ADDR_BIT2 PIN_C2
#define PIN_I2C_ADDR_BIT3 PIN_C5

#include "Test.h"

#if defined(__PCM__)
#byte SSPADD=0x93
#elif defined(__PCH__)
#byte SSPADD=0xFC8
#endif
#define i2c_set_address(x) (SSPADD=(x & 0xFE))

#int_SSP
SSP_isr()
{
  int8 incoming, state;

output_high(PIN_LED2);
  state = i2c_isr_state();

  if (state & 0x80) { // Master is requesting data
    i2c_write(0);
  } else { // Master is sending data
    incoming = i2c_read();
    output_toggle(PIN_LED1);
  }
output_low(PIN_LED2);
}

void init()
{
  int8 addr;

  setup_wdt(WDT_ON);

  // set the I2C pins
  output_float(I2C_SDA);
  output_float(I2C_SCL);

  // read I2C address and set it
  addr = 0x90;
  if (input(PIN_I2C_ADDR_BIT0)) bit_set(addr, 1);
  if (input(PIN_I2C_ADDR_BIT1)) bit_set(addr, 2);
  if (input(PIN_I2C_ADDR_BIT2)) bit_set(addr, 3);
  if (input(PIN_I2C_ADDR_BIT3)) bit_set(addr, 4);
  i2c_set_address(addr);

  // setup interrupts
  enable_interrupts(INT_SSP);
  enable_interrupts(GLOBAL);
}

void main()
{
  init();
  for (;;) {
    restart_wdt();
    output_toggle(PIN_LED3);
  }
}
Then "Test.h":
Code:
#include <18F2480.h>
#device adc=8

#FUSES WDT                    //Watch Dog Timer
#FUSES WDT1024                //Watch Dog Timer uses 1:1024 Postscale
#FUSES HS                       //High speed Osc (> 4mhz)
#FUSES NOPROTECT                //Code not protected from reading
#FUSES BROWNOUT                 //Reset when brownout detected
#FUSES BORV20                   //Brownout reset at 2.0V
#FUSES PUT                      //Power Up Timer
#FUSES NOCPD                    //No EE protection
#FUSES STVREN                   //Stack full/underflow will cause reset
#FUSES NODEBUG                  //No Debug mode for ICD
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOWRT                    //Program memory not write protected
#FUSES NOWRTD                   //Data EEPROM not write protected
#FUSES IESO                     //Internal External Switch Over mode enabled
#FUSES FCMEN                    //Fail-safe clock monitor enabled
#FUSES NOPBADEN                 //PORTB pins are configured as digital I/O on RESET
#FUSES BBSIZ2K                  //2K words Boot Block size
#FUSES NOWRTC                   //configuration not registers write protected
#FUSES NOWRTB                   //Boot block not write protected
#FUSES NOEBTR                   //Memory not protected from table reads
#FUSES NOEBTRB                  //Boot block not protected from table reads
#FUSES NOCPB                    //No Boot Block code protection
#FUSES LPT1OSC                  //Timer1 configured for low-power operation
#FUSES NOMCLR                   //Master Clear pin used for I/O
#FUSES NOXINST                  //Extended set extension and Indexed Addressing mode disabled (Legacy mode)

#use delay(clock=CLOCK_FREQ,restart_wdt)
#use i2c(slave,slow,sda=I2C_SDA,scl=I2C_SCL,force_hw,address=0x90)
The fragment of the master code doing the sending is:
Code:
void test_i2c(int8 addr, int8 dat)
{
  output_high(PIN_C0);
  i2c_start();
  i2c_write(addr);
  i2c_write(dat);
  i2c_stop();
  output_low(PIN_C0);
}
I've used that exact bit of sending code before, just with more data. Pin C0 has an LED - it doesn't stay on, which I presume indicates no problems sending.

Now, the problem. The main() function in Test.c calls the init function, then drops into a loop keeping the watchdog timer from timing out, and toggling LED3. As soon as I call the test_i2c() function on the master, on the slave LED2 comes on and stays on (it should be turned off when the SSP ISR exits) and LED3 stops toggling. The watchdog timer times out in a few seconds and resets the device.

If I setup the master to call test_i2c() about five times a second, and watch LED2 on the slave with a cro, for each message the following occurs:

* LED2 turned on
* LED2 turned off
* LED2 turned on
* LED2 turned off
* LED2 turned on

It looks like the third call to the ISR never exits!

Other info: The master is an 18F2455. The slave is an 18F2480. I've tried a total of three 18F2480s and one 18F2580 with the same results. On a previous project I've used almost identical code (16F88 master, 18F4580 slave) with no trouble. I'm using PCWH 3.234.

Any ideas what's going on? Things to try?

Thanks.
_________________
Andrew
andrewg



Joined: 17 Aug 2005
Posts: 316
Location: Perth, Western Australia

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

PostPosted: Wed Mar 08, 2006 8:59 am     Reply with quote

No ideas? Me either Sad

FWIW, I have got pullups (4k7) and looking at both the clock and data lines shows neither are being held down.

For my first post I had been using the software I2C master support. That results in good comms, but the slave playing up. If I switch the master to hardware, the comms becomes unreliable (sometimes need 1 to 9+ tries before the data is received by the slave), but the slave otherwise behaves correctly, i.e. the mainline reset_watchdog keeps happening.

In hardware mode it also fails to work at all using just 'slow', but if I try 'slow=90000' right down to 'slow=1' (!!! is there a minimum speed?) then I get the reliable comms and the slave doesn't reset itself.

I'm really scratching my head here! Confused
_________________
Andrew
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Mar 08, 2006 11:39 pm     Reply with quote

I see two or three things. They may not be the cause of the problem,
but they are anomalies.

1. You're using the Watchdog timer, but your #use i2c() statement
doesn't have the restart_wdt parameter.

2. In your #use i2c() statement, you have the "slow" parameter.
But in slave mode, the master supplies the clock. Slave mode
never sets the baud rate generator, so there's no need to specify
the speed. In fact, it's ignored by the compiler.

3. In your #int_ssp isr, you're not using the i2c_isr_state() result in
the same way that CCS does in EX_SLAVE.C. You could try
changing it to match the way CCS does it. The CCS slave
example mimics a 24LC01 eeprom. They suggest using the
routines in the 2401.C example file to talk to the slave example.
I think you should do it this way initially. Currently, you're not
sure what's wrong. It's better to start with an example that's likely
to work. If it does, then you know your hardware is OK and
you can proceed in a more confident manner.
andrewg



Joined: 17 Aug 2005
Posts: 316
Location: Perth, Western Australia

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

PostPosted: Thu Mar 09, 2006 11:29 pm     Reply with quote

Thanks for your reply.

1. I have very rarely had the I2C slave routines lock up - hence the watchdog timer to get things going again. I normally set the watchdog to timeout only after several seconds, which should be plenty of time for an I2C interrupt (indeed any interrupt) to be serviced. In any case, my new test apps (see below) don't have the watchdog enabled.

2. You're right. I missed that. Removing it didn't do anything, as expected.

3. Your point is taken and acted on in my new test apps, but I thought my I2C slave routine was even simpler for testing purposes.

Here are my new test apps. Master first:
Code:
// Test I2C Master

#include <18F2455.h>

#FUSES NOWDT,INTRC_IO,PUT,NOMCLR,NOBROWNOUT,NOLVP,NOCPD,NOWRT,NODEBUG,NOPROTECT,FCMEN,IESO

#define LED1 PIN_C0
#define LED2 PIN_C1
#define LED3 PIN_C2

#use delay(clock=8000000)

#define EEPROM_SDA PIN_B0
#define EEPROM_SCL PIN_B1
#include <2401.c> // for slave emulation
//#include <24512.c> // for real 24FC512

void main() {
  EEPROM_ADDRESS c;
  EEPROM_ADDRESS stop = 1;//EEPROM_SIZE;

  init_ext_eeprom();

  delay_ms(1000);

  output_high(LED1);
  for (c = 0; c < stop; c++) {
    write_ext_eeprom(c, (int8)((c&255)^255));
  }
  output_low(LED1);

  output_high(LED2);
  for (c = 0; c < stop; c++) {
    delay_ms(1);
    if (read_ext_eeprom(c) != (int8)((c&255)^255)) {
      output_low(LED2);
    }
    delay_ms(1);
  }

  output_high(LED3);

}
Slave:
Code:
// Test I2C Slave

#define PIN_INISR PIN_A3
#define PIN_RUNNING PIN_A4
#define PIN_SPARE PIN_A5
#include <18F2480.h>
#fuses H4,NOWDT,NOPROTECT,NOLVP,NOMCLR,NOBROWNOUT,PUT,NOCPD,NOSTVREN,NODEBUG,NOXINST,NOPBADEN
#use delay(clock=32000000)

#use i2c(slave,sda=PIN_C4,scl=PIN_C3,address=0xA0)

BYTE address, buffer[256];

#INT_SSP
void ssp_interupt ()
{
  BYTE incoming, state;

  output_high(PIN_INISR);

  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
      buffer[address] = incoming;
  }
  if (state == 0x80) //Master is requesting data
  {
    i2c_write(buffer[address]);
  }

  output_low(PIN_INISR);
}

void main ()
{
   enable_interrupts(INT_SSP);
   enable_interrupts(GLOBAL);

   for(;;) output_toggle(PIN_RUNNING);
}
As you can see, I've modified the slave a little to twiddle some LEDs so I can see what's going on. It's running off an 8MHz crystal PLL'd to 32MHz (confirmed using CRO). I have also had it running off the crystal with PLL disabled, and using the internal 8MHz oscillator block. Nothing changes between those oscillator settings. I thought I'd use the PLL to ensure it was running plenty fast enough to handle the incoming data.

I've tested the master code using a 24FC512 - which works great writing/reading from 1 byte all the way up to EEPROM_SIZE (which takes 5 minutes!).

When I flip the master to use the 2401 driver and swap the 24FC512 for my 18F2480, then the following happens:

1. On power-up the master has a 1 second delay before starting the test. That allows a little time for everything to power up and I can see the slave 'running' LED toggling on the CRO.

2. The master writes a byte - LED1 blinks briefly indicating the master does not get hung up in the write.

3. The master starts the read test and gets stuck in there. LED2 lights up and does not go out. LED3 never lights.

4. At some time after the start of step 2, the slave stops toggling the 'running' LED and the 'inisr' LED lights up and stays on.

I've tried adding 'force_hw' to the slave #use i2c instruction, but it has no effect on what I'm seeing.

The slave code is so simple, I'm struggling to figure out what could be going wrong?

*Any* hints or thoughts would be greatly appreciated!

Thanks.
_________________
Andrew
andrewg



Joined: 17 Aug 2005
Posts: 316
Location: Perth, Western Australia

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

PostPosted: Fri Mar 10, 2006 1:59 am     Reply with quote

One other point, I've checked Microchips errata which says it only applies to 18F2480s with device ID 01 1010 100 00001. My 18F2480 has a device ID 1AE1. None of the errata issues seem relevant anyway.
_________________
Andrew
andrewg



Joined: 17 Aug 2005
Posts: 316
Location: Perth, Western Australia

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

PostPosted: Fri Mar 10, 2006 3:29 am     Reply with quote

I've now noticed that when the PICs seize up, the clock and data lines are being held low. I put the slave onto its own board and set up a header plug so I could pull it out of circuit. Pulling the slave from the circuit releases the clock and data allowing the master to complete the test.

I then tried removing the LED code in the slave ISR, just in case modifying outputs in the ISR was interfering, but it had no effect.

So, something in the slave is holding the clock and data low?
_________________
Andrew
andrewg



Joined: 17 Aug 2005
Posts: 316
Location: Perth, Western Australia

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

PostPosted: Fri Mar 10, 2006 9:10 am     Reply with quote

Well, I've solved it! Sort of.

I tracked the problem down to the line in the slave ISR "incoming = i2c_read();". It was getting stuck in there. Checking the assembly listing revealed just three instructions; it was checking the SSPSTAT "buffer full" flag and waiting until it got set before processing the data in the buffer. The flags must have been clear, but surely it should have been set (otherwise the interrupt wouldn't have been generated)? I couldn't find any code that clears that flag.

The fix was to replace that line with "incoming = *0xFC9;", i.e. ignore the buffer full flag and just read the buffer anyway.

I've checked the code for my previous I2C project (16F88 master and 18F4580 slave) and the code looks the same! Makes me very nervous that what looks like identical code should work in one place and not another!
_________________
Andrew
mpfj



Joined: 09 Sep 2003
Posts: 95
Location: UK

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Fri Mar 10, 2006 9:42 am     Reply with quote

andrewg wrote:
I tracked the problem down to the line in the slave ISR "incoming = i2c_read();". It was getting stuck in there. Checking the assembly listing revealed just three instructions; it was checking the SSPSTAT "buffer full" flag and waiting until it got set before processing the data in the buffer.

You could use the i2c_poll() function which returns TRUE is there's a received byte waiting in the buffer.

e.g.
Code:
   // if we've received some data ...
   if (i2c_poll()) {
      // read rxed byte
      rx_char = i2c_read();
   }
andrewg



Joined: 17 Aug 2005
Posts: 316
Location: Perth, Western Australia

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

PostPosted: Fri Mar 10, 2006 10:47 am     Reply with quote

You're right! Thanks, that looks much better.

I think ex_slave.c could do with an overhaul...
_________________
Andrew
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