|
|
View previous topic :: View next topic |
Author |
Message |
soundscu
Joined: 29 Jun 2007 Posts: 62 Location: Raleigh, NC
|
dspic33 i2c slave resets on bad i2c address |
Posted: Fri Aug 17, 2012 9:25 am |
|
|
I'm using a dsPIC33FJ128MC804 as an i2c slave. To test it, I'm using a pickit serial analyzer. The application is simple, it emulates eeprom: write a 16-bit address, read as many bytes back as needed.
It all works perfectly when there is a single master (pickit analyzer) and single slave (my dspic33 device) and both are set to the same address. I understand the i2c protocol, and it's doing exactly what I expect when actually moving data around on the bus. (By the way, the pickit analyzer refers to addresses with 8-bits, so i2c addr 0x54 is treated as 0xA8 to write and 0xA9 to read.)
But if the master (pickit analyzer) sends messages on a different address, the slave (my dspic33 device) resets. It should ignore the incorrect address and simply do nothing.
The WDT is disabled. Setting a breakpoint at the start of the i2c interrupt never causes a break. When running with the ICD3 debugger, the code breaks at address zero, i.e. reset. When running standalone, the device restarts.
A couple of snippets of code:
in the .h file:
Code: |
//i2c - this device is an i2c slave, external device is master
#use i2c(force_hw,slave,i2c1,restart_wdt,stream=sI2C)
|
my interrupt routine:
Code: |
#int_si2c
void I2C_isr(void)
{
i2c_state = i2c_isr_state(); // is actually i2c packet byte count
if((i2c_state == 0 ) || (i2c_state == 0x80)) // first byte, with or without write bit set, is i2c address
i2c_read(sI2C); // read i2c address, always a match so no need to compare
if(i2c_state >= 0x80) // if write bit is set, send dmx channel data
{
i2c_write(sI2C,dmxInput[(i2c_dmxAddr + i2c_state - 0x80) & 511]); // dmx address + byte count, write bit cleared, limit range to 511
}
else if (i2c_state > 0) // if write bit is set, send dmx channel data
{
i2c_data = i2c_read(sI2C);
switch (i2c_state)
{
case 1: //first byte is MSB of dmx address
i2c_dmxAddrH = i2c_data & 1; //limit to 0 or 1, dmx channels only use 9 bits, this is the 9th
break;
case 2: //second byte is LSB of dmx address
i2c_dmxAddrL = i2c_data;
i2c_dmxAddr = ((unsigned int16)i2c_dmxAddrH * 256) + i2c_dmxAddrL; //construct the full dmx address
break;
}
}
}
|
(The variables are declared at the top of code to be global, for easier debugging.)
As i said, this all works perfectly. The problem is when the address used by the master is wrong. The slave should simply ignore it, but instead the device resets.
Am I doing something wrong? Or is this a CCS problem?
Thank you for your time!
Jim |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1358
|
|
Posted: Fri Aug 17, 2012 3:04 pm |
|
|
You are not setting your slave address based on the code you provided. Look at #use i2c() in the manual for details. |
|
|
soundscu
Joined: 29 Jun 2007 Posts: 62 Location: Raleigh, NC
|
|
Posted: Fri Aug 17, 2012 3:56 pm |
|
|
Sorry, the i2c address is being set near the beginning of Main. Here are some additional code snippets to eliminate ambiguity:
Code: |
unsigned int8 i2cAddr = 0xa8;
|
Code: |
I2C_SlaveAddr(sI2C,i2cAddr >> 1); //specified pickit address is 8-bit format (incl. write bit as bit0)
enable_interrupts(INT_si2c);
|
Jim |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1358
|
|
Posted: Sat Aug 18, 2012 9:05 am |
|
|
Next thing to check is the status and control registers for your I2C module. Print those out and see what options are turned on by default. I wonder if you have general call turned on for example and are getting bad array accesses.
Furthermore, you really need to read the forum guidelines and the newbies post and post the information they specify as you are not providing us the information we need to help you fully. It's hard to troubleshoot your problem on this end if you don't provide the needed info and code. |
|
|
soundscu
Joined: 29 Jun 2007 Posts: 62 Location: Raleigh, NC
|
|
Posted: Sat Aug 18, 2012 9:24 am |
|
|
Thanks, jeremiah.
I fully agree with you; in another instance I would be the guy saying the same thing in a reply.
I've already gone down the road you suggest, checking and watching the hardware i2c registers. There's nothing wrong that I can see.
Now I'm tracing through the sequence of asm instructions produced by the compiler, looking for anything that doesn't agree with Microchip's recommended sequence of events for i2c setup.
[whine]
Soon I will give up on this lunacy and write my own i2c routines, just so I know exactly what they are doing -- much as I would do with C30. This has happened to me too many times now: I start a simple little project in CCS C because it should be quick and easy using the built-in routines, then I end up wasting a metric tonne of time trying to debug those built-in routines because they don't work right.
[/whine]
When I started this thread I was hoping someone would point me to an errata note I'd missed, or something else known and documented that causes the symptoms described.
Since that's not the case, I'll continue with my own troubleshooting. When I find the answer, I'll come back here and post an update to help the next guy.
Thanks again,
Jim |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1358
|
|
Posted: Sat Aug 18, 2012 11:39 am |
|
|
Try a simple program in your project (add fuses and change #use delay() to fit your setup ):
Code: |
#case
#include <33FJ128MC804.h>
//put your fuses here
#use delay(clock=22118400) //change to fit your setup
#use i2c(force_hw,slave,I2C1,restart_wdt,address=0xA8,stream=I2C_STREAM)
//set these pins to something available
#pin_select U1TX=PIN_B3
#pin_select U1RX=PIN_B4
#use rs232(UART1,errors,bits=8,stop=1,parity=N,baud=9600)
#INT_IC1
void i2c_isr(){
unsigned int8 state;
unsigned int8 value;
state = i2c_isr_state(I2C_STREAM);
if(state <= 0x80){
value = i2c_read(I2C_STREAM);
}
if(state >= 0x80){
i2c_write(I2C_STREAM,0x12);
}
}
void main(void){
printf("\r\nStart\r\n");
enable_interrupts(INT_IC1);
enable_interrupts(INTR_GLOBAL);
while(TRUE){
restart_wdt();
}
}
|
Run that and try talking to it with both the a8 address and a wrong address. See if it resets. If it doesn't, then put in your version of the ISR and see if it resets. If it doesn't, add in some other parts of your code. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19562
|
|
Posted: Sat Aug 18, 2012 2:44 pm |
|
|
Er. The obvious thing is the address.
Comments inline.
Code: |
unsigned int8 i2cAddr = 0xa8; //This is already an 8bit address
I2C_SlaveAddr(sI2C,i2cAddr >> 1);
//specified pickit address is 8-bit format (incl. write bit as bit0)
//Get rid of the rotation. The I2C_SlaveAddr function, takes an
//8bit 'PIC format' address
enable_interrupts(INT_si2c);
|
The PIC does not use 7bit address format.
Best Wishes |
|
|
|
|
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
|