Software query...

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

Hi Freaks!

I've just written a small library using GCC to do serial comms on a Mega 8: did it both for the kicks of it, to get something much smaller than PRINTF would give me and to get to know my hardware better [wait, that's three reasons, not two?!].

Functions are "write control registers", "direct putchar", "direct getchar", and with buffers enabled I have "getchar", "putchar", "getstring" and "putstring".

Questions are:
1: How large should such a code be? I get something around 800 bytes with a minimal program that has all library enabled but just transmits a word repeatedly.

2: Where can I get a super-compact but easy to use library to do this kind of stuff [im not looking for anything fancy and fearture-rich like PRINTF]

3: To the Code-Gurus, is initalization needed in GCC? Some people claim you can do without it, but I still initialized variables which needed to start always from zero. Is this a waste of space or cant i do otherwise?

4: What's the best library to use for TWI communications with only the most essential bits, such as sendStart, sendStop, sendByte, ReceiveByte??? Will be used with my AVR as the only bus master possible. I would try roll my own [gutsy, huh?], but I'm afraid I wouldn't win first prize for efficiency!

Greatly appreciate all your help - this forum has been invaluable for me!

NxP - addicted 2 engineering

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

1) That sounds quite a lot for what you describe - you are building with -Os aren't you? Have you looked at the .lss to see where the majority of the code space has gone?

2) well the itoa() library function is usually considered the "halfway house" between putchar() and printf() ;-)

3) Globals that you don't provide an initial value on in the definition are GUARANTEED to be set to 0 as the C pre-amble includes code that writes 0's to the entire .bss - HOWEVER automatic variables (that is locals that aren't 'static') are NOT guaranteed to hold 0. They are created on the stack on entry to the function and removed on exit and they inherit any old crap that happens to be in the stack SRAM at that time. So for those you should ALWAYS provide initial values. But the real "belts and braces" solution is to always provide initial values for all variables.

4) While libraries are great and I'm all for re-use of tried and tested code I'd have said that for somehting like TWI you are far better off implementing it from scratch using the info in the datasheet so that you understand 100% what's going on when something (almost inevitably) doesn't work as expected. The usual fault people make on I2C in fact is to forget the external pull-up resistors

Cliff

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

Thanks for the tips Cliff!

1: yes I'm building using -Os, size optimization. Checked in the .lss file [first time I've seen one] but I'm not really into AVR assembler, so I can't actually tell how good or bad the code is. I can understand some of the mnemonics, but i have no idea what could be changed.

What seems wierd is how code is generated for all the functions in my little library, even those which are not called in my main code. How on earth can I avoid that???

Also something very fishy is the huge amount of stack used by interrupt vector, say "vector 13" which does 14 pushes and 14 pops - wtf?

Something which I'm also curious about is how the transmit and receive interrupts listed in the datasheet as vectors 12 and 14 respectively become 11 and 13. Why does GCC shift them down to zero-based indexing?

2 Thanks for the tip, I will definitely use it a lot when sending simple numeric data back to the pc terminal. I plan to experiment with Pascal Stang's uart libraries too because I hear the "putchar" style functions are good. What's your opinion?

3: Another great thanks. I will not waste resources playing with the globals, but I'll keep initializing the local stuff as I'm already doing [gcc grumbles if I don't do it]

4: Your point is very valid, but I'd like to try a library for the moment [lazy_flag = 1]. At first glance the AvrLib i2c library looks pretty, and I've read the i2c section of the datasheet completely, so I should manage, but I'm having a few problems.

I've written some code which does initialization, sets baud rate, sends start, and sends one byte [should be address+r/w but i'm not considering the hardware yet]. After each event I send a message to terminal, and I wait for the "send" to end before messaging. Unfortunately the "i2cWaitForComplete" never seems to finish.

i2cInit();				//Initialize i2c
	putstring_uart("I2C init\n\r");
	
	i2cSetBitrate(100);		//Set bitrate in KHz
	putstring_uart("I2C bitrate\n\r");

	i2cSetLocalDeviceAddr(0b0001000, FALSE);
	//Set arbitrary local address, do not respond to general calls.
	putstring_uart("I2C address\n\r");

	i2cSendStart();			//Send start bit
	putstring_uart("I2C start\n\r");

	i2cSendByte(45);		//Send data byte
	i2cWaitForComplete();
	putstring_uart("I2C data\n\r")

The library code which does the wait is this

while( !(inb(TWCR) & BV(TWINT)) );

which I assume waits until the TWINT bit in TWCR is set. I never saw anything like "(inb(TWCR)" in my life, so what does it do?

