Jump to content

#138 expression must be a modifiable lvalue main.c

Recommended Posts

Hi all.


First of all i have made a program who blink a led.

I use the interrupt of Timer0B interrupt.

I enabled this interrupt with this instruction : IntEnable(INT_TIMER0B_BLIZZARD);

This interrupt is defined in the header file hw_ints.h : #define INT_TIMER0B_BLIZZARD    36          // 16/32-Bit Timer 0B

And program works very well.


But now, i want to enable the same interrupt with another instruction : NVIC_EN1 = 0x00000010;

In my program the Timer0B interrupt has number 36.

in header file hw_nvic.h : #define NVIC_EN1                0xE000E104  // Interrupt 32-54 Set Enable


Is the same interrupt ?


I receive an error #138 expression must be a modifiable lvalue.


An lvalue is an expression to which a value can be assigned.

The lvalue expression is located on the left side of an assignment statement, whereas an rvalue is located on the right side of an assignment statement.

Each assignment statement must have an lvalue and an rvalue.

The lvalue expression must reference a storable variable in memory. It cannot be a constant.


Why i receive this error ?


Thank you.


Link to post
Share on other sites

I think the gist of it is, while NVIC_EN1 is defined to the memory address of the register, C can't just use numbers like that as "variables" which is what it's trying to do.  It thinks you're just assigning it another constant number, and assigning a value to a constant number makes no reasonable sense.  You can't say 2 = 3, in other words.  You can't say 0xE000E104 = 0x00000010, likewise :)


So you have to take that number and "cast" it to something it understands.  TivaWare provides some macros to do this, called HWREG and its siblings.


HWREG(NVIC_EN1) = 0x00000010;

might do it.


From inc/hw_types.h, HWREG is:

// Macros for hardware access, both direct and via the bit-band region.
#define HWREG(x)                                                              \
        (*((volatile uint32_t *)(x)))
#define HWREGH(x)                                                             \
        (*((volatile uint16_t *)(x)))
#define HWREGB(x)                                                             \
        (*((volatile uint8_t *)(x)))
#define HWREGBITW(x,                                                        \
        HWREG(((uint32_t)(x) & 0xF0000000) | 0x02000000 |                     \
              (((uint32_t)(x) & 0x000FFFFF) << 5) | (( << 2))
#define HWREGBITH(x,                                                        \
        HWREGH(((uint32_t)(x) & 0xF0000000) | 0x02000000 |                    \
               (((uint32_t)(x) & 0x000FFFFF) << 5) | (( << 2))
#define HWREGBITB(x,                                                        \
        HWREGB(((uint32_t)(x) & 0xF0000000) | 0x02000000 |                    \
               (((uint32_t)(x) & 0x000FFFFF) << 5) | (( << 2))

So HWREG(NVIC_EN1) is actually HWREG(0xE000E104), which is then:

(*((volatile uint32_t *)(0xE000E104)))


Peeling back the parentheses...


(volatile uint32_t *)(0xE000E104) says that the expression "0xE000E104" is actually a volatile pointer to a data structure of type uint32_t (i.e. unsigned 32-bit integer), and as such the C compiler should generate machine (ASM) instructions appropriate for doing 32-bit-word-at-once operations in reference to the data in that variable.


Encapsulating that with parentheses and then putting a * in front, is like having a pointer whose value you want to reference:


uint32_t *ptr = &some_other_variable;


*ptr = 0;  // Set the memory housing "some_other_variable" to 0

*ptr = 255;  // Set the memory housing "some_other_variable" to 255


Likewise, (*((volatile uint32_t *)(0xE000E104))) = 0x00000010;

is saying "Set the 32-bit word defined at memory location 0xE000E104 to 0x00000010" which is what you're looking to do.


If I'm not mistaken, another way to slice & dice this one is to do something like this:


volatile uint32_t *nvic_en1_ptr = (void *)NVIC_EN1;  // (void *) just says "this expression is a pointer, not a generic number"


*nvic_en1_ptr = 0x00000010;


The use of the keyword "volatile" is important to make the C compiler acknowledge that this is a portion of memory whose contents may change behind the CPU's back at any time, and as such, the C compiler shouldn't get sneaky and try optimizing accesses away by, say, storing its value in a register and manipulating that instead (for improved execution speed), assuming that its register copy will always be valid even when it's not (because the underlying contents of that memory location may change without the CPU's knowledge).

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Create New...