![](templates/subSilver/images/CCSLogo.jpg) |
![CCS C Software and Maintenance Offers](templates/subSilver/images/forumAd6.jpg) |
View previous topic :: View next topic |
Author |
Message |
Tom1234
Joined: 06 Jan 2013 Posts: 39
|
HMC5883L Help |
Posted: Thu Apr 04, 2013 8:29 pm |
|
|
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 ???? |
|
![](templates/subSilver/images/spacer.gif) |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Apr 04, 2013 9:53 pm |
|
|
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 |
|
![](templates/subSilver/images/spacer.gif) |
Tom1234
Joined: 06 Jan 2013 Posts: 39
|
HMC5883L Help |
Posted: Fri Apr 05, 2013 11:51 am |
|
|
"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 ? |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19369
|
|
Posted: Fri Apr 05, 2013 12:05 pm |
|
|
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 |
|
![](templates/subSilver/images/spacer.gif) |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Apr 05, 2013 11:38 pm |
|
|
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);
}
} |
|
|
![](templates/subSilver/images/spacer.gif) |
Tom1234
Joined: 06 Jan 2013 Posts: 39
|
HMC5883L Help |
Posted: Sun Apr 07, 2013 6:02 pm |
|
|
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);
}
|
|
|
![](templates/subSilver/images/spacer.gif) |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Apr 07, 2013 6:24 pm |
|
|
You might want to read the Status register or the Identification Registers
sometime. |
|
![](templates/subSilver/images/spacer.gif) |
amjad_alahdal
Joined: 19 Feb 2013 Posts: 50
|
|
Posted: Sat May 11, 2013 1:27 pm |
|
|
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 |
|
![](templates/subSilver/images/spacer.gif) |
savotech
Joined: 13 Dec 2012 Posts: 12
|
|
Posted: Sun Jan 19, 2014 6:17 pm |
|
|
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 |
|
![](templates/subSilver/images/spacer.gif) |
Douglas Kennedy
Joined: 07 Sep 2003 Posts: 755 Location: Florida
|
|
Posted: Sun Jan 19, 2014 6:40 pm |
|
|
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. |
|
![](templates/subSilver/images/spacer.gif) |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
![](templates/subSilver/images/spacer.gif) |
savotech
Joined: 13 Dec 2012 Posts: 12
|
|
Posted: Mon Jan 20, 2014 8:02 am |
|
|
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. |
|
![](templates/subSilver/images/spacer.gif) |
savotech
Joined: 13 Dec 2012 Posts: 12
|
|
Posted: Mon Jan 20, 2014 8:16 am |
|
|
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. |
|
![](templates/subSilver/images/spacer.gif) |
|
|
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
|