views:

179

answers:

5

i have the memory address of certain register(the address LCDCW1 is C000).

c codes:

#define LCDCW1 0xC000

*LCDCW1=0x31;

i just want to write data to this register. The codes have problems, how to correct it?

thx!

A: 

I don't know what LCDCW1 means, but to write to a constant address:

*(int*)0xC000 = 42;

Adjust to suit (your register may not be int-sized).

Marcelo Cantos
+1  A: 

Assuming that register has the same size as a long:

volatile long * ldccw1 = (long*)0xc000;

*lcdcw1 = myValue;
mouviciel
@mouviciel: my solution is quite the same, but there is an error: pointer expression required, do u know why?
martin
@martin: Can you post your code which gives error?
Naveen
Pointers to external registers should be `volatile` qualified.
caf
@martin: Naveen's answer gives the explanation: you need to cast your integer value to a pointer type.
mouviciel
@caf: agreed, my answer is edited accordingly.
mouviciel
+13  A: 

You can, as others have suggested, declare an appropriate pointer, ie,

volatile uint32_t *reg = (volatile uint32_t *)0xc000;

Note that I've added the volatile qualifier. This is always a good idea when reading or writing hardware registers, because it ensures that each access you perform in your C code actually shows up in the generated code.

However, I usually prefer to write macros like this

#define READ_LCDCW1() ...
#define WRITE_LCDCW1(value) ...

and then fill these in with the appropriate gcc asms. I like these better than direct use of pointers because:

  • I think they read better in the code, identifying what I'm doing, reading a register, instead of focusing on how I'm doing it.
  • Some registers require a multi-step process to read from the hardware. This is hidden easily in macros of this style, and the bulk of my code still refers to the registers I'm interested in, not the complicated way the hardware makes me touch them.
  • Finally, by using asms, I know exactly how I'm accessing the register. Sometimes there are special instructions or address spaces needed to get to a register, which usually can't be generated by the C compiler.
  • Even if you disagree with the motivation for using asm statements, I'd still suggest wrapping your register accesses in macros (or inline functions) like these.

In your case, the simplest definitions should be:

#define LCDCW1_ADDR       0xc000
#define READ_LCDCW1()     (*(volatile uint32_t *)LCDCW1_ADDR)
#define WRITE_LCDCW1(val) ((*(volatile uint32_t *)LCDCW1_ADDR) = (val))
Dale Hagglund
+1 for the appropriate (and too often, neglected) use of 'volatile'.
JustJeff
See @Schedler's comment below for an interesting paper on how most compilers fail to implement `volatile` properly in various tricky cases.
Dale Hagglund
Do use `uint32_t` etc instead of `long`.
Craig McQueen
@Craig: Quite right. I've edited the response appropriately.
Dale Hagglund
+1  A: 

LCDCW1 is just an integer value. You can not do * on that. You need to cast it to integer (or type you require) pointer and then use it. For example: *(int*)LCDCW1=0x31;

Naveen
Do use `uint16_t` etc instead of `int`.
Craig McQueen
+3  A: 

I believe one word of caution about the use the keyword volatile is in order.

Sometimes (often) what the compiler thinks volatile means is not what the programmer intended (and vice versa). It is good practice to always check the resulting machine code when the volatile keyword is used, in order to avoid surprises.

For further references, see e.g. http://www.cs.utah.edu/~regehr/papers/emsoft08-preprint.pdf

Schedler
This is a very good paper. Its proposed technique to make volatile work reliably in C compilers are interesting, but I think they're worse from a readability perspective than simply providing a read/write pair of macros of functions for each register, as I suggest in my answer to this post.
Dale Hagglund