Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

Three-Axis Digital Compass IC HMC5883L

Status
Not open for further replies.
Three Axis compass has six register's ( two for X , two for Y , two for z )
That mean 16bit or two 8byte register's

when we need to read x Axis we have to read two register's

Like This

Code:
msb 	=I2CRead();
	I2CAck();
	lsb =I2CRead();
	I2CAck();
	raw_z=( (msb<<8) | lsb);

First we need read msb and then read lsb and msb shift 8 times and OR with lsb

I need to verify
why shift 8 times and what are the data this two registers contain (Data Output X Registers A and B)
why we can not use single register instead of two registers
Please advice

You have answered your own question concerning:

why we can not use single register instead of two registers

Three Axis compass has six register's ( two for X , two for Y , two for z )
That mean 16bit or two 8byte register's

when we need to read x Axis we have to read two register's

The registers of the PIC16F877A are 8-bit, the complete value for each of the coordinate axes, X, Y and Z, require 16-bit of storage.

Can you squeeze 16-bit into 8-bit without loss of data or resolution? No.

Therefore, two 8-bit registers or an integral type, "int" for example in the case of the Hi-Tech C Compiler, with a size of at least two bytes is required to contain the entire value for each of the coordinate axes.




why shift 8 times ....

The code snippet you posted is one method of "reassembling" the two bytes into a single 16-bit entity and storing it into an integral type of at least 2 bytes.

Code:
	msb = I2CRead();
	I2CAck();
	lsb =I2CRead();
	I2CAck();
	raw_z=( (msb<<8) | lsb);

Assuming the variable raw_z is of sufficient size, 2 bytes or more, the Most Significant Byte (MSB) of the axis value is read and stored into the msb variable, the Least Significant Byte (LSB) of the axis value is read and stored into the lsb variable and are combined with the actions of the last statement:

Code:
raw_z=( (msb<<8) | lsb);

Although I would suggest explicitly casting msb before shifting, otherwise depending on your compiler as you will soon see, you may end up loosing data:

Code:
raw_z=( ((signed int)msb<<8) | lsb);

Lets example each part of the above statement.

Assuming:

msb = 0b00101011
lsb = 0b01010101

Code:
raw_z=( ([COLOR="#FF0000"](signed int)msb[/COLOR]<<8) | lsb);

First the cast of msb to signed int essential extents the 8-bits of msb to 16-bits:

msb 0b00101011
(signed int)msb 0b0000000000101011


Code:
raw_z=( [COLOR="#FF0000"]((signed int)msb<<8)[/COLOR] | lsb);

The left shift eight places of the value contained in msb moves the entire value into the MSB from the LSB of the now two bytes of storage:

(signed int)msb 0b0000000000101011
((signed int)msb<<8) 0b0010101100000000


Code:
raw_z=( ((signed int)msb<<8) [COLOR="#FF0000"]| lsb[/COLOR]);

The ORing of the value now contained in two bytes of storage with the value contained in lsb, essentially copies the value contained in the single byte of lsb into the LSB of the two bytes of storage:

((signed int)msb<<8) 0b0010101100000000
lsb 0b01010101
((signed int)msb<<8) | lsb 0b0010101101010101


And then the final two byte value is stored into the variable raw_z.



what are the data this two registers contain (Data Output X Registers A and B)

Essentially the two bytes of data for each axis represent the magnitude of the magnetic force which lies along each particular axis.

Remember physics where the magnitude and direction of the total force is comprised of the magnitudes of force along each of the axes/vectors, X, Y and Z. A similar scenario exists here.

Time to dust off those vector mathematics books.


BigDog
 
  • Like
Reactions: PA3040

    PA3040

    Points: 2
    Helpful Answer Positive Rating
Dear Bigdog
Thank you so much for reply and that is really really great
Your explanation is really clear my doubts

please let me a chance to ask a five questions

