CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

HMC5883L Help

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Tom1234



Joined: 06 Jan 2013
Posts: 39

View user's profile Send private message

HMC5883L Help
PostPosted: Thu Apr 04, 2013 8:29 pm     Reply with quote

I tried to write a driver to use the HMC5883L sensor but unfortunately i get the value 255 all the time for the 3 axes.

I tried to follow the datasheet's example at page 18 and the example for a similar sensor at
http://www.ccsinfo.com/forum/viewtopic.php?t=35914
but without success.

(The problem is not the pull-up resistors)

The code which i use is :


Code:


int main(void)
{
 unsigned int16 X;
 unsigned int16 Y;
 unsigned int16 Z;

   i2c_start();
   i2c_write(0x3C);
   i2c_write(0x00); //set Configuration Register A
   i2c_write(0x70);
   i2c_stop();
   
   i2c_start();
   i2c_write(0x3C);
   i2c_write(0x01); //set Configuration Register B
   i2c_write(0xA0);
   i2c_stop();
   
   i2c_start();
   i2c_write(0x3C);
   i2c_write(0x02); //set Mode Register
   i2c_write(0x00);
   i2c_stop();
   
   i2c_start();
   i2c_write(0x0A);
   i2c_stop();
   delay_ms(6);
 
 
while(1)
{
i2c_start();
   i2c_write(0x03D);
   i2c_write(0x06);

   X=i2c_read();
    X<<=8;
    X=i2c_read(); //read X component
   printf("X:%u\n",X);
   
   Z=i2c_read();
    Z<<=8;
    Z=i2c_read(); //read Z component
   printf("Z:%u\n",Z);
   
   Y=i2c_read();
    Y<<=8;
    Y=i2c_read(0); //read Y component
   printf("Y:%u\n",Y);
   
   i2c_write(0x03D);
    i2c_write(0x03);
    delay_ms(67);
   i2c_stop();

}
return 0;
}


What is going wrong ????
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Apr 04, 2013 9:53 pm     Reply with quote

I wonder if you compiled this code, because your printf statements
should be using %lu, because you're printing 16-bit variables.
Since you're not using %lu, the compiler generates these errors:
Quote:

*** Error 114 "PCH_Test.c" Line 47(20,21): Printf format type is invalid ::
*** Error 114 "PCH_Test.c" Line 52(20,21): Printf format type is invalid ::
*** Error 114 "PCH_Test.c" Line 57(20,21): Printf format type is invalid ::


Fix that, and post these missing lines:
1. #include for your PIC.
2. #fuses
3. #use delay()
4. #use i2c()
5 #use rs232()

Also,
6. What is your compiler version ?
7. What is the Vdd voltage for your PIC ?
8. What is the Vdd voltage for the HMC5883L ?

9. Did you buy the HMC5883L on a pre-made board ?
If so, post a link to the website for the board. If you built the board
yourself, then post the schematic on a free image hosting website
and post a link to it here.

In addition to all that, the HMC5883L data sheet gives sample data to
send to the HMC chip to program it. You follow it somewhat, but you
deviate from it in significant ways. Your differences appear to be
coding errors. Is there a reason for the differences ?
HMC5883L data sheet:
http://dlnmh9ip6v2uc.cloudfront.net/datasheets/Sensors/Magneto/HMC5883L-FDS.pdf
Tom1234



Joined: 06 Jan 2013
Posts: 39

View user's profile Send private message

HMC5883L Help
PostPosted: Fri Apr 05, 2013 11:51 am     Reply with quote

"I wonder if you compiled this code, because your printf statements
should be using %lu, because you're printing 16-bit variables.
Since you're not using %lu, the compiler generates these errors:"



Of course I compiled this code (with success). I also used your way %lu (after your suggestion) and the results are the same.

(1) #include <33fj128MC802.h>
For the steps 2,3,4,5 I followed a thread in the forum. (I checked this configurations for other smaller project and is look like ok)
(2)
#FUSES NOPROTECT
#FUSES NOIESO
#FUSES FRC
#FUSES NOWDT
(3) #use delay(clock=7.37MHz)
(4)
#PIN_SELECT U1TX=PIN_B14
#PIN_SELECT U1RX=PIN_B15
#USE RS232(UART1,ERRORS,BAUD=9600)
(5) #use i2c(Master, sda=PIN_B9, scl=PIN_B8)
(6)Compiler version: PCD V4.093
(7)Vdd=3.3
(8)The sensor and the dspic have the same Vdd
(9)I used a pre- made board (I am looking to find more resources to upload them)


I tried to follow the sample code, from the datasheet.
The datasheet example is:

Code:

Below is an example of a positive self test process using continuous-measurement mode:
(1)Write CRA (00) - send 0x3C 0x00 0x71 - (8 average, 15 Hz default, positive self test measurement)
(2)Write CRB (01) – send 0x3C 0x01 0xA0 (Gain=5)
(3)Write Mode (02)– send 0x3C 0x02 0x00 (Continuous-measurement mode)
(4)Wait 6 ms or monitor status register or DRDY hardware interrupt pin
(5)Loop
   Send 0x3D 0x06 (Read all 6 bytes. If gain is changed then this data set is using previous gain)
   Convert three 16 - bit 2’s compliment hex values to decimal values and assign to X, Z, Y, respectively.
   Send 0x3C 0x03 (point to first data register 03)
   Wait about 67 ms (if 15 Hz rate) or monitor status register or DRDY hardware interrupt pin
