How to implement twi library for HMC5883L project

Go To Last Post
44 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

First of all thanks for all the members who helped me in my previous thread about the same topic, and I'm still working on the same problem for quite a time and I really want to finish it.

First, members thought that there is a problem with my I2C library and clawson suggested to me to try:

Peter Fleury I2C library

But the same problem happens, so I concluded that the problem is in the HMC5883L library.

So now I have 3 HMC5883L libraries with 3 different I2C libraries.

1. With my I2C library

#include <avr/io.h>
#include <Arduino.h>
#include "HMC5883L_my.h"
#include "I2C.h"

int16_t results[3];

void HMC5883L_init (void)
{
I2C_start(HMC5883L_write);
I2C_tx(Configuration_Register_A);
I2C_tx(0x78);
I2C_tx(0x20);
I2C_tx(0x00);
I2C_stop();
}

int8_t data_read (int16_t *results_ptr)
{
uint8_t lsb,msb, status_of_process = 0;

I2C_start(HMC5883L_write);
I2C_tx(Data_Output_X_MSB_Register);

I2C_start(HMC5883L_read);
Serial.print("x-axis = ");
msb = I2C_rx();
lsb = I2C_rx();
results[0] = msb << 8 | lsb;
Serial.println(results[0]);

Serial.print("z-axis = ");
msb = I2C_rx();
lsb = I2C_rx();
results[1] = msb << 8 | lsb;

Serial.println(results[1]);
Serial.print("y-axis = ");
msb = I2C_rx();
lsb = I2C_rx();
results[2] = msb << 8 | lsb;
Serial.println(results[2]);

I2C_stop();
Serial.println(status_of_process);
return status_of_process;
}

void HMC5883L_read_reg (uint8_t reg)
{
uint8_t reg_dat;
I2C_start(HMC5883L_write);
I2C_tx(reg);
I2C_start(HMC5883L_read);
reg_dat = I2C_rx();
I2C_stop();
Serial.println(reg_dat);
}

 

 

2. With Peter Fleury I2C library:

#include <avr/io.h>
#include <Arduino.h>
#include "HMC5883L_i2cmaster.h"
#include "i2cmaster.h"

int16_t results[3];

void HMC5883L_init (void)
{
i2c_start(HMC5883L_write);
i2c_write(Configuration_Register_A);
i2c_write(0x78);
i2c_write(0x20);
i2c_write(0x00);
i2c_stop();
}

int16_t data_read (int16_t *results_ptr)
{
uint16_t lsb,msb;
int16_t status_of_process = 0;
i2c_start(HMC5883L_write);
i2c_write(Data_Output_X_MSB_Register);
i2c_start(HMC5883L_read);
msb = i2c_readAck();
lsb = i2c_readAck();
results[0] = msb << 8 | lsb;

Serial.print("x-axis = ");
Serial.println(results[0]);

msb = i2c_readAck();
lsb = i2c_readAck();
results[1] = msb << 8 | lsb;

Serial.print("z-axis = ");
Serial.println(results[1]);

msb = i2c_readAck();
lsb = i2c_readAck();
results[2] = msb << 8 | lsb;

Serial.print("y-axis = ");
Serial.println(results[2]);

i2c_stop();

Serial.println();

return status_of_process;
}

void HMC5883L_read_reg (uint8_t reg)
{
uint8_t reg_dat;
i2c_start(HMC5883L_write);
i2c_write(reg);
i2c_start(HMC5883L_read);
reg_dat = i2c_readAck();
i2c_stop();
Serial.println(reg_dat);
}

 

 

3. With original AVR twi library:

#include <avr/io.h>
#include <Arduino.h>
#include "HMC5883L_twi.h"
#include "twi_edit.h"

uint8_t init_config[4]={Configuration_Register_A,0x78,0x20,0x00};
int16_t results[3];

void HMC5883L_init (void)
{
twi_writeTo(HMC5883L_address,init_config,4,0,1);
}

int8_t data_read (int16_t *results_ptr)
{
uint8_t lsb,msb, status_of_process = 0;

twi_writeTo(Data_Output_X_MSB_Register,0x00,1,0,1);
msb = twi_readFrom(HMC5883L_address,msb,0,0);
lsb = twi_readFrom(HMC5883L_address,lsb,0,0);
results[0] = msb << 8 | lsb;

Serial.print("x-axis = ");
Serial.println(results[0]);

msb = twi_readFrom(HMC5883L_address,msb,0,0);
lsb = twi_readFrom(HMC5883L_address,lsb,0,0);
results[1] = msb << 8 | lsb;

Serial.print("z-axis = ");
Serial.println(results[1]);

msb = twi_readFrom(HMC5883L_address,msb,0,0);
lsb = twi_readFrom(HMC5883L_address,lsb,0,1);
results[2] = msb << 8 | lsb;

Serial.print("y-axis = ");
Serial.println(results[2]);

twi_stop();

Serial.println();

return status_of_process;
}

 

Now, what I really want is to detect the problem in code 1 & 2.

Then, because twi library is for sure the most code to eliminate the ideas that there's a problem in the I2C library so I compiled and run code 3 with twi library implementation but there's no results I think there's a problem in my HMC5883L implementation library.

I'm really trying to learn here I don't want to apply wire library because that's the easy way and I won't get much more skills in programming if I only choose the easy way, I'm really interested to know what the problem really is.

This topic has a solution.

Last Edited: Sun. Apr 1, 2018 - 08:14 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Get a logic analyzer - look at the signalling on the wires - compare the work vs don-t-work cases. What differs?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Excuse me, but you go too fast :

you should use an easier circuit than a 3 axes compass and use it to test the library ;

if you have an Arduino + avr (or two arduini), you can transfor the arduino into a slave, which can display -on the serai line- what the master sends https://www.arduino.cc/en/Tutori... , or sends data you know to the master. This would make a stable basis for finding out whetehr tehere are flawss in the part you are interested in.

 

