1

BACKGROUND:

I'm learning embedded system programming. During the process I have learn that "Pointer" is a most for embedded system. A pointer is a variable declared in C whose value is the address of another variable. And I can manipulate/change the value of this other variable by dereferencing a pointer.

Example:

int *pt;    // Integer pointer variable declaration.

float *pf;  // Float pointer variable declaration.

int *pt means that pt is a pointer variable capable of pointing to variables of type int. On the other hand, the pointer variable fp can only store the address of a float type variable.

To assign an value (address) to a pointer variable the address operator (&) must be used.

int var = 20;   //Actual variable declaration
int *pt;        //Pointer Variable Declaration

pt = &var;      //Here with the ampersand (&)operator we denotes an 
                //address in memory to the pt pointer.

/*Changing the variable value from 20 to 79*/
*pt = 79;   //Dereference

printf (“Value of *pt variable: %d\n”, *pt); //Output:79
printf (“Value of var variable: %d\n”, var); //Output:79

These two links were very helpful for me to understand pointers:

Pointer Basics in C

Pointer (computer programming)

QUESTION

My question arises when I come across the header file of the Stellaris LM4F120H5QR Microcontroller. This header file define the register locations with memory addresses as follows:

#define GPIO_PORTF_DATA_R       (*((volatile unsigned long *)0x400253FC))

When I came across this syntax I was confused if indeed " (volatile unsigned long *)0x400253FC) " has been define as a pointer and the whole sentence can be interpreted as shows in picture below?

Pointer Dereference Memory Location

If that is not correct, can someone explain the correct way to interpret the register definition for an embedded system header file?

Link for the header file --> Here

