If the array is processed sequentially, and you have at least two reserved values (say
NEXT and
END), you can use
const TYPE output_pin_enable[] = { /* 0 */ NEXT, /* 1 */ 1, 2, NEXT, /* 2 */ 3, NEXT, END };Note that neither
NEXT or
END can appear as a value. To process each enable, you do
const TYPE *ptr; PINNUMTYPE pin; for (pin = 0, ptr = output_pin_enable; *ptr != END; ptr++) { if (*ptr == NEXT) { pin++; } else { /* Apply (*ptr) to pin (pin). */ } }
If the list is sparse, or you cannot/do not want to use sequential pin numbers, you can do
const TYPE output_pin_enable[] = { 1, 1, 2, NEXT, 5, 3, NEXT, END };where the first value (and first value after each
NEXT) indicates the element index, and
END must be an invalid index (but may appear as a value); only
NEXT must be an invalid value. To process each enable,
const TYPE *ptr = output_pin_enable; while (*ptr != END) { const PINNUMTYPE pin = *ptr; while (*(++ptr) != NEXT) { /* Apply enable (*ptr) to pin (pin). */ } }
To find a specific enable sequence for a pin, you do need to scan through the array, in both above cases. This means that non-sequential access (access to a specific pin, instead of each pin in the defined order), is relatively slow.
This kind of single-array structure is dense and useful when the accesses are sequential, and you don't need random access to specific pin sequences. They (
Verlet lists) are used in e.g. molecular dynamic simulations, listing the neighbor atom identifiers as a list for each atom, so that interactions that are limited in range do not need to be calculated for all atom pairs/triplets/quads/groups in the system. It offers a significant speedup for all non-trivial classical-type/non-quantum-mechanical atomic simulations.
This does not rely on any GCC-isms, and I originally saw it used in FORTRAN code. In Simon's case, I'd consider using the latter form, perhaps guarding some specific lines or line sets with
#ifdef FEATURE_OR_PINS ... #endif.
You could even split the pin identifier into two nibbles, high nibble (four bits) identifying the PORT (with
END = 0xF0), and low nibble identifying the bit within the port (with
NEXT = 0x0F). Use an AND mask for the PORT (so that 0x00 = PORTA, 0x10 = PORTB, 0x20 = PORTC, and so on) instead of shifting it to low bits in a helper variable, for best efficiency; letting the compiler combine the bit shifts needed to get the correct address for the port control register. This also means you can define and use macros for each IO pin, as a single byte then identifies both the port and the pin within the port, with macro names matching whatever you use on the board silkscreen.)