Generally, people have-at most-, just one head, unlike Hindu deities : they cannot doo two difficult things at a time, and Iµ think you should validate a library with circuits which give a known result before using compasses (results cannot be reroduced if you move them, like walking on (alph order) sand/snow and it is uncomfortable. Oµnce your library seems to work, driiving compass is another task (for me, it was simple, because problems had already been splitted into tiny ones , ready to be solved)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MY problem is weird, the serial monitor works fine for displaying the values of the data_read function.

x,y,z works ok, y,z works OK for positive and negative values, but x working only in negative values but as soon as it hits 0 and above the serial monitor stops working.

 

 

Could you look into code 3 and see if my implementation for the twi library is adequate?

Last Edited: Wed. Aug 2, 2017 - 04:03 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You don't need a Logic Analyser. Just follow the advice you were given several weeks ago. i.e. use the return values from i2c_start() and i2c_write()
A Saleae clone is a wise $10 investment, but you have to wait for weeks for it to arrive.
.
I confess that I have not studied your code. I will not study it until you use the error return values.
If there is any problem, you will see exactly what it is. If unsure, we can help you.
.
I would start with a simpler chip. But the principles are exactly the same:
Use return value from i2c_start()
Write to a register.
Read it back.
Then follow the datasheet. e.g. write commands to registers, read the results.
.
David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wolfrose wrote:
MY problem is weird, the serial monitor works fine

Serial monitors assume valid signalling. If you are debugging I2C you can not assume valid signals on the bus.

 

Listen to Clawson (and me) Get a logic Analyser.

I like sigrok / Pulseview as I've said (many times) before/

https://www.avrfreaks.net/search/...

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
int gError ; // only way to keep original structure
void HMC5883L_init (void)
{
     gError = 0;
     if (!i2c_start(HMC5883L_write)){ gError = 1; return;}
     if (!i2c_write(Configuration_Register_A)) {gError = 2;return;}
// x78, x20 x00 are magical numbers : should be given a human readable name
    if (!i2c_write(0x78)} {gError = 3; return;}
    if (!i2c_write(0x20)} {gError = 4; return;}
    if (!i2c_write(0x00);
    i2c_stop();
}

I hope I did not betray David Prentice recommendation of systematically checking error code ; this is not that difficult, as your code uses functions; I just added a global variable GError whioch should be tested this way :

if (gError != 0) {
 serial.print("Error:"); Serial.println(gError);
/**/ while(1)// and endless loop as one cannot continue once an error is met, program stops
}

OF course, you should do the same thing for data_read function -it is very easy-. but you can already see whether your TWI accepts orders...

 

If one (you/people who try to help you) cannot see whether your TWI is pourly wired/configure or (incl.)  there are wrong commands,  things are very complicated and they can be made simpler.

 

Last Edited: Wed. Aug 2, 2017 - 05:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

Get a logic analyzer - look at the signalling on the wires - compare the work vs don-t-work cases. What differs?

OK, now I'm considering the logic analyzer. My best options are from Aliexpress but I couldn't find sigrok products.

 

dbrion0606 wrote:

Excuse me, but you go too fast :

you should use an easier circuit than a 3 axes compass and use it to test the library ;

if you have an Arduino + avr (or two arduini), you can transfor the arduino into a slave, which can display -on the serai line- what the master sends https://www.arduino.cc/en/Tutori... , or sends data you know to the master. This would make a stable basis for finding out whetehr tehere are flawss in the part you are interested in.

 

Generally, people have-at most-, just one head, unlike Hindu deities : they cannot doo two difficult things at a time, and Iµ think you should validate a library with circuits which give a known result before using compasses (results cannot be reroduced if you move them, like walking on (alph order) sand/snow and it is uncomfortable. Oµnce your library seems to work, driiving compass is another task (for me, it was simple, because problems had already been splitted into tiny ones , ready to be solved)

 

No, I'm not because I worked on another I2C device which is the LCD1602 I2C adapter and I haven't had any problems except I couldn't read DATA from the LCD.

 

if you have an Arduino + avr (or two arduini), you can transfor the arduino into a slave

That's a good idea but I'm don't problems in the I2C library because if you read my main post I mentioned I applied another I2C library for Peter Fleury and got the same problem.

 

 

david.prentice wrote:
You don't need a Logic Analyser. Just follow the advice you were given several weeks ago. i.e. use the return values from i2c_start() and i2c_write() A Saleae clone is a wise $10 investment, but you have to wait for weeks for it to arrive. . I confess that I have not studied your code. I will not study it until you use the error return values. If there is any problem, you will see exactly what it is. If unsure, we can help you. . I would start with a simpler chip. But the principles are exactly the same: Use return value from i2c_start() Write to a register. Read it back. Then follow the datasheet. e.g. write commands to registers, read the results. . David.

OK, I want to work with your advice, I changed the functions to uint8_t, but I don't know how to implement the return values.

 

For example, how to implement return values for these functions:

 

uint8_t I2C_init(void)
{
    //set SCL to 100kHz
    TWSR = 0x00;
    TWBR = 0x48;
    //enable TWI
    TWCR = (1<<TWEN);
}

uint8_t I2C_start(uint8_t address)
{
    TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
    while (!(TWCR & (1<<TWINT)));
    TWDR = address;
    TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
    while (!(TWCR & (1<<TWINT)));
}

uint8_t I2C_stop(void)
{
	TWCR = (1<<TWINT)|(1<<TWSTO)|(1<<TWEN);
}

uint8_t I2C_tx(uint8_t data)
{
	TWDR = data;
	TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
	while (!(TWCR & (1<<TWINT)));
}

And also I don't need return values for the I2C because I don't have serious problems with it, I guess my problems comes from the HMC5883L library.

 

A Saleae clone is a wise $10 investment, but you have to wait for weeks for it to arrive.

You're right I won't wait all this time, I may solve the problem within this time, but also how to get the Saleae from china? I don't think I can find it on Aliepxress. Anyway I want to buy one for future projects.

 

I confess that I have not studied your code

See there are 3 codes in the main post.

 

1. Is with my I2C library.

2. With Peter Fleury I2C library.

3. twi library from AVR, but this one I need help because I don't know exactly how to work with functions from twi library. Because I ran the code and nothing happens so I knew my implementation is wrong.

 

I will not study it until you use the error return values. If there is any problem, you will see exactly what it is. If unsure, we can help you

OK, now I'm working to implement return values but don't know exactly how to and where are the proper locations in the functions and whether to include them with if or while statements.

 

 

I would start with a simpler chip. But the principles are exactly the same

I worked with LCD1602 I2C adapter with no problems.

 

 

 

 

Paulvdh wrote:

wolfrose wrote:
MY problem is weird, the serial monitor works fine

Serial monitors assume valid signalling. If you are debugging I2C you can not assume valid signals on the bus.

 

Listen to Clawson (and me) Get a logic Analyser.

I like sigrok / Pulseview as I've said (many times) before/

https://www.avrfreaks.net/search/...

Serial monitors assume valid signalling. If you are debugging I2C you can not assume valid signals on the bus.

 OK, so I can get the I2C transmit/receive values for I2C debugging, but I can't do other debugging which is not related to the I2C operations?

 

Listen to Clawson (and me) Get a logic Analyser.

I like sigrok / Pulseview as I've said (many times) before/

My best country to buy from is China because they have cheap shipping rates. But I can't find sigrok/pulseview on Aliexpress.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Nobody knows whether the way you implemented libraries is faulty, or whether you have a shaky wire/pullup resistor , or whether trouble come from ... elsewhere in the software.

 

Only way to debug is to follow D Prentce advice (and I already wrote -not tested- the way init should be done..... : it is very simple, through tedious)  or to know how to use a logical analyser (takes time and practice )

 

sigrok is a free software suite to display logical analyser data on a screen https://sigrok.org/wiki/Main_Page

 

I think you can read the documentation more comfortably when you have less trouble with bugs (it can be a very minor bug/wiring error) and your brain has time to learn new topics...

Last Edited: Wed. Aug 2, 2017 - 06:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

1.  Look at a proven I2C library.   See which functions have return values and which ones don't.  

2.  If a library function has a return value,  use it.   If it is void,  it is probably a wise design choice.

 

3.  Clone Saleae Logic Analysers are very cheap.   I suspect Ebay or AliExpress will even call them "Saleae" clones. e.g.

http://www.ebay.co.uk/itm/24MHz-8-Channel-USB-Logic-Analyzer-Saleae-8-CH-Logic-Analyzer-for-MCU-ARM-FPGA-/291193504688?hash=item43cc7c43b0:g:0gUAAOSwxKtYBDpC

 

4.  Sigrok is just the name of Open Source Software.

 

I do not know how easy it is for you to buy stuff from Ebay or China.

I can either wait 10-40 days to buy directly from China for £5 or buy from a local UK shop for £10 and wait 1-2 days.

Buying from a local UK shop is worth the reliable service.

 

Likewise,  "UK" Google finds most things on the Internet.   If I do not understand something,  I ask Google first.

 

David

Last Edited: Wed. Aug 2, 2017 - 09:32 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I bought one for $5, the time of arrival is more than 50 days.

 

1.  Look at a proven I2C library.   See which functions have return values and which ones don't. 

 That's' what I'm doing, code 2 and 3 are proven libraries.

 

Code 2 is for Peter Fleury.

Code 3 is for AVR twi library.

 

If you can look at code 3 and help me with the twi functions implementation, in this stage I don't want to go and implement from Arduino wire library because that's the easy way, but rather than that I want to apply the original twi functions, I thought I'm doing pretty well but the code didn't work.

 

 

2.  If a library function has a return value,  use it.   If it is void,  it is probably a wise design choice.

I think the best I2C libraries I found are peter fleury and AVR twi libraries.

 

twi library has return values and it's actually applying error return values.

 

 

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

 

  I recommend that you back up a bit and move to this favorite (to me, at least) standard hardware platform: the Arduino Nano V3 from eBay or BangGood.com (link: https://www.banggood.com/ATmega3...)

 

It's $3...$3...$3 for a complete AVR development system.  For goodness sakes!  If you work for global minimum wage of about $4 an hour then you still come out ahead by just buying the working, tested, proven Nano platform than you would by spending even just one more hour trying to get the other TWI libraries to work with your current AVR board.  In a nutshell, here's how to get the $3 Arduino platform working:

 

    Download the Arduino IDE.  Download and install the PC USB driver for the CH340 USB/serial IC found on the Arduino Nano.  Review the tutorial about Wire.h (which is the included library for handling I2C/TWI).  Then download the Adafruit Compass HMC5883 library and their Sensor library.   Install both library sets into the Arduino libraries directory.   Load, compile, and upload the example program to the Nano.  Connect Vcc, GND, SDA, and SCL from the Nano to the HMC compass board.  Run the Serial monitor in the Arduino IDE.  Discard all the other libraries, code samples, and good advice from everyone else on this board.  You won't need it, because ....    You now have a working HMC5883L compass tool!   You can go on to other things like    < trying to understand just what exactly does the data mean that the compass IC is putting onto the serial monitor >  < just what exactly is a microTesla? >   < how can I make use of this strange HMC5883 compassing-magnetometer tool? >

 

As far as I can tell, the HMC has three tiny magnetic MEMS (microelectronic machine systems) poles that are extremely sensitive to the earth's magnetic field. These poles/bars/sensors are tiny voltage generators.  These MEMS sensors are oriented East:West,  North:South, and Up:Down inside the HMC IC.

 

 When earth's magnetic lines of force run through the length of the bar, a tiny voltage is generated between the two bar ends and amplified.  The Serial monitor will show a positive or negative 20-40 microTeslas reading. When the bar (the HMC IC body) is turned so that the earth's magnetic lines of force are perpendicular to the MEMS bar, then there is no voltage generated and the serial mon shows about 0 microTeslas.  Your specific readings will depend on how you are holding the HMC chip and your location on the planet.  Consult the web for your local magnetic declination.

 

  A good project for a HMC5883L would be to wear the test board on your ring finger and use the HMC as a PC mouse replacement.  You could have your hands over the keyboard in typing position normally.  They you could lift and turn the hand with the HMC ring.  This would activate the mouse cursor on the PC display so that you don't have to move your entire hand/upper arm back-and-forth from keyboard to mouse.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

About Logic Analysers...

 

Sigrok, have you ever used a search engine on the Internet:

https://duckduckgo.com/html?q=si...

http://sigrok.org/

http://sigrok.org/wiki/Supported...

 

If you read any of the links I posted you would have known:

On Ali express you search for "24m 8ch"

https://www.aliexpress.com/whole...

 

@david.prentice

The name "saleae" seems to be dissapearing from Ali. A lot of the Cypres CY7 boxes are becoming nameless.

In one of my posts I recommend asking Ali vendors if their box is compatible with sigrok.

It would give those boxes a name and after some time hopefully they will start using the "works with sigrok" logo.

http://sigrok.org/wiki/Advertisi...

Works_with_sigrok

 

(converted from svg to jpg with gimp. AVRfreaks does not like svg).

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I was thinking of a way to debug my code which is code 1.

 

So I thought to see each 8-bit value coming from the I2C_rx() function, which are assigned to msb and lsb every time.

 

Then the msb and lsb are declared as uint8_t, then I changed them to int16_t and int8_t and what happens is not clear to me.

 

1. When they are declared as int16_t, all the two bytes are positive all the time where they have to be negative!

 

And the is the readings log:

x-axis =
255
125
z-axis =
254
151
y-axis =
1
121

x-axis =
255
187
z-axis =
254
194
y-axis =
1
161

x-axis =
255
217
z-axis =
255
32
y-axis =
1
209

x-axis =
255
179
z-axis =
255
93
y-axis =
1
231

x-axis =
0
14
z-axis =
255
206
y-axis =
1
213

At the end as you can see as soon as x is 0 the code stops and I have to close the serial monitor and disconnect the Arduino board and connect it again without re-uploading the code again.

 

The weird thing is that msb never goes below 255 or 254 all the time otherwise it goes to 0 and the code crashes and

 

2. int8_t:

Shows negative numbers??!

x-axis =
-1
101
z-axis =
0
-35
y-axis =
0
-111

x-axis =
-1
47
z-axis =
0
-60
y-axis =
0
-84

x-axis =
-2
52
z-axis =
-1
126
y-axis =
0
-2

x-axis =
-1
125
z-axis =
-2
63
y-axis =
-1
-75

x-axis =
0
56
z-axis =
-2
105
y-axis =
0
18

 

As soon as x hits 0 the code crashes.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Last Edited: Thu. Aug 3, 2017 - 03:20 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

x-axis =
255
125

I assume that is high byte then low byte order. So think of this in terms of binary 255 is 0xFF and 125 is  0x7D so if the complete number is 16 bit it is 0xFF7D. There are two possible ways to interpret that. If it is unsigned then it's simply a number between 0 and 65535 and in this case that means it is 65,405. But I guess it is signed 16 bit so it is in 2's complement format. In that case anything with the very top bit set is negative and to convert to decimal you subtract it from 0x10000 so it is -(0x10000 - 0xFF7D) which is -(0x83) or -131

 

It does not really make sense to interpret it as two lots of signed 8bit. If you did that it would be -(0x100 - 0xFF), 0x7D which would appear as -1, 125 which makes little sense.

 

If you do not understand about 2's complement and the representation of signed binary integers now would be a good time to find yourself a book about basic C programming (actually a book about basic "anything" programming - I think they'll all explain 2's complement because it's the basis of how all binary, digital computers work!)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But I guess it is signed 16 bit so it is in 2's complement format

 

Is confirmed by http://www.jameco.com/Jameco/Products/ProdDS/2150248.pdf

page 14, line #3-4

This does not explain why , at some orientations, program "crashes" 'this might be a broken/shaky wire and an infinite loop waiting for I2C to answer ... or a translation issue)

From the picture, orientation variation is managed by shaking and twisting  a broad beard and lose wires (it is natural for compass checking, as Earth cannot be shaken: a magnet might be more stable and give indications before verifying computations are OK) .

If OP plugs a logic analyzer into this kind of stuff -which does not deserve any name- , this might add/multiply/expenentiate confusion (I am not an expert in this domain).

Simpler (and it does not need 5 weeks) solution seems to be

 

a) to verify crashes has the same meaning (program stops emitting some values at regular times, as it is expected to do)

 

b) check and display the diagnostic values, if they can be read.... (some can , according to D. Prentice)

 

BTW : if , at each time an issue was found, one added 2 or 3 libraries, complexity would suffer from a terrible operation....

 

Last Edited: Thu. Aug 3, 2017 - 12:21 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, high and low bytes order.

 

 

I tried to observe what's going on:

The high byte for all the three vectors has only three values, but depends on each vector.

 

For example,

x-axis has three three values, 255, 254 and 0.

z-axis has three three values, 255, 254 and 0.

y-axis has three three values, 255, 1 and 0.

 

According to the screenshot when the x-axis wants to overflow, it crashes. I think this is the problem.

 

But in the HMC5883L library the data read function doesn't have the request function which I found in the sparkfun code.

 

But, I just uploaded the sparkfun code and I noticed an interesting action, there are only two values for the high byte for all the vectors!

 

x-axis has three three values, 255 and 0.

z-axis has three three values, 255 and 0.

y-axis has three three values, 255 and 0.

 

I think there's something I have to adjust in the configuration of the chip.

 

Last Edited: Fri. Aug 4, 2017 - 10:56 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Wouldn't put it on the carpet if I were you.  Static electricity can do weird things.  S.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The microcontroller is covered by the breadboard and the sensor is moved in the air by me so I don't think there's a source of static electricity in this condition.

 

If I run sparkfun code which the working code based on wire library, it works with no problems.

 

I'm just now confused by the latest notice I posted in comment #17 about the extra values coming from the registers.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think you underestimate the problem of static electricity! The breadboard offers little protection.

You need to work through the code that works and compare it to your code step by step. This task has taken waaaaaaay too long and this suggests your process is flawed. Basicallyyou make assumptions about things you don't understand and you're usually wrong. Stop making assumptions and look towards evidence to prove/disprove the problem.

Recently i spent half a day debugging an annoying problem i had with the usart on an xmc1100 micro. Most of the time was spent proving it wasn't my code. Then i read the datasheet some more and realised the usart had two receive buffers. Then i looked at the arduino serial code and found that it didn't manage this well, so i added code to do this and the problem was solved. Informed the person managing that code and now the latest release has the fix. I extensively used a cheapy logic analyser to provide me evidence.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wolfrose wrote:

The microcontroller is covered by the breadboard and the sensor is moved in the air by me so I don't think there's a source of static electricity in this condition.

If I run sparkfun code which the working code based on wire library, it works with no problems.

 

Couple things: 

a) Your body carries quite a bit of static electricity on its own.  Shuffling around on that carpet could charge you up quite a bit.