Each axis of compass has two result register's (Data Output X Registers A and B)
The result very from 0 to 360 that mean one 8bit register can not stores the result because one 8bit register can only store 0 to 255 result therefore compass needs two result registers for one axis. Am I correct?

Please correct me
Thanks in advance
 

Each axis of compass has two result register's (Data Output X Registers A and B)
The result very from 0 to 360 .... Am I correct?

No. The two bytes contain a 16-bit two complement value and can range from 0xF800 to 0x07FF or -2048 to 2047.

Reference: 3-Axis Digital Compass IC HMC5883L Datasheet, Page: 15
Data Output X Registers A and B

The data output X registers are two 8-bit registers, data output register A and data output register B. These registers store the measurement result from channel X. Data output X register A contains the MSB from the measurement result, and data output X register B contains the LSB from the measurement result. The value stored in these two registers is a 16-bit value in 2’s complement form, whose range is 0xF800 to 0x07FF. DXRA0 through DXRA7 and DXRB0 through DXRB7 indicate bit locations, with DXRA and DXRB denoting the bits that are in the data output X registers. DXRA7 and DXRB7 denote the first bit of the data stream. The number in parenthesis indicates the default value of that bit.

In the event the ADC reading overflows or underflows for the given channel, or if there is a math overflow during the bias measurement, this data register will contain the value -4096. This register value will clear when after the next valid measurement is made.

Each axis of compass has two result register's (Data Output X Registers A and B)
.... one 8bit register can not stores the result because one 8bit register can only store 0 to 255 result therefore compass needs two result registers for one axis.
Am I correct?

Yes. It is quite difficult to squeeze 16-bit of data into a single 8-bit register, which is why two are required to hold the value.


Remember the values are magnitudes along each axis, which you use to find the total magnitude and direction.



BigDog
 

There is also a more elegant, however unfortunately under utilized, approach to combining the two register values which uses structs and unions:

Code:
unsigned int temp;

typedef union {
   unsigned int Word;
   struct {
      unsigned char Lo;
      unsigned char Hi;
   } Byte;
} DoubleByteType;

DoubleByteType XValue;
DoubleByteType YValue;
DoubleByteType ZValue;


XValue.Byte.Hi = msb;   //Assign Hi Byte (0x12) to variable HiByte
XValue.Byte.Lo = lsb;  //Assign Lo Byte (0x34) to variable LoByte


temp = XValue.Word;  // temp = 0x1234;

There are several advantageous to using the above approach, not to mention it "automates" and simplifies the two byte combining process.

BigDog
 

Dear Bigdog
Thank you so much for your continuous reply
So I need more help to continue this project

Now I have found error message when I write the following codes
Codes
Code:
float headingDegrees_x = atan2((double)raw_y,(double)raw_x)* 180 / 3.14159265 + 180; 
float headingDegrees_y = atan2((double)raw_x,(double)raw_z)* 180 / 3.14159265 + 180;
float headingDegrees_z = atan2((double)raw_z,(double)raw_y)* 180 / 3.14159265 + 180;  

sprintf( bufx, "X %7.3f  ", headingDegrees_x );

for (int k=0;k<6;k++){
      	lcddata( bufx[k] );

}

sprintf( bufy, "  Y %7.3f", headingDegrees_y );

for (int l=0;l<8;l++){
      	lcddata( bufy[l] );
}

sprintf( bufz, "  z%7.3f", headingDegrees_z );

for (int j=0;j<8;j++){
     	lcddata( bufz[j] );

}

