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/) 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/), which provides APIs for the core ARM peripherals in every Cortex-M MCU.
- f3 (https://docs.rs/f3/) - High level APIs for the peripherals and external sensors on the STM32 F3 Discovery board.
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.
> 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/) 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.
The vendor SDKs are the weak link, not the compiler or language. Development could go 4x smoother if the vendor code were solid. Every single project I bump into sdk bugs that make it clear they have shipped code that was not tested. Its like they expect us to debug their code that they had developed on the cheap. Lately my philosophy is to just take the vendor header file and write my own low level drivers because it takes a more predictable amount of time. Our industry could use 3rd party SDKs.
There's a big conflict in mcu development:On the one hand, you have developers who want reliable and comfortable libraries so they can develop well and fast. Higher-level is a word to describe that(it's only partially accurate).
On the other hand, mcu companies make a significant amount of their profit based on lock-in(since mcu's are mostly commodities). And the higher level the API's and the rapid development is, the less lock-in companies get.
That together with the fact it costs a lot to develop quality libraries are the main reason to the state we're in today.
As for solutions:
1. ARM did face that problem once, with cores, and played it hand well , they came to mcu vendors and told them - the carrot: using our core will save you money on development(core/compilers/etc). The stick: a startup called luminary-micro , who could do cheap mcu's since all that was free.
- So can this move be played again, today, with peripherals ? maybe build a nice peripheral set, with libraries, well tested, etc - and offer it cheaply to the risc-v guys , and other mcu vendors, including Chinese mcu vendors ?
- Maybe the place to look for quality libraries is at mcu startups ? the last one was the acquired energy-micro , are their libraries better ?
2. The IOT is a big shift. Shifts create different leverage points sometime. Is there something that open-source community can do together to push the vendors towards great libraries in that niche , even if vendors don't like it much?
For example, what if the open-source community focused on one of the hardest building block of the IOT - security ? what if we created a highly proven security IP, proven by the community to a great extent, but used it licensing to push mcu vendors ?
Are there any other ideas how to solve this conflict ?
Maybe. But you have to consider that such surveys(and similar tools) could make semi company managers believe that software lock-in is important, and that's all it takes.
Is creating a Hardware API that complex that it would require a kickstarter ? because this link talk about using rustbindgen to automatically generate embedded bindings to a vendor library:
Creating a _nice_ and _reliable_ API with _good_ documentation that is _native_ to the language - I do think so, yes. Also keep in mind that there would be a specific devboard to build a community around.
And even before the coding starts, defining good abstractions for complex peripherals is very difficult. Counter/timer modules are the poster child for that. Heck, if you look at the Micropyothon issues on github you will find pages of vigorous debate about GPIO pullup configuration, much less anything complex.
True, the libraries are C.
Compilers are often gcc. Like AVR has a gcc compiler and as you said some parts of C++ are implemented.
For ARM there is also a gcc.
I only have experience with AVR and ARM (STM) to be honest.
I came across this [0] yesterday while looking for STM32 stuff for those cheap ass $2 STM32F103 boards. To your point, however, it's still just writing values directly out to register pointers. That entire repo can be boiled down to one little C program like this [1].
Perhaps a viable way to break the monopoly that C/C++ has in embedded software is to create a language that compiles to C. That way people can use their existing compilers (which may be the only choice for a particular target), drivers, middleware, etc. but benefit from a modern, safe(r) language.
Look out for ISBN 978-3-8007-4395-7 "Evaluation of MicroPython as Application Layer Programming Language on CubeSats" in the "ARCS 2017 Conference Proceedings" - paper should come out soon enough.
I do plan to make it a website, like a gitbook or something, but right now, I'm hammocking.
Also don't expect much. I'm a Mechanical Engineer and it was a bit out of my league, so it didn't go very deep.
The paper will hopefully soon be published: ISBN 978-3-8007-4395-7 "Evaluation of MicroPython as Application Layer Programming Language on CubeSats" in the "ARCS 2017 Conference Proceedings"
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/ for a while but then I've discovered ivory/tower framework made by Galois (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/).
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
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
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.
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.
[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`
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.
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.
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).
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.
I'm curious about the uses of the UART bridge. I've only used small chips with few pins to spare, so I've always used RTT or semihosting for logging. Is there an advantage to doing that using the UART instead? Or is the UART bridge for something else entirely?
The STM32 board includes an STLink programmer which you can use via OpenOCD. The EFM32 board includes a J-Link programmer and the IDE comes with the JLink command line tools. You may be able to use it via OpenOCD too, but I haven't tried that.
I prefer the EFM32 peripheral library (efmlib) to what's available for the STM32, which is either the old clunky Standard Peripheral Library or some new thing that's closely tied to their awful IDE.
If you use the EFM32 and the JLink software you can log using RTT (https://www.segger.com/jlink-rtt-viewer.html), which is much faster than using semihosting, which is the most straightforward option with the STM32.
The STM32 board has the advantage that you can set it up as a programmer by removing a jumper, whereas to set the EFM32 board as a programmer you have to load the horrible IDE and click buttons.
It's generally fairly easy to compile code for these chips using the arm embedded gcc toolchain:
The only tricky part is finding (or writing) a suitable '.ld' file. You can usually find one by poking around the files included with the IDE or with the peripheral libraries. In principle you can just write it yourself, if you know enough about ARM architecture, but that's above my pay grade.
Anyway, take all the advice above with a pinch of salt -- I'm no expert on this stuff. But sometimes the perspective of a relative newbie can be useful too.
I learned using the Silab's Zero Gecko starter kit that you linked to. As someone whose only other microcontroller experience was a few simple led projects with an arduino, I found the gecko family's tools and docs incredibly helpful and intuitive. It really helped me learned all the ins and outs of the Cortex M, such that I was able to build my own hardware programmer/debugger after a few weeks of tinkering.
The newer ST's HAL library is not that bad, and is definitely not tied to their IDE. The STM32CubeMX has pretty decent pin and peripheral configuration tool (definitely better than spreadsheet) which generates project skeleton. After that, you can use SW4STM32 (eclipse packaged with arm-none-eabi-gcc) or commercial IDEs. You can also use plain old make if that's what you prefer.
There are also other fully open options for STM32- libopencm3 (just HAL) or ChibiOS (an RTOS with HAL).
>The newer ST's HAL library is not that bad, and is definitely not tied to their IDE.
Fair enough. I'm sure the library itself isn't literally tied to the IDE, but it's not easy to find as a separate download, and there is little example code available online for the HAL library.
> libopencm3
It's a nice project, but I prefer to use vendor-supplied libs unless there's a good reason not to. Efmlib has a permissive license and seems pretty nice from my relatively limited experience of it.
>You can also use plain old make if that's what you prefer.
This is the thing. I really want to be able to easily set up a project just using GNU make and the regular arm gcc toolchain. I've found it easier to do that on the whole with the EFM32.
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.
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.
- 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/), which provides APIs for the core ARM peripherals in every Cortex-M MCU.
- f3 (https://docs.rs/f3/) - High level APIs for the peripherals and external sensors on the STM32 F3 Discovery board.