Joerg
Here's the method used in the uTasker project - it is a C based project and runs on a number of different processors and boards. The project can be moved between targets and boards by defining two flags (one for the processor and one (optional) for one of a number of boards)
Assume every project has an output which can be set or cleared.
To turn the output on and off the main project code does the following:
SET_TEST_OUTPUT(); // set the test output to '1'
CLEAR_TEST_OUTPUT(); // set the test output to '0'
A project header file (linked in every file) includes a hardware header file which is dependent on the processor and board.
// C file start
#include "config.h" // every file has this and here the processor and board type can be configured
..
SET_TEST_OUTPUT();
CLEAR_TEST_OUTPUT();
..
// C file end
In config.h the user can specify which board and processor is being used (eg. whether running on demo board or real target, where the ports may not necessarily be in the same place) [the processor can alternatively be configured by using a C pre-processor define. ie. configured in the compiler set up and the compiler then supplies the define to all code being compiled]
// start config.h
...
//#define _HW_68302 // commented out
#define _HW_M52233 // this processor is active
//#define _HW_HCS12D256 // commented out
#if defined (_HW_M52233)
#define M52233_DEMO // this board for the target is active
//#define M52233_PRODUCTION // commented out
#include "M52233_app.h" // include the application details for this processor
#endif
...
// end config.h
In the last include file (conditionally included for the particular processor) the details are sorted out.
For example in M52233_app.h the hardware and target dependent code is defined (it can also be in assember...!)
// start of M52233_app.h
...
#if defined (M52233_DEMO)
#define TEST_OUTPUT_BIT 0x02 // use this port output on the demo board
#elif defined (M52233_PRODUCTION)
#define TEST_OUTPUT_BIT 0x40 // use this output on the real target
#endif
SET_TEST_OUTPUT() GPIO_PORT_A |= TEST_OUTPUT_BIT
CLEAR_TEST_OUTPUT() GPIO_PORT_A &= ~TEST_OUTPUT_BIT
...
// end M52233_app.h
All of the detail work is therefore performed in one header file (it requires a header per processor type to be maintained). All configuration detail is in config.h.
It should be evident that if you are testing code on the demo board where the test port is bit 1 of the fictional GPIO_PORT_A, by modifying M52233_PRODUCTION to be defined rather than M52233_DEMO the code for the production board will use bit 6 (of course it could also be on a completely different GPIO).
The register defines are of course also defined in a hardware header for the processor but this is standard to all projects with that particular processor (eg. the hardware header delivered by the manufacturer).
This is what the register define may look like which would be compatible with the code above
#define GPIO_PORT_A *(unsigned char *)(PORT_MODULE_ADD + 0x1f)
As noted above there is also no reason why you can't also use assembler at that level - since most C-compilers allow inline assembler.
eg.
#define SET_TEST_OUTPUT() asm { xxxxxxx }
Last notes
- the defines can also be multiple lines long by using the '\' at the end of a line
eg.
#define INITIALISE_LCD_CONTROL_LINES() IO_BUS_PORT_DDR = 0; \
IO_BUS_PORT_DDR = 0; \
O_CONTROL_PORT_DDR &= ~(O_CONTROL_LINES | O_LCD_BACKLIGHT); \
O_CONTROL_PORT_DAT &= ~(O_CONTROL_LINES | O_LCD_BACKLIGHT); \
O_CONTROL_PORT_DAT |= (O_LCD_BACKLIGHT | O_WRITE_READ); \
O_CONTROL_PORT_DDR |= (O_CONTROL_LINES | O_LCD_BACKLIGHT);
- the defines can also be macros
#define SET_PORT_OUTPUT_VALUE(x) GPIO_PORT_A = x
or
#define SET_PORT_OUTPUT_VALUE(x) GPIO_PORT_A = ~x
(the second inverts the output should the hardware use negative logic so that the main project code always remains the same.
Once you get the hang of using these (or similar) techniques you should find project control of modules quite easy.
Regards