Alex Roman
  • 13
  • 4
  • *Why* do you think the Stellaris Firmware Development Package should have an incorrect definition? One thing about it, since it refers to an actual device, is that it should be using either `uint32_t` or `uint64_t` since `unsigned long` does not have the same size in all C implementations, and so is ambiguous. – Weather Vane Jul 28 '20 at 19:27
  • @WeatherVane TI programmers have allergy for fixed size integers :). BTW the quality of code is from TI is very low (even worse than STM HAL libraries) – 0___________ Jul 28 '20 at 20:04
  • A pointer is not necessary the address of another variable. It is just an address somewhere in memory. And the syntax `&var` gives you the memory address of the variable `var`. – Joël Hecht Jul 28 '20 at 20:10
  • 1
    @JoëlHecht `#define GPIO_PORTF_DATA_R` is a *specific* register definition. It is not a 'variable' in the usual sense of that term. – Weather Vane Jul 28 '20 at 20:11
  • The linked diagram is almost right, *except* dereferencing the pointer will give an `unsigned long` value, not a single byte. The sequence of bytes starting at that address in memory vs. the value of the long integer depends on the architecture being big- vs. little-[endian](https://en.wikipedia.org/wiki/Endianness). – dxiv Jul 28 '20 at 20:12

2 Answers2

0

this is a correct defiention !! and if you try it it will work just fine .... let me help you in understanding this statment how it work

Explanation of accessing a specific memory address

if you have an a specific address in memory FOR example (this address 0x400253FC) and you like to write value of (50) to the first byte the following code will be wrong

// the following code is wrong
0x400253FC = 50 ;

the above code will give a compile error .. so how to tell compiler to make this (0x400253FC) as a memory address ??, simply will do that with help of casting

(unsinged char *)0x400253FC  // cast this number 0x400253FC as char pointer

now you have a pointer so you could dereference it and write the value in memory (where the pointer point to) like this

*((unsinged char *)0x400253FC) = 50; // write the value 50 in address 0x400253FC

some compilers when they make Optimization they will delete this line of code so to prevent the compiler from doing that we add the volatile specifier ... so the expression will be like this

*((volatile unsinged char *)0x400253FC) = 50; // write the value 50 in address 0x400253FC

so this is how to accssess a byte in memory

if you like to access 4 byte in memory so it will be like this

//assuming your compiler consider the long variable as 4 byte
*((volatile unsinged long*)0x400253FC) = 50; // write the value 50 in 4 byte in memory start with address 0x400253FC
Ibram Reda
  • 1,882
  • 2
  • 7
  • 18
  • 1
    Historically, compilers used to inherently treat accesses to pointers formed from constants as `volatile` because there were relatively few cases where trying to consolidate accesses would be worth the effort, and requiring that programmers bloat source files and headers with `volatile` qualifiers would slow down compilation for no purpose. Since then, however, "clever" compilers like clang and gcc have decided that the Standard's failure to mandate that compilers do something all compilers were doing anyway should be treated as an invitation to regard code relying on that as "broken". – supercat Jul 28 '20 at 20:22
  • @supercat "When your only tool is a hammer, every problem is a nail" should also include "and when you run out of nails, you start smashing non-nails with your hammer". Way too many developers are limited to only throwing more code at things, even when it's not needed. I don't know how many times I've had to fix some screwed-up process where, instead of actually fixing the root cause of problems, more code was added to handle the symptoms. And often the problem originated from adding extraneous "solutions-in-search-of-a-problem". Good developers know when to stop effin' with it... – Andrew Henle Jul 28 '20 at 22:37
0
(*((volatile unsigned long *)0x400253FC)) = 0x12345678

Elementary C, just parse it out. I think you get it not sure where the confusion is.

I think you understand typecasting

unsigned int x;  //a variable

x = 0x400253FC; //assign the variable a value

(volatile unsigned long *)x  //typecast x into a different type volatile unsigned long *

Likewise using a pointer

volatile unsigned long *z;

(*z)=0x12345678; //at the address pointed to by z place the value 0x12345678;

break it into parts.

(
*
(
(volatile unsigned long *)0x400253FC
)
)


0x400253FC a value

(volatile unsigned long *)0x400253FC typecast that value into an unsigned long pointer

((volatile unsigned long *)0x400253FC)  enclose that pointer as a whole at this point it is a pointer, like z from above.

*((volatile unsigned long *)0x400253FC) dereference it one level, like *z above you can now use this to manipulate the unsigned long address.

(*((volatile unsigned long *)0x400253FC)) good idea to wrap defines in parens to not confuse the compiler.  doesn't hurt.

(*((volatile unsigned long *)0x400253FC)) = 0x12345678.  Like *z = 0x12345678 above, write/store 0x12345678 to the address 0x400253FC

#define GPIO_PORTF_DATA_R       (*((volatile unsigned long *)0x400253FC))

GPIO_PORTF_DATA_R = 0x12345678; store/write 0x12345678 to the address 0x400253FC

unsigned long k; k = GPIO_PORTF_DATA_R; load/read from address 0x400253FC and save it in k

If you have an 8 bit wide register then adjust the typecast as such.

#define SOME_8BIT_REG  (*((volatile unsigned char *)0x5006789A))

SOME_8BIT_REG = 0x33;

you could just try it

#define GPIO_PORTF_DATA_R       (*((volatile unsigned long *)0x400253FC))
#define SOME_8BIT_REG  (*((volatile unsigned char *)0x5006789A))

void fun ( void )
{
    GPIO_PORTF_DATA_R = 0x12345678;
    SOME_8BIT_REG = 0x33;
}

00000000 <fun>:
   0:   e3a02033    mov r2, #51 ; 0x33
   4:   e59f1010    ldr r1, [pc, #16]   ; 1c <fun+0x1c>
   8:   e59f0010    ldr r0, [pc, #16]   ; 20 <fun+0x20>
   c:   e59f3010    ldr r3, [pc, #16]   ; 24 <fun+0x24>
  10:   e58103fc    str r0, [r1, #1020] ; 0x3fc
  14:   e5c3209a    strb    r2, [r3, #154]  ; 0x9a
  18:   e12fff1e    bx  lr
  1c:   40025000    
  20:   12345678    
  24:   50067800    

  10:   e58103fc    str r0, [r1, #1020] ; 0x3fc
  32 bit store (write) 0x12345678 to address 0x400253fc

  14:   e5c3209a    strb    r2, [r3, #154]  ; 0x9a
  8 bit store (write) 0x33 to address 0x5006789a

Instruction set doesn't matter

0000000000000000 <fun>:
   0:   48 c7 04 25 fc 53 02    movq   $0x12345678,0x400253fc
   7:   40 78 56 34 12 
   c:   c6 04 25 9a 78 06 50    movb   $0x33,0x5006789a
  13:   33 
  14:   c3                      retq  

Disassembly of section .text:

00000000 <fun>:
   0:   123457b7            lui x15,0x12345
   4:   40025737            lui x14,0x40025
   8:   67878793            addi    x15,x15,1656 # 12345678 <fun+0x12345678>
   c:   3ef72e23            sw  x15,1020(x14) # 400253fc <fun+0x400253fc>
  10:   500687b7            lui x15,0x50068
  14:   03300713            li  x14,51
  18:   88e78d23            sb  x14,-1894(x15) # 5006789a <fun+0x5006789a>
  1c:   8082                    ret
halfer
  • 19,824
  • 17
  • 99
  • 186
old_timer
  • 69,149
  • 8
  • 89
  • 168