END LOOP
(6)Check limits –
If all 3 axes (X, Y, and Z) are within reasonable limits (243 to 575 for Gain=5 , a djust these limits basing on the    
gain setting used. See an example below. ) Then
      All 3 axes pass positive self test
         Write CRA (00)   –   send0x3C 0x00 0x70( Exit self test mode and this procedure)
         
else
      If Gain<7
          Write CRB (01) – send 0x3C 0x010x_0 (Increase gainsettingand retry, skip the next data set)

      ELSE
         At l east one axis did not pass positive self test
      Write CRA (00) – send 0x3C 0x00 0x70 (Exit self test mode and this procedure)
      
      End if
   



Now I realise that I did not implement the sixth step of the algorithm. The other 5 steps is ok?

Also how to "Convert three 16 - bit 2’s compliment hex values to decimal values and assign to X, Z, Y, respectively." In step 5 ?
Ttelmah



Joined: 11 Mar 2010
Posts: 19516

View user's profile Send private message

PostPosted: Fri Apr 05, 2013 12:05 pm     Reply with quote

No:
Code:

   i2c_write(0x03D);
   i2c_write(0x06);

Once the chip is set to 'read' mode, nothing can be written.

The sequence with I2C, is basically:

1) Start
2) Send write address.
3) Write a register number
goto 4a or b
4a) To write just keep writing. Data goes sequentially to registers
goto 9

4b) To read.
5) Restart. (send a start)
6) Send the read address.
7) Now read bytes. Register address will be the one sent in 3.
8) NACK the last byte read

9) Send stop