errors
Code:
HI-TECH C Compiler for PIC10/12/16 MCUs (Lite Mode)  V9.83
Copyright (C) 2011 Microchip Technology Inc.
(1273) Omniscient Code Generation not available in Lite mode (warning)
Error   [1347] ; 0. can't find 0xE words (0xe withtotal) for psect "text903" in class "CODE" (largest unused contiguous range 0xD)
Error   [1347] ; 0. can't find 0xB words (0xb withtotal) for psect "text898" in class "CODE" (largest unused contiguous range 0x5)
Error   [1347] ; 0. can't find 0xB words (0xb withtotal) for psect "text899" in class "CODE" (largest unused contiguous range 0x5)
Error   [1347] ; 0. can't find 0xA words (0xa withtotal) for psect "text900" in class "CODE" (largest unused contiguous range 0x5)
Error   [1347] ; 0. can't find 0xA words (0xa withtotal) for psect "text901" in class "CODE" (largest unused contiguous range 0x5)
Error   [1347] ; 0. can't find 0xA words (0xa withtotal) for psect "text902" in class "CODE" (largest unused contiguous range 0x5)
Error   [1347] ; 0. can't find 0x8 words (0x8 withtotal) for psect "clrtext" in class "CODE" (largest unused contiguous range 0x5)

********** Build failed! **********


finally I blocked one code and related codes then no build failed ( Build success )

blocked code

Code:
//float headingDegrees_z = atan2((double)raw_z,(double)raw_y)* 180 / 3.14159265 + 180;

and related codes
Code:
/*
sprintf( bufy, "  Y %7.3f", headingDegrees_y );

for (int l=0;l<8;l++){
      	lcddata( bufy[l] );
}
*/
I think compiler version may be this Using driver C:\Program Files\HI-TECH Software\PICC\9.83\bin\picc.exe
Please help when you free
Thanks in advance
 
Last edited:

Looks like you maybe running out of allocatable storage in the PIC16F877A.

What version of the compiler are you currently using?

Either post or upload the entire code of your project. I'll compile it here and see what exactly is occurring.

BigDog
 
  • Like
Reactions: PA3040

    PA3040

    Points: 2
    Helpful Answer Positive Rating
Hi Bogdog
Really thanks for reply and request
version of compiler is 9.83

Here is my entire code
Code:
#include <htc.h>
#include <pic.h>
#include <math.h>
#include <stdio.h>
#define _XTAL_FREQ 4000000 // 4 MHz clock 

__CONFIG(0X3F39);
#define LCD_EN RB3
#define	LCD_RS RB5
#define	LCD_RW RB4
#define LCD_DATA	PORTD
#define	LCD_STROBE()	((LCD_EN = 1),(LCD_EN=0))
char bufx[10];
char bufy[10];
char bufz[10];
unsigned char tmp;
unsigned char i;
double raw_x =0;
double raw_y =0;
double raw_z =0;
unsigned char msb   =0;
unsigned char lsb   =0;


void I2CInit(void){
        TRISC3 = 1;      	/* SDA and SCL as input pin */
        TRISC4 = 1;      	/* these pins can be configured either i/p or o/p */
        SSPSTAT |= 0x80; 	/* Slew rate disabled */
        SSPCON = 0x28;   	/* SSPEN = 1, I2C Master mode, clock = FOSC/(4 * (SSPADD + 1)) */
        SSPADD = 0x28;    	/* 100Khz @ 4Mhz Fosc */
}

void I2CStart(){
        SEN = 1;         	/* Start condition enabled */
        while(SEN);      	/* automatically cleared by hardware */
                     		/* wait for start condition to finish */
}

void I2CStop(){
        PEN = 1;         	/* Stop condition enabled */
        while(PEN);      	/* Wait for stop condition to finish */
                     		/* PEN automatically cleared by hardware */
}

void I2CRestart(){
        RSEN = 1;        	/* Repeated start enabled */
        while(RSEN);     	/* wait for condition to finish */
}

void I2CAck(){
        ACKDT = 0;       	/* Acknowledge data bit, 0 = ACK */
        ACKEN = 1;       	/* Ack data enabled */
        while(ACKEN);    	/* wait for ack data to send on bus */
}

