Project Architecture - Sugested reading?

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

Greetings,

I am attempting to transition to a more organized project structure.  I am having several issues that issues that seem to stem from my lack of understanding of the pre-process, compile, linking process.

My current (soon to be old) method is to put almost everything in main.c and include various reused code via #include "kirk_utils.h" (good) and  #include "motors.c" (not so good).  Old hats will know the pits that I have fallen into this way. Not a big deal in small projects, but a real tangle can result as projects grow.

 

Please make suggestions, point out good examples and/or suggest some reading and tutorials to help me make the transition to better code practice.

 

Some specific questions and issues that I am having.

Should I to explicitly include motors.h in motors.c?

Global variables defined in main are generate errors in other .c files. What is a good practice to fix this? Should I make a global.h with various #defines #includes and global vars? (Then add #include global.h to everything?)

Thanks

Kirk

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

There is a great tutorial written by Dean Camera. The most recent version can be found here:

 

http://www.fourwalledcubicle.com...

 

Look for "Modularizing Code .... "

 

Jim

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

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

Perfect! Exactly what I needed. Now I have a more logically divided project! (it even compiles and runs correctly!)

Thanks ka7ehk and thanks Dean Camera.

Kirk

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

At the risk of opening a can of worms ... What goes in .h vs .c?

 

  1. Comments like /* PORTA.1 is for the flame sensor */
  2. extended comments like /* Project History Blah Blah */
  3. Macros
  4. Constant defines like #define Ki = .001  and  #define MINSPEED 70
  5. Conditional compile defines like #define REPORT_LEVEL = 2
  6. includes like #include <avr/interrupt.h>

Thanks

Kirk

Last Edited: Sat. Nov 22, 2014 - 09:57 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There are 'best practices' for c that have been developed over the last 40 years. There are lots of books on software engineering that discuss How To Configuration-Manage Large Projects. Things get out of hand when a job has lots of programmers, files, classes, modules, versions of source files. The guy that wrote c++ was trying to solve some of the c-in-a-large-project problems. The whole software engineering environment involves version control software, bug tracker databases. Even though I am aware that modern software development technique is redshifting away from me, I keep thinking I'm going to change my Put-All-The-Files-In-The-Same-Dir for each project. I have used that system since 1974., when I was fretting about disk space consumed by having the same c files in different dirs. Some one got the clever idea to not even save the c source. Just the edit commands from the last version. Yikes. After having Someone Elses config mgmnt system lose my files more than once, I got skittish about trusting any other system other than: have the source, object, and makefile in the same dir, One exe is produced. No version confusion. Flame Suit On. Tell me why losing the 17th diff out of 123 revs shouldnt give me indigestion. I know Cliff has a detailed and specific system he uses at work with teams, and it was clear and easy to understand. I dont know if we can dredge that up from the legacy archives. Wherever Humans are required to follow a procedure to keep things from messing up, it will fail. Usually the night before selloff.

 

 

 

Imagecraft compiler user

Last Edited: Sat. Nov 22, 2014 - 11:06 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

if we just consider three things:

1. Constants

2. Function prototypes

3. Variable extern declarations

 

The function of the .h file is to provide information about the c file it is associated with. Think of it like a menu - "these are the functions you can call along with the constants and vars you can access"

 

I think you need to get yourself a good C book. Some books i'd recommend is Code Complete and Writing Solid Code.

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

bobgardner wrote:
Wherever Humans are required to follow a procedure to keep things from messing up, it will fail.
Android uses Gerrit to aid their process.

IIRC when an Android developer creates a patch the developer also creates a test for the patch.

Android Developers

Life of a Patch

http://source.android.com/source/life-of-a-patch.html

Google

Gerrit Code Review

Web based code review and project management for Git based projects.

https://code.google.com/p/gerrit/

"Dare to be naïve." - Buckminster Fuller

Last Edited: Sun. Nov 23, 2014 - 01:38 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:

I think you need to get yourself a good C book. Some books i'd recommend is Code Complete and Writing Solid Code.

Both on order from Amazon. Thanks for the recommendation.

  1. Comments like /* PORTA.1 is for the flame sensor */
  2. extended comments like /* Project History Blah Blah */
  3. Macros
  4. Constant defines like #define Ki = .001  and  #define MINSPEED 70
  5. Conditional compile defines like #define REPORT_LEVEL = 2
  6. includes like #include <avr/interrupt.h>

So if I understand you correctly

item 1 (not answered)

item 2 (not answered)

