1

I have two files

blink1.cpp

typedef unsigned char int8_t;
typedef volatile int8_t * volatile port_type;


port_type portB = (port_type) 0x25;
port_type ddrB = (port_type) 0x24;


void delay_500ms()
{
    asm (
    // 8000000 cycles
    "ldi  r19, 150 \n\t"
    "ldi  r20, 128 \n\t"
    "ldi  r23, 41 \n\t"
    "L1: \n\t"
    "dec  r20 \n\t"
    "brne L1 \n\t"
    "dec  r19 \n\t"
    "brne L1 \n\t"
    "dec  r23 \n\t"
    "brne L1 \n\t"
    );
}

int main()
{
    ddrB = (port_type) 0x20;

    while(true)
    {
        *portB = (int8_t) 0x20;
        delay_500ms();
        *portB = (int8_t) 0x00;
        delay_500ms();
    }
}

Blink1

blink2.cpp

typedef unsigned char int8_t;
typedef volatile int8_t * volatile port_type;


port_type portB = (port_type) 0x25;
port_type ddrB = (port_type) 0x24;
port_type portC = (port_type) 0x28;
port_type ddrC = (port_type) 0x27;
port_type portD = (port_type) 0x2B;
port_type ddrD = (port_type) 0x2A;


void delay_500ms()
{
    asm (
    // 8000000 cycles
    "ldi  r19, 150 \n\t"
    "ldi  r20, 128 \n\t"
    "ldi  r23, 41 \n\t"
    "L1: \n\t"
    "dec  r20 \n\t"
    "brne L1 \n\t"
    "dec  r19 \n\t"
    "brne L1 \n\t"
    "dec  r23 \n\t"
    "brne L1 \n\t"
    );
}

int main()
{
    ddrB = (port_type) 0x20;

    while(true)
    {
        *portB = (int8_t) 0x20;
        delay_500ms();
        *portB = (int8_t) 0x00;
        delay_500ms();
    }
}

Blink2


The difference between these two files are the definition of only a few unused ports in blink2.

I compile and upload them to the board by

avr-g++ blink1.cpp -o blink1
avr-objcopy -O ihex -R .eeprom blink1 blink1.hex
avrdude -F -V -c arduino -p ATMEGA328P -P /dev/ttyUSB0 -b 115200 -U flash:w:blink1.hex


avr-g++ blink2.cpp -o blink2
avr-objcopy -O ihex -R .eeprom blink2 blink2.hex
avrdude -F -V -c arduino -p ATMEGA328P -P /dev/ttyUSB0 -b 115200 -U flash:w:blink2.hex

The first one does not blink properly but the second one does. What is the problem of the first code?

9
  • Do both files behave the same when using avr-g++ -Os blink1/2.cpp -o blink1/2 for compilation? Apr 11, 2021 at 13:29
  • @MaximilianGerhardt, using -Os both act as before.
    – ar2015
    Apr 11, 2021 at 14:04
  • I think ddrB = (port_type) 0x20; should be *ddrB = (int8_t) 0x20; no? Apr 11, 2021 at 14:05
  • @MaximilianGerhardt, You are correct. However, this fix does not change anything. The board behavior highly depends on the variable you define after ddrB and its value.
    – ar2015
    Apr 11, 2021 at 14:18
  • 1
    Maybe you're killing the contents of registers that are being used. Check the disassembly to see if the registers you are using in assembly are being pushed to the stack before use and popped off afterwards - if they aren't then you should be doing that manually.
    – Majenko
    Apr 11, 2021 at 14:31

1 Answer 1

2

There are a couple of issues here.

One of them has already been raised in a comment:

 
 int main()
 {
-    ddrB = (port_type) 0x20;
+    *ddrB = (int8_t) 0x20;
 
     while(true)
     {

The more serious issue is in the command line you used to compile the program. When compiling for an Uno, you should add the option -mmcu=atmega328p. This way the compiler knows which version of the C runtime it should add to your program. Otherwise the compiler will include no runtime at all.

The C runtime does some important initializations. For example, it clears the register r1, which is required by the ABI, and assumed to be the case by the compiler. For example, when you write *portB = (int8_t) 0x00;, the compiler copies r1 to the port, without first clearing it. Another important role of the C runtime is to call main(). Without it, the program will start by executing whichever function happens to be at the start of the flash. Unless you program in assembly, you have no control over that. If the first function happens to be main(), the program may work as expected. If it is another function, that function may then return to the bootloader. Any seemingly innocuous change in the program can then change the behavior.

Then a few minor issues, that should not prevent the program from working:

  • int8_t is meant to be a signed type. If you want the unsigned version, use uint8_t instead. Sure, these are your own type definitions, but creating a type that conflicts with a standard type can only create confusion. Best option is to drop the definition and include <stdint.h> instead.

  • Whereas port_type should certainly be a “pointer to volatile” data, there is no point in making the pointer itself volatile. That can only hinder valid optimizations.

  • portB and ddrB should be const, and that may allow the compiler to better optimize them. But why not use PORTB and DDRB from <avr/io.h>?

1
  • I confirm using avr-g++ -mmcu=atmega328p instead of avr-g++ has fixed the problem. Thank you so much.
    – ar2015
    Apr 11, 2021 at 21:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.