View previous topic :: View next topic |
Author |
Message |
TYDOM17
Joined: 24 Apr 2017 Posts: 17
|
I2C Problems after Porting To PIC18F25K22 |
Posted: Mon Apr 24, 2017 4:59 pm |
|
|
Hello CCS Forum,
I recently ported working code from a Rev A to Rev B brd after changing microcontrollers 18F25K20 to 18f25k22. The change in targets was for the express purpose of gaining an extra I2C pair on MSSP.
Problem Statement: the I2C bus sniffer tool no longer finds the Si7021 temp/humidity sensor that it used to find on the 18f25K20 target - it does however find all other I2C components. To be really short the part has an address of 0x80 with W and 0x81 with R and simply will not ACK when written to. I have the following addresses on the bus that work just fine - 0x30, 0x32, 0xA0, 0xA6 - the last 2 will be familiar to some.
Troubleshooting: I have also verified that the pull-up values (2k ea) are creating a < 200ns rise time on SDA and SCL (the waveforms also appear clean to me with 10x probes). I have verified total capacitance between SDA/ground and SCL/ground to be < 100pF with board unpowered. I have even swapped the I2C device out with a known working part from another board with the same results - no ACK.
What could I be missing here?
Code: | #include <18F25K22.h>
//#include <18F25K20.h>
#FUSES INTRC_IO, MCLR
#use delay(clock=1Mhz, internal)
#use rs232(baud=9600,INVERT,parity=N,xmit=PIN_C7,rcv=PIN_C6,bits=8,ERRORS)
#use i2c(Master,slow,sda=PIN_C4,scl=PIN_C3) |
Code: | for(i=0x10; i < 0xF0; i+=2)
{
status = get_ack_status(i);
if(status == TRUE)
{
printf("addr: %X\n\r", i);
count++;
delay_ms(2000);
}
}
status = get_ack_status(0x80);
if(status == TRUE)
{
printf(" si7021 ACK:\n\r");
count++;
delay_ms(1000);
} |
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Apr 24, 2017 6:00 pm |
|
|
What happens if you take your old PIC, the 18F25K20, and insert it into
the new board ? Test it with your current program, except change the
#include to 18F25K20.h. Does it then work ? |
|
|
TYDOM17
Joined: 24 Apr 2017 Posts: 17
|
|
Posted: Mon Apr 24, 2017 6:09 pm |
|
|
I can try that - it's challenging as the part is a DFN package but it can be done - that will rule out the new layout and or part as the problem sure. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Apr 24, 2017 7:11 pm |
|
|
1. Before you do that, check if you accidentally swapped the SDA and SCL
lines going to the Si7021 chip on your new board layout.
2. If possible, check for continuity between the PIC and pins on the Si7021 chip.
3. Did you accidentally connect the two DNC pins on the Si7021 to ground ?
The datasheet says not do to that.
4. Use an external off-board PIC and connect it to SDA and SCL on
your new board (and also connect ground). See if it can talk to the
Si7021. Program the PIC on your main project board to float pins C3
and C4 so it will be out of the circuit. Then have it sit in a while() loop
doing nothing. The off-board PIC will be in control.
5. The datasheet mentions a power-up delay of up to 80ms before the
Si7021 can do a conversion. This may also apply to the i2c interface.
Add a 100ms delay before you attempt to talk to the Si7021.
6. Post your compiler version.
7. Post your Vdd voltage for the PIC and the Si7021. |
|
|
TYDOM17
Joined: 24 Apr 2017 Posts: 17
|
|
Posted: Tue Apr 25, 2017 10:07 am |
|
|
Quote: |
1. Before you do that, check if you accidentally swapped the SDA and SCL
lines going to the Si7021 chip on your new board layout.
2. If possible, check for continuity between the PIC and pins on the Si7021 chip.
3. Did you accidentally connect the two DNC pins on the Si7021 to ground ?
The datasheet says not do to that.
4. Use an external off-board PIC and connect it to SDA and SCL on
your new board (and also connect ground). See if it can talk to the
Si7021. Program the PIC on your main project board to float pins C3
and C4 so it will be out of the circuit. Then have it sit in a while() loop
doing nothing. The off-board PIC will be in control.
5. The datasheet mentions a power-up delay of up to 80ms before the
Si7021 can do a conversion. This may also apply to the i2c interface.
Add a 100ms delay before you attempt to talk to the Si7021.
6. Post your compiler version.
7. Post your Vdd voltage for the PIC and the Si7021.
|
Item 1: SDA/SCL set up correctly
Item 2: continuity is good
Item 3: no, left floating
Item 4: ***changed uC to PIC18F25K20, I can now find, get ACK, and request data from the part
Item 5: I have a 1000ms delay after powering up Vcc on the part before attempting to talk to it in firmware for both targets
Item 6: PCH 5.049
Item 7: Vdd 3.3
Could there be some hardware differences than in between the 2 targets MSSI or possibly they way the SDA and SCL handle tSTH or tSPS? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Apr 25, 2017 10:56 am |
|
|
Your version of the compiler defaults to software i2c for both the
18F25K20 and the 18F25K22. You could force it to use i2c hardware
mode for the K22 and see if it makes a difference. Add FORCE_HW
to the end of the #use i2c() statement as shown below:
Code: | #use i2c(Master,slow,sda=PIN_C4,scl=PIN_C3, FORCE_HW) |
Also, I never use 1 MHz for anything. I'd feel more comfortable if
you tested at 4 MHz or higher. |
|
|
TYDOM17
Joined: 24 Apr 2017 Posts: 17
|
|
Posted: Tue Apr 25, 2017 11:31 am |
|
|
PCM programmer,
Tried the FORCE_HW and no change in behavior while running the I2C scanner test code.
However after some messy code drag and drop in my main application loop I am able to talk to the part up until an EXT0 interrupt fires (this happens later in my application) - I will play with this some more and get back to you on my findings. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19553
|
|
Posted: Tue Apr 25, 2017 12:05 pm |
|
|
The obvious thing you are going to have to look at is what happens in your INT EXT0 code?.
That it talks to this, suggests something here is actually the cause. Big difference with the K22, is there are a lot more peripherals that may need to be turned off. CCP4 on the INT0 pin, and the SR latch input. The fault input to the PWM is also here, though that is common to both chips, the default behaviour may be different. Is this peripheral disabled?. |
|
|
TYDOM17
Joined: 24 Apr 2017 Posts: 17
|
|
Posted: Tue Apr 25, 2017 12:55 pm |
|
|
Ttelmah,
I am looking at pages 142,143 of the data sheet now - your are right that there are quite a few more peripherals than the 20k part.
I can set the appropriate SLRCON and CCP3COM registers to turn off the extra peripherals but I guess I just don't see how these could be affecting 1 of 4 devices on the I2C line.
Here is a snippet of my code:
Code: |
void main() {
int8 intro=0;
output_low(PIN_B3);//NC
output_low(PIN_B5);//NC
output_high(I2C_EN);
output_high(E2PWR);
output_high(HUMTEMP);
output_high(LED);
delay_ms(1000);
output_low(LED);
////////////////// INITILIZE ALL REGISTERS ON I2C BUS /////////////////////////
init_ext_eeprom();
rtc_init();
H3_init();
setup_shock_H3_1();
setup_shock_H3_2();
delay_ms(100);
printf("\n\r**********************************************");
intro=1;
for(x=0;x<5;x++){
measure_temp();
measure_RH();
read_user_reg1();
printf(" USER REG1 %U ",usereg1);
printf(" temp %U %U ",dataMS, dataLS);
printf(" RH %U %U \n\r",dataMSB, dataLSB);
delay_ms(100);
}
event_cnt = read_ext_eeprom(1);
page_num = read_ext_eeprom(2);
//////////////////// INITIALIZE EXT INT0 INTERRUPT //////////////////////////
ENABLE_INTERRUPTS(INT_EXT);//INT0
ENABLE_INTERRUPTS(GLOBAL);
///////////////////////////////////////////////////////////////////////////////
printf(" post interrupts enabled \n\r");
for(x=0;x<5;x++){
measure_temp();
measure_RH();
read_user_reg1();
printf(" USER REG1 %U ",usereg1);
printf(" temp %U %U ",dataMS, dataLS);
printf(" RH %U %U \n\r",dataMSB, dataLSB);
delay_ms(100);
}
while(TRUE) {
//////////////////////// POWER DOWN THE PERIPHERALS /////////////////////////
output_float(PIN_C0); //CLK
output_float(PIN_C1); //CLK
output_float(PIN_C2); //CLK
output_low(I2C_EN); //PWR DOWN
output_low(E2PWR); //PWR DOWN
output_low(HUMTEMP);//PWR DOWN
output_float(EEPROM_SCL);
output_float(EEPROM_SDA);
output_low(LED); //PWR DOWN
delay_ms(50); //PWR DOWN
///////////////////////////////// SLEEP //////////////////////////////////////
sleep();
///////////////////// IMMEDIATELY AFTER WAKING UP DO THIS ///////////////////
if(flag_set==1){
output_high(LED);
output_low(PIN_B5);//NC
output_high(I2C_EN); //PWR DOWN
output_high(E2PWR); //PWR DOWN
output_high(HUMTEMP);//PWR DOWN
delay_ms(100);
init_ext_eeprom();
printf("you woke up ");
delay_ms(100);
measure_temp();
measure_RH();
printf(" temp %U %U ",dataMS, dataLS);
printf(" RH %U %U \n\r",dataMSB, dataLSB);
update_eeprom();
}
///////////////////////// RE-ENTER LOW POWER MODE ///////////////////////////
//setup_shock_H3_1();
//setup_shock_H3_2();
///////////////////////// RE-ENABLE THE INTERRUPT /////////////////////////////
ENABLE_INTERRUPTS(INT_EXT);//INT0
ENABLE_INTERRUPTS(GLOBAL);
///////////////////////////////////////////////////////////////////////////////
}
}
|
The for loop with 5x read temp and humidity (from Si7021) does work prior to the interrupt but not after.
And here is a snippet from my interrupt
Code: |
#INT_EXT //INTO EXTERNAL INTERRUPT
void EXT_isr(void){
// POLL PINS TO DETERMINE WHICH INTERRUPT OCCURRED
// SET APPROPRIATE FLAGS
output_high(LED);
output_low(PIN_B5);//NC
output_high(I2C_EN); //PWR DOWN
output_high(E2PWR); //PWR DOWN
output_high(HUMTEMP);//PWR DOWN
delay_ms(100);
init_ext_eeprom();
rtc_init();
H3_init();
setup_shock_H3_1();
delay_ms(100);
if(input(ACC1_INT1)){
printf("\n\r***********************************************************************\n\r");
printf("\n\rACC1 SHOCK EVENT INT1\n\r");
//////////////////////////////// CLEAR INTERRUPT //////////////////////////////
///////////////////////// UPDATE STATUS BITS //////////////////////////////////
status=0b00000001;
flag_set=1;
///////////////////////////////////////////////////////////////////////////////
}
}
|
You can see, I set the some pins for I2C enable to the pull up resistors as well as Vdd on the Si7021 part to apply power prior to sending I2C commands/requests. |
|
|
TYDOM17
Joined: 24 Apr 2017 Posts: 17
|
|
Posted: Tue Apr 25, 2017 4:34 pm |
|
|
I have disabled SR, CCP4 as follows:
Code: | #byte SRCON0 = 0xF47 //SR LATCH
#byte SRCON1 = 0xF46 //SR LATCH
#byte CCP4CON = 0xF57 //SR LATCH
SRCON0 = 0x00; //SR LATCH DISSABLED
SRCON1 = 0x00; //SR LATCH DISSABLED
CCP4CON = 0x00; //CCP4CON DISSABLED |
According to pin priority list on pages 138 and 139 of datasheet - CCP4 has the highest priority for the 18f25k22 as you said.
After disabling SR & CCP4CON and testing, there was no change in behavior. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19553
|
|
Posted: Wed Apr 26, 2017 12:41 am |
|
|
Honestly re-think your INT_EXT. It is doing far too much. Delays, init_ext_eeprom, rtc_init. Aargh....
The general rule is that interrupts should always exit ASAP.
If this is triggering a reset on some peripherals, then set a flag and have this done in your main code. This is doing I2C I/O. You can't have two separate 'timelines' in your code both trying to talk to the same bus. You will be getting a huge plethora of 'interrupts disabled to prevent re-entrancy' warnings from this. Now, while sometimes this may be acceptable, if you know _why_ and when this is going to happen, the sheer number this will give should be telling you that this is not the right way to be doing things....
There are CCS functions to disable the peripherals. Start using the compiler. Generally don't talk to registers directly, unless there is a problem with the compiler functions.
I'd guess that it is just luck. Possibly the timing of the I/O, just results in this being called 'part way' through the peripheral code that is failing. Is the other chip running at the same speed?. |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1358
|
|
Posted: Wed Apr 26, 2017 7:35 am |
|
|
Just to add to what Ttelmah said:
The problem with using your I2C code in the both the main and isrs is that it makes all kinds of things go wrong in nefarious ways:
1. If you have some functions that are never used in the ISR, they don't have the interrupt disable barriers around them. What this means is you can be in a function in the main that uses the sensor (which doesn't have interrupt disabled protection) running being interrupted by an ISR that also tries to use the sensor. If you were in the middle of an I2C transaction when this occurs, it can wreak havoc on your I2C bus.
2. If you have some functions that use the sensor that call other functions that use the sensor, you can have the interrupt disable protections accidentally turned off early.
Say you have
Code: |
void init_sensor(){
//CCS disables interrupts
/* your I2C code */
//CCS enables interrupts
}
void sensor_do_something(){
//CCS disables interrupts
init_sensor(); <== interrupts are re-enabled here!!!!!
/* your I2C code */
//CCS enables interrupts
}
|
If both those functions happen to be used in the ISR, then they disable the interrupt protections in the main, which leads to the same result that I mentioned in #1
The nefarious problem here is that issues like this can go largely unnoticed except when a very specific scenario happens. We had #2 happen in one of our products when an engineer decided to do exactly what we are talking about (using calls in both the main and ISR code). The issue only cropped up on specific board revs (same exact code on both revs as changes were not PIC related) and it only happened after long hours of operation. It took me a long time to figure it out and fix the problem.
You are switching micros, which can change a lot of timing related things in your code. I would not be surprised if this was part of your issue.
You really should avoid using any of your I2C code in the ISR. Do as Ttelmah suggests. |
|
|
TYDOM17
Joined: 24 Apr 2017 Posts: 17
|
|
Posted: Wed Apr 26, 2017 8:55 am |
|
|
PCM programmer/Ttelmah/jeremiah,
Thank you for the great input, you all clearly have some great experience to draw from. I will be making some changes to the interrupt routine including the removal of all I2C calls from the ISR.
I'll let you know how this goes. |
|
|
TYDOM17
Joined: 24 Apr 2017 Posts: 17
|
|
Posted: Tue May 02, 2017 2:46 pm |
|
|
update:
After cutting almost all I2C code from the INT_EXT (in my case I must have I2C enabled and read registers from the accel to clear a latch 1st) the program is behaving very well.
It did take some time to wrap my head around the multiple timelines, so I added a DISABLE_INTERRUPTS(INT_EXT) after waking from sleep to prove to myself how often the interrupt was being triggered during function calls i.e. while dumping memory from eeprom in my main loop.
It's been quit a learning experience for me, thanks for the help everyone!
Also, having played with the INT_RB (for the same project, this time for levels changes on programmable temp switch) I am amazed at how well the high to low, and low to high level transitions are captured after my part goes to sleep.
The key really is to keep those interrupts short and then clear the interrupts with the appropriate method before moving on. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19553
|
|
Posted: Tue May 02, 2017 3:03 pm |
|
|
Good.
It's one of those 'once you learn/understand' things, that will stand you in very good stead in the future. i sometimes spend quite a lot of work designing tighter/faster/smaller interrupt code, and once you do, it is amazing just how much the system really can do.... |
|
|
|