Search code examples
clinkeravr

AVR ATtiny814 program executes first function, ignoring main


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.


Solution

  • 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)