HMC6352

Hi All,

I'm trying to get the Arduino Mini to work with the HMC6352(Compass Module - HMC6352 - SEN-07915 - SparkFun Electronics) .

I've connected the analogue pin 4 to SDA and pin 5 to the SCL of the Compass, both lines are connected to the +5V with a 1,8R resistor.

This is the output I get:
Start
Step 1
Step 2
Stop
<41-0->

I've noticed if i remove the 2 resistors the code runs to Step 2 and stops, than I get the following output:
Start
Step 1
Step 2

I'm using the following code:

#include <Wire.h>

void setup()
{
  Wire.begin();
  Serial.begin(9600);
  
  digitalWrite(13,HIGH);
}

byte val = 0;

void loop()
{
  Serial.println("Start");
  Wire.beginTransmission(0x42);
  Serial.println("Step 1");
  Wire.send(0x41); 
  Serial.println("Step 2");
  Wire.endTransmission();
  Serial.println("Stop");
  
  delayMicroseconds(7000);
  
  Wire.requestFrom(0x42, 2);
  
  Serial.print('<');
  while(Wire.available()) 
  { 
    char c = Wire.receive();
    Serial.print(c,HEX);
    Serial.print('-');
  } 
  Serial.print('>');
  
  delay(500);
}

What is wrong?

Thanks,
Frederik

haven't used this module but your post rang a bell.. see this one
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1163405566/2

D

How did you wire up to the HMC6352? - as I remember, it's a really small BGA chip?

At Sparkfun you can by one that is already soldered on a print.

I am trying to get the same setup to work and I keep hitting the same problem :-/

Ardunio NG connected to a Sparkfun HMC6352, SDA is connected to analog pin 4, SCL is connected to analog pin 5, both SDA & SCL are pulled high with 1.8 kOhm.

The odd thing is that this setup works with my PIC18 microcontroller! That's the code I use there

// controlling HMC6352 via I2C bus

#include "18f452.h"

#fuses hs,nowdt,put,noprotect
#use delay(clock=20000000)    // 20 MHz
#use i2c(master, sda=PIN_C4, scl=PIN_C3)
#use rs232(baud=19200, xmit=PIN_C6, rcv=PIN_C7)

#define SLAVE 0x42            // address of slave
#define ACK 0x01
#define NACK 0x00


// functions
byte readByte(byte *buffer, int len);
byte writeByte(byte command, byte *buffer, int len);


void main()
{
  byte buffer[2] = {0,0};
  float yaw;
  long sum;

  // set operational mode
  buffer[0] = 0x74;          // register address
  buffer[1] = 0x62;          // 20 Hz, continuous mode
  writeByte(0x47, buffer, 2);
  delay_ms(70);

  // get data
  writeByte(0x41, 0, 0);
  delay_ms(6000);

  while (true)
  {
     readByte(buffer, 2);

     sum = buffer[0];
     sum = sum << 8;         // can't shift buffer[n] itself
     sum += buffer[1];
     yaw = sum/10.0;
     printf("%05.1f\n", yaw);
  }
}

byte readByte(byte *buffer, int len)
{
  byte i;

  i2c_start();

  if (i2c_write(SLAVE+1))
  {
     // no ACK received

     // this fails quite often, maybe we should wait somewhere?
     //printf("readByte: i2c_write() failed\n");
     return 0;
  }

  for (i=0; i<len; i++)
  {
     // fill buffer
     *(buffer+i) = i2c_read(ACK);
  }

  // might be needed..
  //i2c_stop();

  return 1;
}


byte writeByte(byte command, byte *buffer, int len)
{
  byte i;

  i2c_start();

  if (i2c_write(SLAVE))
  {
     // no ACK received

     // DEBUG (never occured so far)
     //printf("writeByte: i2c_write() failed for address\n");
     return 0;
  }

  if (i2c_write(command))
  {
     // no ACK received

     // DEBUG (never occured so far)
     //printf("writeByte: i2c_write() failed for command\n");
     return 0;
  }

  for (i=0; i<len; i++)
  {
     // send buffer
     if (i2c_write(*(buffer+i)))
     {
        // no ACK received

        // DEBUG (never occured so far)
        //printf("writeByte: i2c_write() failed for data byte %u\n", i);
        return 0;
     }
  }

  // might be needed..
  //i2c_stop();

  return 1;
}