void I2CNak(){
        ACKDT = 1;       	/* Acknowledge data bit, 1 = NAK */
        ACKEN = 1;       	/* Ack data enabled */
        while(ACKEN);    	/* wait for ack data to send on bus */
}

void I2CWait(){
        while ( ( SSPCON2 & 0x1F ) || ( SSPSTAT & 0x04 ) );		/* wait for any pending transfer */

}

void I2CSend(unsigned char dat){
        SSPBUF = dat;    		/* Move data to SSPBUF */
        while(BF);       		/* wait till complete data is sent from buffer */
        I2CWait();       		/* wait for any pending transfer */
}

unsigned char I2CRead(void){
        unsigned char temp;
								/* Reception works if transfer is initiated in read mode */
        RCEN = 1;        		/* Enable data reception */
        while(!BF);      		/* wait for buffer full */
        temp = SSPBUF;   		/* Read serial buffer and store in temp register */
        I2CWait();       		/* wait to check any pending transfer */
        return temp;     		/* Return the read data from bus */	
}

void lcddata(unsigned char value)
	{
LCD_DATA = value;
LCD_RS = 1; 
LCD_RW = 0;
LCD_EN = 1;
__delay_ms (1);
LCD_EN = 0;

	}

void lcdcmd(unsigned char value)
	{
LCD_DATA = value;
LCD_RS = 0;
LCD_RW = 0;
LCD_EN = 1;
__delay_ms (1);
LCD_EN = 0;
	}

void display (){
lcdcmd(0x80);

float headingDegrees_x = atan2((double)raw_y,(double)raw_x)* 180 / 3.14159265 + 180; 
float headingDegrees_y = atan2((double)raw_x,(double)raw_z)* 180 / 3.14159265 + 180;
float headingDegrees_z = atan2((double)raw_z,(double)raw_y)* 180 / 3.14159265 + 180;  

sprintf( bufx, "X %7.3f  ", headingDegrees_x );

for (int k=0;k<6;k++){
      	lcddata( bufx[k] );

}

sprintf( bufy, "  Y %7.3f", headingDegrees_y );

for (int l=0;l<8;l++){
      	lcddata( bufy[l] );
}

sprintf( bufz, "  z%7.3f", headingDegrees_z );

for (int j=0;j<8;j++){
     	lcddata( bufz[j] );

}
}

void lcd_init(){
TRISD	= 0;
TRISB	= 0;
LCD_EN =0;
__delay_ms(175);
lcdcmd(0x38);
__delay_ms(175);
lcdcmd(0x0e);
__delay_ms(15);
lcdcmd(0x01);
__delay_ms(10);
lcdcmd(0x06);
__delay_ms(10);
lcdcmd(0x80);
__delay_ms(10);
lcdcmd(0x0c);
__delay_ms(10);
				}

void Init_HMC5883L(){

	I2CStart();				//(HMC5883L_WRITE);
	I2CSend(0x3c);
	I2CSend(0x00); // set pointer to CRA
	I2CSend(0x70); // write 0x70 to CRA
I2CNak();
	I2CStop();

	I2CStart();				//(HMC5883L_WRITE);
	I2CSend(0x3c);
	I2CSend(0x01); // set pointer to CRB
	I2CSend(0xA0);
I2CNak();
	I2CStop();

	I2CStart();				//(HMC5883L_WRITE);
	I2CSend(0x3c);
	I2CSend(0x02); 			// set pointer to measurement mode
	I2CSend(0x00); 			// continous measurement
I2CNak();
	I2CStop();
}


void getHeading(){

	I2CStart();
	I2CSend(0x3c);
	I2CSend(0x03);		

	I2CRestart();	
	I2CSend(0x3d);

	msb 	=I2CRead();
	I2CAck();
	lsb =I2CRead();
	I2CAck();
	raw_x=( (msb<<8) | lsb);

	msb 	=I2CRead();
	I2CAck();
	lsb =I2CRead();
	I2CAck();
	raw_y=( (msb<<8) | lsb);

	msb 	=I2CRead();
	I2CAck();
	lsb =I2CRead();
	I2CAck();
	raw_z=( (msb<<8) | lsb);

	I2CNak();

	I2CStop();

}

