[SOLVED] undefined reference to function I2C library and HMC5883L read function

Go To Last Post
119 posts / 0 new

Pages

Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

I'm developing my code to run HMC5883L module.

First I get this error while compiling my C code in Arduino IDE as I'm writing all my codes in C.

The error is:

Arduino: 1.8.0 (Windows 10), Board: "Arduino Nano, ATmega328"

C:\Users\wolfrose\AppData\Local\Temp\cc9f8iwd.ltrans0.ltrans.o: In function `setup':

D:\microcontroller dev\Sketches, codes, measurements & configs\AVR C\Sensors\HMC5883L/HMC5883L.ino:33: undefined reference to `I2C_init()'

collect2.exe: error: ld returned 1 exit status

exit status 1
Error compiling for board Arduino Nano.

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

 

 

I attached my I2C library.

This is my application code in Arduino IDE:


#include <I2C.h>

//Output = [Mset - Mreset] / 2

#define HMC5883L_read   0x3D
#define HMC5883L_write  0x3C

#define Configuration_Register_A    0x00  //Read/Write
#define Configuration_Register_B    0x01  //Read/Write
#define Mode_Register               0x02  //Read/Write
#define Data_Output_X_MSB_Register  0x03  //Read
#define Data_Output_X_LSB_Register  0x04  //Read
#define Data_Output_Z_MSB_Register  0x05  //Read
#define Data_Output_Z_LSB_Register  0x06  //Read
#define Data_Output_Y_MSB_Register  0x07  //Read
#define Data_Output_Y_LSB_Register  0x08  //Read
#define Status_Register             0x09  //Read
#define Identification_Register_A   0x10  //Read
#define Identification_Register_B   0x11  //Read
#define Identification_Register_C   0x12  //Read

void HMC5883L_init (void);
int16_t data_read (void);
int16_t data;

//  I2C_start();
//  I2C_tx();
void setup() {
  // put your setup code here, to run once:
  I2C_init();
}

void loop() {
  // put your main code here, to run repeatedly:

}

void HMC5883L_init (void)
{
  I2C_start(HMC5883L_write);
  I2C_tx(Configuration_Register_A);
  I2C_tx(0x07);
  I2C_stop();
  I2C_tx(Configuration_Register_B);
  I2C_tx(0x01);
  I2C_stop();
  I2C_tx(Mode_Register);
  I2C_tx(0x02);
  I2C_stop(); 
}

int16_t data_read (void)
{
  I2C_start(HMC5883L_write);
  I2C_tx(0x06);
  I2C_stop();
  I2C_start(HMC5883L_read);
  I2C_rx();
  data = TWDR << 8;
  I2C_stop();
  I2C_start(HMC5883L_read);
  I2C_rx();
  data = TWDR << 8;
  I2C_stop(); 
}

 

 

Second, I want help with developing data_read function, is my way correct until now.

I know I have to read 6 bytes of data.

 

Regards,

Attachment(s): 

Last Edited: Sat. Jun 17, 2017 - 12:03 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

\Sketches, codes, measurements & configs\

REALLY? Is that path name acceptable?

 

How is the I2C.c file added to the project? I don't understand the Arduino environment too well.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

In Arduino's library folder.

 

\Sketches, codes, measurements & configs\

Is my codes path and it's OK for all of my codes I compile and run.

 

This problem is because of I2C_init function.

 

You know? Let me try running the code in Atmel Studio 7.

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

I2C_Init() has no prototype. There has to be a definition or prototype before its first use, as far as I know. Is it in I2C.h? And, where is the actual function, I2C_Init()? Is the file that contains it part of the project (not sure how Arduino IDE does it, but you have to explicitly add it to the project with Atmel Studio). Either the absence of a prototype or the inability to find the actual  function will lead to that error message.

 

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Wed. Jun 7, 2017 - 03:58 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I2C_Init() has no prototype.

It's there in the header file and the code is in the .c file but I don't know how the Arduino adds .c file to a project, I guess it's another .ino file tab?? (both files in the .rar file posted above)

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

js wrote:

\Sketches, codes, measurements & configs\

REALLY? Is that path name acceptable?

It could work, but is very likely to cause problems: http://www.avrfreaks.net/comment...

 

To be safe:

Simple rule: Never use spaces nor any other "special" characters (including ampersand '&', punctuation, etc) in file or folder names.

 

EDIT

 

in this particular case, it does seem to be something that the Arduino IDE has done itself - so, presumably, it knows how to cope with it.

 

However, in general, when creating one's own files & folders, the rule holds.

Last Edited: Wed. Jun 7, 2017 - 06:52 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

God gave you the Arduino.
.
Why not use it? e.g. Wire.h library
There is probably an existing third party HMC5883 library. This would be built on the Wire.h library anyway
.
Yes, you can use a C library but you have to make it C++ compatible e.g. wrap the function declarations as extern "C"
There is plenty of documentation about the Arduino environment. But it does become more technical when you are writing a non-standard library.
Most libraries are regular C++ classes implemented as a H file and CPP file
.
David.

Last Edited: Wed. Jun 7, 2017 - 06:06 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

God gave you the Arduino.

Or was it the devil? devil

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

No, it was God.
.
It means that anyone can get started in the hobby. Even be very useful in a professional career.
.
You can just use existing libraries. You can add your own C++ classes. Write your own libraries. Cooperate with other Arduino users from anywhere in the world.
.
The Arduino IDE works very well. It is fairly painless to add C, S, CPP, H files to a project
Coniderably easier than struggling with AS7.
.
Perhaps AS7 was the Devil's invention!
.
David.

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

