2

I'm referring a document for writing reusable firmware, and code came with the book uses pointer arrays to map memory.

I'm little confused about memory mapping

From this post, If an 8 bit memory is mapped at 0X123, then I can use following

uint8_t volatile * p_reg = (uint8_t volatile *) 0x1234;

or

#define PORT 0X1234
uint8_t volatile * p_reg = (uint8_t volatile *) PORT;

or in case of pointer array

#define PORT 0X1234
uint8_t volatile * const portsout[NUM_PORTS] =
    {
        (uint8_t*)PORTB, ....,
    };

I tried the code came with the book with atmega168 and this is what I had to do to map memory

uint8_t volatile * const portsout[NUM_PORTS] =
{
    (uint8_t*)&PORTB, (uint8_t*)&PORTC, (uint8_t*)&PORTD,
};

PORTB is defined in the header file "avr/io.h" as this

#define PORTB   _SFR_IO8 (0x05)

What I don't understand is the need of & in pointer array??

When I had compilation error when used lpc2148, I send a mail to the author and in his reply mail, he mentioned

Then it looks like your pointer arrays may not actually be pointers. For example:

(uint32_t*)&IOPIN0, (uint32_t*)&IOPIN1,

might actually be

(uint32_t*)IOPIN0, (uint32_t*)IOPIN1,

depending on how IOPIN0 and IOPIN1 are defined for your part

IOPIN0 macro for lpc2148 is

#define IOPIN0          (*((volatile unsigned long *) 0xE0028000))

I don't have much experience in C. I know if the macro refers to memory then I don't have to use & when defining pointer arrays. How to can I know if macro(eg: PORTB, IOPIN0) refers address or value??

Athul
  • 429
  • 2
  • 5
  • 19

2 Answers2

0

The defines for C are something like this:

#if __AVR_ARCH__ >= 100
#    define __SFR_OFFSET 0x00
#else
#    define __SFR_OFFSET 0x20
#endif

#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)
#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
#define PORTB   _SFR_IO8(0x05)

This means that PORTB expands to something like:

// presuming __AVR_ARCH__ >= 100
(*(volatile uint8_t *)(0x05 + 0x00))

which is already dereferenced, hence the need for the ampersand to get the address, i.e. you will have to write:

volatile uint8_t * p = &PORTB;
vgru
  • 49,838
  • 16
  • 120
  • 201
  • That's what I first thought. `(uinit8_t*)&(_SFR_IO8 (0x05))` this works as it supposed to, but when I write `(uinit8_t*)0x05`, code is not working. Why?? As per your explanation it should, Right?? Or should I try `(uinit8_t*)0x0C` – Athul Oct 05 '18 at 16:12
  • @Athul: how exactly does your line look like? I.e. `volatile uint8_t * p = (volatile uint8_t*)0x05;` should work (if `__AVR_ARCH__` is at least `100`). Depending on your compiler, you should be able to get the preprocessed output (e.g. `-E` option [in GCC](https://stackoverflow.com/q/4900870/69809)). This will show you how the resulting code looks like after all macros have been substituted, just before compiling. – vgru Oct 09 '18 at 07:37
  • `uint8_t volatile * const portsout[NUM_PORTS] = { (uint8_t*)0x05, (uint8_t*)&PORTC, (uint8_t*)&PORTD, };` I tried this way but didn't work but `uint8_t volatile * const portsout[NUM_PORTS] = { (uint8_t*)&(_SFR_IO8 (0x05)), (uint8_t*)&PORTC, (uint8_t*)&PORTD, };` this worked – Athul Oct 09 '18 at 11:52
  • That's strange, I've tested it [here](https://godbolt.org/z/Z-8UHj) and there are no warnings. You should definitely use `-E` and check how your macros are actually defined. – vgru Oct 09 '18 at 12:44
0

When writing register maps, it is common to do so with a method that leaves the registers just as if they were variables:

#define REGISTER (*(volatile uint8_t*)0x1234)

where the left * dereferences the pointed-at address, meaning you can now use REGISTER just like any plain variable. This would be why you have to write &PORTB, which after macro expansion ends up as &*pointer. And this is guaranteed to be equivalent to pointer, by C (c17 6.5.3.2):

The unary & operator yields the address of its operand. /--/
If the operand is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted

As for if & is needed or not, it does indeed depend on how the registers are defined in the register map. Which should be available as a header file you can check, no matter system.

As a side note, your casts are fishy. (uint8_t*)&PORTB shouldn't be needed. This suggests some qualifier mismatch with volatile and const. In general, it is always ok to go from pointer to qualified-pointer, but not the other way around.

Based on your example with IOPIN0, the code should look something like this:

volatile uint32_t*const portsout [NUM_PORTS] =
{
  &IOPIN0, ...
};
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Can you explain little bit about qualifier mismatch. May be with some small example – Athul Oct 05 '18 at 16:13
  • @Athul Suppose for example that the original pointer is defined as `const volatile uint8_t*` but you want to store it in an array of `volatile uint8_t*`. You would get incompatible pointers: you can always go from less qualifiers to more qualifiers, but not the other way around. These kind of errors often arise when there are bigger picture design mistakes: having failed to define properly in advance which pointers that point at hardware registers (`volatile*`), which that point to read-only data (`const*`) and which that are to be stored in ROM `(*const)`. – Lundin Oct 08 '18 at 08:19
  • `const volatile uint8_t*` so if I've something like this then I can only store in in an `const volatile uint8_t*` Right?? Also `volatile uint8_t*` can be stored in `const volatile uint8_t*` Right??. I've a function like this void `Adc_RegisterWrite(uint8_t const Address, uint8_t const Value) { uint8_t * const RegisterPointer = (uint8_t *) Address; *RegisterPointer = Value; }` And the compiler gives me warning `cast to pointer from integer of different size` What is the proper way to cast here? – Athul Oct 10 '18 at 06:36
  • @Athul Yes and yes. So the casts aren't needed, which is why they are fishy. Your function here does however not make any sense, since those are plain `uint8_t` and not pointers. Perhaps you could post that part as a separate question (with a copy/paste of the actual code). – Lundin Oct 10 '18 at 07:55
  • The function takers a `uint8_t` type variable, and in function body this non pointer value is type casted in to pointer right?? What if `Address` variable passed when calling the function is actually an adress lik `0x28` or something. – Athul Oct 17 '18 at 11:53