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?

asked Apr 11, 2021 at 12:24
9
  • Do both files behave the same when using avr-g++ -Os blink1/2.cpp -o blink1/2 for compilation? Commented Apr 11, 2021 at 13:29
  • @MaximilianGerhardt, using -Os both act as before. Commented Apr 11, 2021 at 14:04
  • I think ddrB = (port_type) 0x20; should be *ddrB = (int8_t) 0x20; no? Commented 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. Commented 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. Commented 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>?

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

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.