To start with I am not too sure if this will work if it does i am still not sure if this will help me reach my goal. For my DIY GPR I need a way to count really really small time units. So i can determine how long the reflected radio wave have taken to reach an object bounce off it and return to my antenna.
So I have devised a possible solution to solve my problem. what i have is a 1Ghz oscillator ASG-P-X-A-1.000GHZ, this device is $28au from digikey it has LVDS output which is nice for my first UHF PCB. coupled to that i have a MCA100EP016AFAG 8 bit counter capable of 2.2Ghz.
I started to lay the board out then i realized that if i make this board i will have spares as its a prototype for my GPR so i may use it somewhere else in the future if all goes well, so i changed the PCB from 50x50 to fit a arduino MEGA footprint. this would allow me to Hack on either an arduino MEGA or a MAX32. once i found the footprint of the MEGA (see my previous post) i noticed that i have Stacks of free space on the board so i thought about what else this counter might need.. i started with space for 16Mbytes of FLASH serial SPI memory, then i my attention moved to how i was going to trigger the counter to start and stop so i can read it at my pleasure as this counter will be configured as a single shot to avoid having to incorporate highspeed RAM to store results in, i figured a simple counter on the enable line might do the trick, so i have added a decade counter 744017and a four element SR latch 74279.
As the Counter and oscillator are 3.3v devices this board really shouldn't be used on a mega, i will only use it with a MAX32 as they are a 3.3 volt board.
With the SR latch and the decade counter being stock standard 74 series logic chip i could replace these with any logic chip from that family. i have only wired the ground and power pins the others go to pads so they can be rats nested into any configuration that i may need in the future..
I also have a alternate Voltage regulator for 3.3v a stack of decoupling caps, the voltage regulator is either supplied off the arduino 5v rail or its own input header.the regulator accepts any 78xx SOT223 regulator that has a IN-GND-OUT pin footprint.
So the plan seems sound enough for now, i will spend $40 and get 10 of these boards made once i have laid out all the tracks. hopefully i will be able to get the oscillator running, the counter counting, then i can see about starting and stopping the counter with a signal. my plan is to use the SR latch to latch the enable pin to high (its active low) then when a ping is sent i release the counter to ... count :) then when a echo comes along we can stop it. read the time and reset everything and start again, this time we use the decade counter to miss/ignore the first echo and capture the second echo, this may require me to pre-seed the decade counter before i send a ping, but this board is so generic i can work that out while i wait for the oscillator and counter to arrive..
thats what i have laid out so far. just a few tracks here and there and a carefully placed ground plane and i should be good to send it off to the factory in china to be made..
I have picked my pins i hope carefully, so all the important information can be read and written in single word instructions, i have the first 8 bits of the 1Ghz counter on one port the next 12 bits are on another port and the preset data are on two ports of the PIC, this means some are side by side and others are spread across the PCB. the pins for the SPI flash chips are broken out so i can solder thwm to where ever i like. this is not the main purpose of the board so they will need to be rats nested to the MAX32 pins. you may notice that there are places for two variable resistors, for my initial purpose these pins are pulled high, but other oscillators use these pins to set the frequency, in case i find 1ns is not fine enough resolution i can simply change parts without needing fresh boards to be made or horrible hacks near the RFHF's in that area.. i will enclose the oscillator and support components under a tin can to keep the signal from radiating everywhere and potentially causing problems.
Thursday, 20 September 2012
Tuesday, 18 September 2012
Arduino Mega Hole Spacings
Found a useful link. I don't like eagle, as i use Altuim, so i was trying to find the hole spacing for the arduino clone Chipkit MAX32.
http://www.wayneandlayne.com/files/common/arduino_mega_drawing_5000x2642.png
also hosted on that site is the uno hole spacings. as i said i was looking for the chipkit max32 hole spacing, all you need is to add second row here and there and your done. enjoy.
http://www.wayneandlayne.com/files/common/arduino_mega_drawing_5000x2642.png
also hosted on that site is the uno hole spacings. as i said i was looking for the chipkit max32 hole spacing, all you need is to add second row here and there and your done. enjoy.
Sunday, 16 September 2012
Ground Penetrating Radar
At a loss with the compass my attention has wondered to a thought experiment regarding GPR or Ground Penetrating Radar.
I was asked a while ago by a friend if it was possible to build a GPR DIY style. At the time i thought no there is no way.. A few years have passed since and i thought i would revisit the idea.
So What is GPR. Its just like normal Aircraft Radar. Aircraft Radar works by sending out a radio wave or pulse and listening for the signal to bounce off and return. If you know the direction of where you sent the pulse and how long the pulse takes to return, if you measure the difference in the frequency of the echo you get the speed of the aircraft. look up the Doppler effect for the speed i wont go into that here as the ground has no speed.
So with GPR the same thing happens, only we send the pulse into the ground and wait for the reflections. as a Radio wave is just a light wave in a different band the distance traveled is the same as the speed of light. there should be a little shift in this value (according to my logic in my head) for when traveling through rock, soil, sand and water, but i haven't found this information on the net i may not need to worry about it as this device would be just a indicator of where you might find something, not a life or death situation.
Having a little search around on the internet I see that High frequencies don't travel well through solid objects, i found a note on a forum that said the signals with the best results for GPR are likely to be around 10Mhz to 100Mhz. From playing around in the past i should be able to generate a pulse in that range and receive it with a pair of small antenna's. The hard part will be counting the time travel time of the pulse.
Lets define a few parameters.
Depth : 30 Meters
Resolution (D) : < 1 Meter
Resolution (H) : < 1 Meter^2
Hardware
To work out what hardware will be required we need to do a little maths.. First the speed of Light is 299792358m/s. that means our pulse will travel 299792358 meters in one second. if we are to get <1m accuracy in the Depth Axis we will need to count really fast. Aircraft Radar has a lower resolution that we need, the planes are further away so the timing is easier with old hardware. if we are to have a range of 30 or so meters we will need to be able to count for
(30*2)/299792358 = 1 / 4996539.3 = 0.0000002 or 200ns (*2 is because we need to time the reflection so 50 meters)
our Resolution will depend on how high we can count to. in 200ns. i don't know of too many MCU's that are capable of achieving the speeds this is looking like requiring. so lets do the maths and see.
eg.
if we can count to 4 in 200ns that will mean 60m/4 = 15m / count.
in 200ns the pulse/wave will travel 60meters, if we want sub meter resolution we will need to break that 200ns into slots, 4 slots would = 15meters per slots. not very clear, clearer than a traditional metal detector. but not GPR worthy.
what if we can count to 256 in 200ns?
60/256 = 0.2343m/count or 23cm so when we see i ping bounce back on the display, and it is actually at 1meter depth, our system may report it as being between 1.00m and 1.25m if its actually at 1.1meters depth the system may report it at a depth of between 1.0 and 1.25m. not to bad if you had to dig another foot or a foot less that's ok with me.
so to count to 256 in 200 nano seconds we need a clock of... 1.28Ghz.. ARGH!!!!
as i said i dont know of a MCU that will do this...
As a possible solution to the crazy clocking speeds I would need on a MCU what actually needs to happen at that rate is, when an echo is received we need to know the time. echo strength would be nice too, that would depend on the radio set used if that information is available. what we might be able to do is have a free running oscillator at 1.2Ghz that is fed to a 8bit counter, then when an echo is read the count/time is stopped and is latched out of the counter and presented to a user in a usable fashion.
This system will work for a the first echo. now the output i have seen on TV when these things are used on documentary films is a multiple echo report. that is their data uses multiple echos to create the map for interpretation..
Depending on what we want to use the GPR for this may be all that we need, to overcome this limitation my first solution is a simple one, have multiple (A)counters all started and reset in parallel but stopped and read out individually by a second (B)counter or gate that counts the number of echos. so the theory is, the (B)counter counts echos and stops (A)counter1 when on the first echo, (A)counter2 is stoped on echo pulse 2 and so on until there are no more counters. the MCU is then free to read the counters out and do some maths and present the data.
I would like to know the nitty and gritty of how the commercial units do the data acquisition are they doing it at these speeds? i don't think so from what i read on the net for shallower GPR a higher base frequency is used but its the reflection time that gets reduced by a massive amount that results in the crazy high clock speed that i have calculated, and they are probably much higher when they use a GPR on a shallow setting..
so my parts list would look something like this..
MCU
LCD screen
Rf Transmitter 20-100Mhz
Rf Receiver 20-100Mhz
Storage
GPS ( so the data can be mapped against an existing map)
1.2Ghz clock and counter
looking at Element14.com i am not seeing a easy way for me to be generating 1.2Ghz clock for the counter. I have located
8bit Counter MC100E137FNG @ $20au each. its rated to 2.2Ghz :)
10ghz oscilator @$66au ea will need a gate and buffer
ITDB02 2.4E LCD with touch screen
UNO32 Arduino clone80MIPS
RF transmitter and receiver to be located. or constructed from discrete components (the old school way. .that is the hard way)
so the plan is to us the 1Ghz oscillator to clock the counter until an echo signal is received, stop the clock and display the number that results. maybe i could use one counter and stop it with a second counter like the above idea but without the multiple counters, this will require me to make multiple passes at the one spot but with a sample time of 200ns i think this could be an option for a hand held device. with a 1Ghz clock each count on the timer will be 1ns to get the 200ns that equals 60 reflected meters i would need to count to 200. a count of 256 would be equal to 38.4 meters if our power level allows.
that leads me to another method of capturing multiple echo's with one timer, some how it maybe possible to alter the power of the ping in say 5 steps then make a reading at each power level and accumulate the data into something understandable.
having troubles of thinking of a place that would be good to test this. maybe a cemetery but thats just a bit rude.. I know of a place where i could use it to find some interesting structures but i need to be able to confirm my GPR results, ie dig a hole.. thats why a cemetery would be good (for at least the first 6 or so feet of depth).. the plots are all marked out in rows nice and neat and uniform.
So over the next few weeks i might gather up some bits and pieces and see what can't be seen..
I was asked a while ago by a friend if it was possible to build a GPR DIY style. At the time i thought no there is no way.. A few years have passed since and i thought i would revisit the idea.
So What is GPR. Its just like normal Aircraft Radar. Aircraft Radar works by sending out a radio wave or pulse and listening for the signal to bounce off and return. If you know the direction of where you sent the pulse and how long the pulse takes to return, if you measure the difference in the frequency of the echo you get the speed of the aircraft. look up the Doppler effect for the speed i wont go into that here as the ground has no speed.
So with GPR the same thing happens, only we send the pulse into the ground and wait for the reflections. as a Radio wave is just a light wave in a different band the distance traveled is the same as the speed of light. there should be a little shift in this value (according to my logic in my head) for when traveling through rock, soil, sand and water, but i haven't found this information on the net i may not need to worry about it as this device would be just a indicator of where you might find something, not a life or death situation.
Having a little search around on the internet I see that High frequencies don't travel well through solid objects, i found a note on a forum that said the signals with the best results for GPR are likely to be around 10Mhz to 100Mhz. From playing around in the past i should be able to generate a pulse in that range and receive it with a pair of small antenna's. The hard part will be counting the time travel time of the pulse.
Lets define a few parameters.
Depth : 30 Meters
Resolution (D) : < 1 Meter
Resolution (H) : < 1 Meter^2
Hardware
To work out what hardware will be required we need to do a little maths.. First the speed of Light is 299792358m/s. that means our pulse will travel 299792358 meters in one second. if we are to get <1m accuracy in the Depth Axis we will need to count really fast. Aircraft Radar has a lower resolution that we need, the planes are further away so the timing is easier with old hardware. if we are to have a range of 30 or so meters we will need to be able to count for
(30*2)/299792358 = 1 / 4996539.3 = 0.0000002 or 200ns (*2 is because we need to time the reflection so 50 meters)
our Resolution will depend on how high we can count to. in 200ns. i don't know of too many MCU's that are capable of achieving the speeds this is looking like requiring. so lets do the maths and see.
eg.
if we can count to 4 in 200ns that will mean 60m/4 = 15m / count.
in 200ns the pulse/wave will travel 60meters, if we want sub meter resolution we will need to break that 200ns into slots, 4 slots would = 15meters per slots. not very clear, clearer than a traditional metal detector. but not GPR worthy.
what if we can count to 256 in 200ns?
60/256 = 0.2343m/count or 23cm so when we see i ping bounce back on the display, and it is actually at 1meter depth, our system may report it as being between 1.00m and 1.25m if its actually at 1.1meters depth the system may report it at a depth of between 1.0 and 1.25m. not to bad if you had to dig another foot or a foot less that's ok with me.
so to count to 256 in 200 nano seconds we need a clock of... 1.28Ghz.. ARGH!!!!
as i said i dont know of a MCU that will do this...
As a possible solution to the crazy clocking speeds I would need on a MCU what actually needs to happen at that rate is, when an echo is received we need to know the time. echo strength would be nice too, that would depend on the radio set used if that information is available. what we might be able to do is have a free running oscillator at 1.2Ghz that is fed to a 8bit counter, then when an echo is read the count/time is stopped and is latched out of the counter and presented to a user in a usable fashion.
This system will work for a the first echo. now the output i have seen on TV when these things are used on documentary films is a multiple echo report. that is their data uses multiple echos to create the map for interpretation..
Depending on what we want to use the GPR for this may be all that we need, to overcome this limitation my first solution is a simple one, have multiple (A)counters all started and reset in parallel but stopped and read out individually by a second (B)counter or gate that counts the number of echos. so the theory is, the (B)counter counts echos and stops (A)counter1 when on the first echo, (A)counter2 is stoped on echo pulse 2 and so on until there are no more counters. the MCU is then free to read the counters out and do some maths and present the data.
I would like to know the nitty and gritty of how the commercial units do the data acquisition are they doing it at these speeds? i don't think so from what i read on the net for shallower GPR a higher base frequency is used but its the reflection time that gets reduced by a massive amount that results in the crazy high clock speed that i have calculated, and they are probably much higher when they use a GPR on a shallow setting..
so my parts list would look something like this..
MCU
LCD screen
Rf Transmitter 20-100Mhz
Rf Receiver 20-100Mhz
Storage
GPS ( so the data can be mapped against an existing map)
1.2Ghz clock and counter
looking at Element14.com i am not seeing a easy way for me to be generating 1.2Ghz clock for the counter. I have located
8bit Counter MC100E137FNG @ $20au each. its rated to 2.2Ghz :)
10ghz oscilator @$66au ea will need a gate and buffer
ITDB02 2.4E LCD with touch screen
UNO32 Arduino clone80MIPS
RF transmitter and receiver to be located. or constructed from discrete components (the old school way. .that is the hard way)
so the plan is to us the 1Ghz oscillator to clock the counter until an echo signal is received, stop the clock and display the number that results. maybe i could use one counter and stop it with a second counter like the above idea but without the multiple counters, this will require me to make multiple passes at the one spot but with a sample time of 200ns i think this could be an option for a hand held device. with a 1Ghz clock each count on the timer will be 1ns to get the 200ns that equals 60 reflected meters i would need to count to 200. a count of 256 would be equal to 38.4 meters if our power level allows.
that leads me to another method of capturing multiple echo's with one timer, some how it maybe possible to alter the power of the ping in say 5 steps then make a reading at each power level and accumulate the data into something understandable.
having troubles of thinking of a place that would be good to test this. maybe a cemetery but thats just a bit rude.. I know of a place where i could use it to find some interesting structures but i need to be able to confirm my GPR results, ie dig a hole.. thats why a cemetery would be good (for at least the first 6 or so feet of depth).. the plots are all marked out in rows nice and neat and uniform.
So over the next few weeks i might gather up some bits and pieces and see what can't be seen..
Friday, 14 September 2012
I am lost.. and i have a compass...
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..
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..
Subscribe to:
Posts (Atom)