(the PICC suite lacks high level I2C routines, and as you see from the comments in readByte and writeByte, this does not all work according to the spec here either, but it does work and maybe this gives a hint..)
Using similar code with Arduino or even trying to read values from EEPROM returns just (static) garbage. Help!


I am not that much into electronics, but maybe something is wrong wrt to levels or the pull-up resistors? The HMC6352 spec says

The HMC6352 Serial Clock (SCL) and Serial Data (SDA) lines do not have internal pull-up resistors, and require resistive pull-ups (Rp) between the master device (usually a host microprocessor) and the HMC6352. Pull-up resistance values of about 10k ohms are recommended with a nominal 3.0-volt supply voltage. Other values may be used as defined in the I2C Bus Specification 2.1.

And I am also unsure whether or not pull-up resistors are even needed, because http://research.techkwondo.com/blog/julian/279 is quoting Nicolas

I have pull-up resistors on those lines, but Nicolas explains that his library enables the internal pull-ups on the Atmega, so I can probably pull those from my breadboard.

Just tried, using 10kOhm pull-up resistors does not change anything.. I get exactly the same 41 0 pattern (which seams to mirror the byte sent) as freddotnet :confused:

I am encountering the same problem with the HMC6352. I'm using a 168, but the pins should be the same for SCL and SDA. Have either of you gotten this to work with Arduino yet?

Thanks,

D.

I'm still working on this, and have learned a few things. First of all, in Fred's example code, we never recieve anything at all. The buffer used by Wire is the same for transmit and receive - if we received anything, it would replace the 0x41 command. I tried to put the device in continuous mode, and when I attempted to read I just got my old command back.

Looking at the Wire code, it doesn't seem like it knows if a read has occurred or not. If you ask for N bytes, you just get the first N bytes of the buffer.

Looking at the ATMega 168 datasheet, it seems like the hardware is supposed to wait if the slave device holds SCL low, and I don't see any way to wait for SCL to change.

I did find a non-interrupt driven TWI project at AVR Freaks - I have gotten it to compile under Arduino, and since we are just trying to figure out why ONE device doesn't behave, this seems like it will be much easier to debug.

If anyone else is still interested in getting this device, and TWI in general, working smoothly with Arduino, I would be glad for any help or suggestions. I don't have a scope or much experience with this, but I do have time to work on getting this device to talk to us.

D.

Hi drd!

Thanks for working on this! I too am struggling to get satisfactory results, although I have managed to get the Arduino Mini working with a DS1307 real-time clock using Wire - so I'm glad I've gotten that far.

Have you looked at the HMC6352 lines on an oscilloscope? I'm looking at the waveforms here;

http://www.ssec.honeywell.com/magnetic/datasheets/hmc6353_i2c_operational_overview.pdf

Two things I've noticed (but I have a terrible oscilloscope, so this isn't guaranteed correct)

i) there appear to be 9 clock cycles between each acknowledge, but the HMC6352 is expecting only 8. Perhaps the Wire library includes a parity bit that shouldn't be there?

ii) the documented waveforms suggest that the SDA line should set up each bit in time for the next rising SCL edge, but what I'm seeing on the oscilloscope is that the SDA line changes at the same instant as the SCL rising edge (which seems a bad thing).

Have you seen the comment on the sparkfun forum concerning 'clock stretching' on the HMC?

(Problems with Arduino and HMC6352 - SparkFun Electronics Forum)

Please keep me posted on your efforts - many thanks!

Hi mattp!

Thanks for the information - it helps make a little more sense out of this. I got the software TWI code to start doing things, had to redefine some pins for the avr168. The way this code works is to actually time out the data on the SCL and SDA lines using a loop and delays instead of using the twi hardware.

