Introduction
HMC5883L Magnetometer Module
The HMC5883L magnetometer measures both the direction and strength of the Earth’s magnetic field, making it suitable for low-cost compassing and magnetometry applications.
This sensor captures magnetic field values along the X, Y, and Z axes, with a range of milli-gauss to 8 gauss. It’s often used to determine the heading or orientation of a device.
The HMC5883L communicates with microcontrollers via the I2C protocol.
For more details on the HMC5883L and how to utilize it, refer to the “HMC5883L Magnetometer Module” section in Sensors and Modules.
Programming HMC5883L Magnetometer
Let’s interface the HMC5883L magnetometer with the PIC18F4550 and calculate its heading angle. The HMC5883L uses the I2C protocol for communication, where we will configure the PIC18F4550 as the master device and the HMC5883L as the slave device.
The I2C address for the HMC5883L is 0x3C. The communication with the HMC5883L will use the following read and write operation addresses:
- Slave device write address (SLA+W): 0x3C
- Slave device read address (SLA+R): 0x3D
In this setup, the PIC18F4550 will use its I2C interface to communicate with the HMC5883L and retrieve data to calculate the heading angle.
Connection Diagram HMC5883L Magnetometer to PIC18F4550
HMC5883L Magnetometer Interface With PIC18F4550
Programming steps
First, we need to configure the HMC5883L magnetometer by setting the configuration registers:
- Configuration Register A: Set it for an average of 8 samples with a default data output rate of 15 Hz.
- Configuration Register B: Set the gain value using this register. For example, we can use 0xA0, or choose another gain value as needed.
- Mode Register: Select the continuous measurement mode of operation by setting the Mode Register value to 0x00.
After initialization, we proceed to:
- Output Data Registers: Write the starting location for the output data registers (X, Y, and Z) at address 0x03.
- Read Raw Values: Read all six output registers (X, Y, Z for both high and low) to obtain the raw data.
Finally, we calculate the heading value using the formula:
This formula computes the heading angle in degrees, using the X and Y axis raw data from the magnetometer.
HMC5883L Magnetometer Code for PIC18F4550
/*
* Magnetometer interface with PIC18F4550
* http://electronicwings.com
*
*/
#include <pic18f4550.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h> /* Include math header file */
#include "Configuration_header_file.h"
#include "I2C_Master_File.h"
#include "LCD_16x2_Header_file.h"
#define PI 3.14159265359 /* Define Pi value */
#define Declination -0.00669
/* Define declination of location from where measurement going to be done. e.g. here we have added declination from location Pune city,India. we can get it from http://www.magnetic-declination.com */
void Magneto_init() /* Magneto initialize function */
{
I2C_Start(0x3C); /* Start and write SLA+W */
I2C_Write(0x00); /* Write memory location address */
/* Configure register A as 8-average, 15 Hz default, normal measurement */
I2C_Write(0x70);
I2C_Write(0xA0); /* Configure register B for gain */
I2C_Write(0x00); /* Configure continuous measurement mode */
I2C_Stop(); /* Stop I2C */
}
int Magneto_GetHeading()
{
int x, y, z;
double Heading;
I2C_Start_Wait(0x3C); /* Start and wait for acknowledgment */
I2C_Write(0x03); /* Write memory location address */
I2C_Repeated_Start(0x3D); /* Generate repeat start with SLA+R */
/* Read 16 bit x,y,z value (2?s complement form) */
x = (((int)I2C_Read(0)<<8) | (int)I2C_Read(0));
z = (((int)I2C_Read(0)<<8) | (int)I2C_Read(0));
y = (((int)I2C_Read(0)<<8) | (int)I2C_Read(1));
I2C_Stop(); /* Stop I2C */
Heading = atan2((double)y,(double)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 ((int)(Heading* 180 / PI)); /* Convert into angle & return */
}
int main(void)
{
char buffer[25];
OSCCON = 0x72; /* Internal Oscillator frequency 8 MHz */
TRISB &= ~(1<<4); /* Make pin5 of PORT B as input */
LCD_Init(); /* Initialize LCD */
I2C_Init(); /* Initialize I2C */
Magneto_init(); /* Initialize magneto */
LCD_String("Magnetometer");
while (1)
{
sprintf(buffer,"Heading = %d%c ",Magneto_GetHeading(),0xDF);
LCD_String_xy(2,0,buffer); /* Print heading on LCD */
}
}