item 3 (in .h according to Dean Camera's Tutorial)

item 4 (in .h) as well as stuff like const char beep_button_top[] PROGMEM = "!c32";

item 5 (not answered)

item 6 (in .c ???)

plus function prototypes (in .h according to Dean Camera's Tutorial)

plus external variable declarations (in .h according to Dean Camera's Tutorial)

 

Thanks

Kirk

Last Edited: Sun. Nov 23, 2014 - 02:16 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

1. Comments - where they make sense. For something like PORTA.1, you would probably do #define PORTA.1 FLAME_SENSOR_INPUT

2. Comments - again, where they make sense. The overall description you would probably put in the main file. if it's a serial library, then describing the overall operation would be best in those files.

4. You would put anything that allocates memory in a .h file. Why? If you have two files that include the .h file that has variable declarations, then you have two copies of the same variable. The linker will barf. In the case you describe,

 const char beep_button_top[] PROGMEM = "!c32"; would be in the C file,  extern const char beep_button_top[]; would be in the .h file - only if you expect to share it.

5. Where it makes sense. In a .h file if you want to share it, otherwise if it only affects the one C file, then put it in there.

 

In many cases, you want to limit the 'scope' - as in who can see it. It's no use making a function globally visible if it's only used within the one C file it appears in. The same with variables. Sometimes you need to break the rules - you could be fighting performance or size issues so you need to take shortcuts.

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

Ah.  Now the logic is becoming a bit more clear to me.

1 & 2) Comment locations are really a matter of style and readability.

The location of macros and other defines depend on whether they are shared or private to the module and to a lesser extent readability/editability

4) Don't allocate any memory (RAM nor flash) in .h

 

I have seen so much variation in examples that I was lost. I could not tell a good example from a bad one.  Thanks so much for the help in beginning to understand reasons. I guess that my head will spin a bit more when the books arrive. :-)

 

Kirk

 

 

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

KirkCharles wrote:
Conditional compile defines like #define REPORT_LEVEL = 2
= is probably a bad idea.

@kartman: I think that 9.4 is missing a not.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

KirkCharles wrote:

I have seen so much variation in examples that I was lost.

 

Pick a style that works for you and stick to it. Consistency between your files is more important than doing the same as someone else.

#1 This forum helps those that help themselves

#2 All grounds are not created equal

#3 How have you proved that your chip is running at xxMHz?

#4 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

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

skeeve wrote:

Conditional compile defines like #define REPORT_LEVEL = 2

= is probably a bad idea.

Oops Typo!

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

Skeeve - right you are. I could blame the dodgy forum code!

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

Brian Fairchild wrote:

Pick a style that works for you and stick to it. Consistency between your files is more important than doing the same as someone else.

Brian,

Picking a style that works is what I am trying to do.  I have quite enough projects done in styles that don't work all that well.

Quote:

Skeeve - right you are. I could blame the dodgy forum code!

It must be the forum code. I read it with the "not" :-)

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

At the risk of opening a can of worms ... What goes in .h vs .c?

The more general answer to that question (rather than "does this?", "what about this?", "or this?"...) is only put in a .h what needs to be there ;-)

 

Think of .h as "documentation" for the routines and variables you use. It's your opportunity to tell someone else what they need to know to be able to use your code.

 

In the old days code was written in precompiled chunks called libraries ("lib<something>.a" files). You gave another programmer a .a file and a .h file. The second told them how to use the first. A classic example is that your C compiler comes with a library called libc.a and the instructions on how to use it are in the well known headers: stdio.h, string.h, stlib.h and so on.

 

if you want to know how to use printf() or strcpy() you go to stdio.h or string.h and you find entries such as:

extern int      printf(const char *__fmt, ...);
extern char *strcpy(char *, const char *);

That's all you get - you have no idea how the person who implemented the library has actually written printf() or strcpy(). Simply that the first has a variable number of parameters or which the first is a pointer to a char array that holds a "format" or in the case of strcpy() that it takes two parameters - one is a pointer to destination string (non const) and the other is a pointer to a fixed source string.

 

You don't see actual C code to implement printf() or strcpy() in these .h files and you certainly don't get to "see" any hidden internal variables that they might be using to implement what they do.

 

So just try to always think of .h in this: it's your opportunity to share with a 3rd party what it is your code offers to do and it contains the minimum amount of shared information necessary to allow them to make use of your code.

 

In the unlikely event that you need to share a variable (rather than just functions) with them then define it with extern as something like:

extern int the_variable_we_all_need_to_access;

Never put anything that would lead to the generation of code or data bytes in a .h:

extern char *strcpy(char *, const char *);

just tells you what you need to know to be able to call strcpy() - it does not actually implement any code or data when you #include this. Even:

extern int the_variable_we_all_need_to_access;

just tells you the name and the type of this variable - it does not create it or assign storage (that is done in just one "hidden" .c that is used to build libc.a or whatever).

 

Of course another use of .h is for configuration. You might provide an lcd.h so the user knows how to access your LCD functions (and variables?) but it might also give them an opportunity to:

#define LCD_DATA_PORT PORTD

or something like that - which your lcd.c takes as "input" when it's built to vary which port it operates on.

 

Of course the C pre-processor is something entirely different to the C language and is often misunderstood. A #define is just a text substitution - not code.

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

clawson wrote:
Of course another use of .h is for configuration. You might provide an lcd.h so the user knows how to access your LCD functions (and variables?) but it might also give them an opportunity to:

#define LCD_DATA_PORT PORTD

or something like that - which your lcd.c takes as "input" when it's built to vary which port it operates on.

Whether users of lcd need to know this is a significant decision.

If the answer is no, the #define should probably go into a .h file that is only #include-d by lcd implementation files.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

Last Edited: Tue. Nov 25, 2014 - 07:35 AM