CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

A Robot / Pic Project Digital compass chip - RS232....

 
Post new topic   Reply to topic    CCS Forum Index -> Code Library
View previous topic :: View next topic  
Author Message
Arizona Chris



Joined: 20 Dec 2014
Posts: 69
Location: Arizona

View user's profile Send private message Visit poster's website

A Robot / Pic Project Digital compass chip - RS232....
PostPosted: Sat Sep 26, 2015 6:57 am     Reply with quote

This project was initiated because the amount of time spent on incorporating a digital compass into our latest indoor robot (Plant watering robot) was huge, and the amount of ROM space it took up on the main processor with its math libraries took just over 3k of valuable program space.  The robot is done now and working great on a daily basis, but I vowed never to try to put such a large sensor driver code into my next robots!  Thus this chip was born, out of a need to accomplish this task.  Before I go any further on this, just a brief overview of what is needed these day to put a compass in a project such as a mobile robot or any other PIC project requiring a sense of orientation. 

In the past, we had to use such expensive devices as the Devantech digital compass which is around $70 and puts out a pulse that varies from 0 to 36mS for zero to 360 degrees.  While it is fairly straightforward to measure such data, it would be far better to directly
receive fast serial data from a compass to fetch our orientation quickly and without the time it takes to measure such a long pulse.  Today, we have dual and triple axis magnetometers for under $30 that require rather complex code to read and set up.  In addition, nearly all of the popular compass products require a 3.3v power supply and need bi directional level shifters to both send and revive data.  This puts a huge damper on most of us using these devices for sure.  Now, however you can buy from Adafruit a triple axis magnetometer break out board, only an inch square that has BOTH voltage regulation and level shifters built in for direct interface with standard 5v PIC micros.  And the price - $9.  Buy a handful of these like we did for this price!  You can read about this awesome value here:

http://www.adafruit.com/products/1746

For this project, I used a still extant PIC16F876a processor, but any smaller PIC with a UART and I2C will work fine. As I said, the program takes up about 3k of ROM, so make sure your chip can have this amount with a bit of room to spare for mods.    Data is sent at a constant rate of at least 10 measurements per second. Here I have added in the drivers for my New Haven LCD from Jameco, (which is a $16 killer deal in itself) so you can see the data live to check the function of the compass out.  In addition, it sends data over the serial line at 9600kb with the RS232 protocol.  The update rate of the data even with the LCD output running at the same time is about once every 68mS.  For a compass, thats really fast and plenty for navigation of any mobile robot.  For flying robots you might want to pull the LCD stuff out to gain a bit of speed. 

By off loading the extensive compass driver and math libraries off your main processor, you have given yourself a lot more programming space, and do not have to hassle with putting such complex and perhaps interacting code into your main program!  We use this exact code in our latest robot and it has worked flawlessly for months now.  So get those compass boards on order now and get that robot rolling!

For more info, Ive posted a complete write up, schematics, and photos here:
http://www.schursastrophotography.com/PICprojects/compasschip1.html

To access the compass data from the main processor, here is some typical code. Remember, Im not trying to highlight the full full receive code because it is something you put in your main processor, which can be any device with a UART! :

Code:

//For example reciving compass data on pin A0:
#use rs232(baud=9600, rcv=Pin_A0, bits=8,parity=N, DISABLE_INTS,stream=COMPASS)
//Note: the "disable_ints is critical, turns off constant barrage of interrupts
//during the aquisition of data in the get command so timing is not affected.

GETCOMPASS(); //go get compass data serially

//********* Functions which have prototypes at top of program ****************

void GETCOMPASS(void) {
//test loop to see if data has come in lasting maximum of 1/2 second:

compasstimeout = 0; //reset timeout

while (!kbhit(COMPASS) && (++compasstimeout < 50000)) //half second wait IF NO SIGNAL
delay_us(10);

if (kbhit(COMPASS)) { //We have a signal
lowbyte = fgetc(COMPASS);
highbyte = fgetc(COMPASS);
bearing2 = MAKE16(highbyte,lowbyte); //RAW
bearing = (float) bearing2 / 100; //degrees with 2 decimals
}
}



