0

I'm just starting with the HMC5883L, I'm writing my code with C and also my own I2C library.

I noticed that the values on the Arduino IDE's serial monitor are constant.

Then I developed a function to read the mode register. So the value should be 0x00 for continuous mode.

But, what I get is that the device work in single mode which has the value 0x01 at first cycle of the program, then it goes to idle mode with the value 0x03.

So, why is that and what to do?

#include <I2C.h>

#define HMC5883L_read   0x3D
#define HMC5883L_write  0x3C

#define Configuration_Register_A    0x00  //Read/Write
#define Configuration_Register_B    0x01  //Read/Write
#define Mode_Register               0x02  //Read/Write
#define Data_Output_X_MSB_Register  0x03  //Read
#define Data_Output_X_LSB_Register  0x04  //Read
#define Data_Output_Z_MSB_Register  0x05  //Read
#define Data_Output_Z_LSB_Register  0x06  //Read
#define Data_Output_Y_MSB_Register  0x07  //Read
#define Data_Output_Y_LSB_Register  0x08  //Read
#define Status_Register             0x09  //Read
#define Identification_Register_A   0x10  //Read
#define Identification_Register_B   0x11  //Read
#define Identification_Register_C   0x12  //Read
#define declination_angle 3.46


void HMC5883L_init (void);
int16_t data_read (int16_t *results);
uint8_t HMC5883L_read_reg (uint8_t reg);

int16_t results[3];
float m_scale = 1.0;

void setup() {
  // put your setup code here, to run once:
  I2C_init();
  Serial.begin(9600);
  HMC5883L_init();
  Serial.print("mode register ");
  HMC5883L_read_reg(Mode_Register);
  Serial.println();
}

void loop() {
  // put your main code here, to run repeatedly:
  data_read(results);
  Serial.print("x-axis = ");
  Serial.println(results[0]);
  Serial.print("z-axis = ");
  Serial.println(results[1]);
  Serial.print("y-axis = ");
  Serial.println(results[2]);
  Serial.println();
  Serial.print("mode register ");
  HMC5883L_read_reg(Mode_Register);
  Serial.println();
_delay_ms(500);
}


void HMC5883L_init (void)
{
  I2C_start(HMC5883L_write);
  I2C_tx(Configuration_Register_A);
  I2C_tx(0x07);
  I2C_tx(Configuration_Register_B);
  I2C_tx(0x01);
  I2C_tx(Mode_Register);
  I2C_tx(0x00);
  I2C_stop();  
}

int16_t data_read (int16_t *results)
{
  uint16_t lsb,msb;
  int16_t status_of_process = 0;
  I2C_start(HMC5883L_write);
  I2C_tx(Data_Output_X_MSB_Register);
  I2C_start(HMC5883L_read);

  msb = I2C_rx();
  lsb = I2C_rx();
  results[0] = msb << 8 | lsb;  

  msb = I2C_rx();
  lsb = I2C_rx();
  results[1] = msb << 8 | lsb;

  msb = I2C_rx();
  lsb = I2C_rx();
  results[2] = msb << 8 | lsb;

  I2C_stop();

  return status_of_process;
}

uint8_t HMC5883L_read_reg (uint8_t reg)
{
  uint8_t reg_dat;
  I2C_start(HMC5883L_write);
  I2C_tx(reg);
  I2C_start(HMC5883L_read);
  reg_dat = I2C_rx();
  Serial.println(reg_dat);
}

Regards,