Initially, I couldn't get an ACK when I sent out the 0x42 to the sensor. I mistakenly shifted the 0x42<<1 and suddenly started getting an ACK for that and the next 0x41 command to generate a heading. After waiting 7 ms, I issue another start, send address 0x43<<1, and try to read two bytes. Unfortunately, they are always 0xFF. I notice the code isn't looking at the SCL line before reading the data, I think I can change it so it will wait on SCL - I'll work on that. It seems there is a timing problem, or why would I need to left shift the adress to make it work. This might have something to do with you seeing an extra bit in there?

I don't have an oscilloscope, so can only shoot in the dark as of now. If you have any interest in playing with the software twi code for Arduino, I'd be happy to send you what I have. It would be interesting to know if it is actually doing what it is trying to do.

I'd also be curious to see how you got the DS1307 to work with Wire.

D.

mattp, I also would love to see the DS1307 code if you're willing to share it.

Unfortunately, the code for the DS1307 isn't that exciting...

#include <Wire.h>

// TWI (I2C) sketch to communicate with the DS1307 Real Time Clock

void setup()

{
  Wire.begin(); // join i2c bus (address optional for master)
  Serial.begin(9600);

  // program the time & enable clock  (each hex digit corresponds to one clock digit)
  Wire.beginTransmission(0x68);
  Wire.send(0);
  Wire.send(0x00);          //seconds
  Wire.send(0x30);          //minutes
  Wire.send(0x80 | 0x21);    //hours (24hr time)
  Wire.endTransmission();


}

void loop()
{
  Wire.beginTransmission(0x68);
  Wire.send(0);
  Wire.endTransmission();

  Wire.requestFrom(0x68, 3);
  byte secs = Wire.receive();
  byte mins = Wire.receive();
  byte hrs = Wire.receive();
  
  Serial.print(hrs,HEX);    
  Serial.print(":");
  Serial.print(mins,HEX);
  Serial.print(":");
  Serial.println(secs,HEX);

  delay(1000);    //wait a second before next output

}

I've found that the Wire library doesn't send any data until the Wire.endTransmission line is executed. ie: all the other commands are just to initialise parameters and fill the buffer.

By way of debugging, I've been looping the following;

Wire.beginTransmission(0x68);
Wire.endTransmission();

this just sends the address of the DS1307 over and over. When I view this on an oscilloscope, I can confirm that the DS1307 drives the SDA line low during an ACK (acknowledge). I confirmed this by removing the DS1307 from circuit and saw that the SDA line then remains high.

Also, if I send the wrong address for the DS1307, there is no change on the SDA lines (as expected).

No such luck with the HMC6352. When I loop its address over and over there is no ACK.

How did you get the HMC6352 to acknowledge, drd? Care to post your code?

Okay - FINALLY got an acknowledge from the HMC6352 using the Wire library...

There seems to be timing troubles within the Wire library (in the case of this compass) because sending the actual address of 0x42 doesn't work...

However, I had a hunch when looking at the oscilloscope and decided to shift the address right:

loop:

Wire.beginTransmission(0x21); //HMC6352 address bit shifted right
Wire.endTransmission();

THIS gets an acknowledge, so now I can work on fixing the timings...

