HMC5883L stops reading when x-axis value get to positive value

I’ve been looking for a solution for this problem for quite a time I tried different I2C libraries; like,

I2C master library using hardware TWI interface * Author: Peter Fleury

But the same problem, I think my problem is in the HMC5883L_my library not in the I2C library.

I have to mention that I’m including I2C_TWSR_Check(); function to check the status of the I2C operations success in the HMC5883L library in int16_t data_read (int16_t *results_ptr); function, and as a result I get a successful operations all the time. But, the problem is when the serial monitor of the Arduino IDE is open and I’m watching the values and moving the module with different vectors, as soon as x-axis changes from -ve to +ve values, the serial monitor stops and I have to close it and open it again.

Now, this is my HMC5883L library:

#include <avr/io.h>
#include <Arduino.h>
#include "HMC5883L_my.h"
#include "I2C.h"

int16_t results[3];

void HMC5883L_init (void)
{
  I2C_start(HMC5883L_write);
  I2C_tx(Configuration_Register_A);
  I2C_tx(0x78);
  I2C_tx(0x20);
  I2C_tx(0x00);
  I2C_stop();
}

int16_t data_read (int16_t *results_ptr)
{
  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);
  Serial.print("x-axis = ");
  msb = I2C_rx();
  lsb = I2C_rx();
  results[0] = msb << 8 | lsb;
  Serial.println(results[0]);

  Serial.print("z-axis = ");
  msb = I2C_rx();
  lsb = I2C_rx();
  results[1] = msb << 8 | lsb;

  Serial.println(results[1]);
  Serial.print("y-axis = ");
  msb = I2C_rx();
  lsb = I2C_rx();
  results[2] = msb << 8 | lsb;
  Serial.println(results[2]);

  I2C_stop();
  Serial.println();
  return status_of_process;
}

void 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();
  Serial.println(reg_dat);
}

And this is my I2C library:

/*
 * I2C_Library.c
 *
 * Created: 6/6/2017 12:34:39 AM
 * Author : wolfrose
 */

#include <avr/io.h>
#include <Arduino.h>
#include "I2C.h"

void I2C_init(void)
{
    //set SCL to 100kHz
    TWSR = 0x00;
    TWBR = 0x48;
    //enable TWI
    TWCR = (1<<TWEN);
}

void I2C_start(uint8_t address)
{
    TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
    while (!(TWCR & (1<<TWINT)));
    TWDR = address;
    TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
    while (!(TWCR & (1<<TWINT)));
}

void I2C_stop(void)
{
    TWCR = (1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
}

void I2C_tx(uint8_t data)
{
    TWDR = data;
    TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
    while (!(TWCR & (1<<TWINT)));
}

uint8_t I2C_rx(void)
{
    TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
    while (!(TWCR & (1<<TWINT)));
    return TWDR;
}

void I2C_TWSR_Check(void)
{
  uint8_t Status;
  Status = TWSR & 0xF8;
  switch(Status)
  {
    /////////////// MASTER TRANSMITTER //////////////
    case 0x08:
    Serial.println("Start is OK");
     break;
    case 0x10:
    Serial.println("Re start");
     break;
    case 0x18:
    Serial.println("SLA+W OK ACK");
     break;
    case 0x20:
    Serial.println("SLA+W no ACK");
     break;
    case 0x28:
    Serial.println("data TX OK ACK");
     break;
    case 0x30:
    Serial.println("data TX OK no ACK");
     break;
    case 0x38:        // arbitration lost in SLA+W or data TX
    Serial.println("arbitration lost in SLA+W or data TX");
     break;

    /////////////// MASTER RECEIVER //////////////
    case 0x40:
    Serial.println("SLA+R OK ACK");
     break;
    case 0x48:
    Serial.println("SLA+R OK NO ACK");
     break;
    case 0x50:
    Serial.println("data RX OK ACK");
     break;
    case 0x58:
    Serial.println("data RX OK NO ACK");
     break;

     default:
     Serial.println("Error");
    break;
  }
}

Have you tried declaring msb & lsb as "byte" or "char" or uint8_t ?

Why do you put values in the (presumably global) array results directly, and not use the pointer in the call argument list? This is bad programming practice for a “library”.

int16_t data_read (int16_t *results_ptr)
{
  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);
  Serial.print("x-axis = ");
  msb = I2C_rx();
  lsb = I2C_rx();
  results[0] = msb << 8 | lsb;
  Serial.println(results[0]);

  Serial.print("z-axis = ");
  msb = I2C_rx();
  lsb = I2C_rx();
  results[1] = msb << 8 | lsb;

  Serial.println(results[1]);
  Serial.print("y-axis = ");
  msb = I2C_rx();
  lsb = I2C_rx();
  results[2] = msb << 8 | lsb;
  Serial.println(results[2]);

  I2C_stop();
  Serial.println();
  return status_of_process;
}

