I’ve tried to minimise the prerequisites as far as possible, but there are a few unavoidable things you need to get in place before you can start using this crate. AVR development with Rust is just like that…
Prerequisites
- The
avr-gcc
toolchain must be installed on your machine; we need the assembler and linker to link our binaries. - You will want to install a particular version of the Rust toolchain; at the
time of writing, that is
nightly-2024-05-01
.
Installing avr-gcc & avr libraries
You are probably thinking “I’ll just install these with [Homebrew]
on my Mac
or using apt
on my Linux machine, right?
Stop!
It’s extremely frustrating to write this, but the packaged versions of avr-gcc in most package managers are out of date and do not support all the currently available AVR microcontrollers. In particular, the ATmega4809 that was the reason I wrote this package in the first place is blessed with either missing or broken support in the open source distributions of avr-gcc.
The good news is, ATmega/Microchip do distribute a working version of the toolchain for their controllers. Download it from here:
What | Where |
---|---|
ATmega toolchain | https://www.microchip.com/en-us/development-tools-tools-and-software/gcc-compilers-avr-and-arm |
Using the correct Rust toolchain
To compile Rust for the AVR, you need to use the nightly
toolchain.
Bleeding edge versions may have problems, so at the moment I am using the
nightly-2024-05-01
toolchain which is recent enough, but confirmed working.
You will need to install the toolchain like so:
rustup install nightly-2024-05-01
An avr-oxide project
An AVR Rust project needs a little more than just adding avr-oxide
to your
Cargo.toml
file:
- You need to tell Rust to use the
nightly-2024-05-01
toolchain. - You need to give the Rust compiler a ’target spec’ file which tells it how to compile/link for the particular microcontroller you are using.
- You need to install the ATmega “ATpack” files which contain device-specific static libraries and compiling/linking instructions.
- And yes, you do need to add
avr-oxide
toCargo.toml
.
In the interests of making all this as simple as possible, I have provided
blank ’template’ projects for all1 the microcontroller types that AVRoxide
supports in the templates
directory of the Gitlab Repo. Just copy the
appropriate template for the microcontroller you are targetting out of there,
and off you go.
If you use one of these templates, you don’t need to read any further. But for the curious I will briefly explain what we do to achieve each of the above steps :).
Using the correct toolchain
You can set the correct toolchain to use with the rustup override
command.
But personally I don’t like this - it relies on local configuration ‘magic’
to know which toolchain to use, which breaks one of the principles of
reproducible builds. A much better approach is to include a
rust-toolchain.toml
file in your project, which makes explicit the
instruction to Rust to use a different toolchain. That’s what we do in the
template project, and it looks like this:
[toolchain]
channel = "nightly-2024-05-01"
components = ["rust-src"]
Target Spec
The target spec file tells Rust (and more importantly llvm
and the linker)
specific details of the chip you are targetting - like how memory is laid out
and what static libraries to link in. These are stored in a JSON file with a
name like avr-atmega4809.json
and look something like this:
{
"arch": "avr",
"atomic-cas": false,
"cpu": "avrxmega3",
"data-layout": "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8",
"eh-frame-header": false,
"env": "",
"exe-suffix": ".elf",
"executables": true,
"late-link-args": {
"gcc": [
"-lgcc"
]
},
"linker": "avr-gcc",
"linker-flavor": "gcc",
"linker-is-gnu": true,
"llvm-target": "avr-unknown-unknown",
"code-model": "small",
"embed-bitcode": false,
"max-atomic-width": 8,
"inline-threshold": 0,
"no-default-libraries": false,
"os": "unknown",
"pre-link-args": {
"gcc": [
"-mmcu=atmega4809",
"-Wl,--as-needed",
"-Wl,--verbose",
"-Wl,-Map=target/memory.map",
"-Wl,--script,avrxmega3-novectors.x",
"-L","./atmel-atpack/atmega4809/avrxmega3",
"-B","./atmel-atpack/atmega4809/",
"-nostartfiles"
]
},
"target-c-int-width": "16",
"target-endian": "little",
"target-pointer-width": "16",
"vendor": "unknown"
}
Just adding the JSON file to your project is not enough, though; you also need
to tell Rust to use it. We can do this by putting a target
attribute into
the .cargo/config.toml
file like so:
[build]
target = "avr-atmega4809.json"
ATmega ATpack files
Atmel distributes “ATpacks” for each of their microchips. These contain a machine-readable description of the chip’s capabilities and also some support libraries you need to link into your eventual binary, specific to each chip. The original packs are available from packs.download.atmel.com. Once they are downloaded and stashed somewhere, you should modify the target JSON file to refer to them.
In our template version, we include the relevant ATpack for the controller being targetted; these files are distributed in accordance with the Apache 2 license of the original materials from Microchip/ATmega.
-
At the time of writing, that just means the ATmega4809 :) ↩︎