b) Static electricity can damage (or destroy) chips at levels far lower than you can feel.

 

and as Kartman pointed out, the breadboard doesn't include a lot of protection, from you or the carpet.  Given that your other code works, it's probably not zapped.  However, I've zapped enough stuff in my life to look at that shaggy rug and go "Oh no my electronics aren't going ANYWHERE NEAR that!".  There's a reason parts like that are shipped in those glossy plastic bags, and the techies have those mats and wristbands.

 

Just my $0.02.  Have fun,  S.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
I think you underestimate the problem of static electricity! The breadboard offers little protection. You need to work through the code that works and compare it to your code step by step. This task has taken waaaaaaay too long and this suggests your process is flawed. Basicallyyou make assumptions about things you don't understand and you're usually wrong. Stop making assumptions and look towards evidence to prove/disprove the problem. Recently i spent half a day debugging an annoying problem i had with the usart on an xmc1100 micro. Most of the time was spent proving it wasn't my code. Then i read the datasheet some more and realised the usart had two receive buffers. Then i looked at the arduino serial code and found that it didn't manage this well, so i added code to do this and the problem was solved. Informed the person managing that code and now the latest release has the fix. I extensively used a cheapy logic analyser to provide me evidence.

 

That's what I'm thinking now is to include the functions from wire library and see where I miss the data handling.

 

