AVR development in podman

· 3min · Pasi Lammi

What is podman?

Podman is a daemonless, open source, Linux native tool designed to make it easy to find, run, build, share and deploy applications using Open Containers Initiative (OCI) Containers and Container Images

What is avr?

AVR is a family of microcontrollers developed since 1996 by Atmel.

Why use podman in development

  • Eliminates the need for local installation of tools and libraries.
  • Facilitates easy switching between different versions of compilers and libraries.
  • Integrates seamlessly into CI/CD pipelines.
  • Allows operations without requiring root user permissions.
  • Simplifies the process of creating snapshots of the build environment, which can be saved as a single tar file.

Building local container

Setup Dockerfile

pashi@dev:~/src/avr/test1$ cat Dockerfile 
FROM docker.io/library/debian:12.8
RUN apt-get update && apt-get -y install gcc-avr avrdude avr-libc make

Build local container image "avrbuilder"

pashi@dev:~/src/avr/test1$ podman build -t avrbuilder .
STEP 1/2: FROM docker.io/library/debian:12.8
STEP 2/2: RUN apt-get update && apt-get -y install gcc-avr avrdude
Get:1 http://deb.debian.org/debian bookworm InRelease [151 kB]
...
COMMIT avrbuilder
--> 1843ec6727d
Successfully tagged localhost/avrbuilder:latest
1843ec6727d88c75c97f628c8577f0ca2ec21e90c5bbf1021d51a7edfe13d823

Check container

pashi@dev:~/src/avr/test1$  podman images|grep avrbuilder
localhost/avrbuilder        latest         1843ec6727d8  About a minute ago  231 MB

Publish container

First we need to have a credentials for docker hub and do login. This phase store credentials to local cache.

Login to docker.io

pashi@dev:~/src/avr/test1$ podman login docker.io
Username: pashi
Password: 
Login Succeeded!

Add new tag with my docker hub path and tag

Now we need to match our previous build hash to docker hub user specific url and tags.

pashi@dev:~/src/avr/test1$ podman tag 1843ec6727d8 docker.io/pashi/avrtools:20241229

push to docker.io

To make container image to public we need to push it docker hub.

pashi@dev:~/src/avr/test1$ podman push docker.io/pashi/avrtools:20241229
Getting image source signatures
Copying blob c4d97b6906ed done  
Copying blob 0a96bdb82805 skipped: already exists  
Copying config 1843ec6727 done  
Writing manifest to image destination
Storing signatures

Logout from docker.io

Just make sure we are not anymore logged in to docker hub.

pashi@dev:~/src/avr/test1$ podman logout docker.io
Removed login credentials for docker.io

Remove local images

Just for demonstration purpose I have remove all tags which match to our previous build.

podman rmi docker.io/pashi/avrtools:20241229
podman rmi 1843ec6727d8

Fetch image back from dockerhub

Now we get our previous pushed container back to our machine. Now we have a public backup of our container.

pashi@dev:~/src/avr/test1$ podman pull docker.io/pashi/avrtools:20241229
Trying to pull docker.io/pashi/avrtools:20241229...
Getting image source signatures
Copying blob 87dccbf06420 done  
Copying blob 0a96bdb82805 skipped: already exists  
Copying config 1843ec6727 done  
Writing manifest to image destination
Storing signatures
1843ec6727d88c75c97f628c8577f0ca2ec21e90c5bbf1021d51a7edfe13d823

Application Example

  • One pin give a PWM signal with 50%
  • One pin go on and of each half of second
pashi@dev:~/src/avr/test1$ cat main.c
/*
 instead of use arduino, etc now we read datasheet and setup some registers
 https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf
*/

#include <avr/io.h>
#include <util/delay.h>

