top of page
Writer's pictureShubham Ubale

HMC5883L triple axis magnetometer interfacing with LPC2148

Introduction

 

Magnetometer HMC5883L is used for measuring the direction and magnitude of the Earth’s magnetic field. It is used for low cost compassing and magnetometry.

It measures the Earth’s magnetic field value along the X, Y and Z axes from milli-gauss to 8 gauss.

It can be used to find the direction of heading of the device.

It uses I2C protocol for communication with microcontroller.

For more information about Magnetometer HMC5883L and how to use it, refer the topic HMC5883L Magnetometer Module in the sensors and modules section.

 

Programming HMC5883L Magnetometer

Let’s interface magnetometer HMC5883L with LPC2148 and calculate its heading angle.

HMC5883L uses I2C protocol for communication. Here we are connecting LPC2148 as a master device and HMC5883L as a slave device. Its I2C device address is 0x3C. Its read and write operation addresses are:

Slave device write address (SLA+W): 0x3C

Slave device read address (SLA+R): 0x3D

 

Interfacing Diagram

Interfacing HMC5883L Magnetometer Module with LPC2148

 

I2C0 interface is used to communicate with the magnetometer module.

P0.2 (SCL0) is connected to the SCL pin and P0.3 (SDA0) is connected to SDA pin of the magnetometer.

These pins need to be configured accordingly using the PINSEL register.

 

Function for Initializing Magnetometer

  • Configure registers A (address 0x00) and B (address 0x01) according to need of the application.

  • Configure mode register (address 0x02) for required measurement mode.

void MAG_INIT()		/* Magnetometer initialize function */
{
	I2C_START();	/* Start I2C */
	I2C_WRITE(0x3C);    /* Address of magnetometer with write */
	I2C_WRITE(0x00);	/* Write memory location address */
	I2C_WRITE(0x70);	/* Configure register A as 8-average, 15 Hz default, normal measurement */
	I2C_WRITE(0xA0);	/* Configure register B for gain */
	I2C_WRITE(0x00);	/* Configure mode register  for continuous measurement mode */
	I2C_STOP();			/* Stop I2C */
}

 

Function for Calculating Magnetometer Heading

  • Read X,Z,Y High and Low registers that start from 0x03 address.

  • Find heading using formula.

int MAG_HEADING( void )
{
	char xyz[6];
	int16_t x, y, z ;
	double heading;
	I2C_START();
	I2C_WRITE(0x3C); /* Address of magnetometer with write */
	I2C_WRITE(0x03); /* Memory location address */
	I2C_START();/* Repeated start */
	I2C_WRITE(0x3D); /* Address of magnetometer with read */
	I2C_MULTIREAD( xyz, 6); /* Read 6 bytes starting from location 0x03 */
	x = ( ( (int)(xyz[0]<<8) ) | ( (int)xyz[1] ) ); 
	z = ( ( (int)(xyz[2]<<8) ) | ( (int)xyz[3] ) ); 
	y = ( ( (int)(xyz[4]<<8) ) | ( (int)xyz[5] ) );
	I2C_STOP();
	heading = atan2(y,x) + Declination;
	if (heading>2*PI)		/* Due to declination check for >360 degree */
	heading = heading - 2*PI;
	if (heading<0)			/* Check for sign */
	heading = heading + 2*PI;
	return (heading* 180 / PI);	/* Convert into angle and return */
}

 

Program

/*
  HMC5883L magnetometer interfacing with LPC2148(ARM7)
  http://www.electronicwings.com/arm7/hmc5883l-triple-axis-magnetometer-interfacing-with-lpc2148
*/

#include <lpc214x.h>
#include <stdint.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>

#define PI	3.14159265359		/* Define Pi value  */
#define Declination	-0.00669	/* Define declination of location from where measurement going to be done. */

void delay_ms(uint16_t j) /* Function for delay in milliseconds  */
{
    uint16_t x,i;
	for(i=0;i<j;i++)
	{
    for(x=0; x<6000; x++);    /* loop to generate 1 millisecond delay with Cclk = 60MHz */
	}
}

void LCD_CMD(char command)
{
	IO0PIN = ( (IO0PIN & 0xFFFF00FF) | (command<<8) ); /* Put command on data pins */
	IO0SET = 0x00000040;
	IO0CLR = 0x00000030;
	delay_ms(2);
	IO0CLR = 0x00000040;
	delay_ms(5);
}

void LCD_INIT(void)
{
	IO0DIR = 0x0000FFF0; /* P0.8 to P0.15 LCD Data. P0.4,5,6 as RS RW and EN */
	delay_ms(20);
	LCD_CMD(0x38);  /* Initialize lcd */
	LCD_CMD(0x0C);   /* Display on cursor off */
	LCD_CMD(0x06);  /* Auto increment cursor */
	LCD_CMD(0x01);   /* Display clear */
	LCD_CMD(0x80);  /* First line first position */
}

void LCD_STRING (char* msg)
{
	int i=0;
	while(msg[i]!=0)
	{
		IO0PIN = ( (IO0PIN & 0xFFFF00FF) | (msg[i]<<8) ); /* Put msg[i] on data pins */
		IO0SET = 0x00000050; /* RS = 1, , EN = 1 */
		IO0CLR = 0x00000020; /* RW = 0 */
		delay_ms(2);
		IO0CLR = 0x00000040; /* EN = 0, RS and RW unchanged(i.e. RS = 1, RW = 0) */
		delay_ms(5);
		i++;
	}
}