(I should mention, I'm running everything at 5V and have a 4.7k pullup resistor on each of the I2C lines).

Hi mattp,

I think the ACK I'm getting may just be accidental. Reading the AVR168 twi reference some more, it seems like both the SDA and SCL lines are "wire ANDed", so the master tries to raise SCL, but if the slave holds it low SCL stays low until until slave raises SCL. I'm sure my code doesn't do this, and I'm trying to change it so it does. There are multiple states a port can be left in, and I'm trying to figure out how to write HI on SCL, then release the line and check SCL, and wait till it goes HI to start timing the SCL pulse.

From what you've observed, I bet Wire isn't waiting for slave to raise SCL, and is just writing or reading data at whatever rate it wants. It may be that it isn't leaving the SCL pin in the right state for the twi hardware to recognize SCL being held low.

I gotta get me an oscilloscope! Are any of the inexpensive USB scopes usable for stuff like this?

I'm going to pursue the software twi for now to try and understand twi, and it also has the advantage that you could use other pins, and run it on microcontrollers that don't have hardware twi.

Good luck!

D.

mattp - I stumbled onto some more information. There is a software twi application from Atmel called AVR300 which is a single-master, written in assembler, but it has some good documentation. It seems the only time that the slave holds SCL low is immediately following an ACK - not for each SCL pulse in each 8-bit transfer. This is the way this software behaves, anyway. I'm wondering if you see something like this on your scope, for example, following the ACK after the 0x43 we use to read the heading from the 6352. If you see SCK staying low after the ACK for the read request that would be what we are looking for.

I understand the reason for pull-up resistors now - if either master or slave hold a line low, it will be low - otherwise it will be HI. So the SCL is used for handshaking between master and slave.

Based on you needing to shift the 0x42 right, it seems like we have timing issues even within each 8-bit transfer - I'll try to learn about that.

D.

Here is the output from my loop that sends "0x42 0x21" over and over....

The top trace is SDA, the bottom trace is SCL. You'll notice that there are 9 clock cycles per byte (the spec says that the 9th cycle should be on the ACK). If you work from the far right off the screen, above the last SCL cycle, you'll see a small spike where the master tried to drive the SDA line high but the slave held it low (ie: an "ACK"). This ACK can't be seen on the first byte where I was sending the proper address of 0x42.

I've labelled the 1s and 0s above the 2nd trace to demonstrate how the 0x21 is being misinterpreted as the correct 0x42 address. It seems that the data on the SDA line starts just a fraction too early when compared to the SCL line. I don't know why this is - I've checked the Wire code and it seems to rely solely on internal TWI registers within the ATMEGA that automate this process.

Even more curious, the SDA data was quite early when communicating with the DS1307 clock as well, but that seemed to work out okay. I guess the timing tolerances of the HMC6352 and the internal ATMEGA routines just aren't compatible.

SO! That leaves us with trying to bit-bash a solution. Which is fine by me, cos if I implement this on digital Arduino lines then that frees up a couple more analog inputs.

Thanks for pointing out that AVR300 app note... Atmel seems to have removed it from their own site, but I found the note (and code) at AVRfreaks.

Haven't tried it yet, but a fellow Peter Fleury has implemented the AVR300 appnote in C:

http://jump.to/fleury

(under Software -> Libraries -> I2C Master Interface)

He notes:

"The module i2cmaster.S is based on the Atmel Application Note AVR300, corrected and adapted to GNU assembler and AVR-GCC C call interface. Replaced the incorrect quarter period delays found in AVR300 with half period delays."

Okay, so unfortunately that Peter Fleury implementation was in assembly, which I can't easily get Arduino to compile....

However, I've found a software implementation in C:

http://hubbard.engr.scu.edu/avr/avrlib/

his library of C files includes "i2csw.h" and "i2csw.c" which are I2C SoftWare implementations. Real simple - I'm going to try it now....

mattp - Thanks for the scope picture - that makes it clear the timing is off. The i2csw code you mentioned is very similar to the software implementation I've been playing with, and neither looks like it checks SCL to see if the slave is holding it low. I have tried to do that, but it still doesn't work. Without a scope, I can't see what is happening, except I only get an ACK if I shift one bit left instead of right.

It would be nice to get this working on some other pins - I need all the analog inputs too!

I asked questions about i2c over on AVR Freaks - they thought the hardware twi should just work if the slave is holding SCL low, but obviously it isn't.

Good luck!

mattp - A guy who replied to my post on AVR Freaks said he has the HCM6352 working using the hardware twi code in the library you mentioned. I looked at the interrupt handling in it and it has some differences with Wire. I'll see if I can compile it under Arduino and make it work.