5

I am writing some embedded code to interface with an external device over SPI. The device has several registers of varying length and to help keep things straight I have defined the following structure

typedef struct
{
    uint16_t    Signed          :1;  // Register is signed or unsigned
    uint16_t    CommLengthBytes :3;  // The width of the register in bytes 
    uint16_t    Address         :12; // Register address
}ts_register;

I have then defined each register in my sources as follows

static const ts_register    SAGCYC      = {0, 1, 0x000};
static const ts_register    DISNOLOAD   = {0, 1, 0x001};
static const ts_register    LCYCMODE    = {0, 1, 0x004};
static const ts_register    IRMSA       = {0, 4, 0x31A};
static const ts_register    IRMSB       = {0, 4, 0x31B};
static const ts_register    VRMS        = {0, 4, 0x31C};

etc.

I have a function that will take a pointer to an array of ts_registers and queue up the SPI transfers required to read all of the registers in the array and call a callback function to handle the reply

My issue comes when I try to make the array of ts_registers that I want to read as follows:

ts_register regs_to_read[3] = {VRMS, IRMSA, IRMSB};

This generates the error: "expression must have a constant value" 3 times (once per array element).

Since they are defined as constants, what have I overlooked?

Wolf
  • 9,679
  • 7
  • 62
  • 108
user812624
  • 71
  • 1
  • 1
  • 6
  • structs are not constant expressions (even when they are `const`).. I think in C++11 you can solve this with `constexpr`. but somebody with more knowledge will cite the relevant part of the standard. – Karoly Horvath Jul 28 '12 at 16:20

4 Answers4

8

Since they are defined as constants, what have I overlooked?

In C objects declared with the const modifier aren't true constants. A better name for const would probably be readonly - what it really means is that the compiler won't let you change it. And you need true constants to initialize objects with static storage (I suspect regs_to_read is global).

You could try assigning regs_to_read in a function called before anything else uses that array.

cnicutar
  • 178,505
  • 25
  • 365
  • 392
  • @user812624 Are you sure this solves your problem ? You should unaccept this answer and see if better stuff comes up. – cnicutar Jul 28 '12 at 16:31
  • This reply is a little late, but since 16k people have since stumbled across this same problem I'll add a little more detail on why this was the answer for me -> VRMS, IRMSA IRMSB might not have been constants, but their addresses were. I switched to using an array of pointers instead and that cured my problem. Not the most elegant, but it got me passed that roadblock. The code has since been refactored more times than I can count and bares no resemblance to this original snippet, so I cant show the final solution and have it help anyone – user812624 Sep 07 '18 at 22:54
2

const doesn't make them constants at compile time. Make them #defines and the compiler will be happy.

ddyer
  • 1,792
  • 19
  • 26
1

I think this may be a compiler issue, and it would be helpful to know your platform and how you are building this code. I just took most of your code, doctored it up to compile it, and compiled on Linux using gcc. There were no warnings.

#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>

typedef struct
{
    uint16_t    Signed          :1;  // Register is signed or unsigned
    uint16_t    CommLengthBytes :3;  // The width of the register in bytes 
    uint16_t    Address         :12; // Register address
}ts_register;

int main(int argc, char **argv) {

    static const ts_register    SAGCYC      = {0, 1, 0x000};
    static const ts_register    DISNOLOAD   = {0, 1, 0x001};
    static const ts_register    LCYCMODE    = {0, 1, 0x004};
    static const ts_register    IRMSA       = {0, 4, 0x31A};
    static const ts_register    IRMSB       = {0, 4, 0x31B};
    static const ts_register    VRMS        = {0, 4, 0x31C};

    ts_register regs_to_read[3] = {VRMS, IRMSA, IRMSB};

    return(0);
}

Have you tried casting the values? It's not always the best thing to do, but will get you around the error.

Have you considered creating #define entries?

Also, please be aware consts take a bit of getting used to in C. They do not always behave the way you might expect.

octopusgrabbus
  • 10,555
  • 15
  • 68
  • 131
  • 1
    In your code `ts_register` is an automatic object. I suspect it has static storage in the code the OP posted. – cnicutar Jul 28 '12 at 16:34
  • Yes, to get a complete listing in the OP would have been nice. – octopusgrabbus Jul 28 '12 at 16:35
  • 1
    Casting to get around an error is never a good way. Understanding why the warning is there is paramount. Then fix the code, which in 99 out of 100 cases you can do without a cast. A cast, more often than not, is a code smell. – Jens Jul 28 '12 at 16:43
  • 1
    I'm using ARM's compiler within the Keil IDE. Your code list produces the same error when the file is compiled as c, but no errors when compiled as cpp which I guess highlights some of the subtle differences between the languages. – user812624 Jul 28 '12 at 16:50
  • 1
    It's quite funny that I've found this Q/A in Google because I, too, have this error with Keil's compiler. – Trevor Jun 24 '14 at 18:08
0

If regs_to_read is declared in any function scope, the error would be cleared.

iamnagaky
  • 1,033
  • 7
  • 7