david.prentice wrote:
the Devil

https://2.bp.blogspot.com/-yprOXdTkbDY/VsILE1k-4SI/AAAAAAAAHGU/i96Y2g0UKYw/s1600/232-gates-devil-1s2agva.jpg

cheeky

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

There is probably an existing third party HMC5883 library.

There is  at least one : I donot remember whethet I used  https://github.com/adafruit/Adaf...

This would be built on the Wire.h library anyway

Once it was done , it gave realistic values ... for me.

 

Oh, btw : I was terrified by the naming of folders in this thread... I doubt Dog nor the Great Flying Spaghetti Monster dared such a thing...

 

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

@David

God gave you the Arduino.
.
Why not use it? e.g. Wire.h library
There is probably an existing third party HMC5883 library. This would be built on the Wire.h library anyway
.
Yes, you can use a C library but you have to make it C++ compatible e.g. wrap the function declarations as extern "C"
There is plenty of documentation about the Arduino environment. But it does become more technical when you are writing a non-standard library.
Most libraries are regular C++ classes implemented as a H file and CPP file
.
David.

 Well, that was my first introduction with Arduino and working with its pre written libraries. But that lead me to problems working with functions and that also forced me to go back to look to the actual library files in Arduino libraries folder. Then by reading the lines, which some is written in C and other programmers write their libraries in C++. So ultimately I learned that C is very important to learn first then my next step is to learn C++ as it would be much more easier after learning C.

 

Also I want to write my own libraries because I have to write libraries for very important communication protocols like I2C, because many modules apply I2C.

 

Because when I see programmers' libraries in Arduino folders and in repositories like in GitHub, then that encourages me to start developing my codes and that also gives me more in depth understanding in the language.

 

Because I'm electronics trainer, and my next goal is to provide microcontrollers short courses in my work, so I better learn from now, and also I have to do my masters degree after some time, so also I better learn programming from now. SO I don't get confused and look into basics of programming when I get started doing my masters degree.

 

<< :) I think I know you, you are David from Arduino forum.

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

@js

It's there in the header file and the code is in the .c file but I don't know how the Arduino adds .c file to a project, I guess it's another .ino file tab?? (both files in the .rar file posted above)

Yes, it's in .h and .c files. But they compile OK in Atmel Studio 7.

 

It's not another .ino tab, I'm adding the library to the project with #include.

 

Adding it to other tabs is a good way too because that means they are in the same folder; like, Arduino libraries with projects' libraries and examples for different applications.

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

dbrion0606

There is probably an existing third party HMC5883 library.

There is  at least one : I donot remember whethet I used  https://github.com/adafruit/Adaf...

This would be built on the Wire.h library anyway

Once it was done , it gave realistic values ... for me.

 

Oh, btw : I was terrified by the naming of folders in this thread... I doubt Dog nor the Great Flying Spaghetti Monster dared such a thing...

I know there are existing libraries out there. But I'm working on my own codes.

 

LOL ... Great Flying Spaghetti Monster :) What is that??!

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

awneil

It could work, but is very likely to cause problems: http://www.avrfreaks.net/comment...

It works for my other codes until this one, so I think the problem is something else.

 

in this particular case, it does seem to be something that the Arduino IDE has done itself - so, presumably, it knows how to cope with it.

 

However, in general, when creating one's own files & folders, the rule holds.

 

I think so, but you know I'm looking to see when folders' naming would cause problems to me. Then I would consider renaming my folders.

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

@wolfrose,

 

I suggest that you stick with the Arduino IDE and Arduino libraries.

It is so much easier to develop a project.  Zip it up.  Attach it to Arduino forum or to this forum.

Ask questions about any problems.

 

I always recommend starting with proven code.   And adapting to your needs.

e.g.  write a project with proven HMC5883 library and fully test it.

then replace the library with your own code (if you want).

 

Yes,  most Arduino libraries are C++ classes that might even use some C helper functions.

You can write and test code in local "tabs" of your project.

 

You have probably noticed that I go ballistic when I see I2C with void functions.   

 

There is nothing wrong with re-inventing your own wheels.

But don't blindfold yourself.   (when all existing cars have steering wheels and allow you to see where your car is going)

 

David.

Last Edited: Wed. Jun 7, 2017 - 02:55 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wolfrose wrote:
some is written in C and other programmers write their libraries in C++. So ultimately I learned that C is very important to learn first then my next step is to learn C++
Wrong approach in my (arrogant) opinion - C is a subset of C++ so start with C++ and you learn C anyway. If you stick to a C++ environment you won't hit "boundary" conditions where things like extern "C" start to become important. On the whole all C can have the .c filename switched to .cpp and will be immediately compiled as C++ (and therefore gets mangled linkage names without trying very hard!).

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

@Clawson :

"C is a subset of C++ so start with C++ and you learn C anyway" I agree with teh first part, but not with the conclusion: as C is a subset of C++, it is easier to learn than C++;  therefore, a lazy man should  start with C (the only thing which might make me change my mind is that Arduino is simplified C++, perhaps easier than C).

@wolfrose : I hope https://en.wikipedia.org/wiki/Fl... , https://www.venganza.org/ and http://talklikeapirate.com/wordp... will  teach you not to have complicated folder names ( and using spaces, & signs ..., which have a syntactical meaning with command line  interpreters, at least in the Unix/GNUlinux world can make life very complicated in the long term - emulators such as cygwin can be installed in the windows world, and make a PC compatible with RapsberryPi, say... but they like simple folder names....).

 

