Reputation: 21
I'm trying to compile the following C code using IAR EWARM but I'm getting three compilation errors (Error[Pe028]: expression must have a constant value). See below:
#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
typedef uint8_t I2C_BusIdentifier;
typedef uint8_t I2C_SlaveAddress;
typedef enum {
I2C_BUS_STATE_UNINITIALIZED = 0,
I2C_BUS_STATE_GPIO_HARDWARE_READY,
I2C_BUS_STATE_READY_TO_OPERATE,
} I2C_BusState;
typedef struct BUS_I2C_BUS_INSTANCE_TYPE {
I2C_BusIdentifier BusIdentifer; // 0 for I2C0, 1 for I2C1
I2C_BusState CurrentState; // bus status
} I2C_Bus; // I²C Bus Instance Type, I2C_BusInstanceType
typedef struct DEVICE_I2C_GENERIC {
I2C_Bus* DeviceBusPointer;
I2C_SlaveAddress DeviceAddress;
} I2C_Device;
// inherits from I2C_Device
typedef struct DEVICE_ADC123 {
I2C_Device Device;
} ADC123_Device;
#define NUMBER_OF_I2C_PORTS 2
static I2C_Bus g_I2C_Bus[NUMBER_OF_I2C_PORTS] = {
{ 0, I2C_BUS_STATE_UNINITIALIZED, },
{ 1, I2C_BUS_STATE_UNINITIALIZED, },
};
I2C_Bus* const g_I2C_BusPtr_Port0 = &(g_I2C_Bus[0]);
I2C_Bus* const g_I2C_BusPtr_Port1 = &(g_I2C_Bus[1]);
const ADC123_Device g_Device_ADC123_U14 = {
{ g_I2C_BusPtr_Port0, 0xAE, }, // <--- Error[Pe028]: expression must have a constant value
};
const ADC123_Device g_Device_ADC123_U15 = {
{ g_I2C_BusPtr_Port1, 0x8A, }, // <--- Error[Pe028]: expression must have a constant value
};
const ADC123_Device g_Device_ADC123_U9 = {
{ g_I2C_BusPtr_Port1, 0xAA, }, // <--- Error[Pe028]: expression must have a constant value
};
#define NUMBER_OF_ADC123_DEVICES 3
const ADC123_Device* g_ADC123_Array[NUMBER_OF_ADC123_DEVICES] = {
&g_Device_ADC123_U14,
&g_Device_ADC123_U15,
&g_Device_ADC123_U9,
};
int main(void)
{
while(1);
}
However, everything compiles OK if I use the g_I2C_Bus addresses directly instead of through the g_I2C_BusPtr_PortX pointers:
const ADC123_Device g_Device_ADC123_U14 = {
{ &(g_I2C_Bus[0]), 0xAE, },
};
const ADC123_Device g_Device_ADC123_U15 = {
{ &(g_I2C_Bus[1]), 0x8A, },
};
const ADC123_Device g_Device_ADC123_U9 = {
{ &(g_I2C_Bus[1]), 0xAA, },
};
I want to use the const pointers (g_I2C_BusPtr_Port0, g_I2C_BusPtr_Port1) because they are extern'd in a .h file, whereas the array (g_I2C_Bus[]) will not be exposed globally but static in a specific .c file.
Why is the compuler unhappy about this when the definitions/values should be equivalent since they reference the same thing?
Upvotes: 2
Views: 4661
Reputation: 141618
In case it is not clear from the other answers already; the correct code organization is:
// stuff.h
//
extern I2C_Bus *const g_I2C_BusPtr_Port0;
extern I2C_Bus *const g_I2C_BusPtr_Port1;
and then:
// stuff.c
//
#include "stuff.h"
I2C_Bus* const g_I2C_BusPtr_Port0 = &g_I2C_Bus[0];
I2C_Bus* const g_I2C_BusPtr_Port1 = &g_I2C_Bus[1];
const ADC123_Device g_Device_ADC123_U14 =
{ { &g_I2C_Bus[0], 0xAE } };
and so on.
Upvotes: 1
Reputation: 158529
Intializers for a static object must either be a constant expression or a string literal, if for example ADC123_Device g_Device_ADC123_U14
was a automatic variable then there would be no error, for example if you declared it in main
. We can see this by going to the draft C99 standard section 6.7.8
Initialization which says:
All the expressions in an initializer for an object that has static storage duration shall be constant expressions or string literals.
The reason why the second case works is that constant addresses are allowed, we can see this from 6.6
Constant expressions which says:
More latitude is permitted for constant expressions in initializers. Such a constant expression shall be, or evaluate to, one of the following:
and includes the following bullet:
- an address constant, or
Upvotes: 0
Reputation: 16039
This is a limitation with C language. Values of variables, like
int const a = 1;
cannot be used in constant expressions, like as initializers:
int b = a; /* Will not work */
Not even with const
qualifier. Reasoning is that compiler cannot know values of variables, even if it would seem completely trivial. Variable with const
is not a constant in C, it's only variable which cannot be changed by you.
Addresses of global variables are a different matter. Linker complete control where these variables are located, and can use the same information for initializers.
Work-around is to use preprocessor:
#define g_I2C_BusPtr_Port0 (&(g_I2C_Bus[0]))
Upvotes: 6