Building against cutting edge avr-libc on Mac

Posted on Apr 13, 2024
tl;dr: How to build code for the new attiny chips on Mac (install git avr-libc@main)

Introduction

The new ATtiny chips feature enhanced performance parameters including up to 32 KB of flash memory and up to 2 KB of RAM. These microcontrollers operate at clock speeds up to 20 MHz and include support for the Universal Serial Interface, which allows for I2C, SPI, and UART configurations. Importantly, they feature improved 12-bit ADCs, 10-bit DACs for better analog performance, and the capability to operate at a voltage range of 1.8V to 5.5V, making them highly adaptable for various power supply conditions. Some models also include a built-in hardware multiplier, which facilitates faster calculations for control applications. Additionally, the new ATtiny series maintain compatibility with the AVR instruction set and can be programmed via UPDI for straightforward development and debugging.

However, tooling for them is not yet straightforward - avr-libc supports them only in the main branch, and a lot of people are waiting for the next release which seems to have stalled sadly.

Getting avr-libc ‘main’ branch working on OSX

This was complicated, until I figured it out. Once I’d figured it out it was - as is usual - easy.

Setting up the latest avr-libc from the main branch involves a few steps but is straightforward once you understand the process. First, ensure that you have Homebrew installed, as it will be used to install avr-gcc.

Install Dependencies

Install avr-gcc, which includes avr-binutils and a version of avr-libc, using Homebrew:

brew install avr-gcc

(Un)helpfully, the maintainers upstream include a version of avr-libc into the directory that this installs into, which the linker etc will want to use.

If you installed the default version (9) then you will have a directory here, until the minor version number changes.

/opt/homebrew/Cellar/avr-gcc@9/9.4.0_1

You can find out where it was installed by running

brew info avr-gcc

We then need to build and install the current version of avr-libc. This is probably a stop gap solution, until the next version is released.

The build process for avr-libc is documented at https://www.nongnu.org/avr-libc/user-manual/install_tools.html.

To wit, run these commands:

mkdir ~/src/avr/ && cd ~/src/avr  # Create and change to the directory where avr-libc will be cloned
export PREFIX=$HOME/local/avr  # Set installation prefix

git clone https://github.com/avrdudes/avr-libc.git  # Clone the latest avr-libc from the repository

cd avr-libc

The following will run configure with the appropriate arguments, installing it into the local home directory.

./configure --prefix=$PREFIX --build=`./config.guess` --host=avr
make && make install # Compile and install

Now we have it installed ok, let’s go to the directory that brew installed files to, and overwrite the installed version via a symlink to our new install:

cd /opt/homebrew/Cellar/avr-gcc@9/9.4.0_1
mv avr avr.orig # Backup original AVR library
ln -sf $PREFIX/avr avr # Create symlink to new library

You should now be able to build and compile code for the new Atmel/Microchip ATTiny series!

Blinky

main.c

#include <avr/io.h>
#include <avr/interrupt.h>

#define LED_PIN PIN3_bp // Adjust based on actual connected pin

void setup_timer(void) {
    TCB0.CCMP = 50000; // Compare match value
    TCB0.CTRLB = TCB_CNTMODE_INT_gc; // Periodic Interrupt Mode
    TCB0.INTCTRL = TCB_CAPT_bm; // Enable interrupt on compare match
    TCB0.CTRLA = TCB_ENABLE_bm | TCB_CLKSEL_CLKDIV2_gc; // Enable timer, clock source prescaler: CLK_PER/2
}

ISR(TCB0_INT_vect, ISR_BLOCK) {
    PORTB.OUTTGL = (1 << LED_PIN); // Toggle LED state
    TCB0.INTFLAGS = TCB_CAPT_bm; // Clear the interrupt flag
}

int main(void) {
    // Set LED pin as output
    PORTB.DIR |= (1 << LED_PIN);

    setup_timer(); // Setup timer
    sei(); // Enable global interrupts

    while (1) {
        // Main loop does nothing, just sleep
        asm("sleep");
    }

    return 0; // Return statement added for clarity, though typically not reached
}

Makefile

Change the /dev/ttyUSB0 to be what it is on MacOS, e.g. /dev/tty.usbserial-A50285BI.

MCU = attiny1614
F_CPU = 5000000  # 5 MHz, adjust as per your clock settings
CC = avr-gcc
OBJCOPY = avr-objcopy
CFLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU) -Wall -Os

TARGET = main
SRCS = main.c

all: $(TARGET).hex

$(TARGET).hex: $(TARGET).elf
	$(OBJCOPY) -O ihex -R .eeprom $< $@

$(TARGET).elf: $(SRCS)
	$(CC) $(CFLAGS) -o $@ $^

clean:
	rm -f $(TARGET).elf $(TARGET).hex

flash: $(TARGET).hex
	avrdude -p $(MCU) -c serialupdi -P /dev/ttyUSB0 -U flash:w:$<:i

.PHONY: all clean flash