
Programming ARM Cortex-M Microcontrollers with Rust - randtl
https://github.com/japaric/copper
======
kam
The author of this guide (Jorge Aparicio) has done an amazing amout of work
getting the ecosystem started for Rust on microcontrollers. A few of his other
projects worth mentioning:

\- svd2rust ([https://docs.rs/svd2rust/](https://docs.rs/svd2rust/)) generates
register-level APIs from SVD files, an XML format provided by most ARM
microcontroller vendors. This gets you the equivalent of the low-level C
header files for manipulating the hardware registers.

\- cortex_m ([https://docs.rs/cortex-m/](https://docs.rs/cortex-m/)), which
provides APIs for the core ARM peripherals in every Cortex-M MCU.

\- f3 ([https://docs.rs/f3/](https://docs.rs/f3/)) - High level APIs for the
peripherals and external sensors on the STM32 F3 Discovery board.

------
turbinerneiter
I recently finished my Master's Thesis on using anything other than C/C++ on
microcontrollers.

It's a mess. A couple of languages like Rust, D, etc. can be hacked on micros
due to their compiler suite, but it is always messy. Above guide uses inline
assembly to blink a GPIO. Hardware access is on register level. None of this
makes your live easier.

In order to be viable for microcontrollers you need two things: * a compiler
or interpreter for your target * a Hardware API

There are some projects, like MicroPython and Espruino who realized the
importance of the Hardware API. They basically transcend micro-interpreters
and RTOS (especially given the languages asyncio features).

Microcontroller vendors ship C/C++ libraries for their hardware. You either
have to wrap that, like the micro-interpreters do, or your language has to be
able to use them natively.

What Rust on microcontrollers need is a Kickstarter campaign with a nice dev-
board as perk to finance a project that implements a nice API for that
specific micro.

~~~
kam
> Above guide uses inline assembly to blink a GPIO.

No, the only inline ASM in this guide is to cause a debugger breakpoint. You
also need inline ASM to enable/disable interrupts, and that's about it.

The code in this guide looks ugly because it's written to show how to use the
hardware without any abstraction. Higher level APIs like f3
([https://docs.rs/f3/0.3.1/f3/](https://docs.rs/f3/0.3.1/f3/)) exist, though
not nearly for every chip or peripheral. Even in C, for anything outside the
popular Arduino/mbed/etc boards, your choice is usually between buggy vendor
bloatware and using the registers directly after carefully reading the
datasheet.

~~~
snovv_crash
From what I've seen, using registers is often simpler and easier than using
the vendor GPIO libraries.

------
flowless
I've been looking for such thing for a long time as I don't really like C for
embedded programming. Was following [https://zinc.rs/](https://zinc.rs/) for a
while but then I've discovered ivory/tower framework made by Galois
([http://ivorylang.org/](http://ivorylang.org/)). It's basically haskell DSL
and C code generator for embedded world, currently targeting stm32 family.

Compared to these early rust attempts they've written a port of ArduPilot in
ivory/tower called smaccmpilot
([http://smaccmpilot.org/](http://smaccmpilot.org/)).

In one of the papers they even compare ivory with rust in terms of memory
safety.

I'm now trying to port ODriveFirmware written in C (+CubeMX/CMSIS) to ivory
and so far I'm pretty surprised how well it goes. I'm going to start
documenting this process on a blog soon, if interested I've created a wiki
page collecting resources about embedded programming in Haskell at
[https://wiki.base48.cz/EmbeddedHaskell](https://wiki.base48.cz/EmbeddedHaskell)

~~~
geokon
In a similar vein there is ATS - which already compiles to C. And you can in
theory write controller code in ATS if you want those functional features and
more

ex:

(this guy has done examples on a ton of boards!)
[https://github.com/fpiot](https://github.com/fpiot)

www.metasepi.org/doc/metasepi-icfp2015-arduino-ats.pdf

[https://github.com/fpiot/msp430-ats](https://github.com/fpiot/msp430-ats)

------
carrigan
I go through this cycle every 6 months or so where I will get excited about
Rust and try it out for embedded development again. Typically in the first
day, I get frustrated with some major limitation and put it back on the shelf.
This last time, I wanted to make a simple timer that displayed the time since
a button was last pressed on a 16x2 character LCD screen.

In addition to the HAL annoyances that turbinerneiter talked about, I wound up
giving up this time because I could find no way to manipulate strings in a
static buffer. In C I could just allocate two 17 byte strings statically and
then `sprintf` to them, but there seems to be no equivalent in Rust.

I'd be curious to know what limitations others have run into when trying this
and if I was just doing it wrong.

~~~
steveklabnik
The write! macro would be the equivalent here. Something like this:

    
    
        use std::io::Write;
        
        static mut S1: [u8; 17] = [0; 17];
        
        fn main() {
            unsafe {
                write!(&mut S1[..], "Hi: {}", 5).unwrap();
                
                let s = std::str::from_utf8_unchecked(&S1[..5]);
                
                println!("string: {}", s);
            }
        }

~~~
pjc50
Having all your main inside unsafe doesn't seem like the best advert for Rust
:)

~~~
steveklabnik
You're right, it's not; I was just showing the smallest code. :)

This is unsafe specifically because of the mutable static; you can deal with
that in a few different ways, but that wasn't the point of the example.

------
japaric
Hello, HN folks. Author here.

Sadly, the repository posted by the OP is not up to date. That's not to say
it's wrong but just that doesn't contain recent developments. Also, this
repository documents every single part of putting together a microcontroller
project _from scratch_. Although interesting, this is not the fastest way to
"Hello World". That's why I have put together a nicely packaged Cargo template
[1] [2] to quickly start a new microcontroller project. This template works
for _any_ Cortex-M microcontroller and paired with the svd2rust code generator
[3] you can easily get _full_ device support (register level API) for any
microcontroller for which the vendor has released a CMSIS-SVD file [4].
There's a database of such files in this repo. [5]

For some, directly working with registers may be too low level. In that case
you can look at my Discovery book [6] which showcases a higher level API and
several examples for the STM32F3DISCOVERY board. The API is documented here.
[7]

What's missing from all this is a good concurrency history. (All the examples
in the Discovery book are single task.) So, I have been working (not alone) on
a multitasking framework that's suitable for real time systems and the focus
has been minimizing overhead while guaranteeing memory safety. At this point,
we have pretty much reached the goal and we are currently working on the
ergonomics. I hope that we'll have something show Soon (TM).

I'm happy to answer questions about Rust on microcontrollers. I may take some
time answer, though, as it's weekend :-).

P.S. I hope I got the formatting right. First time posting on HN.

P.P.S. Congratulations for reading the whole thing! As a reward, here's a
robot [8] built with the framework I mentioned.

[1] [https://github.com/japaric/cortex-m-
template](https://github.com/japaric/cortex-m-template)

[2] It seems that the Cargo template feature got removed recently (due to not
having gone through a RFC process) but you can easily rollback your Rust
installation to get an older Cargo that supports templates: `rustup default
nightly-2017-04-01`

[3]
[https://docs.rs/svd2rust/0.5.1/svd2rust/](https://docs.rs/svd2rust/0.5.1/svd2rust/)

[4]
[http://www.keil.com/pack/doc/CMSIS/SVD/html/index.html](http://www.keil.com/pack/doc/CMSIS/SVD/html/index.html)

[5] [https://github.com/posborne/cmsis-
svd/tree/master/data](https://github.com/posborne/cmsis-svd/tree/master/data)

[6]
[https://japaric.github.io/discovery/](https://japaric.github.io/discovery/)

[7] [https://docs.rs/f3/0.3.1/f3/](https://docs.rs/f3/0.3.1/f3/)

[8]
[https://mobile.twitter.com/japaricious/status/84569793557265...](https://mobile.twitter.com/japaricious/status/845697935572656128)

~~~
dbcurtis
Thanks for doing this. I'm really excited about the potential of Rust for
embedded, but it still requires too much trail-blazing to be compatible with
my current priorities. FWIW I've been doing realtime embedded C for years, but
feel it is high time we made some progress in the embedded world with respect
to memory safety and concurrency correctness. I'm also a huge fan of
Micropython, but of course Micropython isn't applicable everywhere.

------
foldr
This is cool. I wonder how much benefit Rust is likely to yield in this space.
I generally tend to try to avoid any dynamic memory allocation when writing
code for small μcs, so that takes away one of the big pain points of writing
in C. Rust does have pattern matching, which is always useful. My experience
is with Cortex M0s with ~32K flash, though, so I can see how Rust might have
real benefits with bigger μcs.

~~~
alnitak
Which Cortex M CPU/board would you recommend for a seamless development
experience?

~~~
flowless
I really like F3 and F4 discovery boards. You can re-flash dedicated stm32f1
with blackmagic probe (instead of ST-Link) and bridge UART to it to get really
awesome development board.

After this is done you'll get /dev/ttyACM0 and ACM1 for GDB interface and UART
bridge. From GDB you can connect to it directly with 'target extended-remote
/dev/ttyACM0' (no need for OpenOCD middleware).

[https://wiki.base48.cz/STM32#Running_BMP_on_Discovery_boards](https://wiki.base48.cz/STM32#Running_BMP_on_Discovery_boards)

~~~
pantalaimon
What is the advantage of blackmagic probe over plain ST-Link?

Genuine question, I've never used the ST-Link for more than flashing the image
and some debugging, so what am I missing?

~~~
flowless
It implements GDB stub directly so you don't need to run OpenOCD to be able to
attach to it from GDB.

Also UART bridge is nice but this might be supported newer in ST-Link too. I
haven't used STLink for a while - flashing all the stuff with blackmagic right
away.

~~~
rvense
Quite sure they do have the UART bridge now, but the black magic probe looks
interesting. Thanks for the link.

------
bigredhdl
Kind of off topic, but I can't recommend Quantum Leaps (www.state-machine.com)
enough for microcontroller development. Also, the book by Miro Samek (quantum
leaps founder) is very good. I've had a really good experience using his
software on the cortex-m MCUs.

------
faragon
Ugly stuff. In my opinion, plain C is much better (the pain was to program
microcontrollers in pure assembly). If you want higher/"safer" level, I would
recommend you using Lua, Python, or even Node.js.

~~~
jackmott
python and node js would have orders of magnitude worse performance /
overhead.

~~~
pjmlp
MicroPython can compile to native code and also allows for inline Assembly.

[https://docs.micropython.org/en/latest/pyboard/reference/spe...](https://docs.micropython.org/en/latest/pyboard/reference/speed_python.html)

[https://docs.micropython.org/en/latest/pyboard/reference/asm...](https://docs.micropython.org/en/latest/pyboard/reference/asm_thumb2_index.html)

