We can’t yet compile the stock version of libcore, so in the meantime we have our own version with the essentials. Because we’ve directly added this code to our project, each recompile takes a while. It’d be really nice if we could use Cargo like a Real Rust Project would, allowing us to compile our modified libcore once and reuse it.
Create a new library crate (cargo new avr-core
) and move all of the
hacked-up core files that we created before into the src
directory:
- clone.rs
- cmp.rs
- intrinsics.rs
- marker.rs
- ops.rs
- option.rs
Additionally, create a lib.rs
with the top-level core items:
- all the
feature
flags - the
prelude
- module references
- the
eh_personality
andpanic
handlers.
Now we can create a binary crate that will use our AVR-compatible
libcore. After cargo new --bin blink
, add a path to the core library:
1 2 |
|
We can remove a bunch of junk from our main.rs
and just import the
interesting core items:
1 2 3 4 5 |
|
Now when we compile, we only need to rebuild our own code, not all of libcore! Much better.
Let’s continue improving our code. The last thing we did was to hook up an interrupt handler for the timers, but we had to add a bunch of assembly to make the handler behave in the proper way. As suggested in the previous post, there’s a much better way to do it.
Rust allows us to declare extern
functions with a calling
convention. A calling convention describes where the arguments are
located, where the return value should be placed, and what registers a
function is allowed to change.
There are two special calling conventions for AVR code:
avr-interrupt
and avr-non-blocking-interrupt
. They are basically
the same, except that the latter immediately re-enables interrupt
handling when it starts. With the former, you don’t have to worry
about one interrupt happening while you are handling another.
That means we can rewrite our interrupt handler much easier:
1 2 3 4 5 |
|
Now that we are using Cargo, it would be nice if we didn’t have to
directly call avr-gcc
ourselves. We can accomplish this with a
target file. This is JSON configuration that can enhance the Rust
compiler’s knowledge about how to compile a piece of code.
There are many fields that are required (check the repository
for the full reference), but the important one is that we can tell the
compiler to use avr-gcc
as our linker:
1 2 3 4 |
|
And we can use this JSON target file when compiling:
1
|
|
This will create our ELF file, automatically linking to our interrupt
vector definition, and ready to be processed with avr-objcopy
and
uploaded to the board. We are getting closer and closer to an
enjoyable development experience!
As before, the complete source is available on Github.