There are some chips that skip individual bits (for instance ones that can
only be read and automatically do it from a preset start address with no register number being needed, but except for this the sequence is the same.

Best Wishes
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Fri Apr 05, 2013 11:38 pm     Reply with quote

Here is a sample driver. I don't have a HMC5883L to test, so I can't
guarantee this code will work, but at least it has a chance to work.

I don't have the PCD compiler, but I do have PCH. That's why I did it for
the 18F4520. If you convert this code to PCD, remember that in PCD
most data types are signed by default, whereas in PCH they are unsigned
by default. Also, in PCD you use "%x" to display a 16-bit value, but
in PCH you use "%lx".
Code:

#include <18F4520.h>
#fuses INTRC_IO, NOWDT, PUT, BROWNOUT
#use delay(clock=4M)   
#use i2c(Master, sda=PIN_C4, scl=PIN_C3)
#use rs232(baud=9600, UART1, ERRORS)

// i2c slave addresses
#define HMC5883L_WRT_ADDR  0x3C
#define HMC5883L_READ_ADDR 0x3D

// Register addresses
#define HMC5883L_CFG_A_REG 0x00
#define HMC5883L_CFG_B_REG 0x01
#define HMC5883L_MODE_REG  0x02
#define HMC5883L_X_MSB_REG 0x03

//------------------------------
// Low level routines
//------------------------------
void hmc5883l_write_reg(int8 reg, int8 data)
{
i2c_start();
i2c_write(HMC5883L_WRT_ADDR);
i2c_write(reg);
i2c_write(data);
i2c_stop();
}

//------------------------------
int8 hmc5883l_read_reg(int8 reg)
{
int8 retval;

i2c_start();
i2c_write(HMC5883L_WRT_ADDR);
i2c_write(reg);
i2c_start();
i2c_write(HMC5883L_READ_ADDR);
retval = i2c_read(0);
i2c_stop();

return(retval);
}

//------------------------------
typedef struct
{
int16 x;
int16 y;
int16 z;
}hmc5883l_result;

// This global structure holds the values read
// from the HMC5883L x,y,z registers.
hmc5883l_result compass = {0,0,0};

//------------------------------
void hmc5883l_read_data(void)
{
int8 x_lsb;
int8 x_msb;

int8 y_lsb;
int8 y_msb;

int8 z_lsb;
int8 z_msb;

i2c_start();
i2c_write(HMC5883L_WRT_ADDR);
i2c_write(HMC5883L_X_MSB_REG);  // Point to X-msb register
i2c_start();
i2c_write(HMC5883L_READ_ADDR);

x_msb = i2c_read();
x_lsb = i2c_read();

y_msb = i2c_read();
y_lsb = i2c_read();

z_msb = i2c_read();
z_lsb = i2c_read(0);   // do a NACK on last read

i2c_stop();
 
// Combine high and low bytes into 16-bit values.
compass.x = make16(x_msb, x_lsb);
compass.y = make16(y_msb, y_lsb);
compass.z = make16(z_msb, z_lsb);
}


//==========================================
void main()
{
delay_ms(500);  // A power-on init delay may be needed 

printf("\n\rStart:\n\r");

// Init the HMC5883L.  Set Mode register for
// continuous measurements.
hmc5883l_write_reg(HMC5883L_CFG_A_REG, 0x70);
hmc5883l_write_reg(HMC5883L_CFG_B_REG, 0xA0);
hmc5883l_write_reg(HMC5883L_MODE_REG, 0x00);

// Continuously read and display the x,y,z results.
// Wait at least 67 ms between reads, re the HMC5883L data sheet.
while(1)
  {
   delay_ms(100); 
   hmc5883l_read_data();   
   printf("%lx, %lx, %lx \n\r", compass.x, compass.y, compass.z);
  }   
   
}
Tom1234



Joined: 06 Jan 2013
Posts: 39

View user's profile Send private message

HMC5883L Help
PostPosted: Sun Apr 07, 2013 6:02 pm     Reply with quote

Thanks PCM Programmer.

I test this code and is look like that is working. (I will further study and i will ask some things).

But firstly there is any way to check if the sensor is give me correct data??

and also why you write the function int8 hmc5883l_read_reg(int8 reg) (below) but you never use it !!!

Code:

int8 hmc5883l_read_reg(int8 reg)
{
int8 retval;

i2c_start();
i2c_write(HMC5883L_WRT_ADDR);
i2c_write(reg);
i2c_start();
i2c_write(HMC5883L_READ_ADDR);
retval = i2c_read(0);
i2c_stop();

return(retval);
}

PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sun Apr 07, 2013 6:24 pm     Reply with quote

You might want to read the Status register or the Identification Registers
sometime.
amjad_alahdal



Joined: 19 Feb 2013
Posts: 50

View user's profile Send private message Yahoo Messenger MSN Messenger

PostPosted: Sat May 11, 2013 1:27 pm     Reply with quote

Question ::

When the x-axis in the sensor points to the North,
what should the output be ?
I Have changed the display type to be :
Code:

fprintf(COM_LCD,"%Lu, %Lu, %Lu ", compass.x, compass.y, compass.z);


TO have integers


I want the value to be 0 at North, and 90 at East
savotech



Joined: 13 Dec 2012
Posts: 12

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Sun Jan 19, 2014 6:17 pm     Reply with quote

pcm programmer, please thanks for that code, its working very fine. i edited the printf code and make it display on lcd and i saw the the X Y and Z values are changing as i change direction.

Please help me, my challenge now is that i don't know how to interpret those values to get my real north, east, west, south stuff or interpret it to degrees stuff.

I read on net that arctan of Yvalue/Xvalue gives the angle but i tried that but i don't get a tangible result, and again, i don't know what to use the Zvalue for.

So please anybody that can help me modify pcm programmer code to give me the result in degrees, thanks in expectance of favourable reply
Douglas Kennedy



Joined: 07 Sep 2003
Posts: 755
Location: Florida

View user's profile Send private message AIM Address

PostPosted: Sun Jan 19, 2014 6:40 pm     Reply with quote

The values are often raw magnetometer readings. If you understand geometry and trigonometry you have a chance of succeeding. I don't see you succeeding by asking questions of this forum it is not so far a forum that teaches mathematics. You might find some code for ardruino's. Multiwi is open source and there is c code you might be able to throw at the wall and have something useful stick. Then again without an understanding of the mathematics it will be difficult. As far as the Z axis it has a role since the x and y axis can rotate into the z axis as the platform pitches and rolls. The Earth's magnetic field has a declination that is dependent on latitude and longitude ...the field varies in 3 dimensions so X,Y and Z are needed unless the Z axis is always vertical and fixed as well as the lat and long.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sun Jan 19, 2014 8:27 pm     Reply with quote

Quote:
So please anybody

How about you learning how to search for the answer.

I typed this into http://www.google.com
Quote:
HMC5883L convert to degrees

Google showed this answer, from Ttelmah on this forum:
http://www.ccsinfo.com/forum/viewtopic.php?t=50920&start=2
savotech



Joined: 13 Dec 2012
Posts: 12

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Mon Jan 20, 2014 8:02 am     Reply with quote

Thank you all for replying me. I have really gained a lot here

I have successfully converted the the values to degree (sorry i can't paste the code, i am browsing the with my phone).

I have another challenge: i am not getting 0-360 degrees on the x-y axis, i.e. when i put the sensor on a plane table, and rotate, it wont give me a full range, its giving me a value between 21 and 150 degrees. But when i turn it through Z-axis, it gives me a full range.

When i rotate it on a table, it the angle displayed begins to decrease till 21 degrees and begins to increase till 150 degrees and then decreases again, until i rotate it through the zaxis. It gives a full range turning it like a circle going in 3D. What could be the problem, please help me sir.
savotech



Joined: 13 Dec 2012
Posts: 12

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Mon Jan 20, 2014 8:16 am     Reply with quote

Ooooh! I am sorry guys, i have seen my mistake, i misinterpreted the xyz diagram on the hmc5883l sensor, i laid it down flat instead of making it stand upright. Thats why the system is reading Zaxis variation instead of Yaxis.

I now have a full working digital compass, thanks to God and thanks to you all.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group