1

I am writing a "gpio" device driver for my AVR ATmega32 microcontroller. The driver has an initialization function that's shown below. I use enums to pass configuration to the function. An example of calling this function is

gpio_init_port(MY_PORT_A, INPUT, HIGH);

This would set PORTA as input with default value HIGH. I use switch statements to check the passed configuration to set the corresponding port as input or output. Now I thought of using #error preprocessor directive in the "default" case of the switch statement in case a user enters a wrong input, for example

gpio_init_port(5, INPUT, HIGH);

so compilation stops and an error message is printed. But I do not know why it's not working as I expect. The part of the code I am referring to is this:

default:
#if (port_number != MY_PORT_A && port_number != MY_PORT_B && port_number != MY_PORT_B && port_number!=MY_PORT_D)
#error "Wrong Input. You have entered invalid port number."
#endif

The expression

(port_number != MY_PORT_A && port_number != MY_PORT_B && port_number != MY_PORT_B && port_number!=MY_PORT_D)

doesn't evaluate and the error message isn't printed. But when I change it to

#if (1)
#error "Wrong Input. You have entered invalid port number."
#endif

It works fine. Another issue that I have is that when I write something like

gpio_init_port(5, ***OUTPUT***, HIGH);

The error message is also printed although I am passing "OUTPUT" this time and this not supposed to happen since the default case is defined only for the "INPUT" case.

gpio.h
______

typedef enum port_number{
   MY_PORT_A=0,
   MY_PORT_B,
   MY_PORT_C,
   MY_PORT_D
} port_number_t;
typedef enum port_direction{
   INPUT=0,    
   OUTPUT
} port_direction_t;
typedef enum output_state{
   LOW=0,
   HIGH
}output_state_t;
void gpio_init_port(port_number_t port_number, port_direction_t port_direction, output_state_t initial_value);

gpio.c
________
#include "gpio.h"
void gpio_init_port(port_number_t port_number, port_direction_t port_direction, output_state_t initial_value)  
{
switch (port_direction)
{
case INPUT:
{
    switch (port_number)
    {
    case MY_PORT_A:
    DDRA=0x00;
    PORTA=initial_value? 0xff:0x00;
    break;
    case MY_PORT_B:
    DDRB=0x00;
    PORTB=initial_value? 0xff:0x00;
    break;
    case MY_PORT_C:
    DDRC=0x00;
    PORTC=initial_value? 0xff:0x00;
    break;
    case MY_PORT_D:
    DDRD=0x00;
    PORTD=initial_value? 0xff:0x00;
    break;
    default:
    #if (port_number != MY_PORT_A && port_number != MY_PORT_B && port_number != MY_PORT_B && port_number!=MY_PORT_D)
    #error "Wrong Input. You have entered invalid port number."
    #endif
    break;
    }
}
case OUTPUT:
{
    switch (port_number)
    {
    case MY_PORT_A:
    DDRA=0xff;
    PORTA=initial_value? 0xff:0x00;
    break;
    case MY_PORT_B:
    DDRB=0xff;
    PORTB=initial_value? 0xff:0x00;
    break;
    case MY_PORT_C:
    DDRC=0xff;
    PORTC=initial_value? 0xff:0x00;
    break;
    case MY_PORT_D:
    DDRD=0xff;
    PORTD=initial_value? 0xff:0x00;
    break;  
    }
}
}
}

So, what is exactly wrong here?

I have read that enums can't be used in conditional compilation, but I don't know if that's truly right and the reason of the problem.

Thanks in advance.

2 Answers2

6

these statements

#if (port_number != MY_PORT_A && port_number != MY_PORT_B && port_number != MY_PORT_B && port_number!=MY_PORT_D)
#error "Wrong Input. You have entered invalid port number."
#endif

are evaluated at preprocessing/compile time. So they're evaluated whatever the outcome of the switch statement. In that case you're trying to test port_number which is a variable in the #if macro, which isn't possible.

In general, the #error directive is useful for instance when some preprocessor constants are conflicting, at compilation time, certainly not at run time. The example you're quoting

#if (1)
#error "Wrong Input. You have entered invalid port number."
#endif

"works" (well it's not very useful as is) because 1 is a compile-time constant.

You want to test that at runtime, and throw a kind of "exception" when running:

default:
{
   fprintf(stderr,"at %s line %d: Wrong Input. You have entered invalid port number.\n",__FILE__,__LINE__);
   exit(1);
}

no need for the if since all 4 cases have been ruled out by the switch statement.

Also note that you can insert the handy __FILE__ and __LINE__ compilation-time values so you know where in the source code the error was raised (unless you want to use assert(0); directly)

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
3

The preprocessor #if can only test for values that have been #define'ed earlier. It knows nothing about variables, parameters, or enum values.

And everything that doesn't come from a #define is given a default macro value of 0.

So your test turns out as

#if (0 != 0 && 0 != 0 && 0 != 0 && 0 !=0)

And this will of course never trigger the #error message.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203