What does this mean exactly in C?

Viewed 395
#define RegisterX_Address (*((volatile unsigned int*)0x400253FC))

I need to understand what this (*((volatile unsigned int*)0x400253FC)) in C, I understands that is a pointer pointing to the address of one of the registers but why is it written like this???

4 Answers

On whatever platform this is, memory address 0x400253FC contains 1 int worth of data.

If you dereference 0x400253FC, you get that value.

This is likely used for something like:

unsigned int GetCPUTemperature()
{
    unsigned int temp;
    temp = RegisterX_Address;
    return temp;
}

It is not terribly common to have data mapped directly to a specific memory address, but happens in embedded development regularly.

The volatile keyword tells the compiler it cannot cache, store or reuse the value it gets. It must retrieve the value from memory on each access, because whatever is writing data there will update the value regularly and outside the scope of the program.

It's a macro to access a (probably) memory-mapped I/O pin.

The compiler or hardware itself knows that specific addresses of memory have an additional purpose. When your source code reads from that register it's really reading the value off of an input pin, a wire hooked up directly to the processor. That address is special, and these sort of things are typically exclusively tied to that particular hardware. (Which is why you need a shmorgas board of different drivers for different hardware and they're not all conveniently interchangeable). For all the misdirection and generalization that higher levels perform, at some point you need to direct read and writes to actual hardware with actual addresses.

The inner * is declaring it as a pointer. The outer * is dereferencing it. And yeah, pointers in C are one of the most confusing parts of the language. The * symbol has two jobs really; one is during declarations, where it describes a chunk of data. And explicitly casting is declaration: (volatile unsigned int*). That forces the compiler to treat the hex value as a pointer to a volatile unsigned integer. The second, in execution when using this macro: *(myPointer), that pointer is dereferenced, so rather than using the value 0x400253FC, you're looking at the data that lives at ADDRESS 0x400253FC in memory. Which isn't even memory since it's probably mapped to HW. (It depends. Either way it's still called a register in ICDs. I think of registers as the specific registers on the chip, what assembly can use directly as opposed to going through the MMU. But everything is a "register" to the HW guys, they use it like we use "address").

And I'm guessing it's an I/O pin because it's volatile. This a an instruction to the compiler not to make presumptions when optimizing because it's value can change behind it's back. Like when hardware gets a signal. Or when an output line automatically resets low. Volatile variables are also used for shared memory between threads, but on a microcontroller it's typically I/O.

Just try it...

#define RegisterX_Address (*((volatile unsigned int*)0x400253FC))
unsigned int fun ( void )
{
    return(RegisterX_Address);
}

gives

00000000 <fun>:
   0:   4b01        ldr r3, [pc, #4]    ; (8 <fun+0x8>)
   2:   6818        ldr r0, [r3, #0]
   4:   4770        bx  lr

   8:   400253fc

It is simpler to write than

volatile unsigned int *RegisterX_Address;
RegisterX_Address = (volatile unsigned int *)0x400253FC;
return(*RegisterX_Address);

That is why it is written that way. Plus as written it doesnt consume memory as a variable.

It is written like that because that is what happens to work on the particular compilers used with that particular microcontroller. Most likely, the volatile serves as an indication to the compiler that it shouldn't make optimizations that would result in not actually reading the hardware register.

Related