int main(void) {
  // PB5 as output
  DDRB |= (1 << 5);

  DDRD |= (1 << DDD6);
  // PD6 is now an output

  OCR0A = 128;
  // set PWM for 50% duty cycle

  TCCR0A |= (1 << COM0A1);
  // set none-inverting mode

  TCCR0A |= (1 << WGM00);
  // TCCR0A |= (1 << WGM01) | (1 << WGM00);
  //  set fast PWM Mode

  TCCR0B |= (1 << CS01);
  // set prescaler to 8 and starts PWM

  for (;;) {
    // Toggle the LED pin
    // _BV here is a macro from the AVR library equivalent to (1<<PB5)
    // PB5 is just naming helper defined as 5
    PORTB ^= _BV(PB5);

    _delay_ms(500);
  }
}

Gnu Makefile

pashi@dev:~/src/avr/test1$ cat Makefile 
CONTAINER?=docker.io/pashi/avrtools
CONTAINERTAG?=20241229
USB?=/dev/ttyUSB0
compile:
	avr-gcc -g -Wall -DF_CPU=16000000 -mmcu=atmega328p -Os -o main.o -c main.c
	avr-gcc -g -mmcu=atmega328p -o main.elf main.o
	rm -f main.hex
	avr-objcopy -j .text -j .data -O ihex main.elf main.hex

show:
	avr-size --format=avr --mcu=atmega328p main.elf

program:
	avrdude -c arduino -P ${USB} -b57600 -p atmega328p -U flash:w:main.hex:i

ccompile:
	podman run --rm -i -t -v $(shell pwd):/app -w /app -v ${USB}:${USB} ${CONTAINER}:${CONTAINERTAG} make compile
cshow:
	podman run --rm -i -t -v $(shell pwd):/app -w /app -v ${USB}:${USB} ${CONTAINER}:${CONTAINERTAG} make show
cprogram:
	podman run --rm -i -t -v $(shell pwd):/app -w /app -v ${USB}:${USB} ${CONTAINER}:${CONTAINERTAG} make program
container:
	podman run --rm -i -t -v $(shell pwd):/app -w /app -v ${USB}:${USB} ${CONTAINER}:${CONTAINERTAG} bash

clean:
	rm -f *.o *.hex *.elf

Use containers

Compile code inside container

We have our application in path ~/src/avr/test1 and call podman command through make command. My makefile contains some default params what i am able to override with environment variables.

pashi@dev:~/src/avr/test1$ make ccompile
podman run --rm -i -t -v /home/pashi/src/avr/test1:/app -w /app -v /dev/ttyUSB0:/dev/ttyUSB0 docker.io/pashi/avrtools:20241229 make compile
avr-gcc -g -Wall -DF_CPU=16000000 -mmcu=atmega328p -Os -o main.o -c main.c
avr-gcc -g -mmcu=atmega328p -o main.elf main.o
rm -f main.hex
avr-objcopy -j .text -j .data -O ihex main.elf main.hex

Use flash programmer inside container

In my case, /dev/ttyUSB0 is the device, and I am a member of the group that has write permission to /dev/ttyUSB0.

So this command we are present some paths from host to contaner. With USB device is just one file what could present to container like other file paths.

pashi@dev:~/src/avr/test1$ make cprogram
podman run --rm -i -t -v /home/pashi/src/avr/test1:/app -w /app -v /dev/ttyUSB0:/dev/ttyUSB0 docker.io/pashi/avrtools:20241229 make program
avrdude -c arduino -P /dev/ttyUSB0 -b57600 -p atmega328p -U flash:w:main.hex:i

avrdude: AVR device initialized and ready to accept instructions
avrdude: device signature = 0x1e950f (probably m328p)
avrdude: Note: flash memory has been specified, an erase cycle will be performed.
         To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file main.hex for flash
         with 186 bytes in 1 section within [0, 0xb9]
         using 2 pages and 70 pad bytes
avrdude: writing 186 bytes flash ...

Writing | ################################################## | 100% 0.10 s 

avrdude: 186 bytes of flash written
avrdude: verifying flash memory against main.hex

Reading | ################################################## | 100% 0.06 s 

avrdude: 186 bytes of flash verified

avrdude done.  Thank you.

End result

A straightforward program has been developed based on the AVR chip datasheet. A simple container has been created using Debian 12.8, and several tools have been installed within it. The container has been pushed to Docker Hub and subsequently retrieved. This container is utilized to compile the application while sharing the serial communication port, allowing its use inside the container.