edgemoron:
Have you tried declaring msb & lsb as “byte” or “char” or uint8_t ?

Well, thank you for the notice, I changed it but the same problem. It’s really weird I don’t know where the problem is but I have a strong feeling it’s in the HMC5883L library.

jremington:
Why do you put values in the (presumably global) array results directly, and not use the pointer in the call argument list? This is bad programming practice for a “library”.

int16_t data_read (int16_t *results_ptr)

{
  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);
  Serial.print("x-axis = ");
  msb = I2C_rx();
  lsb = I2C_rx();
  results[0] = msb << 8 | lsb;
  Serial.println(results[0]);

Serial.print("z-axis = ");
  msb = I2C_rx();
  lsb = I2C_rx();
  results[1] = msb << 8 | lsb;

Serial.println(results[1]);
  Serial.print("y-axis = ");
  msb = I2C_rx();
  lsb = I2C_rx();
  results[2] = msb << 8 | lsb;
  Serial.println(results[2]);

I2C_stop();
  Serial.println();
  return status_of_process;
}

How to do that? Because it’s a function argument and not assigned to the array.

How to do that?

// in the main program:

int data_array[3];
...
int return_value = data_read (data_array);
...

// in the function named data_read:

int data_read(int *results_ptr) {

 *results_ptr++ = 1; //first value returned
 *results_ptr++ = 2; //second value returned
....
   return status_of_process;
}

I did but got this error message:

Arduino: 1.8.0 (Windows 10), Board: “Arduino Nano, ATmega328”

D:\Program Files\Arduino\libraries\HMC5883L_my\HMC5883L_my.cpp: In function ‘uint8_t data_read(int16_t*)’:

D:\Program Files\Arduino\libraries\HMC5883L_my\HMC5883L_my.cpp:30:17: error: lvalue required as left operand of assignment

results_ptr++ = msb << 8 | lsb;

^

D:\Program Files\Arduino\libraries\HMC5883L_my\HMC5883L_my.cpp:36:17: error: lvalue required as left operand of assignment

results_ptr++ = msb << 8 | lsb;

^

exit status 1
Error compiling for board Arduino Nano.
Invalid library found in C:\Users\wolfrose\Documents\Arduino\libraries\I2C_test: C:\Users\wolfrose\Documents\Arduino\libraries\I2C_test
Invalid library found in C:\Users\wolfrose\Documents\Arduino\libraries\I2C_test: C:\Users\wolfrose\Documents\Arduino\libraries\I2C_test

This report would have more information with
“Show verbose output during compilation”
option enabled in File → Preferences.

Looks like you left out the * (dereferencing) operator on the pointer.

jremington:
Looks like you left out the * (dereferencing) operator on the pointer.

It works, but the same problem.

Do you know what my problem is exactly?

MY problem is weird, the serial monitor works fine for displaying the values of the data_read function.

x,y,z works ok, y,z works OK for positive and negative values, but x working only in negative values but as soon as it hits 0 and above the serial monitor stops working.

Do you know what my problem is exactly?

No, because you failed to post your complete code, a wiring diagram and a clear description of what is wrong.

Please read the "How to use this forum" post, and follow the directions.

jremington:
No, because you failed to post your complete code, a wiring diagram and a clear description of what is wrong.

Please read the "How to use this forum" post, and follow the directions.

I know how to post and describe the problem but I want to be brief and not listing a lot of text and unnecessary information or a lot of problem description which would be boring for the readers.

I want to follow the simplicity of describing the problem so other people see it well arranged and simple to understand.

For example, I didn't list the .h files because they are unnecessary if the .cpp files are displayed.

At the first of the main post I mentioned that I applied Peter Fleury I2C library. In this way I think there's no problem with the I2C libraries I have.

The problem would be the implementation of the HMC5883L library.

Do you have this module?