I'm trying to write software for an ATtiny814 (tiny avr 1-series) microcontroller, but I encounter a strange problem: whatever the first function in main.c is, gets executed and the remaining code gets ignored - including the main()
function.
I'm using the avr-toolchain from the Arduino IDE on macOS, but I'm not using the IDE, I just added the bin/ directory of the avr-toolchain to the PATH variable. The code just compiles fine without errors or warnings. Using pyupdi I can successfully flash the program to the chip, and again it works fine - except that only code from the first function in the main.c is executed.
Makefile:
TARGET = project
CLOCK = 2000000 # configured in main
SOURCES = main.c
OBJECTS = $(SOURCES:.c=.o)
COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -D__AVR_DEVICE_NAME__=attiny814 -D__AVR_DEV_LIB_NAME__=tn814
# compiling and linking, target is the finished hex file
all: $(TARGET).hex
# compile source files to object files
.c.o:
$(COMPILE) -c $< -o $@
# link the object files together
$(TARGET).elf: $(OBJECTS)
$(COMPILE) $(OBJECTS) -o $(TARGET).elf
# convert elf file to hex file
$(TARGET).hex: $(TARGET).elf
avr-objcopy -O ihex -j .data -j .text $(TARGET).elf $(TARGET).hex
clean:
rm -f $(TARGET).hex $(TARGET).elf $(OBJECTS)
Example 1
main.c:
#include <avr/io.h>
void test1() {
// turn on PB1
PORTB.DIR |= PIN1_bm;
PORTB.OUT |= PIN1_bm;
}
int main() {
// disable protection to configure clock frequency
CCP = CCP_IOREG_gc;
// configure CPU frequency
CLKCTRL.MCLKCTRLA = CLKCTRL_CLKSEL_OSC20M_gc; // use 20 MHz internal clock as source
CLKCTRL.MCLKCTRLB = CLKCTRL_PDIV_10X_gc | CLKCTRL_PEN_bm; // divide by 10 and enable divider
// turn on PB0
PORTB.DIR |= PIN0_bm;
PORTB.OUT |= PIN0_bm;
// main program loop
while(1) {};
return 0;
}
Here, only test1()
is executed, as only PB1 turns on.
Example 2
main.c:
#include <avr/io.h>
// prototype
void test1();
int main() {
// disable protection to configure clock frequency
CCP = CCP_IOREG_gc;
// configure CPU frequency
CLKCTRL.MCLKCTRLA = CLKCTRL_CLKSEL_OSC20M_gc; // use 20 MHz internal clock as source
CLKCTRL.MCLKCTRLB = CLKCTRL_PDIV_10X_gc | CLKCTRL_PEN_bm; // divide by 10 and enable divider
// turn on PB0
PORTB.DIR |= PIN0_bm;
PORTB.OUT |= PIN0_bm;
// main program loop
while(1) {};
return 0;
}
void test1() {
// turn on PB1
PORTB.DIR |= PIN1_bm;
PORTB.OUT |= PIN1_bm;
}
Here, main()
gets skipped and again test1()
is executed, turning PB1 on.
Example 3
main.c:
#include <avr/io.h>
void test0() {
// turn on PB0
PORTB.DIR |= PIN0_bm;
PORTB.OUT |= PIN0_bm;
}
void test1() {
// turn on PB1
PORTB.DIR |= PIN1_bm;
PORTB.OUT |= PIN1_bm;
}
No main function at all. No compiler errors. Only test0()
gets executed and PB0 turns on.
I have no clue whats going on here. Btw, using the same avr-toolchain setup, I can write software for an ATtiny841 (it's a different architecture/series) as expected.
Okay, found a solution: When I compile it as described here https://github.com/vladbelous/tinyAVR_gcc_setup#example-of-compiler-usage, it works.
So in my case:
avr-gcc -c -Os -DF_CPU=2000000 -mmcu=attiny814 main.c
avr-gcc -mmcu=attiny814 -o main.elf main.o
avr-objcopy -j .text -j .data -j .rodata -O ihex main.elf main.hex
Fixed Makefile
:
TARGET = project
MCU = attiny814
DEVICE = tiny814
# clock settings applied in main.c
CLOCK = 2000000
PROGRAMMER = /dev/tty.usbserial-A50285BI
SOURCES = main.c
OBJECTS = $(SOURCES:.c=.o)
COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(MCU)
# compiling and linking, target is the finished hex file
all: $(TARGET).hex
# compile source files to object files
.c.o:
$(COMPILE) -c $< -o $@
# link the object files together
$(TARGET).elf: $(OBJECTS)
$(COMPILE) $(OBJECTS) -o $(TARGET).elf
# convert elf file to hex file
$(TARGET).hex: $(TARGET).elf
avr-objcopy -j .text -j .data -j .rodata -O ihex $(TARGET).elf $(TARGET).hex
# flash (call make flash), requires pyupdi installed
flash: $(TARGET).hex
python3 -m updi.pyupdi -d $(DEVICE) -c $(PROGRAMMER) -f "$(shell pwd)/$(TARGET).hex"
clean:
rm -f $(TARGET).hex $(TARGET).elf $(OBJECTS)