Question

Trying things out as I learn C code, I wanted to test something. It worked as expected, but throws the warning

Warning 1 assignment from incompatible pointer type [enabled by default]

The code is simple. All I am doing here is toggling PIN B7 on an atmega2560. I have a LED hooked to it and I can see it blinking, so I know it works as expected.

Can anyone explain why I am seeing this error, even though it is executed as expected? The code is as follows:

#include <avr/io.h>
#include <util/delay.h>

void main(void) {
    int *ptr;
    ptr = &PORTB; // This line throws the warning

    DDRB = (1 << 7);

    while(1) {
        *ptr = (1 << 7);
        _delay_ms(1000);

        *ptr = (0 << 7);
        _delay_ms(1000);
    }
}

PORTB is an 8bit register that has a bit per pin to control whether that pin is HIGH or LOW.

For now, I am glad it works. But these warnings annoy me.

Was it helpful?

Solution

int *ptr;
ptr = &PORTB; // This line throws the warning

PORTB is a volatile unsigned char defined with something like this:

*(volatile unsigned char *) 0xBEEF

Change your ptr declaration to a volatile unsigned char *:

volatile unsigned char *ptr;
ptr = &PORTB;

OTHER TIPS

Edit 2:

By the time I got around to correcting my original answer (below) I saw that @ouah had already posted a correct answer so I ask you to please use it instead of mine. For a more detailed explanation please read 'Edit 1' below.

My original answer:

PORTB is probably not defined as an int which means that when you take the address of PORTB you won't get an int *. If you are sure you're right and that you can use an int * for PORTB you can use a cast to tell the compiler that it doesn't need to worry. You can cast it like this:

ptr = (int*)&PORTB

Go to the definition of PORTB and let us know what type it is.

Edit 1:

My assumption was that you knew that PORTB was an int and that you're casting it from void*. Thanks @H2CO3 for pointing out that it is in fact a (*(volatile uint8_t *)(0x25)) which basically means that PORTB is a volatile uint8_t.

This being the case you should NEVER cast it to an int! If this worked it means that your machine is probably little endian and you shouldn't rely on this.

To explain it properly let's set up a simple example:

We have this in memory:

Address  Value
0x02        7
0x03       11

Note: 7 is 0x07 in hex and 00000111 in binary and 11 is 0x0B in hex and 00001011 in binary

Now we have 2 pointers:

uint8_t* BytePointer;
int*     IntPointer; // int is either 2 or 4 bytes, we will assume it is 2 bytes.
int Value16;
uint8_t Value8;

// Let's set both to point to our fake memory address
BytePointer = (uint8_t*) 0x02;
IntPointer  = (int*)     0x02;

// Now let's see what value each holds?
Value8 = *BytePointer;
Value16 = *IntPointer;

// Value8 will contain 0x07
// Value16 will contain 0x0B07 on a Little Endian machine
// Value16 will contain 0x070B on a Big Endian Machine

This example illustrates what happens when we read values from pointers, what about writing? Let's keep the same variables as before and write some values

*BytePointer = 5;

The memory would now look like this:

0x02     5
0x03    11

What about an int pointer?

*IntPointer = 5;

Since an int is two bytes it will alter two bytes:

// Little endian
0x02     5
0x03     0

// Big endian
0x02     0
0x03     5

So if you use PORTB as an int, every time you assign to it you are writing 2 bytes, one to the address of PORTB and one straight after. I hope whatever is after isn't important... You shouldn't do this so you should use an uint8_t* if you really want to use pointers.

But by the time I got around to explaining it all properly @ouah has already posted a correct answer. Please refer to it for how this should be done.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top