About the logic analyzer, I bought one and it's coming from China, but what I want to know, does the logic analyzer absolutely discover any problem happens in the system? Is it that powerful?

 

 

Scroungre wrote:

Couple things: 

a) Your body carries quite a bit of static electricity on its own.  Shuffling around on that carpet could charge you up quite a bit.

b) Static electricity can damage (or destroy) chips at levels far lower than you can feel.

 

and as Kartman pointed out, the breadboard doesn't include a lot of protection, from you or the carpet.  Given that your other code works, it's probably not zapped.  However, I've zapped enough stuff in my life to look at that shaggy rug and go "Oh no my electronics aren't going ANYWHERE NEAR that!".  There's a reason parts like that are shipped in those glossy plastic bags, and the techies have those mats and wristbands.

 

Just my $0.02.  Have fun,  S.

 

 

Yeah, but you know why I don't care about static electricity? Because when I run sparkfun code at the same time the program works fine.

 

I live now in a city on the coast, but when I go to another city which is on the mountains I get a lot of static electricity when I get in and out of the car and in the house :) And really strong strikes LOL.

 

Also, I don't underestimate it here big time, I believe that there still tiny static charges which I may don't feel and could damage my electronics but I don't have problems with static charges now.

 

Because the difference between my code and sparkfun code is obvious and there's something causing a problem in my code. So I don't know what it's, is it the configuration registers and the settings values, the handling of the read data, something I forgot about processing the data or other possibilities of course it should be something at the end.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I included the functions from wire library and sparkfun code and I think there's something interesting with these two lines:

 

Wire.requestFrom(HMC5883L_address, 6);
  if(6<=Wire.available())

 

I don't have request in my code.

 

This is in the sensor datasheet:

 

Below is an example of a (power-on) initialization process for “continuous-measurement mode”:
1. Write CRA (00) – send 0x3C 0x00 0x70 (8-average, 15 Hz default, normal measurement)
2. Write CRB (01) – send 0x3C 0x01 0xA0 (Gain=5, or any other desired gain)
3. Write Mode (02) – send 0x3C 0x02 0x00 (Continuous-measurement mode)
4. Wait 6 ms or monitor status register or DRDY hardware interrupt pin
5. Loop
Send 0x3D 0x06 (Read all 6 bytes. If gain is changed then this data set is using previous gain)
Convert three 16-bit 2’s compliment hex values to decimal values and assign to X, Z, Y, respectively.
Send 0x3C 0x03 (point to first data register 03)
Wait about 67 ms (if 15 Hz rate) or monitor status register or DRDY hardware interrupt pin
End_loop

