Is it bad practice to redefine register masks for PIC24 in order to improve readability?

The preprocessor does not work the same way as code works. For example, consider the following code:

int main(void)
{
    int A = (B+C);
    int B = (C+2);
    int C =  3;
    int x = A;
    return x;
}

That doesn't work because B and C are used before being declared. The output from the compiler is:

cc -Wall demo.c -o demo
demo.c:3:14: error: use of undeclared identifier 'B'
    int A = (B+C);
             ^
demo.c:3:16: error: use of undeclared identifier 'C'
    int A = (B+C);
               ^
demo.c:4:14: error: use of undeclared identifier 'C'
    int B = (C+2);
             ^

Now try the same thing using #defines for A, B, and C:

#define A (B+C)
#define B (C+2)
#define C  3

int main(void)
{
    int x = A;
    return x;
}

This time there are no warnings or errors, even though the #defines are out of order. When the preprocessor sees a #define it simply adds an entry to its dictionary. So after reading the three #defines the dictionary contains

Search   Replacement
 Text       Text
--------------------
   A       (B+C)
   B       (C+2)
   C       3

Note that the preprocessor has not evaluated the replacement text. It simply stores the text. When the preprocessor finds a search term in the code, then it uses the replacement text. So the line

int x = A;

becomes

int x = (B+C);

After performing the substitution, the preprocessor rescans the text to see if more substitutions are possible. After a second scan, we have:

int x = ((C+2)+3);

and the final result is:

int x = ((3 +2)+3);

Most compilers have an option to output the code after preprocessing is finished. With gcc or clang, use the -E option to see the preprocessor output.


OK, so now we should have enough background to actually address your question. Consider the following definitions:

#define PORTD_TRIS_MASK 0x00
#define PORTD_TRIS_MASK ( PORTD_TRIS_MASK | TC1_MISO_SHIFT )
#define PORTD_TRIS_MASK ( PORTD_TRIS_MASK | SB1_DATA_SHIFT )

We have 3 major problems here:

  1. The substitution text is not being evaluated, so the bits are not being OR'd together.
  2. Only one of those definitions (the last one) will be kept in the preprocessor dictionary. That's the reason for the warning. The preprocessor is telling you that it already has an entry for that search term, and it's going to discard the previous replacement text.
  3. When PORTD_TRIS_MASK is found in the code, the preprocessor replaces it with ( PORTD_TRIS_MASK | SB1_DATA_SHIFT ). It then rescans, and finds PORTD_TRIS_MASK again. The result is infinite recursion. Fortunately, the preprocessor has protection against such things, and will stop. The compiler will generate an error later.

The solution is to create uniquely named definitions for each component:

#define TRIS_MASK_D1 TC1_MISO_SHIFT
#define TRIS_MASK_F1 TC1_DRDY_SHIFT

#define TRIS_MASK_D2 SB1_DATA_SHIFT
#define TRIS_MASK_F2 0

And then OR them all together:

#define PORTD_TRIS_MASK (TRIS_MASK_D1 | TRIS_MASK_D2 | ... | TRIS_MASK_D13)
#define PORTF_TRIS_MASK (TRIS_MASK_F1 | TRIS_MASK_F2 | ... | TRIS_MASK_F13)