2024-04-21

Install the following packages:

  1. avr-gcc
  2. avr-binutils (should already come with avr-gcc)
  3. avr-libc
  4. libserialport
  5. avrdude

Assumptions:

Compiling to elf:

avr-gcc -DF_CPU=16000000UL -mmcu=atmega328p main.S -o main

[Optional] compiling without stdlib

For small scripts you may not need the stdlib, so you can pass the argument nostdlib to the compiler. The default is to include the stdlib.

avr-gcc -DF_CPU=16000000UL -nostdlib -mmcu=atmega328p main.S -o main

If you check the size of the compiled file, it should have less bytes now.

avr-size --format=avr --mcu=atmega328p main

In my current version of avr-gcc, this has saved me 132 bytes.

Transform the file into a hex file

When avrdude pushes the compiled code to the arduino, it requires a hex file that can be produced by running the following command:

avr-objcopy -O ihex main main.hex

Find the usb the arduino is connected to

Connect your arduino and run

ls /dev/tty*

If it's not obvious to you which port it is using, disconnect the arduino and run the command again. Check which port disappeared, that is your port!

Run avrdude

This is the one that will differ depending on what programmer you are using. I'm just using the Arduino bootloader, so I can run avrdude like this:

avrdude -v -p atmega328p -c arduino -P /dev/ttyUSB0 -b 115200 -U flash:w:main.hex:i

NOTE: Sometimes you have to play with the bound rate to get it to push properly. I have had success with higher bound rates.

My script

This script turns a LED on/off every 3 seconds.

#include <avr/io.h>
.section .data

.section .bss

.section .text

.global main

main:
  .org 0x00
  ldi r16, (1<<PB5)
  ldi r17, (1<<PB5)
  out _SFR_IO_ADDR (DDRB), r16
loop:
  out _SFR_IO_ADDR (PORTB), r16
  rcall delay_1s
  rcall delay_1s
  rcall delay_1s
  eor r16, r17
  rjmp loop
delay_1s:
  ldi r20, 64
delay1:
  ldi r21, 250
delay2:
  ldi r22, 250
delay3:
  dec r22
  nop
  brne delay3
  dec r21
  brne delay2
  dec r20
  brne delay1
  ret