I2C woes

Grumble. I2C is supposed to be easy, right?

I ordered and received the Honeywell HMC6352 Compass module from Sparkfun. I'm using a PIC 14F452 and I've got proper line level converters for the SCL and SDA lines. What I don't have is a working compass. The compass' datasheet (to me) feels pretty ambiguous about a lot of details and I am unsure of where my problem might lie. I know there aren't many PIC users here ( at least I think...) but the code should be straightforward, which is why this is so enraging.

#include <i2c.h>

#pragma config OSC = XT

#pragma config WDT = OFF

#pragma config LVP = OFF

#pragma config DEBUG = OFF

 

void main(void)

{

char temperatureHI = 0;

char temperatureLO = 0;

int i = 0;

TRISB = 0x00;

PORTB = 0x00;

TRISC = 0x00;

PORTC = 0x00;

 

OpenI2C( MASTER, SLEW_OFF);

SSPADD = 0x28; // generates 100KHz clock

while(1) {

StartI2C();

IdleI2C();

 

WriteI2C( 0x42 );

IdleI2C();

 

WriteI2C( 0x41 ); // 'A' in hex

IdleI2C();

 

LATBbits.LATB3 = 1;

for(i = 0; i < 1000; i++) {

//

}

LATBbits.LATB3 = 0;

for(i = 0; i < 1000; i++) { // delay for heading calculation

//

}

 

WriteI2C( 0x43 );

IdleI2C();

 

temperatureHI = ReadI2C();

IdleI2C();

 

AckI2C();

IdleI2C();

 

temperatureLO = ReadI2C();

IdleI2C();

AckI2C();

IdleI2C();

 

NotAckI2C();

IdleI2C();

 

StopI2C();

temperatureHI = (temperatureHI * 256) + temperatureLO;

if (temperatureHI > 1000)

LATBbits.LATB7 = 1;

else

LATBbits.LATB7 = 0;

 

 

}

}

Essentially, I'm getting nothing. Bit 7 stays low and so do my spirits. Here's a link to the datasheet for anyone familiar with I2C, maybe I'm really off in implementation. Basically, I'm asking for any advice/guidelines/tips on I2C and my little problem here. So thanks in advance!

http://www.sparkfun.com/datasheets/Components/HMC6352.pdf

- Patrick

 

ALSO: Apologies for the terrible code formatting. Are there code tags by any chance?

 

Ok…A few questions

First off, go through and comment your code. That’s a real helper right there. I find most of my own mistakes that way and usually keeps my head from banging on the desk too

Secondly, you are going into user calibration mode and not doing anything or exiting - Show me where I’m wrong, if I am

Correct me if I’m wrong, but you are reading a byte ala (0x41) then going into user calibration (0x43) and then just looking to if there is data coming out (no 0x43 and no exiting user calibration)

 

I’m not seeing a write command to bring it out of calibration

Subroutines and comments are your friends, makes it easier to debug and will help you not tear your hair out(programmers that don’t use either are usually bald j/k)

Mech out

The schematic would be

The schematic would be helpful. Did you use pullup resistors on both sides of the I2C bus? 10kOhm is recommended for I2C (SCL and SDA line). You need 2 pairs one at PIC side and one at the compass side.

 

Hmmm…I hadn’t thought of

Hmmm…I hadn’t thought of user calibration. Though I don’t actually think I am entering user calibration mode…what I thought was that I was sending two separate commands. The first is writing ‘A’ to the compass (0x42 followed by 0x41). The second command should be issuing a read (0x43, which is the address of the compass with the 8th bit, or LSB, being a ‘1’ indicating data direction). I do see now that I should probably send a stop command followed by another start command. That, or I’m wondering if I should be sending a read command (0x43) followed by a data argument (0x55) twice to get both bytes of data…

There’s no real reason to have subroutines here, this is just test code which is going to be integrated into a bigger project as soon as I understand what’s going on.

- P

 

edit: Thanks for the assistance!

I’ll try to get a schematic

I’ll try to get a schematic up soon, but yes I did use 10K pullup resistors as specified in the datasheet at each end. I’m using an N-channel logic level MOSFET to convert the 5 V to 3.3V, as the compass isn’t 5V tolerant on the data lines.

 

EDIT: Schematic! http://imgur.com/RARLQ.png — sans LEDs on PORTB, but that isn’t necessary.

For me, it looks as though
For me, it looks as though SCL snd SDA are cross connected :wink:

Your MOSFET circuit is the

Your MOSFET circuit is the problem, no way to differentiate a signal from SDA or SCL.

Check out this implementiation on page 10 for a useful level convertor using MOSFETs

Nope, I2C spec only requires

Nope, I2C spec only requires one pair of pull-ups, typically at the master. 4.7K for higher speed, 10k for lower speed.

That is, if the 2 devices are at the same voltage level. In this case where there is buffer between, pull-ups on each device would be needed.

pfffft. Lousy eagle skills
pfffft. Lousy eagle skills hehe. That’s not actually what’s going on in implementation.

Update: it works! I forgot

Update: it works! I forgot to add a stop command after writing ‘A’ to the compass, which meant that anothe rstart signal was transmitted before the read. If anyone wants, I can post the updated code (don’t have it on this computer ATM).

Thanks for the input.

- P

Think I just realized the 2
Think I just realized the 2 circuits are similar. Got to tilt your head sideways to see it from the picture B )

So, is it just me or do the
So, is it just me or do the MOSFETs in these level converter circuits just act as low-resistance diodes?

Seems to work that way, but
Seems to work that way, but allows each voltage level device to control lines. Here’s a hopefully a better link to the NXP application note app97055.