Oh, btw: what is easier and gives more time to learn -reading libraries source, as they often are well written; wikipedia mde a huge effort for many parts of C....-:

using existing libraries, code only what one thinks is lacking

or reinvent a square wheel (the wheel technology is well known) and ask the lazy web why it is not circular and shaky?

 

Te aves baxtalo; Que Son Appendice Pâteux vous saisisse de Sa Grâce .

Last Edited: Wed. Jun 7, 2017 - 04:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

dbrion0606 wrote:
as C is a subset of C++, it is easier to learn than C++; therefore, a lazy man should start with C
And I totally disagree. C++ needs you to think with a different mindset at the design stage. If you start with C and then try to move to C++ (as I know to my cost over the last 5+ years) your whole view/approach is tainted by the "C way" to do things. Far better to learn C++ if starting afresh and in the process you'll also be learning what a struct{}, typedef, for() loop and other "C concepts" are. But you will design/program with a more open minded approach as you will instantly think "this, this and this are candidates to be grouped in a class" and "this is a candidate to be derived from that parent class over there". C programmers just don't really think that way.

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

@Clawson : I first learn C -and can do, sometimes,  something good with it- and found difficult to learn C++: -the "c way" is much like "the fortran way" and R - a statistics oriented scriopting language, easier than python -  https://cran.r-project.org/doc/m... hides objects to be very beginner friendly  ;

 

Arduino can make me change my mind : I try to read and understand existing libraries, as they are popular enough to be well written and not to give me bad habits (reading poorly written programs in the lazy web can teach one how to write ... poorly written programs) ...

 

BTW : I would like to thank you for recommanding, 2 years ago, opencv : this year, I begin to find it  very useful -and usable-  on nanoPi and PCs... but I suffer with C++.

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

clawson

If you start with C and then try to move to C++ (as I know to my cost over the last 5+ years) your whole view/approach is tainted by the "C way" to do things.

 

Well, to my point of view, I think I need to learn many things about C. Because when I open a library, I see things; like, extern, inline, virtual, .. etc.

 

There are other keywords; like, struct, enum and pointers which I want to learn their applications and benefits. I think that requires me to have more wider view to C programming because simply, I don't have much experience with programming :)

 

I learned little about C++, and I did SoloLearn course in Android app and got the certificate :) But still not the practical experience with serious C++ applications.

 

And I learned C is very important with programming other microcontrollers and also with Linux.

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

No, it was God.

WOW is he going to mess around with unbelieving programmers or what.....devillaugh oops we are straying into religious subjects now....

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

I need to develop my HMC5883L read axis function, I developed this one from CCS forum topic.

 

int16_t data_read (void)
{
  I2C_start(HMC5883L_write);
  I2C_tx(Data_Output_X_MSB_Register);
  I2C_start(HMC5883L_read);
  
  msb = I2C_rx();
  lsb = I2C_rx();
  X_axis = msb << 8;
  X_axis |= lsb;    

  msb = I2C_rx();
  lsb = I2C_rx();
  Z_axis = msb << 8;
  Z_axis |= lsb; 

  msb = I2C_rx();
  lsb = I2C_rx();
  Y_axis = msb << 8;
  Y_axis |= lsb; 
   
  I2C_stop();

  return X_axis, Y_axis, Z_axis;
}

 

Would this works?

 

Regards,

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


int16_t data_read (int16_t *results)
{
 uint16_t lsb,msb;
 int16_t status = 0;
 
  I2C_start(HMC5883L_write);
  I2C_tx(Data_Output_X_MSB_Register);
  I2C_start(HMC5883L_read);
  
  msb = I2C_rx();
  lsb = I2C_rx();
  
  lsb |= msb << 8;
  result[0] = (int16_t)lsb;
  
  msb = I2C_rx();
  lsb = I2C_rx();
  lsb |= msb << 8;
  result[1] = (int16_t)lsb;

  msb = I2C_rx();
  lsb = I2C_rx();
  lsb |= msb << 8;
  result[2] = (int16_t)lsb;
  
  I2C_stop();

  return status;
}

rather than asking us 'does it works', the first thing is to check whether it complies with the C standard. To my knowlege, in C you can only return one variable. So that is one basic rule you've violated. C has pointers, which is what I've demonstrated here as well as the duality of pointers/arrays.

With embedded systems, you're dealing with the real world. Unfortunately the real world isn't as nice as what we'd like it to be - so what we would like to happen doesn't always work the way we want it. Thus I2C is not guaranteed to work in all cases - you do not attempt to detect or manage potential errors. What happens if the i2c device you're communicating with dies, or there's a transient or a zillion other real possibilities? Your code will lock up and no one will know why. A useful rule -is - if you can detect an error - detect it and manage it.

An example might be a temperature sensor in a car's ECU -

1. we know the expected range of values. The hardware is arranged as such if there is a short or open circuit, that the reported value is out of range.

2. we should know if the engine is running and for how long. Using this knowlege we can estimate what the temperature might be.

3. if we detect an error, we can flag the error for later diagnosis and given other knowlege we have measured or inferred, we can substitute a value so that the engine keeps on running. Or we can flag the error and shutdown if we consider it critical.

 

Currently even if your function worked, how would we know if we read valid data or we just read garbage? I2C has a degree of error reporting, so if you used that, then you could have more confidence in the data you return.

 