After step 5, it asks you to send read and then send 0x06 to read all the 6 bytes, then send write address and send 0x03 to point to the first data register.

 

In sparkfun code they do the opposite:


  //Tell the HMC5883 where to begin reading data
  Wire.beginTransmission(address);
  Wire.write(0x03); //select register 3, X MSB register
  Wire.endTransmission();
  
 
 //Read data from each axis, 2 registers per axis
  Wire.requestFrom(address, 6);
  if(6<=Wire.available()){
    
  Serial.print("x: \t\t");Serial.print("z: \t\t");Serial.println("y: ");
    msb = Wire.read();
    lsb = Wire.read();
    .
    .
    .
    rest of the code
  }

 

I think here's my problem. I should work on how to manage requesting the 6 bytes and pointing to the first data register.

 

 

 

If I don't include it in the code, there's absolutely no real values on the serial monitor they all are 255.

 

So I have to include it with my code and see what happens.

Last Edited: Mon. Aug 7, 2017 - 03:27 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wolfrose wrote:
About the logic analyzer, I bought one and it's coming from China, but what I want to know, does the logic analyzer absolutely discover any problem happens in the system? Is it that powerful?

For me, electricity is invisible, thus I cannot see what is happening. Thus the need for tools like a multimeter, oscilloscope and logic analyser. These don't solve the problem - these allow you to see what was invisible. In the case of your problem, you could use the logic analyser to inspect the i2c bus and capture the data that is communicated. Do this with working code and then compare with your code. Note the differences. Armed with this information, correct your code then try again. Same? If so, problem is solved.

It is a lot more efficient than simply guessing. 

 

With the usart problem I described earlier, I added code that would toggle port pins on certain conditions. With the logic analyser, I could observe the serial data coming in along with the state of port pins. What I found was happening is that one character was getting 'caught' in the second usart buffer. Because my serial data was in packets, I could see the actual packet data going into the micro and my code would toggle a port pin when it thought it had a packet. I then observed my code only though it had a complete packet when another one came in. After a few more tests to make sure my code wasn't wrong, I turned my attention to the usart and re-read the datasheet. Aha! two byte buffer, but I only get an interrupt for each byte. I then modified the arduino serial class code to check for both bytes in the buffer. Problem solved. I could see my code detecting the end of packet at the actual end of packet.

There's an old axiom - "if you can't measure it, then you're guessing". So the moral of the story is that test equipment won't magically solve all your problems for you - you need to understand the limitations of the equipment and use them appropriately. There's no black magic in this stuff - it is created by man. Whilst it might be flattering to consider us professionals as geniuses, the reality is that we have a solid process that allows us to logically work through a problem.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:

For me, electricity is invisible, thus I cannot see what is happening. Thus the need for tools like a multimeter, oscilloscope and logic analyser. These don't solve the problem - these allow you to see what was invisible. In the case of your problem, you could use the logic analyser to inspect the i2c bus and capture the data that is communicated. Do this with working code and then compare with your code. Note the differences. Armed with this information, correct your code then try again. Same? If so, problem is solved.

It is a lot more efficient than simply guessing.

 

Yes, now I know the problem is in my data read function, I just have to work on this function and see what I can do later, I'm getting different results now.

 

Combining sparkfun with wire functions and including them in my code give different results!

 

The high byte is giving different results in the different code configurations.

 

In original sparkfun code: It gives two different high byte values

In my original code: Three different values for the high byte and the code crashes when the high byte of x is 0.

In the combination of sparkfun and my code: The high byte is up to 4 different vaules but no crash because of the good data handling I copied from sparkfun code.

 

So every time I have to discover something.

 

Quote:

With the usart problem I described earlier, I added code that would toggle port pins on certain conditions. With the logic analyser, I could observe the serial data coming in along with the state of port pins. What I found was happening is that one character was getting 'caught' in the second usart buffer. Because my serial data was in packets, I could see the actual packet data going into the micro and my code would toggle a port pin when it thought it had a packet. I then observed my code only though it had a complete packet when another one came in. After a few more tests to make sure my code wasn't wrong, I turned my attention to the usart and re-read the datasheet. Aha! two byte buffer, but I only get an interrupt for each byte. I then modified the arduino serial class code to check for both bytes in the buffer. Problem solved. I could see my code detecting the end of packet at the actual end of packet.

There's an old axiom - "if you can't measure it, then you're guessing". So the moral of the story is that test equipment won't magically solve all your problems for you - you need to understand the limitations of the equipment and use them appropriately. There's no black magic in this stuff - it is created by man. Whilst it might be flattering to consider us professionals as geniuses, the reality is that we have a solid process that allows us to logically work through a problem.

 

I hope USART is very similar to I2C process.

 

There's a lot or little I want to learn about software and hardware buffers. I think hardware buffers are easier than software buffers.

 

I'm sure it feels very interesting when you solve a problem :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello again :)

 

It's been a while and I didn't work on the code lately.

 

So, I was doing some tests with my code and wire library code for data_read function.

 

I discovered that I have to implement 'requestFrom' function from the wire library, the code won't work without this function.

 

So I took a look at the function, I would really like to understand it, it won't be so hard but I need some help to explain its parameters, lines arrangement and the code design.

 

For example:

1. What is the purpose of the first line which deals with 'isize'? And what is the role of this parameter?

2. The second line is obvious which starts the I2C communication.

3. The next part deals with 

if (isize > 3)

where I don't know what 'isize' do, so I don't know what is it about.

 

4. The part after:

while (isize-- > 0)

It says internal registers, sends high byte and:

endTransmission(false);

Why endTransmission has two parameter values, True or false? Because I think it's a direct function, where endTransmission means end of transmission and that's it, what could be other functionality of this function?

 

5. This one deals with quantity and BUFFER_LENGTH. So anyway, I don't know how the code knows the quantity and the buffer length? I mean how it receives this information by hardware or that's just depends on the input decided by the user.

if(quantity > BUFFER_LENGTH)

 

 

 

uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint32_t iaddress, uint8_t isize, uint8_t sendStop)
{
  if (isize > 0) {
  // send internal address; this mode allows sending a repeated start to access
  // some devices' internal registers. This function is executed by the hardware
  // TWI module on other processors (for example Due's TWI_IADR and TWI_MMR registers)

  beginTransmission(address);

  // the maximum size of internal address is 3 bytes
  if (isize > 3){
    isize = 3;
  }

  // write internal register address - most significant byte first
  while (isize-- > 0)
    write((uint8_t)(iaddress >> (isize*8)));
  endTransmission(false);
  }

  // clamp to buffer length
  if(quantity > BUFFER_LENGTH){
    quantity = BUFFER_LENGTH;
  }
  // perform blocking read into buffer
  uint8_t read = twi_readFrom(address, rxBuffer, quantity, sendStop);
  // set rx buffer iterator vars
  rxBufferIndex = 0;
  rxBufferLength = read;

  return read;
}

uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint8_t sendStop) {
	return requestFrom((uint8_t)address, (uint8_t)quantity, (uint32_t)0, (uint8_t)0, (uint8_t)sendStop);
}

uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity)
{
  return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true);
}

uint8_t TwoWire::requestFrom(int address, int quantity)
{
  return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true);
}

uint8_t TwoWire::requestFrom(int address, int quantity, int sendStop)
{
  return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)sendStop);
}

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Again, you've not done your research.

Depending on the actual i2c slave device, the register address may be 1,2 or three bytes. You can see how it uses the address parameter. Your hmc device probably has one address byte, whereas a i2c eeprom might have two, maybe three bytes.

 

endTransmission (I've not looked at that function) probably has the option of sending a restart or a stop sequence. I can make an educated guess here as knowing the i2c protocol, you have a choice of restart or stop at this point. Understand the basics and it all starts to make sense.

 

rxBuffer[BUFFER_LENGTH] is probably defined in the header file.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, thank you, now begin and end transmission functions are more clear to me.

 

I have other questions:

 

The first question is about general concept in embedded systems programming:

1. What's the role of buffers arrays? And I think most buffers if not all are arrays. Why would I need to apply buffers? If I'm getting data from FIFO, SPI, I2C or UART, then the data should be displayed on a graphics unit or stored in an EEPROM.

 

The second question is about twi library:

2. I've looked into twi library many times and I know that's not enough until I understand how it works. But, I'm confused of which specific function in the library for reading data from a slave device.

 

So, looking into all the functions in the header file, here are all the functions:

 

  void twi_init(void);
  void twi_disable(void);
  void twi_setAddress(uint8_t);
  void twi_setFrequency(uint32_t);

  uint8_t twi_readFrom(uint8_t, uint8_t*, uint8_t, uint8_t);
  uint8_t twi_writeTo(uint8_t, uint8_t*, uint8_t, uint8_t, uint8_t);
  uint8_t twi_transmit(const uint8_t*, uint8_t);

  void twi_attachSlaveRxEvent( void (*)(uint8_t*, int) );
  void twi_attachSlaveTxEvent( void (*)(void) );
  void twi_reply(uint8_t);
  void twi_stop(void);
  void twi_releaseBus(void);

 

So, I guess 

twi_readFrom

 

Is the function for reading data, and this is the functions definition:

 

/*
 * Function twi_readFrom
 * Desc     attempts to become twi bus master and read a
 *          series of bytes from a device on the bus
 * Input    address: 7bit i2c device address
 *          data: pointer to byte array
 *          length: number of bytes to read into array
 *          sendStop: Boolean indicating whether to send a stop at the end
 * Output   number of bytes read
 */
uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sendStop)
{
  uint8_t i;

  // ensure data will fit into buffer
  if(TWI_BUFFER_LENGTH < length){
    return 0;
  }

  // wait until twi is ready, become master receiver
  while(TWI_READY != twi_state){
    continue;
  }
  twi_state = TWI_MRX;
  twi_sendStop = sendStop;
  // reset error state (0xFF.. no error occured)
  twi_error = 0xFF;

  // initialize buffer iteration vars
  twi_masterBufferIndex = 0;
  twi_masterBufferLength = length-1;  // This is not intuitive, read on...
  // On receive, the previously configured ACK/NACK setting is transmitted in
  // response to the received byte before the interrupt is signalled.
  // Therefor we must actually set NACK when the _next_ to last byte is
  // received, causing that NACK to be sent in response to receiving the last
  // expected byte of data.

  // build sla+w, slave device address + w bit
  twi_slarw = TW_READ;
  twi_slarw |= address << 1;

  if (true == twi_inRepStart) {
    // if we're in the repeated start state, then we've already sent the start,
    // (@@@ we hope), and the TWI statemachine is just waiting for the address byte.
    // We need to remove ourselves from the repeated start state before we enable interrupts,
    // since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning
    // up. Also, don't enable the START interrupt. There may be one pending from the
    // repeated start that we sent ourselves, and that would really confuse things.
    twi_inRepStart = false;			// remember, we're dealing with an ASYNC ISR
    do {
      TWDR = twi_slarw;
    } while(TWCR & _BV(TWWC));
    TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE);	// enable INTs, but not START
  }
  else
    // send start condition
    TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);

  // wait for read operation to complete
  while(TWI_MRX == twi_state){
    continue;
  }

  if (twi_masterBufferIndex < length)
    length = twi_masterBufferIndex;

  // copy twi buffer to data
  for(i = 0; i < length; ++i){
    data[i] = twi_masterBuffer[i];
  }

  return length;
}

So, the function comment at the beginning describes the outlines of the function. I have different questions about this function.

 

1. At the beginning of the function, there's an if statement's condition,

 

  // ensure data will fit into buffer
  if(TWI_BUFFER_LENGTH < length){
    return 0;
  }

Now, this point checks if the input length passed to the function is not bigger than the specified buffer length of twi, but why it's defined as 32 in the header file ?

 

  #ifndef TWI_BUFFER_LENGTH
  #define TWI_BUFFER_LENGTH 32
  #endif

 

 

2. The next part:

 

  // wait until twi is ready, become master receiver
  while(TWI_READY != twi_state){
    continue;
  }

In the function:

twi_init();

we have twi_state = TWI_READY;

which means twi_state is now equal to 0. That means twi_state is initialized already to 0, and when it comes to:

twi_readFrom

twi_state is still 0, and when the function checks TWI_READY != twi_state. This condition is not true, so the while loop would run forever, I know that's not what happens but this is how I understood it. When this condition would be true? Because changing the value of twi_state to 1 is immediately after this while loop, wit this line: twi_state = TWI_MRX;

  

This line is obvious twi_sendStop = sendStop;

I don't want to know more about it because it's just putting the passed value of sendStop to a variable for further calculation.

 

Then, the next two lines are easy.

 

3. The one after actually has an interesting explanation:

  twi_masterBufferLength = length-1;  // This is not intuitive, read on...
  // On receive, the previously configured ACK/NACK setting is transmitted in
  // response to the received byte before the interrupt is signalled.
  // Therefor we must actually set NACK when the _next_ to last byte is
  // received, causing that NACK to be sent in response to receiving the last
  // expected byte of data.

What I understood is that the author wants to cancel the ACK signal between the first and the last byte of data by decreasing 1 from the input length. But what is the point of this arithmetic operation?

 

On the description, I understand the first two lines, that explains the settings of ACK/NACK as they transmitted to the slave when I receive a byte from the slave.

 

I read this from the mega328p datasheet.

An Acknowledge (ACK) is signalled by the Receiver pulling the SDA line low during the ninth SCL cycle. If the Receiver leaves the SDA line high, a NACK is signalled.

 

But I would like to discuss the next three description lines:

 

  // Therefor we must actually set NACK when the _next_ to last byte is
  // received,

1. How they set NACK? Where is that done? In I2C init function or what exactly?

