View previous topic :: View next topic |
Author |
Message |
benoitstjean
Joined: 30 Oct 2007 Posts: 553 Location: Ottawa, Ontario, Canada
|
PIC24EP512 - Exiting from i2c lock-up on faulty sensor |
Posted: Tue Oct 10, 2023 7:05 am |
|
|
Device: PIC24EP512GP806
Compiler: 5.026
Hi guys,
I know this topic may have already somewhat addressed in the past, including an older post from myself, but I've gone through what I could find and I think I still need help because what I thought was working ended-up not completely working now that I got this new batch of circuits made.
Situation: I have a FW version that I use throughout a few different circuits, some with I2C sensors, some without. When the device boots-up, if it does not find the I2C device due to it either not being present on this version or being faulty on the version with the sensor, then it should just mark-it as 'fail' and continue.
What I have so far is that as the PIC boots, when it reaches the code for the initialization of the I2C devices, it starts a 1 second timer then attempts to initialize the I2C sensor. In an ideal world, if the sensor initialization is successful, then it is marked as PASS then the code continues. But if it is not present or faulty, the timer will eventually expire and i2c_init( SENSORS, FALSE ); is called.... but this doesn't appear to do anything.
What happens during initialization is that the code first attempts to read the I2C device ID by calling an I2C read at the proper device address / memory location on the device. The problem is that it hangs somewhere in the read function:
Code: |
unsigned int8 Read_I2C( unsigned int8 DeviceAddress, unsigned int8 MemoryAddress )
{
unsigned int8 Data;
i2c_start();
i2c_write( DeviceAddress );
i2c_write( MemoryAddress );
i2c_start();
i2c_write( DeviceAddress | 0x01 );
Data = i2c_read( 0 );
output_float( PIN_G2 );
output_float( PIN_G3 );
return( Data );
}
|
This code has been working for years for all my boards since they all have sensors and they never failed. But then this new batch of PCBs doesn't have the sensors but I want it to use the same code as the rest of the circuit is the same.
So my question is I need to know if there's a way / how to "release" CCS's I2C code so that it exits that Read_I2C() function. I haven't identified at which line it hangs specifically since I know it's that function due to fprintfs to TeraTerm before and after the function whereas the one after never prints therefore it hangs in that function...
When the 1 second timer expires, that's where I should have that I2C exit condition to make sure that this Read_I2C unblocks. So isn't there anything I can send like toggling one of the lines a number of times or something to release the internal I2C code?
Thanks!
Ben
[EDIT] In the Read_I2C(), at the first I2c_write( DeviceAddress ), I added a check for what is returned to see if it's an ACK or NACK since I figured that perhaps a NACK would be returned if there's nothing but the function appears to return 0 which is an ACK....
Last edited by benoitstjean on Tue Oct 10, 2023 7:28 am; edited 1 time in total |
|
![](templates/subSilver/images/spacer.gif) |
gaugeguy
Joined: 05 Apr 2011 Posts: 296
|
|
Posted: Tue Oct 10, 2023 7:27 am |
|
|
Why aren't you checking for an acknowledge from the sensor and stopping if that is not returned?
The i2c_write function returns the state of the ACK bit but you aren't checking it. |
|
![](templates/subSilver/images/spacer.gif) |
benoitstjean
Joined: 30 Oct 2007 Posts: 553 Location: Ottawa, Ontario, Canada
|
|
Posted: Tue Oct 10, 2023 7:29 am |
|
|
I guess we wrote at the same time. I modified the function to check what i2c_write( DEVICE_ADDRESS ) returns and it returns a 0 when there's no sensor.... and 0 means ACK.... the circuit does not have anything on I2C therefore I havn't put any pull-up resistors on there. The pins are just unconnected. |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19358
|
|
Posted: Tue Oct 10, 2023 7:34 am |
|
|
Multiple separate things.
First, you can use the no_init option in the i2c setup. Read the SCL & SDA
pins. If these are not high the i2c device is hung. If the SDA line is high
You can repeatedly clock the SCL line yourself and hopefully the SDA line
will release. This is the specified i2c way of unlocking a hung bus.
So:
Code: |
if (input(SDA)==0) //If SDA is not high
{
//Now need to try clocking SCL to release bus
for (int temp8=0;temp8<9;temp8++)
{
output_low(SCL);
delay_us(5);
output_float(SCL);
delay_us(5);
} //9*100KHz clocks
}
i2c_init(I2CBUS,1); //physically initialise bus
|
Then look at how PCM_programmer tests for the device acknowledging
in the I2C scanner program in the code library. The device should always
return a zero as an acknowledgement when you send it it's address byte
if it doesn't the device is either not there or not working. This is your
first write after the start. Test this and if you don't get this acknowledge
then you know not to proceed....
Do this test before trying to i2c_read. |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19358
|
|
Posted: Tue Oct 10, 2023 7:39 am |
|
|
Everybody pointed out the same thing. That the test to do is for the
acknowledge when you send the address, _before_ ever trying to read.
I was trying to handle the other possibility of a hung sensor as well.
Best Wishes |
|
![](templates/subSilver/images/spacer.gif) |
benoitstjean
Joined: 30 Oct 2007 Posts: 553 Location: Ottawa, Ontario, Canada
|
|
Posted: Tue Oct 10, 2023 7:42 am |
|
|
I guess I should be more specific as this may change the way to approach this: on the board with the sensors, I have two I2C devices. On the board without, then there's none obviously.
Therefore on the board with sensors, one can fail while the other works. So reading the SCL and SDA lines may not be the best solution since one device may work while the other does not.
[EDIT] And I do have the NOINIT flag set in #use i2c. |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19358
|
|
Posted: Tue Oct 10, 2023 8:06 am |
|
|
No, you should do the read on SDA first. The point is that I2C devices
can hang, where they have missed a clock, or received a clock during
boot. In that state, trying to send a read or a write will still result in
the bus staying hung. Manually clocking if the bus is high is the way to
release this.
Then before the reads, if you test the acknowledge whenever you send
the device address, you can tell whether a device is hung. |
|
![](templates/subSilver/images/spacer.gif) |
benoitstjean
Joined: 30 Oct 2007 Posts: 553 Location: Ottawa, Ontario, Canada
|
|
Posted: Tue Oct 10, 2023 8:19 am |
|
|
Alrighty.... so at the moment at boot time, I do input( PIN_G3 ) - which is my SDA pin on the version with sensors - and it appears to return 1... but that pin is not connected since there's no sensor!
I guess I should tie both SCL and SDA to low with a resistor on the version without sensors. This will ensure that both pins are forced low.
It appears that at the moment, since they are untied / floating, I guess they are detecting a high state? That's what it seems to be anyhow since reading the pin returns 1 and doing an i2c_write( DeviceAddress ) returns ACK (0)....
[EDIT] So definitely, I should tie SDA and SCL to GND with resistors because at the moment, they are floating and reading them returns 1. |
|
![](templates/subSilver/images/spacer.gif) |
gaugeguy
Joined: 05 Apr 2011 Posts: 296
|
|
Posted: Tue Oct 10, 2023 8:29 am |
|
|
Trying to test a floating pin can be done, but it is very tricky. Best to make sure the pin has a known state for both pass and fail. |
|
![](templates/subSilver/images/spacer.gif) |
benoitstjean
Joined: 30 Oct 2007 Posts: 553 Location: Ottawa, Ontario, Canada
|
|
Posted: Tue Oct 10, 2023 8:36 am |
|
|
What's your 'trick' to test the floating pin? Is it to force it let's say to low as an output then make it an input and read it? What delays do you suggest in-between? For now this would save me lots of headaches (I can't add the resistors to the circuit unless I dismantle everything and I really don't want to do this). |
|
![](templates/subSilver/images/spacer.gif) |
gaugeguy
Joined: 05 Apr 2011 Posts: 296
|
|
Posted: Tue Oct 10, 2023 8:38 am |
|
|
You can test for a floating pin or pullup present by:
driving the pin low
wait a short time
let the pin float
wait a time dependent on your hardware
test the pin
If the timing is done correctly you should reliably get a high for a pullup present or a low for floating. |
|
![](templates/subSilver/images/spacer.gif) |
gaugeguy
Joined: 05 Apr 2011 Posts: 296
|
|
Posted: Tue Oct 10, 2023 8:44 am |
|
|
Further details:
The high test is dependent on the time constant of the pullup and circuit capacitance.
The low test (floating) is dependent on the pin and circuit capacitance holding the low level.
The time the pin is held low is to make sure any pin/stray capacitance is discharged.
You will need to use a scope to watch the pin response in your circuit with both a pullup present and floating to determine the timing. |
|
![](templates/subSilver/images/spacer.gif) |
benoitstjean
Joined: 30 Oct 2007 Posts: 553 Location: Ottawa, Ontario, Canada
|
|
Posted: Tue Oct 10, 2023 8:50 am |
|
|
Oh! I tried it with 10ms after pulling low then 10ms after setting floating and now when I read both pins, they are low.... so this appears to work with the circuit without sensors. I will test a bit later with the circuit that does have sensors to see how that reacts... I expect the pins to be high since they both have a pull-up resistors on the bus.
Now it's a matter of figuring a way to make the sensors appear as failing to see what code will bypass them in the eventuality of a failure.
More to come.
Thanks!
Ben |
|
![](templates/subSilver/images/spacer.gif) |
gaugeguy
Joined: 05 Apr 2011 Posts: 296
|
|
Posted: Tue Oct 10, 2023 9:21 am |
|
|
It would probably work with 10us and 10us but that depends on your hardware. If you need to handle a possible disconnect during operation you may need to repeat this test before each read/write begins. |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19358
|
|
Posted: Tue Oct 10, 2023 9:49 am |
|
|
I'd say no.
The I2C bus should always be resistively pulled up. Then the input pin will
read high _reliably_
Clocking the version without sensors won't matter.
Then when you do the i2c_write of the address, you won't get an ack,
so you know the sensor is not there.
Simple and reliable.
Pulling high can be done simply by turning on the internal pull up resistors
in the chip. Most PIC24's have these individually settable on the pins.
The current drawn by these is tiny, so will have no effect on the versions
where the sensors are present.
I don't like having pins shorted to ground. Doing so could result in a massive
overload if something drives the line high...
You are getting the ACK, because the line is not pulled up. |
|
![](templates/subSilver/images/spacer.gif) |
|