I have a simple test for I2C - when it is running, get a screwdriver and randomly short the SCL and SDA wires. If your project locks up, that is a fail. If your project detects and error, good. If your project detects the error and recovers, double points.

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

Does it compile correctly to start with? Can return handle more that just one value?

 

If X_axis, Y_axis, Z_axis are globals (HAAA HERESY!!) then you don't need to return them.

 

Edit Russel got there before me.
 

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

Last Edited: Thu. Jun 8, 2017 - 03:23 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

An even cleaner solution would be to define a structure with X,Y,Z members and pass a pointer to that. Makes the code more self documenting.

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

wolfrose wrote:

  return X_axis, Y_axis, Z_axis;

Would this works?

 

It will work as defined by the 'C' language specification - but that is almost certainly not what you want!

 

Go back to your 'C' textbook, and study what return does;

 

Also look-up the comma operator ...

 

Here are some 'C' reference & learning resources for you - including a free online textbook: http://blog.antronics.co.uk/2011...

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
int16_t data_read (int16_t *results)

Why you are passing a pointer? And where it points to?

 

 uint16_t lsb,msb;
 int16_t status = 0;

Why you defined these variables in local field?

 


  msb = I2C_rx();
  lsb = I2C_rx();
  
  lsb |= msb << 8;

OK, here I understood that you're short cutting the process of assigning msb and lsb to one int16_t variable, by shifting msb into lsb so now lsb contains all the 16-bit value for x, z, y axis.

 

 

  result[0] = (int16_t)lsb;

I don't know what this line does?! Why you casting it?

 

I understand that you're applying array method, but how you initialize it as an array?

 

 

 

You're right I compiled a code in C compiler which returning two values, and it issued a warning. So thank you I just learned that!

 

C has pointers, which is what I've demonstrated here as well as the duality of pointers/arrays.

I want to learn more about pointers but my experience with pointers is that they are useful when passing a string function. I actually don't have more functions in my mind for pointers.

 

Currently even if your function worked, how would we know if we read valid data or we just read garbage? I2C has a degree of error reporting, so if you used that, then you could have more confidence in the data you return.

 

I have a simple test for I2C - when it is running, get a screwdriver and randomly short the SCL and SDA wires. If your project locks up, that is a fail. If your project detects and error, good. If your project detects the error and recovers, double points.

 Thank you for the advice, first I want to get the HMC5883L working, then I would add functions to detect errors.

 

I guess that would be by checking the returned values of the read function, if values are out of range then that's an error.

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

It will work as defined by the 'C' language specification - but that is almost certainly not what you want!

It issued a warning in code blocks for returning two variables.

 

 

Go back to your 'C' textbook, and study what return does;

All I know it returns a value to the main if you call it as a function, and return in main means end of program.

 

 

Also look-up the comma operator ...

comma is for splitting variables and arguments.

 

Here are some 'C' reference & learning resources for you - including a free online textbook: http://blog.antronics.co.uk/2011...

Thank you for the link, it forwarded me to websites for embedded systems courses in UK, but it's really expensive for a course in 5 days for over 2000 pounds!! I prefer learn on my own rather paying this much of money :)

 

I have a lot of C eBooks.

 

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

wolfrose wrote:
it forwarded me to websites for embedded systems courses in UK

Well, that's not the only thing it did - is it?

 

There were also many other links - weren't there?

 

I prefer learn on my own rather paying this much of money :)

Fair enough - but you still need to put the effort in!

 

 

I have a lot of C eBooks.

So keep studying them.

 

Look again at what they tell you about what return does, and the comma operator - your comments above are close, but no bananas.

 

Specifically, note that the comma operator - used in an expression - is distinct from the comma used as a separator in function argument lists, etc ...

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

Does it compile correctly to start with? Can return handle more that just one value?

This is the message I get.

|21|warning: left-hand operand of comma expression has no effect [-Wunused-value]|

 

 

If X_axis, Y_axis, Z_axis are globals (HAAA HERESY!!) then you don't need to return them.

 I tried but I got this warning.

|22|warning: control reaches end of non-void function [-Wreturn-type]|

 

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

Well, that's not the only thing it did - is it?

 

There were also many other links - weren't there?

Yes there are other links.

 

Fair enough - but you still need to put the effort in!

That's what I'm doing, I learn the hard way I guess. By choosing a module from my box of modules and microcontrollers and try to write a code for that module in C.

 

I have parts which has I2C protocols, SPI and USART. It got much easier after I learnt how to get I2C working, now I can proceed with modules.

 

 

So keep studying them.

Emmmm, well it gets me into codes for computer programming and examples which I think not what I want for embedded systems programming.

So I left looking to these eBooks. I just know the basics and try to program with what I know.

 

When I open libraries, I see lines contain keywords; like, inline and virtual. Multiple keywords; like, inline void, inline size_t, static inline void, const void *, a lot of pointers and function pointers ... etc.

 

 

your comments above are close, but no bananas.

 LOL funny :) I'm happy that you see my comments as OK and I have some skills in my topic.

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

>I tried but I got this warning.
>|22|warning: control reaches end of non-void function [-Wreturn-type]|

That is because your function still had a return type. You must change it to being 'void'.

What you really need to do is to study the basics of C programming. Get a good book, practice on a PC without involving embedded stuff.

What you are practicing right now can be called "hipshot coding" - randomly trying things witbout any understanding of the language. You will not make much progress unless you take on the learning process in a structured way.

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Yes, I know.

 