2. What is _next_ to last byte ? Is it in multiple bytes receive operations ?  

 

  // causing that NACK to be sent in response to receiving the last
  // expected byte of data.

How would that happen ? The last expected byte is determined the input length by the user, I guess. And if the function detect the last value in the length of the data received, how would NACK is sent ?

 

 

twi_slarw = TW_READ;

I searched for TW_READ and found it in another twi.h not in the described directory in the beginning of the twi.c ?

 

Here are all the includes in twi.c:

#include <math.h>
#include <stdlib.h>
#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <compat/twi.h>
#include "Arduino.h" // for digitalWrite

So the included twi files are in compat/twi.h which is in this directory:

E:\Program Files\Arduino\hardware\arduino\avr\libraries\Wire\src\utility

 

Where TW_READ is found in:

E:\Program Files\Arduino\hardware\tools\avr\avr\include\util 

So, how did twi.c found this definition which is located in another header file not included in the twi.c ?

 

 

  twi_slarw = TW_READ;
  twi_slarw |= address << 1;

OK, the same two lines are understandable.

 

 

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

 

Until here I don't want to list the rest of the code to ask about I see the answers for my current questions.

 

Thank you all for the help,

Last Edited: Sat. Aug 26, 2017 - 10:18 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

wolfrose wrote:
What's the role of buffers arrays?
umm it is to provide a buffer. Take a UART for example. You may not have any idea when the PC at the other end is suddenly going to send you a burst of 13 bytes (say) and your AVR code might be in the middle of doing something else when they are sent. So you just set up a UART interrupt with a "ring buffer" then when the 13 bytes arrive you just have 13 very quick interrupts that just store the 13 bytes into a buffer but does nothing else. Finally, when the AVR has finished what it was doing it can now pick up and process the 13 buffered characters. If you didn't buffer them you'd either have to do all the processing in the interrupt (and delay the foreground work) or you'd just have to accept that the 13 bytes would be "lost". 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

So you just set up a UART interrupt with a "ring buffer"

 

How would you do that ? Is it through the UART SFRs ?

 

 then when the 13 bytes arrive you just have 13 very quick interrupts that just store the 13 bytes into a buffer but does nothing else. 

 But this store process require the AVR to stop certain cycles to store these bytes and continue the work at the same time, and that also means that the AVR UART's unit is on all the time and when you set the interrupt function it would check for any interrupt all the time, and that I guess how it works, and when the interrupt is triggered, the AVR (and by a quick look to the datasheet) there is a receive register followed by UDRn (Receive) and directly to the data bus, just take the first byte in the receive register and store it immediately in the user software buffers.

 

Finally, when the AVR has finished what it was doing

 

But, after I store it in the buffers, what would I do next ? When I would process this data, because there might be other packets of data, so I better process this packet of data and be read for the next packet. Because if I store this data and another packet has came, then what would I do ? Do I have to store it in another buffer ?

 

Because in case I have an LCD and I send data chars from the PC, I don't need to store then in buffers, I just directly take them from the UART receive register and put them on the LCD, so where buffers come in hand.

 

Because with the modules I worked with applying I2C or SPI, I just receive data and put them on the LCD, but this is maybe I didn't have other functions to do, like; working with different sensors at the same time and process their received data. Like this case, can I work with different sensors and process their data without the need to buffers ? I think this is a more reasonable case to use buffers if the microcontroller can't work with different sensors and other modules, like; LCDs, and process all the operations at the same time.

 

So I think the concept reason for buffers is that the CPU would be busy to process the just received data, but that depends on the software design, like; you check your system to see if the CPU is ready to process the data then no need for buffers, otherwise, put them in buffers.

 

But what if the CPU is busy for quite a time and another packet of data has came and the dedicated buffers are full and has not been processed?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Why is the buffer length 32? This is a balance between utility and ram size. This number determines the maximum number of bytes this code can read in one go. 32 is a reasonable number methinks. 16 might be a bit small for the larger eeprom chips. Any larger than 32 and you're chewing up precious ram -the Arduino uno only has 2k.
You're reverse engineering a piece of software that implements the i2c protocol, but you don't seem to understand i2c.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Kartman wrote:
you don't seem to understand i2c.

It's all defined here: 

 

NXP (formerly Phillips) UM10204 I 2C-bus specification and user manual

http://www.nxp.com/docs/en/user-guide/UM10204.pdf

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
Why is the buffer length 32? This is a balance between utility and ram size. This number determines the maximum number of bytes this code can read in one go. 32 is a reasonable number methinks. 16 might be a bit small for the larger eeprom chips. Any larger than 32 and you're chewing up precious ram -the Arduino uno only has 2k. You're reverse engineering a piece of software that implements the i2c protocol, but you don't seem to understand i2c.

 

OK, I thought so about 32 it's reasonable.

 

But what about the other questions ?

 

I understand little everytime, I don't know why it's so hard to me right now :) I'm really confused with all the arithmetics in this function.

 

I should ignore the not so important lines and focus on designing a similar function for my I2C library.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

wolfrose wrote:

...when it comes to:

twi_readFrom

twi_state is still 0, and when the function checks TWI_READY != twi_state. This condition is not true, so the while loop would run forever, I know that's not what happens but this is how I understood it. When this condition would be true?

You are missing one fundamental detail - the I2C communication is interrupt-driven in this case. If there is ongoing I2C communication and you call this function, the state variable will have a value different from TWI_READY, so the function will hang in the loop. The ISR will set the state variable to TWI_READY once the ongoing communication is completed, at which point your function will exit from the loop and continue executing.

 

wolfrose wrote:

3. The one after actually has an interesting explanation:

  twi_masterBufferLength = length-1;  // This is not intuitive, read on...
  // On receive, the previously configured ACK/NACK setting is transmitted in
  // response to the received byte before the interrupt is signalled.
  // Therefor we must actually set NACK when the _next_ to last byte is
  // received, causing that NACK to be sent in response to receiving the last
  // expected byte of data.

What I understood is that the author wants to cancel the ACK signal between the first and the last byte of data by decreasing 1 from the input length. But what is the point of this arithmetic operation?

This is just a sensible hardware design choice. The code must setup the proper reply (ACK or NACK) before the byte is received. A master read is ended by the master NACK'ing, so if your master wants to read 3 bytes, then your code must setup the I2C hardware for NACK after 2 bytes are received (not 3!). Then, after the 3rd byte the I2C hardware will send the NACK thus ending the transmission.

 

wolfrose wrote:

1. How they set NACK? Where is that done? In I2C init function or what exactly?

