Are pointers treated differently in AVR compared to say x86

avrc

Microcontroller: ATtiny13

IDE: Atmel Studio

I'm trying to write a Hello World application by writing high to port PB4.

This works fine:

int main(void)
{
    DDRB = 0x10;
    PORTB = 0x10;

    while(1)
    {
    }
}

Looking at the definition of DDRB and PORTB, they point to 0x17 and 0x18, as expected.

This, however, doesn't work:

int main(void)
{
    char *dir = (void *)0x17;
    char *port = (void *)0x18;

    *dir = (unsigned int)0x10;
    *port = (unsigned int)0x10;

    while(1)
    {
    }
}

Is my code wrong, or do I need to do something else to use pointers?

Best Answer

Pointers are pointers. That is what they are. They aren't treated differently at all (how could you treat them differently?)

The main differences between X86 and AVR are:

  • AVR is 8 bit, X86 is 32-bit (or 64 bit for x86_64), so pointers are a different size.
  • The AVR is Modified Harvard architecture, so there is more than one address space, so you have to ensure you are referencing the right address space.

Also, your code makes no sense:

char *dir = (void *)0x17;

Assign a void * to a char *?

error: invalid conversion from ‘void*’ to ‘char*’

I'm not set up for compiling the ATTiny13 at the moment, so these numbers are all for the ATMega328p:

Accessing DDRB and PORTB results in this assembly:

12c:    80 e1           ldi r24, 0x10   ; 16  
12e:    84 b9           out 0x04, r24   ; 4
130:    85 b9           out 0x05, r24   ; 5

Accessing a pointer to a memory location results in this assembly:

12c:    80 e1           ldi r24, 0x10   ; 16
12e:    80 93 17 00     sts 0x0017, r24
132:    80 93 18 00     sts 0x0018, r24

As you can see, DDRB and PORTB aren't normal variables. DDRB is defined as:

#define DDRB _SFR_IO8(0x04)

and PORTB as:

#define PORTB _SFR_IO8(0x05)

_SFR_IO8() is a macro:

#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)

and _MMIO_BYTE is:

#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))

__SFR_OFFSET can be either 0 or 0x20 depending on chip (normally 0x20).

So, that must mean that the addresses of DDRB and PORTB must be more than 0x20.

Looking at the 328P chip, DDRB is 0x04 and PORTB is 0x05. So, accessing as 0x24 and 0x25, with the right data types thus:

volatile uint8_t *dir = (volatile uint8_t *)0x24;
volatile uint8_t *port = (volatile uint8_t *)0x25;

*dir = 0x10;
*port = 0x10;

results in this assembly:

12c:    80 e1           ldi r24, 0x10   ; 16
12e:    84 b9           out 0x04, r24   ; 4
130:    85 b9           out 0x05, r24   ; 5

Look familiar? The compiler has recognized the 0x20 offset, realized they're SFRs, and compiled in the right out instructions without the 0x20 offset.

So, accessing your port addresses + 0x20 may work for the ATTiny13.

Just looking at the ATTiny25, the DDRB = 0x10 results in:

out 0x17, r24

and accessing a pointer at address 0x37 results in:

out 0x17, r24

So that looks like it is probably it (add 0x20 to your pointer address).