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();
}
}
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();
}
}
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?
1 Answer 1
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, useuint8_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
andddrB
should beconst
, and that may allow the compiler to better optimize them. But why not usePORTB
andDDRB
from<avr/io.h>
?
-
I confirm using
avr-g++ -mmcu=atmega328p
instead ofavr-g++
has fixed the problem. Thank you so much.ar2015– ar201504/11/2021 21:35:02Commented Apr 11, 2021 at 21:35
Explore related questions
See similar questions with these tags.
avr-g++ -Os blink1/2.cpp -o blink1/2
for compilation?-Os
both act as before.ddrB = (port_type) 0x20;
should be*ddrB = (int8_t) 0x20;
no?