By setting the TWEA bit in the TWCR register (search the code and you'll see...).

 

wolfrose wrote:

2. What is _next_ to last byte ? Is it in multiple bytes receive operations ?  

Not sure what the question is but, yes - I2C supports arbitrary transmission length (not sure what the upper limit is). It doesn't really matter - regardless of single byte or multiple bytes, the master must setup the desired ACK/NACK before the byte - to which the ACK/NACK is replied - is received. 

 

wolfrose wrote:

  // causing that NACK to be sent in response to receiving the last
  // expected byte of data.

How would that happen ? The last expected byte is determined the input length by the user, I guess. And if the function detect the last value in the length of the data received, how would NACK is sent ?

Somewhere in the ISR code, there is an assignment to TWCR that either sets of clears the TWEA bit...

/Jakob Selbing

Last Edited: Sat. Aug 26, 2017 - 01:11 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The other questions are answered by reading the i2c spec. The fundamental operation has not changed in over 30 years! It spells out the ack/nack process along with start/stop/restart. The twi hardware implements the lower level protocol and the software implements the higher level. You want to jump into a ball game without knowing the rules. All you'll achieve is kicking the ball around randomly.
Why do you think you can ignore the 'not so important lines'? They're there for a reason.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The previous thread went on for about a month.   This one is going the same way.

 

If you read the replies and followed the advice,  you would be working within one hour.

And probably have a full understanding of I2C within another two hours.

 

Ok,  not everyone learns in the same way.    But two months implies that "your" learning strategy is not very successful.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

jaksel wrote:

the I2C communication is interrupt-driven in this case.

Where can I find this information in the documents ? I have the mega328p datasheet and the UM10204 manual but I didn't read anything about hardware interrupt on byte receive.

 

Most I read is about SDA and SCL states and ACK and NACK states.

 

Most near meaning to interrupt signal I read is this:

The acknowledge takes place after every byte. The acknowledge bit allows the receiver to signal the transmitter that the byte was successfully received and another byte may be sent. The master generates all clock pulses, including  the acknowledge ninth clock pulse.

 

 

 

This is just a sensible hardware design choice. The code must setup the proper reply (ACK or NACK) before the byte is received. A master read is ended by the master NACK'ing, so if your master wants to read 3 bytes, then your code must setup the I2C hardware for NACK after 2 bytes are received (not 3!). Then, after the 3rd byte the I2C hardware will send the NACK thus ending the transmission.

I didn't find this explanation in the documents, that's what I'm talking about. The techniques of setting and configuring the code is not found in the documents.

 

You can find how to set basic read, write, start and stop functions; like, page 226 in mega328p datasheet.

 

 

There are no code techniques even in the manual UM10204.

 

I really thank you for informing me of these techniques in configuring the code.

 

 

But there is no explanation about setting the code for reading multiple bytes to NACKs after the first byte, it's the first time I know it from you, seriously.

 

your code must setup the I2C hardware for NACK after 2 bytes are received (not 3!).

How would I know it's a must ??! I didn't read more details about read operations and whether you need to set the code for NACKs or ACKs.

 

All I know is that I can set whether I want acknowledgements in my code or not, but I don't know if they are necessary for read and write operations! How would I learn these details ?

Also, how I suppose to know that I have to setup the I2C hardware for NACK after 2 bytes (not 3) in case of reading 3 bytes. 

 

 

Not sure what the question is but, yes - I2C supports arbitrary transmission length (not sure what the upper limit is). It doesn't really matter - regardless of single byte or multiple bytes, the master must setup the desired ACK/NACK before the byte - to which the ACK/NACK is replied - is received. 

OK, this is another aspect which I don't know about is that as you set the ACK/NACK, you receive the same result; like, if you set the code for ACK, then you must receive an ACK and the same for NACK.

 

But:

to which the ACK/NACK is replied - is received.

Here how it is replied and how it's received ? Is it replied by the slave and receive the same time by the master in case for master write operations. And replied by the master and received by the slave in master read operations ?

 

 

Somewhere in the ISR code, there is an assignment to TWCR that either sets of clears the TWEA bit...

The only function which clears this bit is twi_disable. Rest of the functions set this bit.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wolfrose wrote:
your code must setup the I2C hardware for NACK after 2 bytes are received (not 3!). How would I know it's a must ??! I didn't read more details about read operations and whether you need to set the code for NACKs or ACKs.
 

 

UM10204 para 3.1.6 item 5. A master-receiver must signal the end of the transfer to the slave transmitter. 

If I want to read 3 bytes from a slave device:

SWRARARNP where R is read byte, A is ACK, N is NAK,P is stop. The first two bytes are ACKed, the last is NACKed to signal to the slave this is the last byte we are reading. Some slave devices don't care if the last byte is ACKed or NACKed, some do. To keep the code general and to be compatible it is wise to follow the rules.

When you are reading from the slave, the master issues the ACK/NACK

When you are writing to a slave, the slave issues the ACK/NACK

 

Of course there are no code techniques in the UM10204. The rules of football don't tell you how to kick a ball. For soccer they tell you your hands are not allowed to touch the ball.

 

If you understand the basics of I2C, you should be able to write down the sequences for read and writing one or multiple bytes without referring to the standard. Once you've got that committed to memory, then you can look at the TWI hardware.

Last Edited: Mon. Aug 28, 2017 - 03:48 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

 

It's been a long time but I took a lot of time learning and understanding twi library not mentioning the wire library.

 

I concluded to this solution but didn't test it.

 

I edited the two most important functions for now which are the I2C receive function in the I2C library, and the dataRead function in HMC5883L library.

 

1. The I2C read function

void I2C_rx(uint8_t* dat, uint8_t len)
{
    uint8_t i;
    if (len > I2C_BUFFER_LENGTH)
    {
        return 0;
    }

    I2C_MxBuffLen = len - 1;        // decrementing 1 to NACK the last byte
    I2C_MxBuffIndex = 0;

    for (i=0; i<I2C_MxBuffLen;i++)
    {
        dat[i]= TWDR;                           // Coping the received bytes to the receive pointer
        TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);  // sneding ACKs
        while (!(TWCR & (1<<TWINT)));
    }

        TWCR = (1<<TWINT)|(1<<TWEN);            // sending NACK
        while (!(TWCR & (1<<TWINT)));
}

 

2. The HMC5883L dataRead function

void data_read (uint8_t *results_ptr, uint8_t length)
{
  uint8_t lsb,msb, status_of_process = 0;

  I2C_start(HMC5883L_write);
  I2C_tx(Data_Output_X_MSB_Register);

  I2C_start(HMC5883L_read);
  Serial.print("x-axis\t\t");Serial.print("z-axis\t\t");Serial.println("y-axis");

  I2C_rx(results_ptr,length);

  I2C_stop();
  Serial.println();
}

 

 

Should these two functions work? I'm just concerning now about passing an array pointer from a function to another function which also receives a pointer to an array, is that OK?

Last Edited: Fri. Dec 1, 2017 - 03:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The array pointer is just a value- the language would be rather restrictive if you couldn’t copy a value?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, the code seems to work now with very little crashes very less than before if one happens then it because the code needs more improvement.

 

I think the problem before was about sending the NACK, now it should be something about repeated starts .. I guess.

 

But another new issue coming now is the values on the serial monitor are in big numbers, and don't work like the numbers from the good twi library.

 

In twi and wire libraries, the author configured everything in `uint8_t`, and only `int` in Arduino IDE main code. I did the same but it gives different results!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You’ve said a lot but told us very little. You need to stop guessing and deal with facts. If the numbers are wrong, then you need to look at the raw values read from the device and how they’re interpreted. If you can give us this information, then we have a chance of giving you guidance.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What are the raw values? Are they in the datasheet or what exactly?