This content was originally meant to be presented as part of my workshop on Rust and microcontrollers at
the NITC FOSSMeet 2018. As I was not able to cover it satisfactorily during the workshop,
I thought of writing it down!

Thanks to Jorge Aparicio for his amazing code (and blog posts!) without which
there would be nothing to write about. Please read this blog post by Jorge to
understand more about the principles behind the design of the I/O framework which we will be
using here.

Why Rust on Microcontrollers?

Here are some of the motivating factors:

We can use the considerably more sophisticated type system (compared to C) of Rust, together with ideas
like ownership and move semantics, to encode properties
of peripherals statically in the type of the variables representing those peripherals in our code, thereby
catching certain kind of errors at compile time itself (we will see examples later in this post).

We can write low-level code and still have the luxury of using high-level abstractions like sum types, closures/iterators,
generics, traits etc with very little or no run time overhead.

This is an improvement over code in Level 1 - we do not use hardcoded addresses, no unsafe blocks
and no tricky bit manipulations! You can see the full program here.

Look at this line:

gpioe.moder.write(|w|w.moder11().output().moder9().output());

When the closure is evaluated, initial value of w is the reset state of the MODE register (this register sets the pin mode); writing
w.moder11.output() will extract the two mode register bits corresponding to pin 11 and will give them the value which
configures that pin as an output; this is then chained with moder9().output() which will properly set the bits corresponding
to pin 9 so that that pin is configured as an output. After the closure is evaluated, the write function will simply store
whatever bit pattern is present in w to the MODE register. It is obvious that this is better than direct register manipulation
with bit-twiddling!

A GPIO pin can’t exist in two states (Input and Output) at the same time. Rust move semantics guarantees that you
get a compile time error in the above case (gpioe.pe8 gets moved to the variable pe8; you can no longer
call gpioe.pe8.push_pull_output()).

Level 4: Use a board support crate

A board support crate knows how the peripherals in the microcontroller are connected to various
devices (LED’s, switches, sensors etc). That makes it very easy for us to write code without having
to know about pin connections.