void main(){        
        I2CInit();
        lcd_init();
		Init_HMC5883L();      
		
while(1){
		Init_HMC5883L(); 
        getHeading();				
		display();       

}
}


Thanks again and again for help
 

Out of curiosity, have you examined the memory usage of a previous version which compiled successfully?

View -> Memory Usage Gauge

13_1342605036.jpg


BigDog
 
  • Like
Reactions: PA3040

    PA3040

    Points: 2
    Helpful Answer Positive Rating
Dear Bogdog
what could you mean previous version?, I used same compiler version since start this project

Please help

- - - Updated - - -

Dear Bigdog

without following codes memory sage is as follows

Code:
float headingDegrees_x = atan2((double)raw_y,(double)raw_x)* 180 / 3.14159265 + 180; 
float headingDegrees_y = atan2((double)raw_x,(double)raw_z)* 180 / 3.14159265 + 180;
//float headingDegrees_z = atan2((double)raw_z,(double)raw_y)* 180 / 3.14159265 + 180;  

sprintf( bufx, "X %7.3f  ", headingDegrees_x );

for (int k=0;k<6;k++){
      	lcddata( bufx[k] );

}

sprintf( bufy, "  Y %7.3f", headingDegrees_y );

for (int l=0;l<8;l++){
      	lcddata( bufy[l] );
}
/*
sprintf( bufz, "  z%7.3f", headingDegrees_z );

for (int j=0;j<8;j++){
     	lcddata( bufz[j] );

}
usage.jpg

I think sprintf() function eats lot of memory
Please help
Thanks in advance
 

I was referring to a previous version of your code which compiled successfully.

The code compiles without error here, however I am using the Professional Version, which has considerably more code optimization features.

Warning [1090] C:\Documents and Settings\Administrator.BDG\My Documents\Projects\EDAboard\PA3040\main.c; 17. variable "_i" is not used
Warning [1090] C:\Documents and Settings\Administrator.BDG\My Documents\Projects\EDAboard\PA3040\main.c; 16. variable "_tmp" is not used

Memory Summary:
Program space used 140Ch ( 5132) of 2000h words ( 62.6%)
Data space used AFh ( 175) of 170h bytes ( 47.6%)
EEPROM space used 0h ( 0) of 100h bytes ( 0.0%)
Configuration bits used 1h ( 1) of 1h word (100.0%)
ID Location space used 0h ( 0) of 4h bytes ( 0.0%)

Loaded C:\Documents and Settings\Administrator.BDG\My Documents\Projects\EDAboard\PA3040\PA3040.cof.

********** Build successful! **********

The error messages you posted in a previous reply seem to indicate a lack of available program storage (CODE), which maybe due to the use of various libraries, stdio, math, etc.

You may have to write your own routines rather than use the standard libraries. I'll look over your code and consider the best course of action.

Also remove the following statement as it is not needed:

Code:
#include <pic.h>


BigDog
 
  • Like
Reactions: PA3040

    PA3040

    Points: 2
    Helpful Answer Positive Rating
Dear Bigdog
Thanks for advice

well it is successfully compiled when I removed the one of three of following codes

Code:
float headingDegrees_x = atan2((double)raw_y,(double)raw_x)* 180 / 3.14159265 + 180; 
float headingDegrees_y = atan2((double)raw_x,(double)raw_z)* 180 / 3.14159265 + 180;
[COLOR="#FF0000"]float headingDegrees_z = atan2((double)raw_z,(double)raw_y)* 180 / 3.14159265 + 180;  [/COLOR]

sprintf( bufx, "X %7.3f  ", headingDegrees_x );

for (int k=0;k<6;k++){
      	lcddata( bufx[k] );

}

sprintf( bufy, "  Y %7.3f", headingDegrees_y );

for (int l=0;l<8;l++){
      	lcddata( bufy[l] );
}

[COLOR="#FF0000"]sprintf( bufz, "  z%7.3f", headingDegrees_z );

for (int j=0;j<8;j++){
     	lcddata( bufz[j] );

}[/COLOR]
If removed above red color codes it is compiling success

If removed all codes show above the memory usage is I posted picture above
Please advice
 
Last edited by a moderator:

Yes, I suspect the combination of double floating math combined with sprintf() routine is pushing the PIC16F877A to its storage limits.

BigDog
 
  • Like
Reactions: PA3040

    PA3040

    Points: 2
    Helpful Answer Positive Rating
Hey you guys are just awesome!

I'm having to deal with the HMC5883l module as well using the PIC16F1823 (Cuase its extreme low power!). This conversation has really gone a long way in my task.
Hey PA3040, is your code working properly? (the one uploaded here I mean).

great work you guys.
 

Hi

Yes it is working fine for me.
 
Ok, I noticed you used the same physical pin for both scl and sda, is this the case?

void I2CInit(void){
TRISC3 = 1; /* SDA and SCL as input pin */
TRISC4 = 1; /* these pins can be configured either i/p or o/p */
SSPSTAT |= 0x80; /* Slew rate disabled */
SSPCON = 0x28; /* SSPEN = 1, I2C Master mode, clock = FOSC/(4 * (SSPADD + 1)) */
SSPADD = 0x28; /* 100Khz @ 4Mhz Fosc */
}