And here is the main compass chip code, using an PIC16F876a:

Code:

//****************************************************************************
//Chris Schur
//(Compass Sensor Chip - 1 16F876a)
//Date: 9/14/15
//****************************************************************************

/*Description of this Program:

This version - Sends 5 digits serially of compass data from the Adafruit compass module

*/

 

//I/O Designations ---------------------------------------------------
// RA0:
// RA1:
// RA2:
// RA3:
// RA4: (Open Collector output)
// RA5:

// RB0:
// RB1:
// RB2:
// RB3:
// RB4:
// RB5:
// RB6:
// RB7:

// RC0: Status LED output
// RC1: LCD output
// RC2:
// RC3: INPUT - SCL - to magnetometer
// RC4: INPUT - SDA - to magnetometer
// RC5:
// RC6: serial TX UART
// RC7:

 

//--------------------------------------------------------------------

//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.

//for LCD:
#use rs232(baud=9600, xmit=Pin_C1, bits=8, parity=N,stream=SERIALNH)
//for data out to main processor:
#use rs232(baud=9600, xmit=Pin_C6, bits=8, parity=N,STREAM=COMPASS)

 

//****************************************************************************
//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.
int16 bearing2; //compass bearing x 100, no decimal
int highbyte,lowbyte; //high byte and low byte for serial data

//****************************************************************************


//Functions/Subroutines, Prototypes:

//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.

 

//Clears LCD Display:
void LCDCLR() {
fputc(0xFE,SERIALNH); //Command Prefix
fputc(0x51,SERIALNH); //Clear screen
}

//Sets LCD to line 2 start point
void LCDLN2() {
fputc(0xFE,SERIALNH); //Command Prefix
fputc(0x45,SERIALNH); //set cursor command
fputc(0x40,SERIALNH); //Set cursor to next line, pos 40 = start line 2
}

 

 

 

//****************************************************************************
//-- Main Program
//****************************************************************************

void main(void) {

// Set TRIS I/O directions, define analog inputs, compartors:
set_tris_A(0b10000);
set_tris_B(0b00000000);
set_tris_C(0b00011000);


//(analog inputs digital by default)

//Initialize variables and Outputs: --------------------------------------
output_low(Pin_C0); //status off

delay_ms(1000); //LCD warmup time
//SET BRIGHTNESS OF LCD TO MID RANGE. (DEFAULT = 1)
fputc(0xFE,SERIALNH); //Command Prefix
fputc(0x53,SERIALNH); //set cursor command
fputc(8,SERIALNH); //4 is mid 0-8

delay_ms(25);

LCDCLR();
delay_ms(25);

fprintf(SERIALNH,"AUTO COMPASS-1");
delay_ms(250);

LCDLN2();
fprintf(SERIALNH,"READY");
delay_ms(1000);


//Setup for timers, PWM, and other peripherals:

//----------------------------------------------------------------

//MAIN:

hmc5883_init(); //Initialize compass settings.

while (true) {

read_reg(); //read compass registers, calc xyz

bearing = calc_heading(); //calculates & returns final compass bearing

bearing2 = (int16)(bearing * 100); //mult by 100, redefine type to int16
// so we can send it serially as two bytes. Yeilds 0 - 35999

//Convert to two 8 bit integers to send:
lowbyte = MAKE8(bearing2,0);
highbyte = MAKE8(bearing2,1);

//send data serially:
fputc(lowbyte,COMPASS);
fputc(highbyte,COMPASS);

delay_ms(10);

LCDCLR();
delay_ms(10);
fprintf(SERIALNH,"H= %Lu ",bearing2);
delay_ms(25);




} //elihw

} //naim

//********* Functions which have prototypes at top of program ****************

 

//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;

}

Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> Code Library All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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