I haven't made a post in a while, been rather busy with different projects, went to Portugal for work bit of a whirl wind trip.. but back to the electronics fun and games..
For one of my many side projects it needs a compass to know where it is. this device is to be aware of its location and environment to some degree. I need to know which way is up :)
I purchased from ebay a HMC5883L breakout board for about $15 delivered. once it arrived i located a library and some example code and it worked as advertised when i soldered it to a prototype shield and compiled the code and uploaded it to the mega2560. I thought sweet this will be great. i proceeded to load the code on my MAX32 chipkit arduino clone, i am planning to use this as my main dev platform as its a beast it rips through code at 80MIPs which is ideal for my application as i need the speed to update a graphical LCD at a nice rate a MEGA2560 doesn't have the grunt i need, i like the arduino IDE.
I loaded up the MAX32 with the example code and opened up the serial monitor to see whats doin.
things looked good, i was getting a similar output to the mega, then I did a full rotation of the compass and was not happy with the full results..
The mega gave me a nice 0-360' degree output as i rotated it about.
The MAX32 gave me 0-90' degrees then 0-90' (four times) and it had big holes where the output would jump from 45-75 with no steps between.
This is with the same code using the same compass wired to the same two pins.
I searched around on the net and found that there is a page that states the current status of various library's for the MAX32 and UNO32 platforms. this page didn't look like it had been updated in some time, i i took note of what they were saying. they report that the wire library produces bad stop bits when using the max32 hardware. it works with a uno32 (i tested it), so i then searched the net for any glimmer of hope that i could use the hardware i2c port on the mega, only to find no real info or help. that said i haven't hassled the manufacturer yet.
Seeing as thou i couldn't find a answer to why the wire library wasn't working without reading all the header files and arduino dev bugs and stuff like that, i decided to bit bang me a IIc I2c I^c port on the max32. now that i have completed this, i will dump the code at the end of this post. its a simple bit of code that is broken up in to the essential sections, i hope its not too confusing i have been modifying this for the last two weeks trying different things to explain my results, so there could be a bit of left over crap from previous methods i have tried..
So I seem to have replicated the hardware port 100% exactly the same in my bit banging code. I say this because i get exactly the same results as the example code produces.. that is, on the MEGA2560 i get nice results 0-360' degrees as i rotate the compass chip. with exactly the same code on the MAX32 i get the same results as the example code, 0-90' degrees four times with a hole in the results.. YAY i can copy something that doesn't work..
I have taken both boards to work and connected up the CRO probes to the mega and looked at a bit of the waveform that works.. then repeated the operation on the MAX32 and looked at the waveforms they looked ok until... what i think is the result of a read command the voltage is a little lower than the rest of the transaction.. it was at 3.0v the chip and bus is referenced to the 3v rail when connected to both the mega and max32.. i was worried that the bus voltage might be a little low for the mega as its a 5v device and a Voh of 3.0v might not be read as a high on the mega. but testing has proven that the mega is happy with this for a Vih.
Code....
#define SDA_PIN 13 //20 //76
#define SCL_PIN 12 //21 //75
#define DRDY_PIN 74
#define HMC5883_W_add 0x1E
#define HMC5883_R_add 0x1E
#define HMC5883_A_add 0x1E
#define CONFA 0x00
#define CONFB 0x01
#define MODER 0x02
#define OUTXH 0x03
#define OUTXL 0x04
#define OUTZH 0x05
#define OUTZL 0x06
#define OUTYH 0x07
#define OUTYL 0x08
#define STATU 0x09 // bit 1 of result 0 = data has been read
// bit 0 of result 1 = data is read to be read/
#define IDRA 0x0A
#define IDRB 0x0B
#define IDRC 0x0C
// CONFIG_B value bits 765
#define scale088 0.73 // 000
#define scale130 0.92 // 001
#define scale190 1.22 // 010
#define scale250 1.52 // 011
#define scale400 2.27 // 100
#define scale470 2.56 // 101
#define scale560 3.03 // 110
#define scale810 4.35 // 111
// SShhhBB // hhh = hz setting
#define CONFIG_A 0b01111000 //
#define CONFIG_B 0b00000000 // 001 = 1.3Ga 000 = 0.88Ga
#define CONFIG_M 0b00000000 // continous read mode
float Scale_Factor = scale088;
int compassXYZ[6];
char temps[10];
char data;
char x;
void IIC_Init(void); // sets up the hardware for idle state
void IIC_Startbit(void); // sets the bus into start_bit cond.
void IIC_Stopbit(void); // sets the bus into stop_bit cond.
unsigned char IIC_Send_Byte(unsigned char data);
void IIC_Send_Add_RW(unsigned char add,unsigned char rw);
unsigned char IIC_ReadByte(void);
char w; // used to waste time.
float compass_read(int);
float raw_to_gs(int);
char count = 0;
char t,temp1,temp2,temp3;
//------------------------------------------------------------------------------
void setup(void)
{
pinMode(SDA_PIN,OUTPUT); // data
pinMode(SCL_PIN,OUTPUT); // clock
digitalWrite(74,LOW);
pinMode(DRDY_PIN,INPUT); // DRDY_PIN data ready for reading INTor flag.
Serial.begin(9600);
Serial.println("Done");
IIC_Init(); // sets up pins for I2c chat
compass_init();
// while(1);
}
void loop(void)
{
if (digitalRead(DRDY_PIN) == 1)
{
compass_read(compassXYZ);
Serial.print(" X ");
Serial.print(compassXYZ[0]);
Serial.print(" Z ");
Serial.print(compassXYZ[1]);
Serial.print(" Y ");
Serial.println (compassXYZ[2]);
raw_to_gs(compassXYZ);
Serial.print(" Heading -----> ");
float head = atan2(compassXYZ[2],compassXYZ[0]);
if (head < 0)
head += 2 * PI;
if (head > 2 * PI)
head -= 2 * PI;
head = head * 180/M_PI;
Serial.println(head);
}
else
Serial.print("*");
}
void compass_init(void)
{
unsigned char IIC_t0 = 0;
unsigned char IIC_t1 = 0;
unsigned char IIC_t2 = 0;
unsigned char IIC_t3 = 0;
unsigned char IIC_t4 = 0;
unsigned char IIC_t5 = 0;
unsigned char IIC_temp0 = 0;
unsigned char IIC_temp1 = 0;
unsigned char IIC_temp2 = 0;
unsigned char IIC_tempL=0;
IIC_StartBit();
IIC_Send_Add_RW(HMC5883_A_add,0); // select the chip and command
IIC_t0= IIC_SendByte(CONFA); // send address to write
IIC_t1= IIC_SendByte(CONFIG_A); // write value to register
IIC_StopBit();
delay(100);
IIC_StartBit();
IIC_Send_Add_RW(HMC5883_A_add,0); // select the chip and command
IIC_t0= IIC_SendByte(CONFB); // send address to write
IIC_t3= IIC_SendByte(CONFIG_B); // Write value to register
IIC_StopBit();
delay(100);
IIC_StartBit();
IIC_Send_Add_RW(HMC5883_A_add,0); // select the chip and command
IIC_t0= IIC_SendByte(MODER); // send address to write
IIC_t5= IIC_SendByte(CONFIG_M);
IIC_StopBit();
delay(100);
IIC_StartBit();
IIC_Send_Add_RW(HMC5883_A_add,0); // select the chip and command
IIC_SendByte(CONFA); // addressing CONFIG_A reg
IIC_StopBit();
IIC_StartBit();
IIC_Send_Add_RW(HMC5883_A_add,1); // select the chip and command
IIC_temp0 = IIC_ReadByte(); // read CONFIG_A
IIC_StopBit();
IIC_StartBit();
IIC_Send_Add_RW(HMC5883_A_add,0); // select the chip and command
IIC_SendByte(CONFB); // addressing CONFIG_A reg
IIC_StopBit();
IIC_StartBit();
IIC_Send_Add_RW(HMC5883_A_add,1); // select the chip and command
IIC_temp1 = IIC_ReadByte(); // read CONFIG_B (address auto increment)
IIC_StopBit();
IIC_StartBit();
IIC_Send_Add_RW(HMC5883_A_add,0); // select the chip and command
IIC_SendByte(MODER); // addressing CONFIG_A reg
IIC_StopBit();
IIC_StartBit();
IIC_Send_Add_RW(HMC5883_A_add,1); // select the chip and command
IIC_temp2 = IIC_ReadByte(); // read MODE register
IIC_StopBit();
// Debug info
Serial.println(IIC_t0,HEX);
Serial.println(IIC_t1,HEX);
Serial.println(IIC_t2,HEX);
Serial.println(IIC_t3,HEX);
Serial.println(IIC_t4,HEX);
Serial.println(IIC_t5,HEX);
Serial.print("CONFIG_A =");
Serial.println(CONFIG_A,HEX);
Serial.println(IIC_temp0,HEX);
Serial.print("CONFIG_B =");
Serial.println(CONFIG_B,HEX);
Serial.println(IIC_temp1,HEX);
Serial.print("CONFIG_M =");
Serial.println(CONFIG_M,HEX);
Serial.println(IIC_temp2,HEX);
}
int compass_read(int comp[3])
{
// this attempts to read 6 bytes out of the compass from 0x03 to 0x09
//
unsigned char XH=0,XL=0,ZH=0,ZL=0,YH=0,YL=0;
// unsigned char temp_data;
IIC_StartBit(); // send a start condition on the bus
IIC_Send_Add_RW(HMC5883_R_add,0); // send the address with Write mode
IIC_SendByte(0x03); // send the value of the address we want to read
IIC_StopBit();
IIC_StartBit(); // send a start condition on the bus
IIC_Send_Add_RW(HMC5883_R_add,1); // send the address with Write mode
XH = IIC_ReadByte(); // X high read from chip
IIC_StopBit();
IIC_StartBit(); // send a start condition on the bus
IIC_Send_Add_RW(HMC5883_R_add,0); // send the address with Write mode
IIC_SendByte(0x04); // send the value of the address we want to read
IIC_StopBit();
IIC_StartBit(); // send a start condition on the bus
IIC_Send_Add_RW(HMC5883_R_add,1); // send the address with Write mode
XL = IIC_ReadByte(); // X Low next byte
IIC_StopBit();
IIC_StartBit(); // send a start condition on the bus
IIC_Send_Add_RW(HMC5883_R_add,0); // send the address with Write mode
IIC_SendByte(0x05); // send the value of the address we want to read
IIC_StopBit();
IIC_StartBit(); // send a start condition on the bus
IIC_Send_Add_RW(HMC5883_R_add,1); // send the address with Write mode
ZH = IIC_ReadByte(); // Z high
IIC_StopBit();
IIC_StartBit(); // send a start condition on the bus
IIC_Send_Add_RW(HMC5883_R_add,0); // send the address with Write mode
IIC_SendByte(0x06); // send the value of the address we want to read
IIC_StopBit();
IIC_StartBit(); // send a start condition on the bus
IIC_Send_Add_RW(HMC5883_R_add,1); // send the address with Write mode
ZL = IIC_ReadByte(); // Z Low
IIC_StopBit();
IIC_StartBit(); // send a start condition on the bus
IIC_Send_Add_RW(HMC5883_R_add,0); // send the address with Write mode
IIC_SendByte(0x07); // send the value of the address we want to read
IIC_StopBit();
IIC_StartBit(); // send a start condition on the bus
IIC_Send_Add_RW(HMC5883_R_add,1); // send the address with Write mode
YH = IIC_ReadByte(); // Y high
IIC_StopBit();
IIC_StartBit(); // send a start condition on the bus
IIC_Send_Add_RW(HMC5883_R_add,0); // send the address with Write mode
IIC_SendByte(0x08); // send the value of the address we want to read
IIC_StopBit();
IIC_StartBit(); // send a start condition on the bus
IIC_Send_Add_RW(HMC5883 _R_add,1); // send the address with Write mode
YL = IIC_ReadByte(); // Y Low
IIC_StopBit(); // we have read all the bytes we need and finish the conversation
comp[0] = (XH << 8) + XL;
comp[1] = (ZH << 8) + ZL;
comp[2] = (YH << 8) + YL;
return *comp; // tell everyone what we heard
}
float raw_to_gs(int Scaled[3])
{
float RESULTS[3];
RESULTS[0] = float(Scaled[0]) * Scale_Factor;
RESULTS[1] = float(Scaled[1]) * Scale_Factor;
RESULTS[2] = float(Scaled[2]) * Scale_Factor;
return *RESULTS;
}
// I2c IIC I^c interface core below here
void IIC_Init(void) // sets up intial pin state
{
pinMode(SCL_PIN,OUTPUT);
pinMode(SDA_PIN,OUTPUT);
digitalWrite(SDA_PIN,HIGH); // master idle mode
digitalWrite(SCL_PIN,HIGH); // master idle mode
}
void IIC_StartBit(void) // sends a start condition on the IIC bus
{ // data changes while clock high
pinMode(SDA_PIN,OUTPUT);
delayMicroseconds(21);
digitalWrite(SCL_PIN,HIGH);
delayMicroseconds(21);
digitalWrite(SDA_PIN,LOW);
}
void IIC_StopBit(void) // sends/creates a stop condition on the IIc bus
{ // data changes while clock high
pinMode(SDA_PIN,OUTPUT);
digitalWrite(SCL_PIN,HIGH);
delayMicroseconds(21);
digitalWrite(SDA_PIN,HIGH);
}
unsigned char IIC_SendByte(unsigned char data) // sends byte bit by bit
{ // data changes while clock low
// char dla;
// data = 0xFF;
unsigned char ack_result;
pinMode(SDA_PIN,OUTPUT);
delayMicroseconds(21);
// Start of bit 8 MSB
digitalWrite(SCL_PIN,LOW);
delayMicroseconds(21); //waits a little bit of time to wait for clock to stablize
if ((data & 0b10000000)==0)
digitalWrite(SDA_PIN,LOW);
else
digitalWrite(SDA_PIN,HIGH); // sets or clears the data line according to the bit 8
delayMicroseconds(21); // lets data settle on the bus
digitalWrite(SCL_PIN,HIGH); // tells slave to read the pin
delayMicroseconds(21); // gives the slave time to read the pin
// FINSIH of bit 7 START of bit 6 // and repeats for the rest of the byte
digitalWrite(SCL_PIN,LOW);
if ((data & 0b01000000)==0)
digitalWrite(SDA_PIN,LOW);
else
digitalWrite(SDA_PIN,HIGH);
delayMicroseconds(21);
digitalWrite(SCL_PIN,HIGH);
delayMicroseconds(21);
// FINSIH of bit 6 START of bit 5
digitalWrite(SCL_PIN,LOW);
if ((data & 0b00100000)==0)
digitalWrite(SDA_PIN,LOW);
else
digitalWrite(SDA_PIN,HIGH);
delayMicroseconds(21);
digitalWrite(SCL_PIN,HIGH);
delayMicroseconds(21);
// FINSIH of bit 5 START of bit 4
digitalWrite(SCL_PIN,LOW);
if ((data & 0b00010000)==0)
digitalWrite(SDA_PIN,LOW);
else digitalWrite(SDA_PIN,HIGH);
delayMicroseconds(21);
digitalWrite(SCL_PIN,HIGH);
delayMicroseconds(21);
// FINSIH of bit 4 START of bit 3
digitalWrite(SCL_PIN,LOW);
if ((data & 0b00001000)==0)
digitalWrite(SDA_PIN,LOW);
else
digitalWrite(SDA_PIN,HIGH);
delayMicroseconds(21);
digitalWrite(SCL_PIN,HIGH);
delayMicroseconds(21);
// FINSIH of bit 3 START of bit 2
digitalWrite(SCL_PIN,LOW);
if ((data & 0b00000100)==0)
digitalWrite(SDA_PIN,LOW);
else
digitalWrite(SDA_PIN,HIGH);
delayMicroseconds(21);
digitalWrite(SCL_PIN,HIGH);
delayMicroseconds(21);
// FINSIH of bit 2 START of bit 1
digitalWrite(SCL_PIN,LOW);
if ((data & 0b00000010)==0)
digitalWrite(SDA_PIN,LOW);
else
digitalWrite(SDA_PIN,HIGH);
delayMicroseconds(21);
digitalWrite(SCL_PIN,HIGH);
delayMicroseconds(21);
// FINSIH of bit 1 START of bit 0
digitalWrite(SCL_PIN,LOW);
if ((data & 0b00000001)==0)
digitalWrite(SDA_PIN,LOW);
else
digitalWrite(SDA_PIN,HIGH);
delayMicroseconds(21);
digitalWrite(SCL_PIN,HIGH);
delayMicroseconds(21);
// FINSIH of bit 0 START of ACK bit
digitalWrite(SCL_PIN,LOW);
pinMode(SDA_PIN,INPUT); // set data pin to read
delayMicroseconds(25);
digitalWrite(SCL_PIN,HIGH); // clock in the ack signal
delayMicroseconds(25);
ack_result = digitalRead(SDA_PIN);
digitalWrite(SCL_PIN,LOW);
digitalWrite(SDA_PIN,LOW); // could allow clock streatching here wait for ack (if high wait for low)
pinMode(SDA_PIN,OUTPUT); // set data pin to write
delayMicroseconds(21);
//added above two lines since scoped. seems to work.
return ack_result;
}
unsigned char IIC_ReadByte(void) // receives a byte bit by bit.
{
unsigned char b,result = 0,var; // index bit
// digitalWrite(SDA_PIN,LOW);
pinMode(SDA_PIN,INPUT); // done after clock is low to avoid stop/start cond
digitalWrite(SCL_PIN,LOW); // makes sure the bus is low
delayMicroseconds(21);
digitalWrite(SCL_PIN,HIGH); // pull clock high
delayMicroseconds(21); // wait for data to be valid
var = digitalRead(SDA_PIN); // read the pin
if (var==1)
result = (result | 0b10000000); // load up the result
digitalWrite(SCL_PIN,LOW); // could move up three line to above the if and use the if time for delay
delayMicroseconds(21);
digitalWrite(SCL_PIN,HIGH); // set the clock
delayMicroseconds(21);
var = digitalRead(SDA_PIN); // read the data pin
if (var==1)
result = (result | 0b01000000); // store result
digitalWrite(SCL_PIN,LOW);
delayMicroseconds(21); // start next bit
digitalWrite(SCL_PIN,HIGH);
delayMicroseconds(21);
var = digitalRead(SDA_PIN);
if (var==1)
result = (result | 0b00100000);
digitalWrite(SCL_PIN,LOW);
delayMicroseconds(21);
digitalWrite(SCL_PIN,HIGH);
delayMicroseconds(21);
var = digitalRead(SDA_PIN);
if (var==1)
result = (result | 0b00010000);
digitalWrite(SCL_PIN,LOW);
delayMicroseconds(21);
digitalWrite(SCL_PIN,HIGH);
delayMicroseconds(21);
var = digitalRead(SDA_PIN);
if (var==1)
result = (result | 0b00001000);
digitalWrite(SCL_PIN,LOW);
delayMicroseconds(21);
digitalWrite(SCL_PIN,HIGH);
delayMicroseconds(21);
var = digitalRead(SDA_PIN);
if (var==1)
result = (result | 0b00000100);
digitalWrite(SCL_PIN,LOW);
delayMicroseconds(21);
digitalWrite(SCL_PIN,HIGH);
delayMicroseconds(21);
var = digitalRead(SDA_PIN);
if (var==1)
result = (result | 0b00000010);
digitalWrite(SCL_PIN,LOW);
delayMicroseconds(21);
digitalWrite(SCL_PIN,HIGH);
delayMicroseconds(21);
var = digitalRead(SDA_PIN);
if (var==1)
result = (result | 0b00000001);
digitalWrite(SCL_PIN,LOW);
delayMicroseconds(21);
pinMode(SDA_PIN,INPUT);
delayMicroseconds(21);
digitalWrite(SCL_PIN,HIGH);
delayMicroseconds(25);
var = digitalRead(SDA_PIN);
digitalWrite(SCL_PIN,LOW);
pinMode(SDA_PIN,OUTPUT);
delayMicroseconds(21);
// digitalWrite(SDA_PIN,HIGH);
// result = 0;
return result;
}
void IIC_Send_Add_RW(unsigned char IIC_add,unsigned char IIC_rw)
{
unsigned char IIC_data = 0;
// Serial.print("in add :");
// Serial.print(add,HEX);
IIC_data = (IIC_add <<1); // shifts address to MSB's
IIC_data = (IIC_data | IIC_rw); // adds rw bit in LSB 0=write 1 = read as per datasheet.
// Serial.print(" out data :");
// Serial.println(data,HEX);
IIC_SendByte(IIC_data);
}
What the above code attempts to do is..
setup
prep the SDA, SCL pins for I2c operation
Init_Compass
init the compass chip.
this sends CONFIG_A,B,M to register 0,1,2 it then reads these registers and might print them on the serial port for debugging.
Loop
this reads registers 3-8 of the compass and does a little maths on it and produces the heading value depending on how the compass chip is rotated.
raw_to_gs
scales the results of the compass_read values
the main loop then does the maths on the result to get the heading.
This code (for me) gives the same results as the HMC5883 example code i found.
If anyone can point out where i have gone wrong please publicly ridicule me in the comments section, just be sure to show where i went wrong..
i am sure someone will tell me that my problems are speed related, but i get the same results at 10khz as 370khz.. as measured on the CRO at work. i have tired slowing things down and speeding up but all gives the same results.. pull up resistors are 2k2.. i cant explain the small sections of 3.0v on the bus when using the MAX32..
This comment has been removed by the author.
ReplyDeletei have a solution.. see the post on April 19th. 2013. happy days.
ReplyDelete