|
|
View previous topic :: View next topic |
Author |
Message |
monsun
Joined: 17 Jan 2012 Posts: 17
|
Problem with hanging I2C bus. |
Posted: Wed Apr 07, 2021 6:17 am |
|
|
Hello everyone,
I've two devices slave with PIC24FV16KM202 and master with PIC24FV32KA304
compiler 5.093
master is using crystal 14745600Hz
slave is using internal osc on 8MHz
Line is long about 10cm pulluped to 5V with 3.3kOhm
Every thing works ok master make thousand of readings from slave but on some circumstances slave I2C hangs. Master discover problem with communication with slave and tries to reset_cpu() himself.
Slave device have WDT but it doesn't initiate.
I've uart port for service communication between PC and Slave, and PC software communicate with Slave during this I2C hangs state without any problems. So firmware is working somehow ok.
Could any one analyze my INT_SSP on slave and master function, on any mistakes i don't see?
Do you guys have any ideas on how to debug this?
Slave:
Code: |
#use i2c(SLAVE, FORCE_HW, I2C1, RESTART_WDT, address=0xCC, stream=I2C_PORT)
volatile UNSIGNED int8 incoming;
volatile UNSIGNED int8 state;
volatile UNSIGNED int8 local_register_address=0;
volatile static UNSIGNED int8 i2c_buffer[17];
#INT_SSP
void i2c_isr(){
state=i2c_isr_state(I2C_PORT);
IF(state<=0x80)
{//Master is sending data
IF(state==0x80)
incoming=i2c_read(I2C_PORT,2);
ELSE
incoming=i2c_read(I2C_PORT,1);
IF(state==1){//First received byte is local register address
local_register_address=incoming;
}
ELSE IF(state>=2 && state!=0x80)
{
i2c_buffer[local_register_address]=incoming;
local_register_address++;
}
}
IF(state>=0x80){//Master is requesting data
i2c_write(I2C_PORT,i2c_buffer[local_register_address]);
local_register_address++;
}
}
|
Master:
Code: |
#use i2c(MASTER, I2C1, FAST=400000, FORCE_HW, stream=I2C_SENSOR)
unsigned int8 dech_GetResult(unsigned int8 sensor_address, float *result){
union bytefloat{
unsigned int8 buffer[4];
float float_num;
}float_union;
sensor_address<<=1;
i2c_start();
i2c_write(sensor_address); // transmit to slave device
i2c_write((unsigned int8)9); // sends address to read result
i2c_stop(); // stop transmitting
i2c_start();
i2c_write(sensor_address+1); // transmit to slave device +1 to read
#IFDEF __pcd__
float_union.buffer[0] = i2c_read(TRUE);
float_union.buffer[1] = i2c_read(TRUE);
float_union.buffer[2] = i2c_read(TRUE);
float_union.buffer[3] = i2c_read(FALSE);
#ENDIF
#IFDEF __pch__
float_union.buffer[3] = i2c_read(TRUE);
float_union.buffer[2] = i2c_read(TRUE);
float_union.buffer[1] = i2c_read(TRUE);
float_union.buffer[0] = i2c_read(FALSE);
convert_ieee_to_microchip(&float_union.buffer[0]);
#ENDIF
*result=float_union.float_num;
return 1;
}
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19506
|
|
Posted: Wed Apr 07, 2021 6:33 am |
|
|
Normally in I2C, there are two different type of 'hang'. The first is where the
slave has missed something, the second is where the slave is holding
the clock. On the former, the correct requirement for the master is to send
extra clocks (up to 9). Try this before resetting the master.
If the slave is holding the clock, it is it that has to reset (the master can't
do anything).
The big problem with what you describe is clock speed. Generally you need
the slave to be able to handle it's state changes before the master wants
the next thing to happen. Having a slave that runs a lot slower than the
master, will often give problems....
This is what the watchdog should do. It sounds as if you are incorrectly
resetting the watchdog. The key is that the code that resets this should
only get called if the chip is doing everything it should (including presumably
receiving new data on the I2C). You need to remove 'RESTART_WDT' from
your I2C code, and instead restart it yourself if things are working.
The 'RESTART_WDT' abilities in CCS, are _awful_ code. The key is that
for a watchdog to actually do anything useful, you need to ensure that
the watchdog can only get restarted, if everything in the code is working
correctly. Having 'internal' restarts scattered around in delays, and in the
I2C handler code, basically makes the watchdog 'useless'. |
|
|
monsun
Joined: 17 Jan 2012 Posts: 17
|
|
Posted: Wed Apr 07, 2021 6:55 am |
|
|
Is there any way to distinguish those two states?
"The first is where the slave has missed something, the second is where the slave is holding the clock."
Slave is working as sensor front end so it is basically continuously making measurements and i2c inquiries from master are asynchronous, and i don't have idea how to measure if slave i2c is stuck or not. I'm reseting watch dog in few important places outside INT_SSP
Could you say something more about this:
"On the former, the correct requirement for the master is to send
extra clocks (up to 9). "
How to put it into practice?
I have one place in slave program where i'm disabling interrupts, maybe sometimes i timing it with i2c inquiries and this is the place where it stucks?
I've edited this code because it was from function which isn't used.
Now code is ok and it's only one place during "working" state when int_ssp is disabled
Code: |
disable_interrupts(INT_SSP);
memcpy(&i2c_buffer[9],wynik_unia.lval,4);
i2c_buffer[13]=flaga_result_ready;
enable_interrupts(INT_SSP);
|
Last edited by monsun on Wed Apr 07, 2021 7:20 am; edited 1 time in total |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19506
|
|
Posted: Wed Apr 07, 2021 6:56 am |
|
|
Simply read the SCL line in the master. If it is low the slave is holding the
clock. |
|
|
|
|
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
|