NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.
Project Help and Ideas » I2C Magnetic sensor Module
May 15, 2011 by Ralphxyz ![]() |
As part of my Weather Station I have a I2C Weather Vane i.e. Wind Direction. The device I have is from SURE electronics. The User Manual or instructions are quite lacking and there are only a few examples of it's use on the web. There are a couple of examples using Arduino. I was starting to write up some code when I questioned a I2C function so I thought I'd look at some working I2C code so I opened up Rick's I2C Nunchuck code. Besides the DeviceID and the Nunchuck returning 6 registers versus 5 for the Compass Module it looks like I "should" be able just use Rick's code. Changing the LCD output of course. Well that is what I thought at least. I am getting a "2nd start bad: 207" error. I'll be looking at the code but thought I'd get the ball rolling on using the DC-SS503 Compass sensor Module which I have not seen mentioned here in the Nerdkit's forums. Comments please. Oh this is using Peter Fleury's TWI library of course. Ralph |
---|---|
May 16, 2011 by Ralphxyz ![]() |
I am sure my problem is with Slave device addressing. The Slave 7 bit address is 0x60. A confusion pont is:
So we have the 7 bit address 0x60 + 1 or 01100000 + 00000001 that gives me 01100001. But this is supposed to be a 8 bit address 11000001. See why I am confused? Then the fact that it works just makes it worse. Ralph |
May 16, 2011 by Noter ![]() |
It works? I thought you were getting an error - doesn't that mean it's not working? |
May 16, 2011 by OliverM ![]() |
Hello Ralph, as I just got my nerdkit, this is my first time posting here. I have no real experience on the I2C bus, but googeling a bit gave me an idea that might help you: First the master sends the 7 address bits and then a bit which tells the slave whether the master wants to read or write. In effect one complete byte, consisting of 7bit address + direction. The byte looks like this:
if you just copy the 7bit address into a variable, the byte looks like this:
If you now add the direction bit you get
which is not what you want. You have to shift the bits of the address one place to the left and then add direction. Shifting to the left can be achieved by multiplying with 2. 7 Bit address:
multiply with 2:
add direction:
or in code :
I hope this helps Oliver |
May 16, 2011 by Ralphxyz ![]() |
Paul, "it works" as in it works in the Wii Nunchuck program (and else where). Oliver thank you that would make more sense but I have uint8_t ret=i2c_start(Nunchk_ADR+I2C_WRITE); i2c_start is a library function I do not believe it does a multiplication but I'll have to look. That would make sense. Just to be clear I am still getting the "2nd start bad: 207" error that is the specific problem. The problem I have with uint8_t ret=i2c_start(Nunchk_ADR+I2C_WRITE); is with my understanding of what is going on NOT with the program it comes from (which as I said works)!! Ralph |
May 16, 2011 by Ralphxyz ![]() |
Duh boy uint8_t ret=i2c_start(Nunchk_ADR+I2C_WRITE); had me going around in circles but I suppose that is good as I had to finally pay attention to what was going on. The Slave address for the Nunchuck is 8 bit not 7 bit!!
I am sure Rick had told me that but it had slipped away. For my project when I tried using a 8 bit format with 0 for write. I immediately got the :
error messages. Well I am sure it is Slave address that is causing me problems so I'll keep messing around. Now at least I know what I am doing and why, so that's progress. Ralph |
May 16, 2011 by Noter ![]() |
Ok, now I understand what you meant by it works. There is only a 7 bit device address in I2C land. No such thing as an 8 bit address. Maybe reviewing the TWI doc in the ATmega datasheet will help you get a firm handle on it. When you put the 7 bit device address in bits 8:1 and then put a 1 in bit 0, the resulting byte is called the SLA-W which stands for "slave address - write". Then for a device address of 0x66, depending on how you write your code it may look like any of the following:
They all result in exactly the same value in SLA_W. Seems the Nunchuck code you are looking over uses the (0xCC + 1) type style. I always us the (0x66<<1 | 1) type style because I like to see/recognize the device address and the logical 'or' makes more sense to me for setting a single bit. But they are all equivalent and all will work. |
May 16, 2011 by OliverM ![]() |
0xA4 is 0b10100100 maybe Nunchk_ADR is already in the form
|
May 17, 2011 by Ralphxyz ![]() |
Yes Paul, I like the (0x66<<1 | 1) format, it is definitely more comprehensible for me. All of the specsheets specify the 7 bit address so it is a twist to use a 8 bit representation of the 7 bit address in the expression (Nunchk_ADR+I2C_WRITE). It works but tends to confuse poor soles like me. I always say 8 bit address when what I really mean is "7 bit address left shifted 1 plus function bit" 8 bit address is just easier to say. Actually if I had used your I2C EEPROM code instead of the Nunchuck code this probable would not have been a issue. The Nunchuck code uses very similar 6 register reads and I need to read 5 at a time so I was using that. Yes Oliver, [quote]maybe Nunchk_ADR is already in the form aaaaaaa0[/quote] I really have a hard time understanding your aaaaaaa0 syntax. That must come from a discipline I am not familiar with. What exactly does the "a" represent? "a" = any is my guess. I can see where "d" could/would mean "direction" (read/write). It's interesting to see new things so thank you again. There will be more so stand by!! Ralph |
May 17, 2011 by OliverM ![]() |
Hi Ralph, with 'a' I mean one bit from the address, so the 7 bit address is aaaaaaa. If you shift the address one to the the left you get aaaaaaa0. When left shifted, the last bit on the rigt side gets the value 0. The last bit on the right can then used as the function bit. I hope I made myself a bit clearer. Oliver |
May 17, 2011 by Ralphxyz ![]() |
Hi Oliver, I understand what you are doing just wondering where the "a" syntax came from. Actually "a" works as any format of the bit 1 or 0 Then I suppose you might have used eeeeeee0 as in "e" for either, that is "either 1 or 0". Just another trivial thing to distract my none too focused mind. Ralph |
May 17, 2011 by Noter ![]() |
Bit pattern defs - "a" comes from the word address. "d" would be data. "x" means don't care. "1" means 1. "0" means 0. One place that you will see these used a lot is in avrdude device definitions. |
May 17, 2011 by Ralphxyz ![]() |
Where does "a" address come from in "aaaaaaa0"? I am familiar with "a" = address but not in this context. Ralph |
May 17, 2011 by Noter ![]() |
I don't understand what you're asking. Are you trying to find who used it first, where it first appeared, where it was first used? Or are you after something specifically realted to the SLA-R? Take a look at your default avrdude config file (avrdude.conf.in) - it goes a step farther using "i" for input and "o" for output. It even adds position numbers to the "a" so you won't get them backwards. |
May 18, 2011 by Ralphxyz ![]() |
I said: [quote]Where does "a" address come from in "aaaaaaa0"?[/quote]Noter said: [quote]I don't understand what you're asking. [/quote]As I had said I have never seen the "aaaaaaa0" nomenclature (syntax). That is using "a" to represent a bit, Oliver said: [quote]with 'a' I mean one bit from the address[/quote]I was just asking Oliver where he learned to use that syntax and then asked in general what the "a" stood for? Now "i" for input and "o" for output I can understand but "a" for 1/0, on/off, hi/lo I do not see the logic of. I could see some logic how he used "d". Oliver said: [quote]add direction:
[/quote] I just do not know where the usage of "a" comes from. I do not think of a bit as representing an address. Possible there is an association that I am just missing it would not be the first time. Oh, also my avrdude.conf.in does not use any input (i) or output (o) designations and certainly no "a". And thanks again Paul for re-re-re-explaining I2C Slave device address I believe it has finally sunk in. Your patience is knightworthy. Ralph |
May 18, 2011 by Noter ![]() |
How else would you represent an address if not with bit(s)? Look further into the avrdude configuration file. All the devices use the bitmap nomenclature as described. Here's an excerpt from the device "m328p". I would think it has to be in your config file too even though you're on an apple computer. If not then you surely will find it in the config file on your windows box.
Then near the top around line 150 you should find this description:
|
May 18, 2011 by Ralphxyz ![]() |
Wow Paul, ask and you shall receive. Well I asked and you certainly delivered on that one. Thank you once again. Now that is a detailed comprehensible answer. Ralph |
May 19, 2011 by Ralphxyz ![]() |
Ok now back to my actual programming problem. I was getting the:
Errors (using Rick's modified Nunchck code). So I have been meticulously taking the code apart to see where the "Bad Start" comes from. Here is working code section!!
And here is code that does not work!!
Let me qualify "does not work": In fact the code does work but I get "WORKStart bad"On the LCD readout! If I do anything else in the while(1) loop I get the "start bad" message. I want to run a for loop to read the registers but keep getting "start bad" By removing the comments "//lcd_write_string(PSTR(" working "));" I do not get the "start bad" Now the "start bad" message is outside the while(1) loop so what does that mean? I sure would appreciate some deliverance as this has had me going around in circles for a few days. Ralph |
May 19, 2011 by Noter ![]() |
You're overlaying the text from all prints on the same line. The start bad is first then it is overwritten with works. Move the works and working writes to the 2nd line on your lcd and you won't be overwriting the error message from i2c_start. |
May 19, 2011 by Ralphxyz ![]() |
Yeah but what is causing the problem? Ralph |
May 19, 2011 by Noter ![]() |
The start bad error is likely due to a slave that cannot be found. Could be a wiring problem or a slave address problem. Do you have the pull-up resistors installed? What value do you have in DCSS503_ADR when calling i2c_start? What is the device address of your slave? |
May 19, 2011 by Ralphxyz ![]() |
Ok I put together an entire program instead of trying to build upon working pieces, which never work. I am actually getting some activity according to my Digital Analyzer: Here is the start of a session it is a wide picture so I didn't inbed it. If you went as far as downloading the ikalogic Digital analyzer application you could actually view the whole session with I2C decoding. This list the comments from the specsheet which I tried to emulate step (cycle) by step. So the code runs!! But I am not getting any output to the LCD!! Here is a excerpt from the code: (I have excluded the leading comments)
I "think" the program is actually running, I do not know what to do to get any LCD readout. And of course "running" is a relative term, the program runs but I am not sure the Slave is actually being addressed or acknowledging any transactions. I do not see any [ack] in the Digital Analyzer file! I do not see either the Slave or the Master issuing a [ack] though the SCL is pulled low at times. Well any comments, they are more than welcome. Any insight you might have is certainly more than what I have at the moment. Ralph |
May 19, 2011 by Ralphxyz ![]() |
Further details: If I comment out all of the I2C code I get the expected output to the LCD!!
So what is it about the I2C code the LCD does not like? Ralph |
May 19, 2011 by Noter ![]() |
Seems you may be sticking in the i2c_start_wait(). Probably same issue as before. According to your code your device address is 0x60 - is that correct? |
May 19, 2011 by Ralphxyz ![]() |
Yes the "7 bit address" is 0x60 from the specsheet *"a Magnetic Sensor Module with a 7-bit device address “[0110000]”. * I posted the Digital Analyzer code on the Scanlogic forum to see what they have to say. I'll try using i2c_start() tomorrow in fact I'll look at using your libNoter library instead of Peter Fluery's. Though I do not know what the differences would be besides syntax. Both libraries have to use the "native" TWI registers. I looked at the Wii Nunchuck with the Digital Analyzer and I do not see [ack]s there either. I know the Scanlogic application has gone through at least two major revisions in the past two months so maybe they remove the acknowledgement from the I2C decode. I think "sticking in i2c_start_wait()" is a logical place to start looking thank you. Ralph Ralph |
May 19, 2011 by Noter ![]() |
“[0110000]” is 0x30. Shifted it would be 0x60. I would try defining DCSS503_ADR as 0x60. |
May 20, 2011 by Ralphxyz ![]() |
Damm, I thought I had tried that. I need to find a new hex to binary converter or get back to the place where I can just look at a hex number and know the binary. I used to be able to do that, and now I am afflicted with the Age Virus. Well I am getting the expected output well I do not actually know what the "expected" output would be but I am getting output and it changes as I revolve the breadboard!! Now to right shift the X-MSB axis (8 places) and add the X-LSB axis to get heading X. That is what I want to do isn't? That would be:
Ralph |
May 20, 2011 by Ralphxyz ![]() |
geesch, I think I want to left shift not right shift. Ok here is what I currently have,
headingX and headingy are coming out as 0.00!!
There is something really wrong with my math. I probable have to do some casting, but not sure what, where or how. Ralph |
May 20, 2011 by Noter ![]() |
When you try to print an int16 as a float you will show 0. Change the %.2f to %d and it might work. |
May 20, 2011 by Ralphxyz ![]() |
Here is a terminal screen shot of output:
I changed to using decimal (d) instead of using float (f). I get the above output on my pc while on the LCD I get "X is -32768". Also if I move the compass I freeze all output to both the LCD and the Terminal. Moving the compass essentially kills all functioning It would help if I had/knew expected values. If anyone would like further reading here are some of the IC compass manufacturer's literature. AN-00MM-001_Magnetometer_Fundamentals_r1_2.pdf AN-00MM-003_Magnetic_Sensor_Calibration_r1_1.pdf AN-00MM-004_Electronic_Tilt_Compensation_r1_1.pdf AN-00MM-005_Magnetic_Sensor_Placement_Guidelines_r1_1.pdf SURE Electronics (breakout board manufacturer) in their literature do not mention having to calibrate the compass. The whole documentation line is not good. I really spent a lot of time to come up with what I have. How does the flow of the program look, do you think it is actually working? Why does it freeze up when I move the compass? I must have a overflow somewhere. Is my math correct? Why don't the LCD and the serial dump agree? Thanks for the help, Ralph |
May 20, 2011 by Noter ![]() |
I don't see where you are sending anything over the usart. How's that happening? |
May 20, 2011 by OliverM ![]() |
Hello Ralph, %d in printf function is for signed integer, but headingX and headingY are declared as unsigned integer. try %u |
May 20, 2011 by Ralphxyz ![]() |
Sorry Paul I slipped the usart function in on you with out mentioning it in particular. Oliver thank you, lets see what %u will do. Using %d and removing any delays I had added in order to see output on the LCD I actually am getting a steady continuos output to the usart. The LCD is flickering by to quickly but the Terminal output is readable. well it was until I removed all of the delays. The %u and %d appear to be the same. But I can see that I am returning:
with a occasional headingX = -32768 So my math definitely is not correct! here it is again:
Also Xlsb and Ylsb are always the same value no matter the heading of the compass so there must be something wrong with my I2C register processing. So with the compass stationary I am getting readings of:
Starting tomorrow I will be offline until Wednesday or Thursday so I might not have a quick response. I sure appreciate all of the help. I am starting to look at the Calibration instructions. I'll need steady consistant headingX and headingY readings before I can even think about calibration of course. I am thinking about setting up a 360˚ servo motor to do the calibration instead of moving the compass 10˚ at a time by hand. Well one of the calibration routines just uses Max/Min values and applied math so the servo might be overkill. Without any delays if I move the compass slowly I do not freeze it up but any quick movements will likely freeze it. Sometimes if it does freeze I can just move the compass a little bit and it will restore. Ralph |
May 20, 2011 by Noter ![]() |
Maybe it won't make a difference but I always use a logical or to put the lsb value into the word. Also try grouping with () to eliminate any misunderstanding as to the order of operation.
|
May 21, 2011 by Ralphxyz ![]() |
Thanks Paul, that makes a difference!! I am now getting:
With the compass perfectly still. At least I am not getting 0 for headingX and headingY so that's progress. I do not know why the X and Y valves are not consistant. I also do not know if they are correct. I wonder why the Xlsb and Ylsb are always they same (+-1 or 2) Ralph |
May 21, 2011 by Ralphxyz ![]() |
Please check my math: (from the first row)
Well that appears to be actually working, amazing. Thanks again Paul your <<8 | method did it. Now why would Xlsb and Ylsb always be the same number when the compass is turned? Can somebody explain why we need a X axis and Y axis? Here supposedly is the formula to get Degrees!
Ralph |
May 21, 2011 by OliverM ![]() |
Hello Ralph, the register value from the magnetic module is 7. That means that the "Take Measurement" Bit is still set. According to the usermanual:
The measurement process has not finished, when you read the values. The usermanual also states A STOP command indicates the end of write operation. A minimal 5ms wait should be given to the Magnetic Sensor Module to finish a data acquisition and return a valid output. In your code
You wait 5ms and then give the stop command. try revers the order and
Oliver |
May 21, 2011 by 6ofhalfdozen ![]() |
Congrats Ralph! Getting stuff to work is always a good start to a weekend! As for the "why x and y?" Sad to say, I know the answer, or perhaps it is better to say that I have had it explained to me enough times that some of it has stuck. It is a lot more simple and yet strangely also more complicated than you would think. One of my friends in college was a math grad/phD student and this was one of the things he obsessed about for his grad work. So he could go on and on and on, about long complicated calculations proving why this is this and so on about seemingly simple stuff. The first time its kinda cool, the 500th time its Snoresville. Anyhow, the short version is that if you only have X, you have a "bead on a string". A straight line value, that can only go back and forth along the string. By adding Y into the mix, it becomes "bead on a table", and you then can go left and right in addition to the back and forth. Since the bead has the extra room it can do more fun things like having the line make angles, curves, as well as rotate and all the other fun stuff. For your magnetic sensor, it is trying to tell how it was moved "on the table", which means it needs to know left/right and forward/back. For the most part, since the sensor is just rotating it actually uses the forward/back (x) to scale the right/left(y) (ie if it goes x 10 and y 10 it is much less of a turn than x 1 y 10 ) which is why the equation shows y/x. As for your binary math, it looks ok but to be honest I stink at binary math with that many digits and shifts. Sorry. Also when I use the degrees calculation as given with your zero rotation data I get >360, which makes me wonder if there is some scaling factor missing or else the equation is misprinted. So for your line#1 data point, if you use the equation as typed "degrees = atan(y/x)180/Pi " gives 3336.62 which last I checked is a little out there. if you swap 180/Pi with /180*Pi it gives you 1.016 which while a little high, isn't a bad value for sitting still. Have you tried seeing what the data looks like when you rotate the sensor?? Perhaps the sensor doesn't like sitting still too well. Anyhow, just a little info and some thoughts. Hope it helps! |
May 21, 2011 by OliverM ![]() |
Hello Ralph, as to why we need an X axis and a Y axis: a magnetic field not only has a strenght, but also a direction. It is a vector. Thats why a compass can tell you where north and south is. Usually this is expressed in a X/Y coordinate system with M(x,y).
Strength of M is SQRT(y^2 + x^2) Angle is atan(y/x)*180/3.14; Oliver |
May 21, 2011 by Noter ![]() |
That is the correct forumla to calculate the angle in radians and convert it to degrees. Using your values, the answer is 31.765 degrees. Here's the long answer as to why you need x and y to get direction - http://en.wikipedia.org/wiki/Trigonometric_functions |
May 21, 2011 by bretm ![]() |
If lsb is staying the same and msb is wiggling slightly, then it very likely means you have lsb and msb swapped. Its probably xmsb=7 and xlsb=122, which gives x=256*7+122=1914. |
May 21, 2011 by OliverM ![]() |
Hello Ralph, I spotted something else that might be a problem
The for loop is executed four times, reading one byte and the sending an acknowledge signal; i2c_readAck(). The loop reads register, Xmsb, Xlsb and Ymsb After the loop one byte is read without sending an acknowledge signal; i2c_readNak(). This reads Ylsb, but that value is not stored. I think the line should look like this
Oliver |
May 21, 2011 by bretm ![]() |
For angle calculation you should use atan2(y, x) instead of atan(y/x). atan2 handles the case where x is zero, and distinguishes between all four quadrants, e.g. x positive and y positive give different angle than x negative and y negative, whereas with atan(y/x) they would give the same result. If the x and y values are unsigned and hence the angle is always in the first quadrant, it's still better use use atan2 because of the x==0 problem and numerical stability when x is small. |
May 26, 2011 by Ralphxyz ![]() |
Unbelieveable, I do not know if I could buy such great support. Thank you all so much!! bretm would I use atan2(y, x)180/3.14; instead of atan(y/x)180/3.14; Oliver, I just used/modified Rick's Nunchuck code for querying the slave.
And then he does some decoding which I could not comprehend so i did not use:
Rick was reading 6 bytes while I need to read 5. I was hoping he would jump in here so that I could ask him about what the Decode was doing. It probable is essential. Now Oliver said: *[quote] the register value from the magnetic module is 7. That means that the "Take Measurement" Bit is still set. According to the usermanual: The TM bit(Take Measurement bit in control register) will be automatically reset to "0" after data from A/D converter is ready. The measurement process has not finished, when you read the values. [/quote]* I didn't like the Register return value (07) I think I'll focus on this to see if I can get it to return 0 (read). Ralph |
May 26, 2011 by Ralphxyz ![]() |
Ok I made the recommended changes:
Now Register is 0 BUT:
This is with a stationary compass. Now rotating the compas does not change the readings!! Ralph |
May 26, 2011 by Noter ![]() |
Why would you set C_data[4]=i2c_readNak(); four times in a row? And now there's no readAck's? I think Oliver had something else in mind. |
May 26, 2011 by Noter ![]() |
Maybe he means to set C_data[4]=i2c_readNak(); after the //STOP and leave the readAck's as they were. |
May 27, 2011 by OliverM ![]() |
Hello Ralph, the code to read the values from the module should look like this:
Oliver |
May 27, 2011 by OliverM ![]() |
And the code to start the measurement should be:
Oliver |
May 27, 2011 by Ralphxyz ![]() |
Sorry Oliver, I should have realized what you were saying. Here is what I am getting after the changes (rotated 360˚):
Register 1, is either 7 or 8 never 0. Xmsb and Ymsb are 07 and 22 which does not make any sense. If they are fixed values why bother to read them? Is the problem with Register (I am going to change that to Status) that it is read first? How would I reverse the read order? Ralph |
May 27, 2011 by Ralphxyz ![]() |
Hey bretm, how do I use atan2(y, x)? I added #include <math.h> Here is my code:
Here is the error.
Ralph |
May 29, 2011 by Ralphxyz ![]() |
Well clawson over at AVRfreaks had the answer. It was the -lm flag in the Makefile! Here is the modified Makefile:
Now to get the Xlsb and Ylsb figured out. Ralph |
Please log in to post a reply.
Did you know that you can use a transistor to interface between different voltage levels of digital logic? Learn more...
|