- - - Updated - - -

By the way what pic are you using?
 

Hi
I used PIC16f877a MCU
I think you pointed out this
TRISC3 = 1; /* SDA and SCL as input pin */

As per the comments you will feel like that,but it should be wrong

TRISC3 = 1 // SCL
TRISC4 = 1 // SDA
 

True! thanks

I have initial readings as

raw_x = 175
raw_y = -32
raw_z = 0

without any ferromagnetic material in space, would this be what you've got? how do you improve its sensitivity to magnetic materials? if you've done that because I initially got

raw_x = 58
raw_y = -32
raw_z = 0

for initial values.

- - - Updated - - -

also the program runs once and halts at the bold typed line, except I redownload the code, I cant take a new reading

void I2CNak(){
ACKDT = 1;
ACKEN = 1;
while(ACKEN);
}

do you have similar issues or solution to this?
 

Actually I did only read the device, still I did not try to improve it
did you rotate the device and got the values for xyz 0 to 360 reading from initial values


also the program runs once and halts at the bold typed line, except I redownload the code, I cant take a new reading

void I2CNak(){
ACKDT = 1;
ACKEN = 1;
while(ACKEN);
}

do you have similar issues or solution to this?

How do you simulate it?
 

Sorry mate for the delay. I simulate using a breadboarded circuit and the pickit3 in MPLAB
I'm using my sensor to sense magnetic fields to detect cars. it reads but just once and thats it, at the second attempt it gets stuck at while(!BF); in I2CRead(); it waits forever. The error flags are not actually used in your code. I included some error flags;

if (SSPCON1bits.WCOL) // test if write collision occurred
return (-1); // if WCOL bit is set return negative #

before while(!BF) and the code still does not get past while(!BF);

Have you tried reading multiple times? or do you have any ideas?
 

Hi,
Can any one help me to understand the following formula
float headingDegrees = atan2((double)raw_y,(double)raw_x)* 180 / 3.14159265 + 180;
1) atan2((double)raw_y,(double)raw_x) gives data in radians correct?
2) 180 / 3.14159265 is for converting radians to degree correct?
3) then why we are adding 180 at last in the above formula?
4) Can i use this formula for LSM303DLHC to get magnetometer value in degree?
Thanks in advance
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top