I've been reading the datasheet and saw that if the hardware does not ACK the transmitted byte the interrrupt can never be generated, but what about the interrupt generated after a i2cSendStart();? The datasheet says that an interrupt should be generated there, but if I put a i2cWaitForComplete(); after it the code blocks there.

Now what can I be MISSING?

Sorry for the long post!

Nat

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

You use the word 'library'... to me that means a directory full of c files with one function in each file. They all get compiled to .o files, then the librarian needs to add each one to the .a file, then you need to tell the linker to link against the .a library you created. Since you didnt mention doing all this stuff, I think you mean 'a c file full of functions' instead of library. I'm just a programmer, not a mind reader, so I might be trying too hard here. Sorry if that's the case.

Imagecraft compiler user

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

Oops! My bad!

I mean a c file full of functions and definitions and an accompanying header file full of function prototypes. That's how I always considered it.
Am i understandable now?

Any hints?
*pleading face*

Nat

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

You're using GCC... If you're still working with GCC 3.x, then you have two options:
1) Don't use multiple source files for any aspect of the application. Copy all the C source code together into one huge C file, and label all functions as "static". Now, the compiler has all the information needed to optimize out unused functions.

2) Go ahead and do as Bob suggests -- place each library function into its own distinct C source file, compile each of them into an object file, use the librarian to put all those object files together into an archive file, and finally instruct the linker to link against that archive whenever it needs to resolve any references to any of your library functions.

If you're using GCC 4.x then you have the third option of using whole-program compilation/optimization. That means that all C source files are "in scope" simultaneously within a single pass of the compiler. It is effectively very similar to option (1) above. I can't go into any more detail than that, since I've never worked with this capability.

Of course, dropping GCC 4.x into either of options (1) or (2) above will still work just fine as well.

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

Quote:
What seems wierd is how code is generated for all the functions in my little library, even those which are not called in my main code. How on earth can I avoid that???

Correct me if i'm wrong but i don't think you can. The linker will load only the parts that are needed by the program you're linking.

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

If I am right in understanding what you want to achieve is a single source file containing functions each of which you may/not want to include idepending on your project, without the use of the librarian?

Well one way of achieving it is to surround each 'set' of fucntions (could be just 1 in a set) with conditional compile directives. Then create an include file that defines the conditional variables and set them accordingly to if the should be used/not, e.g.

Quote:

/************************************
* "library.h"
***********************************/
... // usual stuff
#define TRUE (1==1)
#define FALSE !TRUE

#define CONF_FUNCTION1 TRUE
#define CONF_FUNCTION2 FALSE
#define CONF_FUNCTION3 TRUE
...
/* end "library.h" */

/**********************************
* "library.c"
*********************************/
#include "library.h"

#if (CONF_FUNCTION1 == TRUE)
void function1(void) {
... // do something
}
#endif

#if (CONF_FUNCTION2 == TRUE)
void function2(char ch) {
... // do something
}
#endif

#if (CONF_FUNCTION3 == TRUE)
void function3(void) {
... // do something
}
#endif

/* end library.c */

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

To lfmorrison,
seems I'm still using version 3.xx of GCC, so I'll probably download something more recent and try to use option 3. Havent tried options 1 and 2 yet, I will try 2 as soon as I manage to, because it seems the most neatly structured from 1 and 2.

To zarmi82,
The way you see it makes sense to me, but in the .lss file there is code even for the function which I never called in my code, so they were not optimized out. Cant claim im an expert, but that's what I see.

To Caviar,
It's not exactly that way. I already have a file with all functions in it, and I'm including it in my project, but I want to find a way to have the unused functions removed without having to manually define them. I'm already using conditional compilation, but it would be a bit tedious if every time I use a function in my code I have to change a "compile flag" in the include header.

Thanks for the tips, I will try lfmorrison's suggestion and report on that.

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

If you're using Windows, then the most convenient distribution of avr-gcc, WinAVR, hasn't been updated to include GCC 4.x yet. Also, keep in mind that the standard WinAVR makefile template will need modification in order to support whole-program compilation.

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

Quote:

Correct me if i'm wrong but i don't think you can. The linker will load only the parts that are needed by the program you're linking.

You are wrong, and hereby corrected. At least when we are talking about GCC 3.x used "the normal way". Read lfmorrisons post above which hints towards the facts.

Heres my view of it: The linker will link in all code that came from one compilation unit (one source file) as soon as any code from that file is actually needed. This is true wether or not the object file transited through a library or went directly to the linker.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"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

nxp wrote:
Also something very fishy is the huge amount of stack used by interrupt vector, say "vector 13" which does 14 pushes and 14 pops - wtf?

