Arizona Chris
Joined: 20 Dec 2014 Posts: 69 Location: Arizona
|
Adafruit Magnetometer Compass Project |
Posted: Mon Jul 13, 2015 10:24 am |
|
|
Greetings all,
This was truly a fun project, building an automotive dash mounted compass using the Adafruit 3 axis magnetometer which uses our favorite chip - the Honeywell HMC5883. You can read about this $9 board here:
http://www.adafruit.com/products/1746
I bought a handful of these for this price, and the best part is it is the ONLY available breakout board that also contains the TTL level shifters. Just hook it up to the 5v line and direct into your favorite microcontroller! In this case here, I used my last dinosaur chip - the PIC16F876a, but you can use any PIC with at least 2k words of ROM.
I did a nice write up on our compass and posted it yesterday on my web site too. You can see the schematic, board layout and box construction.
http://www.schursastrophotography.com/PICprojects/autocompass.html
The code is fairly well optimised to use minimal space, but still requires the math.h library for the arctan2 function.
Thanks for looking - Hope you enjoy our latest project!
Chris
The code is here:
Code: | //****************************************************************************
//Chris Schur
//(Auto Compass1 16F876a)
//Date: 7/2/15
//****************************************************************************
/*Description of this Program:
This program reads the Adafruit Magnetometer, then bins the bearings into
16 ranges. Then lights one of the 16 LEDs on the circular ring display.
version 5 - final version */
//I/O Designations ---------------------------------------------------
// RA0: LED9
// RA1: LED10
// RA2: LED11
// RA3: LED12
// RA4: (Open Collector output)
// RA5:
// RB0: LED1
// RB1: LED2
// RB2: LED3
// RB3: LED4
// RB4: LED5
// RB5: LED6
// RB6: LED7
// RB7: LED8
// RC0: Status LED output
// RC1:
// RC2: LED13
// RC3: INPUT - SCL - to magnetometer
// RC4: INPUT - SDA - to magnetometer
// RC5: LED14
// RC6: LED15
// RC7: LED16
//--------------------------------------------------------------------
//Include Files:
#include <16F876A.h> //Normally chip, math, etc. used is here.
#include "math.h" //required for compass
//Directives and Defines:
#fuses NOPROTECT,HS,NOWDT //xtal is used
#use delay(crystal=10MHz) //xtal speed
//set up i2c - data, clock pins are here hardware on 877a.part uses slow spec.
#use I2C(master, sda=PIN_C4, scl=PIN_C3, slow, FORCE_HW) //HARDWARE IMPLEMENT
#use fast_io(ALL) //must define tris below in main when using this
// HMC5883 required Registers
#define W_DATA 0x3C //Used to perform a Write operation
#define R_DATA 0x3D //Used to perform a Read operation
#define CON_A 0x00 //Sets up measurement and sampling parameters.
#define CON_B 0x01 //Send continuous MeVARurement mode.
#define MOD_R 0x02 //Read/Write Register, Selects the operating mode. Default = Single meVARurement
#define X_MSB 0x03 //Read Register, Output of X MSB 8-bit value.
#define X_LSB 0x04 //Read Register, Output of X LSB 8-bit value.
#define Z_MSB 0x05 //Read Register, Output of Z MSB 8-bit value.
#define Z_LSB 0x06 //Read Register, Output of Z LSB 8-bit value.
#define Y_MSB 0x07 //Read Register, Output of Y MSB 8-bit value.
#define Y_LSB 0x08 //Read Register, Output of Y LSB 8-bit value.
//defining the LEDs
#define LED1 Pin_B0
#define LED2 Pin_B1
#define LED3 Pin_B2
#define LED4 Pin_B3
#define LED5 Pin_B4
#define LED6 Pin_B5
#define LED7 Pin_B6
#define LED8 Pin_B7
#define LED9 Pin_A0
#define LED10 Pin_A1
#define LED11 Pin_A2
#define LED12 Pin_A3
#define LED13 Pin_C2
#define LED14 Pin_C5
#define LED15 Pin_C6
#define LED16 Pin_C7
//****************************************************************************
//Global Variables:
int8 M_data[6]; //Array - 6 units 8 bit Measured datas (compass)
int16 Xm=0,Ym=0,Zm=0; //16 bit X,Y,Z variables (compass)
float bearing; //final compass reading.
//****************************************************************************
//Functions/Subroutines, Prototypes:
void LEDZERO(void); //zeros all 16 outputs
//for compass only:
void hmc5883_write(int add, int data); //write to function
int16 hmc5883_read(int add); //Read from function
void hmc5883_init(); //Sets up starting conditions
void read_reg(); //reads compass registers, calc xyz
float calc_heading(); //calculates returns bearing.
//****************************************************************************
//-- Main Program
//****************************************************************************
void main(void) {
// Set TRIS I/O directions, define analog inputs, comparators:
set_tris_A(0b10000);
set_tris_B(0b00000000);
set_tris_C(0b00011000);
//Initialize variables and Outputs: --------------------------------------
output_low(Pin_C0); //status off
//now set all the LEDs on the display module to off.
LEDZERO(); //TURN OFF LEDS
//----------------------------------------------------------------
//MAIN:
hmc5883_init(); //Initialize compass settings.
while (true) {
read_reg(); //read compass registers, calc xyz
bearing = calc_heading(); //calculates & returns final compass bearing
//Next we light the correct LED according to the value.
if (bearing >= 348.8 || bearing < 11.3) {
LEDZERO();
output_high(LED1); }
else if (bearing >= 11.3 && bearing < 33.8) {
LEDZERO();
output_high(LED2); }
else if (bearing >= 33.8 && bearing < 56.3) {
LEDZERO();
output_high(LED3); }
else if (bearing >= 56.3 && bearing < 78.8) {
LEDZERO();
output_high(LED4); }
else if (bearing >= 78.8 && bearing < 101.3) {
LEDZERO();
output_high(LED5); }
else if (bearing >= 101.3 && bearing < 123.8) {
LEDZERO();
output_high(LED6); }
else if (bearing >= 123.8 && bearing < 146.3) {
LEDZERO();
output_high(LED7); }
else if (bearing >= 146.3 && bearing < 168.8) {
LEDZERO();
output_high(LED8); }
else if (bearing >= 168.8 && bearing < 191.3) {
LEDZERO();
output_high(LED9); }
else if (bearing >= 191.3 && bearing < 213.8) {
LEDZERO();
output_high(LED10); }
else if (bearing >= 213.8 && bearing < 236.3) {
LEDZERO();
output_high(LED11); }
else if (bearing >= 236.3 && bearing < 258.8) {
LEDZERO();
output_high(LED12); }
else if (bearing >= 258.8 && bearing < 281.3) {
LEDZERO();
output_high(LED13); }
else if (bearing >= 281.3 && bearing < 303.8) {
LEDZERO();
output_high(LED14); }
else if (bearing >= 303.8 && bearing < 326.3) {
LEDZERO();
output_high(LED15); }
else if (bearing >= 326.3 && bearing < 348.8) {
LEDZERO();
output_high(LED16); }
} //elihw
} //naim
//********* Functions which have prototypes at top of program ****************
void LEDZERO(void) { //this function sets all LED outputs to 0.
output_B(0b00000000);
output_low(LED9);
output_low(LED10);
output_low(LED11);
output_low(LED12);
output_low(LED13);
output_low(LED14);
output_low(LED15);
output_low(LED16); }
//for compass only:
void hmc5883_write(int add, int data) {
i2c_start();
i2c_write(W_DATA); //0x03
i2c_write(add);
i2c_write(data);
i2c_stop(); }
int16 hmc5883_read(int add) {
int retval;
i2c_start();
i2c_write(W_DATA); //0x03
i2c_write(add);
i2c_start();
i2c_write(R_DATA); //0x3D
retval=i2c_read(0);
i2c_stop();
return retval; }
void hmc5883_init() {
hmc5883_write(MOD_R, 0x00); //0x02, 0x00
delay_ms(100);
hmc5883_write(CON_A, 0x10); //0x00, 0x10
delay_ms(100);
hmc5883_write(CON_B, 0x20); //0x01, 0x20
delay_ms(100); }
void read_reg() { //read compass registers
//read registers in compass
M_data[0]=hmc5883_read(0x04); //Read X (LSB)
M_data[1]=hmc5883_read(0x03); //Read X (MSB)
M_data[2]=hmc5883_read(0x08); //Read Y (LSB)
M_data[3]=hmc5883_read(0x07); //Read Y (MSB)
M_data[4]=hmc5883_read(0x06); //Read Z (LSB)
M_data[5]=hmc5883_read(0x05); //Read Z (MSB)
//Create Word from Highbyte and Lowbyte data:
Xm=make16(M_data[1],M_data[0]);
Ym=make16(M_data[3],M_data[2]);
Zm=make16(M_data[5],M_data[4]);
}
float calc_heading() {
//Calculate using math.h function the bearing.
float Heading = atan2((signed int16)Ym,(signed int16)Xm)* 180 / pi + 180;
//correct for this equation yielding values 180 off:
if (Heading < 180)
Heading = Heading + 180;
else if (Heading > 180)
Heading = Heading - 180;
else if (Heading == 180)
Heading = 0;
return Heading;
}
|
|
|