13
  • Try it with the Arduino Wire library. Why did you make your own I2C library ?
    – Jot
    Commented Jun 11, 2017 at 3:15
  • Because I want to know how to activate communication protocols. I can't proceed to further topics in programming without knowing to how program SPI, I2C and USART, that's very important to me. Because I can't do advanced things before doing easy things like writing a library for I2C.
    – R1S8K
    Commented Jun 11, 2017 at 11:01
  • Perhaps it is more useful when you explore the PWM modes of the timers. It took many, many years (far too many years) until the Arduino Wire library for AVR microcontrollers was working well. You seem to use some kind of repeated start, but I don't know if your own I2C library supports that. There are a number of working libraries for I2C. But they will probably only work when used in a straight forward way for only the most compatible sensors. Do you have a logic analyzer ? That makes it possible to pinpoint the problem.
    – Jot
    Commented Jun 11, 2017 at 12:14
  • No I don't have a logic analyzer. But in regard to my I2C library, it works well and I even have a message checking function to check the result of each I2C operation, which I don't apply anymore because I tested it while I was developing the library and when I approved that my tx and rx are working well, I removed the checking function.
    – R1S8K
    Commented Jun 11, 2017 at 15:36
  • When you are serious about learning communications, then you should buy a logic analyzer. It really helps a lot. I prefer 8-channel, 24MS/s with unlimited data length and software that can decode I2C and UART data. The Saleae Logic8 costs 219 dollars, perhaps the Logic4 is good enough for I2C. If you want it very cheap, here are some that costs only 5 dollars: sigrok.org/wiki/Supported_hardware . Get one today!
    – Jot
    Commented Jun 11, 2017 at 18:48

1 Answer 1

1

The problem stems from this function:

void HMC5883L_init (void)
{
  I2C_start(HMC5883L_write);
  I2C_tx(Configuration_Register_A);
  I2C_tx(0x07);
  I2C_tx(Configuration_Register_B);
  I2C_tx(0x01);
  I2C_tx(Mode_Register);
  I2C_tx(0x00);
  I2C_stop();  
}

The way I2C in most chips work, when writing, is that the first byte defines the register to write to. All subsequent bytes are the data to write into that register and successive registers.

To minimize the communication between the master and this device, the address pointer updated automatically without master intervention. This automatic address pointer update has two additional features. First when address 12 or higher is accessed the pointer updates to address 00 and secondly when address 08 is reached, the pointer rolls back to address 03.

So you are actually writing:

REG  CONTENT
0x00 0x07
0x01 Configuration_Register_B (0x01)
0x02 0x01
0x03 Mode_Register (0x02)
0x04 0x00

You need to either start a new transaction for each register you want to set a value in (explicit setting) or rely on the address pointer increment to move to the right register for you (implicit setting).

So either:

void HMC5883L_init (void)
{
  I2C_start(HMC5883L_write);
  I2C_tx(Configuration_Register_A);
  I2C_tx(0x07);
  I2C_stop();  

  I2C_start(HMC5883L_write);
  I2C_tx(Configuration_Register_B);
  I2C_tx(0x01);
  I2C_stop();  

  I2C_start(HMC5883L_write);
  I2C_tx(Mode_Register);
  I2C_tx(0x00);
  I2C_stop();  
}

Or, since the registers are in sequence in the address space:

void HMC5883L_init (void)
{
  I2C_start(HMC5883L_write);
  I2C_tx(Configuration_Register_A);
  I2C_tx(0x07);
  I2C_tx(0x01);
  I2C_tx(0x00);
  I2C_stop();  
}

Oh, and don't forget to have an I2C_stop() after reading your register in:

uint8_t HMC5883L_read_reg (uint8_t reg)
{
  uint8_t reg_dat;
  I2C_start(HMC5883L_write);
  I2C_tx(reg);
  I2C_start(HMC5883L_read);
  reg_dat = I2C_rx();
  I2C_stop(); // <----- MISSING
  Serial.println(reg_dat);
}
4
  • Thank you so much, now I get 0x00 for mode register but the readings still constant.
    – R1S8K
    Commented Jun 11, 2017 at 11:07
  • Your configuration settings look messed up. You're setting an invalid measurement mode in Config Register A, and setting only an unused bit to 1 in Config Register B. I use 0x78 in register A and 0x20 in register B (which gets changes when I select a different gain).
    – Majenko
    Commented Jun 11, 2017 at 15:40
  • OMG, you're so right! How did I miss with the configuration values, I followed the values in the HMC5883L datasheet.
    – R1S8K
    Commented Jun 16, 2017 at 23:23
  • OK, now how to learn about gain and the other calibration stuff? Regards,
    – R1S8K
    Commented Jun 16, 2017 at 23:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.