No one else picked up on this question but the usual reason this happens is when you make the mistake of calling functions from an ISR rather than inlining all the code. When you do this the compiler has no option but to push/pop just about everything as it can't know which registers the called function corrupts.
nxp wrote:
Something which I'm also curious about is how the transmit and receive interrupts listed in the datasheet as vectors 12 and 14 respectively become 11 and 13. Why does GCC shift them down to zero-based indexing?

Answer - because it does. This "oddity" has been noted before. I guess it's because the GCC implementor didn't consider the reset vector as an interrupt vector while the Atmel datasheet nomenclature calls the reset "vector 1" so the first actual interrupt is vector 2 and so on.

Cliff

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

Hmm, and I'm still using WinAvr, which makes me ask:
What's the most painless way to get a more recent GCC distribution running on my pc? Where can I find GCC with AVR compatibility?

I ask this because before using WinAvr and AVR mcus I've always used pre-installed and stable toolchains at the uni lab "press the 'compile' button and you get a .hex file ready to burn" - so I dont know what work is needed to get a more recent version running.

OTOH, would it be easier to go bobgardner's way and build an "object library" instead of a "source library"? I'm going to try to find how to use the librarian and linker, but any help is appreciated.

As for the PUSH-POP issue it's my bad, called a short function from the ISR [basically the ISR pulls a byte from the buffer and give it to a function that writes it to the UART]. I will simplify that.

Thanks to all - you ROCK!

Nat

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

Quote:

I'm going to try to find how to use the librarian and linker, but any help is appreciated.

Look in the tutorial forum. Smiley wrote something about this IIRC.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"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

GCC (and related tools) source code comes "out of the box" with compatibility with most AVR microcontrollers. Additional support is available through a series of patches maintained by Jörg Wunsch in the FreeBSD ports system.

You simply have to "compile the compiler" with appropriate settings to expose the AVR compatibility. (Each "installation" of GCC will be able to generate code for one combination of target CPU and target OS -- be it Windows/x86, Linux/PowerPC, no-operating-system/AVR, etc.)

The real trick is finding a way to build a working compiler out of the GCC sources, especially if you're using Windows. It's relatively painless to build it for yourself if you have a Unix-like environment (such as Cygwin) installed on your Windows machine. But it still doesn't suit most peoples' fancy.

ATmanAVR (a commercial distribution of avr-gcc for Windows) has a version of their toolchain that uses gcc 4.1. In accordance with the applicable open-source licenses, they make the compiler and associated low-level tools available for download free of charge, and they can be extracted right over top of the WinAVR installation. (The commercial aspects of ATmanAVR are distributed as a separate IDE and code wizard.)

However, ATmanAVR's toolchain produces debug images which aren't currently compatible with AVR Studio's simulator/emulator.

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

Thanks Luke... I always wondered why the unix guys made every function static.... static vars in a function I use... never could understand why global functions need to be called static... so they can be thrown away! Cool.

Imagecraft compiler user

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

So, I'm excited - and frustrated - while trying to go bobgardner's way.

As you suggested Bob, I placed a few functions in separate c-files, with an essential "extern" variable in a commonly included h-file, and compiled all of that in an empty project in WinAvr with the GCC compiler. Found the o-files and placed them into an a-file using AVR-ar and AVR-ranlib. (I referred to the tutorial where Smiley was experimenting with this linking)

I added the a-file to a project to test the functions by using AvrStudio's "libraries" tab under project options, and added the common h-file, but when i try to compile I get an error message which goes as:
"C:\Program Files\Atmel\WinAVR\bin\..\lib\gcc\avr\3.4.6\..\..\..\..\avr\bin\ld.exe: cannot find -l-o"

How on earth can I find what's missing? I'm starting to feel out of my depth now, but I don't wnt to give up yet.

Nat - frustrating myself

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

This is avery long shot: The end of the message indicates that a file "-l-o" is missing, but that looks an awful lot like command switches rather than a filename. So I'm guessing that you have a syntax error or some such in your makefile. Could youn post it (or maybe zip the whole project) for us to see?

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"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

> Thanks Luke... I always wondered why the unix guys made every
> function static.... static vars in a function I use... never could
> understand why global functions need to be called static...
> so they can be thrown away!

No, it's a relatively recent addition to GCC that it might throw away static
functions, and certainly not the major reason to declare them that way. Any
clean code should not have unused functions at all, and if you look into a
good source package for some Unix utility, you'll find that's how they work.

The reason to make anything that is only needed locally "static" is just
code cleanness: as a general rule, don't expose more than needed to the
outside. Anything that is just not there cannot be abused, and cannot cause
a conflict with other modules.

This is all relatively recent stuff. Original Unix code used to be quite sloppy
in many respects.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.