But I already did some good codes.

 

I did LCD_I2C with my own coding in C. I have to store strings converted to HEX values and pass them to a function and display them on the LCD1602 module.

 

I also did SPI and driving dot matrix 8*8 module.

 

I have some skills, but you're right I still need a lot to learn in C. But I'm leaving that for learning progress while I'm doing easy stuff; like, programming modules first before moving to bigger projects which requires multiple modules and data processing.

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

OK I'm getting readings, but they are constant! Why?

 

 

This is my code:

#include <I2C.h>

#define HMC5883L_read   0x3D
#define HMC5883L_write  0x3C

#define Configuration_Register_A    0x00  //Read/Write
#define Configuration_Register_B    0x01  //Read/Write
#define Mode_Register               0x02  //Read/Write
#define Data_Output_X_MSB_Register  0x03  //Read
#define Data_Output_X_LSB_Register  0x04  //Read
#define Data_Output_Z_MSB_Register  0x05  //Read
#define Data_Output_Z_LSB_Register  0x06  //Read
#define Data_Output_Y_MSB_Register  0x07  //Read
#define Data_Output_Y_LSB_Register  0x08  //Read
#define Status_Register             0x09  //Read
#define Identification_Register_A   0x10  //Read
#define Identification_Register_B   0x11  //Read
#define Identification_Register_C   0x12  //Read

void HMC5883L_init (void);
int16_t data_read (int16_t *results);
int16_t results[2];

void setup() {
  // put your setup code here, to run once:
  I2C_init();
  Serial.begin(9600);
  HMC5883L_init();
}

void loop() {
  // put your main code here, to run repeatedly:
data_read(results);
Serial.print("x-axis = ");
Serial.println(results[0]);
Serial.print("z-axis = ");
Serial.println(results[1]);
Serial.print("y-axis = ");
Serial.println(results[2]);
_delay_ms(300);
}


void HMC5883L_init (void)
{
  I2C_start(HMC5883L_write);
  I2C_tx(Configuration_Register_A);
  I2C_tx(0x07);
  I2C_tx(Configuration_Register_B);
  I2C_tx(0x01);
  I2C_tx(Mode_Register);
  I2C_tx(0x02);
  I2C_stop();  
}

int16_t data_read (int16_t *results)
{
  uint16_t lsb,msb;
  int16_t status_of_process = 0;
  I2C_start(HMC5883L_write);
  I2C_tx(Data_Output_X_MSB_Register);
  I2C_start(HMC5883L_read);
  
  msb = I2C_rx();
  lsb = I2C_rx();
  results[0] = msb << 8 | lsb;  

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

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

  return status_of_process;
}

 

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

If you write non-void C functions for your I2C, people will offer sensible help.
If you choose to work "blindfolded" you are on your own.

David.

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

I have only one void function for:

void HMC5883L_init (void);

 

int16_t data_read (int16_t *results);

Is non-void.

 

Is that what you mean?

 

Or you mean my I2C library?

 

And could you explain to me why non-void are better than void functions?

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

Do you drive a car with your eyes open or closed?

 

Look at the Fleury library.   e.g. i2c_start() returns a value.   i2c_write() returns a value.  there is a i2c_read_Ack() and an i2c_readNak()

 

Having said that,  you could just use the Arduino Wire.h library.

Or snoop at how any HMC5883 library does things.

 

David.

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

 

Why you defined these variables in local field? You haven't done much reading on how to program have you? One of the rules is to limit scope. msb and lsb aren't needed outside of the function, so why would you declare them anywhere else?

 

 

OK, here I understood that you're short cutting the process of assigning msb and lsb to one int16_t variable, by shifting msb into lsb so now lsb contains all the 16-bit value for x, z, y axis. - Not quite. I assign them to an unsigned 16 bit variable.

 

I don't know what this line does?! Why you casting it? You seem to understand I'm doing a cast. You want the result as a signed value but the i2c functions return 8 bit unsigned values. So, I'm being explicit with my assignment. The cast itself is mainly for the reader - it generates no code.

 

So why doesn't your code work? Something obvious is you've only defined the result array for 2. If I count z,y and z I come up with three. You expect to put three values into a bag that can only hold two. Just about any good C text explains counting from 0.

You also decided to change the code I wrote. I did this for your education, but alas, it fell on barren ground.

What have you done to debug your code? You function read a number of values from the i2c device and assigns these values to an array. We have two things here - which of them works and doesn't work? Are the values being passed back correctly? How would you test that? Once you've proven that the values are being passed back correctly, then that leaves the i2c functions. How can you test these? How do you know if they fail? This brings us back to what David and I have been repeatedly telling you. Do you really think us professionals can just solve problems based on no information and magically solve them? Considering it could be a number of things both hardware and software that might be causing your problems. What if you have more than one problem? Trying to solve these problems randomly will send you around in circles. You need to logically prove/disprove each step.

If I were in your position, I would have some known good Arduino code in order tho check the hardware is good. It would take you literally minutes to prove this. Once the hardware is proven, then you can look at your code. How many times has a faulty connection or wire breaking caused us to go around in circles?

So, asking us 'why it doesn't work' is the wrong question. Tell us what you have checked and what evidence you have.

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

The I2C init code seems to have a STOP followed by a Transmit instruction.   STOP will release both the SDA and SCL.  To read or write data at this point, the Master needs to send another START condition and Slave_Address-with-Read/Write-bit0 byte.

 