void LCD_CHAR (char msg)
{
		IO0PIN = ( (IO0PIN & 0xFFFF00FF) | (msg<<8) ); /* Put msg on data pins */
		delay_ms(2);
		IO0SET = 0x00000050; /* RS = 1, , EN = 1 */
		IO0CLR = 0x00000020; /* RW = 0 */
		delay_ms(2);
		IO0CLR = 0x00000040; /* EN = 0, RS and RW unchanged(i.e. RS = 1, RW = 0) */
		delay_ms(5);
}



void I2C_INIT(void)
{
	PINSEL0 = PINSEL0 | 0x00000050; /* P0.2 and P0.3 as SCL0 and SDA0 */
	I2C0CONSET = 0x40; /* I2C Enable */
	I2C0SCLL = 0x32; /* I2C bit frequency 300 kHz with 50% duty cycle */
	I2C0SCLH = 0x32;
}

void I2C_START(void)
{
	I2C0CONSET = 0x20; /* STA = 1 */
	while ( (I2C0CONSET & 0x08) == 0 ); /* Wait till SI = 1 */
	I2C0CONCLR = 0x28; /* Clear STA and SI */
}

void I2C_WRITE( char data )
{
	I2C0DAT = data;
	I2C0CONSET = 0x40; /* I2C Enable */
	while( (I2C0CONSET & 0x08) == 0 ); /* Wait till SI = 1 */
	I2C0CONCLR = 0x08; /* Clear SI */
}

unsigned char I2C_READ( void )
{
	I2C0CONSET = 0x44; /* I2C Enable with Acknowledge */
	while( (I2C0CONSET & 0x08) == 0 ); /* Wait till SI = 1 */
	I2C0CONCLR = 0x0C; /* Clear SI and Acknowledge */
	return I2C0DAT;
}

unsigned char I2C_READ1( void )
{
	I2C0CONSET = 0x40; /* I2C Enable */
	while( (I2C0CONSET & 0x08) == 0 ); /* Wait till SI = 1 */
	I2C0CONCLR = 0x08; /* Clear SI */
	return I2C0DAT;
}

void I2C_MULTIREAD( char* arr , int bytes )
{
	uint8_t i = 0;
	while( ( bytes - 1 ) != 0 )
	{
		I2C0CONSET = 0x44; /* I2C Enable with Acknowledge */
		while( (I2C0CONSET & 0x08) == 0 ); /* Wait till SI = 1 */
		I2C0CONCLR = 0x0C; /* Clear SI and Acknowledge */
		*( arr + i ) = I2C0DAT ;
		bytes--;
		i++;
	}
	I2C0CONSET = 0x40; /* I2C Enable */
	while( (I2C0CONSET & 0x08) == 0 ); /* Wait till SI = 1 */
	I2C0CONCLR = 0x08; /* Clear SI */
	*( arr + i ) = I2C0DAT ;
}

void I2C_STOP( void )
{
	I2C0CONSET = 0x50; /* STO = 1 */
}

void MAG_INIT()		/* Magneto initialize function */
{
	I2C_START();	/* Start I2C */
	I2C_WRITE(0x3C);    /* Address of magnetometer with write */
	I2C_WRITE(0x00);	/* Write memory location address */
	I2C_WRITE(0x70);	/* Configure register A as 8-average, 15 Hz default, normal measurement */
	I2C_WRITE(0xA0);	/* Configure register B for gain */
	I2C_WRITE(0x00);	/* Configure continuous measurement mode in mode register */
	I2C_STOP();			/* Stop I2C */
}

int MAG_HEADING( void )
{
	char xyz[6];
	int16_t x, y, z ;
	double heading;
	I2C_START();
	I2C_WRITE(0x3C); /* Address of magnetometer with write */
	I2C_WRITE(0x03); /* Memory location address */
	I2C_START(); /* Repeated start */
	I2C_WRITE(0x3D); /* Address of magnetometer with read */
	I2C_MULTIREAD( xyz, 6);  /* Read 6 bytes starting from location 0x03 */
	x = ( ( (int)(xyz[0]<<8) ) | ( (int)xyz[1] ) ); 
	z = ( ( (int)(xyz[2]<<8) ) | ( (int)xyz[3] ) ); 
	y = ( ( (int)(xyz[4]<<8) ) | ( (int)xyz[5] ) );
	I2C_STOP();
	heading = atan2(y,x) + Declination;
	if (heading>2*PI)		/* Due to declination check for >360 degree */
	heading = heading - 2*PI;
	if (heading<0)			/* Check for sign */
	heading = heading + 2*PI;
	return (heading* 180 / PI);	/* Convert into angle and return */
}


int main(void)
{
	char buffer[20];
	int head; 
	PINSEL0 = PINSEL0 | 0x00000050;
	I2C_INIT();
	LCD_INIT();
	MAG_INIT();
	LCD_STRING("Heading:");
    while(1)
    {
		head = MAG_HEADING();
		sprintf(buffer, "%d", head);
		LCD_CMD(0x89);
		LCD_STRING(buffer);
		LCD_CHAR(0xDF);
		LCD_STRING("   ");
    }
}

 

1 view0 comments

Commentaires


bottom of page