I'm looking for some advice from better codesmiths than I -- or at least a healthy debate on merits of different approaches.
I'm not much of a C++ guy (just haven't wrapped my mind around all the syntax of classes yet), so I haven't written any Arduino libs. However, I have a few things that are much larger than a sketch should be, so I want to take advantage of code re-use. Also, I'm philosophically drawn to pure, vanilla AVR libc development, which lends itself to C libraries. I'd especially like to develop libs that can be used in AVR Studio and in Arduino projects.. The Arduino headers offer things that make this fairly convenient, such as returning ports for Arduino pin numbers and whatnot, and I can selectively include Arduino compatibility by checking for defined flags, so that's great. However, I'm stuck on how best to allow the user (me, for now, but especially if I later release any code) to customize the layout of the hardware stuff.
For example, I'm writing an SD Card library that can be used in AVR Studio C projects, so the Arduino SPI class is out.
SIDEBAR: Before anyone jumps on this, bear in mind two things: 1) I'm aware I can create a C++ project, incorporate Arduino libraries, and otherwise pretend I'm still coding C, but this is a thought exercise so I'm not going to take the easy way out; and 2) Yes I know there are fully-baked SD libs already, but sometimes I enjoy reinventing the wheel just to learn how one is made. I now appreciate LBA and the difference between half a dozen FAT signatures where before they were just ethereal concepts.
Given this example, what is a well-established way of allowing the user to define the pin they've chosen for CS? (Or even MOSI/MISO/SCLK if using soft-SPI?) In Arduino code, it would look something like this:
// Use D8 for CS class.init(D8);
But that assumes the Arduino IDE method of blending all the source files into one big .cpp and compiling it with all the proper includes baked in for you. If you take this example into AVR Studio, with independent .c files, you break the "globalness" of all the #defines. Ideally, I'd like something like this:
/* MyProject.c */ #define PIN_CS PORTB0 #include "somelib.h" // Use PORTB pin 0 for CS somelib_init(PIN_CS);
Obviously, this doesn't work. The constants like PORTB are dereferenced to the value at their defined memory location -- i.e., they return a value instead of representing an address (well, unless you define SFR_ASM_COMPAT in the libc headers, breaking the expected behavior for every other module in the process) And there's no way to specify a pin, only the whole port. This makes sense of course, as the return value you get back is a bitmap representing 8 pins' states.
I thought about just using defines:
#define PORT_CS PORTB #define PIN_CS PB0 #include somelib.h somelib_init();
But of course that won't work either. The library wouldn't have access to definitions in the main code file, since by definition they're local to that file.
The only workaround I see is to either 1) have the end-user modify the library headers to configure the pin defines (practically requiring local copies for each project), or 2) #include config.h and have the user supply their own header with their definitions (with pretty much the exact same problem). Both options seem ugly. I would assume someone has thought of a more elegant approach.
Sorry for the enormous post.