I started exploring a HMC5883L module board with an Arduino a few weeks ago.  I recommend downloading and using the AdaFruit library set: the Sensor.h library and the HMC5883L library. 

With Arduino, get everything working at the demo level by using the most reliable libraries that you can find, which are usually the Adafruit ones.

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

david.prentice

Do you drive a car with your eyes open or closed?

 

Look at the Fleury library.   e.g. i2c_start() returns a value.   i2c_write() returns a value.  there is a i2c_read_Ack() and an i2c_readNak()

 

Having said that,  you could just use the Arduino Wire.h library.

Or snoop at how any HMC5883 library does things.

 

David.

Oh sorry I think I got what you mean :)

 

I developed this function while I was learning and writing my I2C library.

void I2C_TWSR_Check(void)
{
 uint8_t Status;
 Status = TWSR & 0xF8;
 switch(Status)
 {
  /////////////// MASTER TRANSMITTER //////////////
  case 0x08:
  Serial.println("Start is OK");
  break;
  case 0x10:
  Serial.println("Re start");
  break;
  case 0x18:
  Serial.println("SLA+W OK ACK");
  break;
  case 0x20:
  Serial.println("SLA+W no ACK");
  break;
  case 0x28:
  Serial.println("data TX OK ACK");
  break;
  case 0x30:
  Serial.println("data TX OK no ACK");
  break;
  case 0x38:        // arbitration lost in SLA+W or data TX
  Serial.println("arbitration lost in SLA+W or data TX");
  break;

  /////////////// MASTER RECEIVER //////////////
  case 0x40:
  Serial.println("SLA+R OK ACK");
  break;
  case 0x48:
  Serial.println("SLA+R OK NO ACK");
  break;
  case 0x50:
  Serial.println("data RX OK ACK");
  break;
  case 0x58:
  Serial.println("data RX OK NO ACK");
  break;

  default:
  Serial.println("Error");
  break;
 }
}

But I don't apply it now because it requires Serial functions by Arduino, I applied it at first to know the messages of I2C transmission status.

 

Because I want to develop USART library to cover Serial functions in C library so I can display messages by USART in projects by Atmel Studio 7 in future.

 

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

Why you defined these variables in local field? You haven't done much reading on how to program have you? One of the rules is to limit scope. msb and lsb aren't needed outside of the function, so why would you declare them anywhere else?

OK, local is better for professional coding, I think I used to define variables in global region because I do most of my coding for testing and not for final code release.

 

 

I assign them to an unsigned 16 bit variable.

Why to unsigned 16 bit variable? Because the values coming form the HMC5883L are signed.

 

 

So why doesn't your code work? Something obvious is you've only defined the result array for 2. If I count z,y and z I come up with three. You expect to put three values into a bag that can only hold two. Just about any good C text explains counting from 0.

Yes, array[2] contains three variables. Am I right?

 

 

You also decided to change the code I wrote. I did this for your education, but alas, it fell on barren ground.

LOL, no your effort didn't go with the winds :) I just took the modified lines and the new skills.

 

 

What have you done to debug your code?

For the I2C I developed the earlier function, but I excluded it from my I2C library because for projects I need other functions to display debugging messages; like, LCD1602 or TFT displays.

 

You function read a number of values from the i2c device and assigns these values to an array. We have two things here - which of them works and doesn't work?

The I2C is working but the HMC5883L read function is not working completely and gives constant readings.

 

 

 Are the values being passed back correctly?

I don't know, I just see constant numbers and when I move the module, the values are the same and not changing while I'm configuring the module for continuous measuring.

 

 

 How would you test that? Once you've proven that the values are being passed back correctly, then that leaves the i2c functions. How can you test these? How do you know if they fail? This brings us back to what David and I have been repeatedly telling you. Do you really think us professionals can just solve problems based on no information and magically solve them? Considering it could be a number of things both hardware and software that might be causing your problems. What if you have more than one problem? Trying to solve these problems randomly will send you around in circles. You need to logically prove/disprove each step.

You absolutely right! It would be so hard to know what is causing my problems. But, the I2C is working OK.

 

I actually don't want to include I2C checking error functions inside my current code for HMC5883L because I think I don't need them because I already tested my I2C library and I tested the I2C functions to work.

 

If I want to test my code without I2C error checking functions, I just need to change the HMC5883L read/write addresses, and if they are wrong then I won't get any values on the serial monitor.

 

 

 

If I were in your position, I would have some known good Arduino code in order to check the hardware is good. It would take you literally minutes to prove this. Once the hardware is proven, then you can look at your code. How many times has a faulty connection or wire breaking caused us to go around in circles?

So, asking us 'why it doesn't work' is the wrong question.

 

I do this most often, I look for working Arduino code and see if the module is working, then I start my own code. But I can't learn from C++ Arduino libraries because they hard to learn.

 

 

Tell us what you have checked and what evidence you have.

That's what I'm doing actually, I now see constant values.

 

They are not changing.

 

x-axis = -720
z-axis = 442
y-axis = 319

x-axis = -720
z-axis = 442
y-axis = 319

x-axis = -720
z-axis = 442
y-axis = 320

x-axis = -720
z-axis = 442
y-axis = 319

x-axis = -720
z-axis = 442
y-axis = 320

x-axis = -720
z-axis = 442
y-axis = 319

 

This is the result I get.

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

wolfrose wrote:

Why you defined these variables in local field? You haven't done much reading on how to program have you? One of the rules is to limit scope. msb and lsb aren't needed outside of the function, so why would you declare them anywhere else?

OK, local is better for professional coding, I think I used to define variables in global region because I do most of my coding for testing and not for final code release.

 

Your choice - it pays to begin with good habits.

I assign them to an unsigned 16 bit variable.

Why to unsigned 16 bit variable? Because the values coming form the HMC5883L are signed.

 

The results[] array is signed, that's why I cast. 

 

So why doesn't your code work? Something obvious is you've only defined the result array for 2. If I count z,y and z I come up with three. You expect to put three values into a bag that can only hold two. Just about any good C text explains counting from 0.

Yes, array[2] contains three variables. Am I right?

 

I've made a point of outlining your problem, but yet you ask 'Am I right?' - of course you're wrong! That is the point. int16_results[2] only allocates room to two int16_t values.

You also decided to change the code I wrote. I did this for your education, but alas, it fell on barren ground.

LOL, no your effort didn't go with the winds :) I just took the modified lines and the new skills. No you didn't - you assumed something rather than investigating.

 

 

What have you done to debug your code?

For the I2C I developed the earlier function, but I excluded it from my I2C library because for projects I need other functions to display debugging messages; like, LCD1602 or TFT displays.

 

You function read a number of values from the i2c device and assigns these values to an array. We have two things here - which of them works and doesn't work?

The I2C is working but the HMC5883L read function is not working completely and gives constant readings. How do you know this? We keep on asking the same questions, but you don't seek to answer them.

 

 

 Are the values being passed back correctly?

I don't know, I just see constant numbers and when I move the module, the values are the same and not changing while I'm configuring the module for continuous measuring. Wrong answer! You should know - that was my point. You need to prove this. Add some code to prove/disprove the assumption.

 

 

 How would you test that? Once you've proven that the values are being passed back correctly, then that leaves the i2c functions. How can you test these? How do you know if they fail? This brings us back to what David and I have been repeatedly telling you. Do you really think us professionals can just solve problems based on no information and magically solve them? Considering it could be a number of things both hardware and software that might be causing your problems. What if you have more than one problem? Trying to solve these problems randomly will send you around in circles. You need to logically prove/disprove each step.

You absolutely right! It would be so hard to know what is causing my problems. But, the I2C is working OK. Again, how do you know it is. You've not proven it to me.

 

I actually don't want to include I2C checking error functions inside my current code for HMC5883L because I think I don't need them because I already tested my I2C library and I tested the I2C functions to work. Your choice - but David and I have gone to great lengths to explain the reasons for error checking. At this point in time you have no clue as to your problem, but yet you maintain you don't need error checking as your i2c functions supposedly work.

 

If I want to test my code without I2C error checking functions, I just need to change the HMC5883L read/write addresses, and if they are wrong then I won't get any values on the serial monitor.

 

 

 

If I were in your position, I would have some known good Arduino code in order to check the hardware is good. It would take you literally minutes to prove this. Once the hardware is proven, then you can look at your code. How many times has a faulty connection or wire breaking caused us to go around in circles?

So, asking us 'why it doesn't work' is the wrong question.

 

I do this most often, I look for working Arduino code and see if the module is working, then I start my own code. But I can't learn from C++ Arduino libraries because they hard to learn.

 

 

Tell us what you have checked and what evidence you have.

That's what I'm doing actually, I now see constant values.

 

They are not changing.

 

x-axis = -720
z-axis = 442
y-axis = 319

x-axis = -720
z-axis = 442
y-axis = 319

x-axis = -720
z-axis = 442
y-axis = 320

x-axis = -720
z-axis = 442
y-axis = 319

x-axis = -720
z-axis = 442
y-axis = 320

x-axis = -720
z-axis = 442
y-axis = 319

 

This is the result I get.

 

At some point you have to stop guessing and rely on facts. This stuff is complex - in that there are many points where an error can cause an undesired result. In a given project, around 30% of the time is spent on design and implementation and 70% on debugging and validation. If these numbers are extracted from projects done by skilled professionals, what does this suggest to you? Its basically saying to me this stuff is complex and mistakes are common. You expect to throw a few things together and have it work as you hope?

Here's some hints:

1. this stuff is complex.

2. learn the fundamentals - otherwise what have you got to fall back on?

3. Expect errors - refer #1. We're human, we make mistakes.

4. Debugging is a skill.If you don't acquire the skill, you will go around in circles.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1
int16_t results[2];
Serial.print("x-axis = ");
Serial.println(results[0]);
Serial.print("z-axis = ");
Serial.println(results[1]);
Serial.print("y-axis = ");
Serial.println(results[2]);

Remind me again how many results are involved here?!?

 

(hint: if there are 3 results then doesn't results[3] make more sense than results[2] ? IOW how many elements are in an array defined as results[2] and what are the index numbers of the elements in such an array?)

 

This is fundamental C stuff - if you make mistakes like this in the basics you are building a house of cards on weak foundations.

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

clawson

Remind me again how many results are involved here?!?

 

(hint: if there are 3 results then doesn't results[3] make more sense than results[2] ? IOW how many elements are in an array defined as results[2] and what are the index numbers of the elements in such an array?)

 

This is fundamental C stuff - if you make mistakes like this in the basics you are building a house of cards on weak foundations.

I believe array indexing starts from 0.

And I'm testing this on code blocks IDE.

 

This is my C code for testing array indexing and passing by reference.

 

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>


uint8_t array[2];

uint8_t i;

int main()
{
    array[0] = 5;
    array[1] = 77;
    array[2] = 98;

    for (i=0;i<3;i++)
    {
      printf("array[%d] = %d\n",i,array[i]);
    }

    return 0;
}

 

And this is the result I get:

 

array[0] = 5
array[1] = 77
array[2] = 98

 

Last Edited: Fri. Jun 9, 2017 - 02:00 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Believe all you like - you need to deal with fact. The size of the array is incorrect.

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

That code is relying on a bug!

 

You do this:

uint8_t array[2];

That creates a two element array (that's what [2] means) in memory. You are quite right that the elements are 0 based so they are referred to as array[0] and array[1]. There is no such thing as array[2] created in your code. So later when you do:

    array[2] = 27 + 98;

what you are actually doing is randomly writing into the next byte in RAM that just happens to come immediately after the two bytes called array[0] and array[1]. If you wrote:

array[17] = 123;

then that would write to the 18th location after the start address of the array[].

 

C is very "dangerous" in this way. It does not (usually) warn you about this kind of mistake and, indeed, about 75% of all serious software faults in C are probably ultimately traced back to out of bounds array writes or more generally writes to memory through invalid pointer locations (array[17] really means nothing most than *(array + 17) so it's still effectively a write through an invalid pointer).

 

If I build your code in GCC then even if I enable all warnings (-Wall) it still won't tell me that there is a fatal bug in this code. But there is. You cannot make a write to array[2] because no memory has been allocated to hold that element.

 

BTW your code makes little if any sense anyway. You have:

uint8_t array_add(uint8_t arr);

That tells us that this call is going to take as input a number between 0 and 255. Later you call it with:

    array_add(*array);

What on earth do you think this line is going to achieve?!?! (It will actually pass 0x00 but do you know why?). Then in the function you do:

uint8_t array_add(uint8_t arr)
{
    array[0] = 7 + 8;
    array[1] = 77 + 58;
    array[2] = 27 + 98;
    return arr;
}

Luckily no use is made of "arr" and when you "return arr;" you are actually returning 0x00. But that does not matter because in:

   array_add(*array);

you completely ignore the fact that this function returns a value anyway.

 

Similar nonsense exists here:

uint8_t add (uint8_t num1, uint8_t num2);

That tells us of a function called add() that takes two input values in the range 0..255 and it returns a value between 0 and 255. But then you use it as:

   add(msb, lsb);

so again you completely ignore the return value so in:

uint8_t add (uint8_t num1, uint8_t num2)
{
    sum = num1 + num2;

    return sum;
}

the "return sum;" might as well not exist. The only reason the line:

    printf("The add result is 0x%.2x\n\n",sum);

has an output is that "sum" is global so that in the function it was the global that was updated and that is what was returned as a result.

 

To be honest I have never come across anyone who has so fundamentally failed to understand how C works!.

 

You tell us:

wolfrose wrote:
But I already did some good codes.

 

I did LCD_I2C with my own coding in C. I have to store strings converted to HEX values and pass them to a function and display them on the LCD1602 module.

Believe me - if that actually works it is only by the grace of God and probably some complete fluke.

 

You just cannot write C programs until you have learned C and just hacking things like:

The add result is 0x77

array[0] = 0x0f
array[1] = 0x87
array[2] = 0x7d

and then thinking "well that worked so I must be doing it right" is NOT the way to learn C programming!!!

Last Edited: Fri. Jun 9, 2017 - 02:00 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"I did LCD_I2C with my own coding in C. I have to store strings converted to HEX values and pass them to a function and display them on the LCD1602 module."

 

What whould happen if you decide to change your greeting language : I bet your LCD tells "Hi" -ie 72, 102 in decimal, according to Python's "ord" : I am too lazy to convert it into hex...). Now, what would happen if I want it to display "Salut" (French translation of "Hi") or "Lasho ges" (Matesa/Kalderash translation : can be shown, even on a 8 char LCD display).

 

BTW, did you notice you can initialize arrays this way:

char cArray[] = "lasho ges" // will be nine chars long -yes, you counted right- , C will add the terminating 0

int iArray[] = {0,1,2,3}; // will be dimensioned as 4; the last admissible index is 3 (else, use Fortran, Pascal or R : there are no satisfying ways of indexing arrays)

 

"well that worked so I must be doing it right"

 

People who number crunched sometimes had to wait days before a bug could be reproduced (it had a 1E-10 chance of occuring... but that was not right at all. and debugging was empirical, without Internet to tell you you were doing horrible things -overflows, say- )

 

 

 

 

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

wolfrose wrote:

I prefer learn on my own rather paying this much of money :)

 

May be time to focus on value rather than just the amount of money ...

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

awneil

May be time to focus on value rather than just the amount of money ...

I know, but it's too much money! over 2000 pounds! Do you people in UK consider this money OK for 5 days course?

 

I absolutely can't afford this much. But I've been to India lately for C course.

 

It's 4 months course in embedded systems, but I only took 1 month which I could achieve is some C lessons in PC programming, but I even didn't get that much of benefits from that course.

 

I considered going to China but I don't know institutions for embedded systems there in English.

 

So I don't know where to go actually.

 

I don't want to consider India again, but only if I want to get a certificate for my job upgrading process. 

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

Go on.   Follow a sensible scheme e.g. start with proven libraries,  and replace small sections with your code.

 

Any educational course will cost money.    Some might be good value.

Likewise,   self-teaching can be effective if you plan it carefully.

 

Show that you are "reading" the replies on this forum.   It might encourage a better response.

 

David.

Pages