What's left for you to explore
We have barely scratched the surface! There's lots of stuff left for you to explore.
NOTE: If you're reading this, and you'd like to help add examples or exercises to the Discovery book for any of the items below, or any other relevant embedded topics, we'd love to have your help!
Please open an issue if you would like to help, but need assistance or mentoring for how to contribute this to the book, or open a Pull Request adding the information!
More of the MB2
We touched most of the hardware on the MB2 in the course of this book. That said, there's still a few MB2 topics left to explore.
Direct Memory Access (DMA).
Some peripherals have DMA, a kind of asynchronous memcpy that allows the peripheral to move data
into or out of memory without the CPU being involved.
If you are working with a micro:bit v2 you have actually already used DMA: the HAL does this for you with the UARTE and TWIM peripherals. A DMA peripheral can be used to perform bulk transfers of data: either from RAM to RAM, from a peripheral like a UARTE, to RAM, or from RAM to a peripheral. You can schedule a DMA transfer — for example "read 256 bytes from UARTE into this buffer" — and leave it running in the background. You can check some register later to see if the transfer has completed, or you can ask to receive an interrupt when the transfer completes. Thus, you can schedule the DMA transfer and do other work while the transfer is ongoing.
The details of low-level DMA can be a bit tricky. We hope to add a chapter covering this topic in the near future.
There are some abstraction for working with PWM in the embedded-hal pwm module and you will
find implementations of these traits in nrf52833-hal.
Digital inputs and outputs
We have used the microcontroller pins as digital outputs, to drive LEDs. When building our snake game, we also caught a glimpse of how these pins can be configured as digital inputs. As digital inputs, these pins can read the binary state of switches (on/off) or buttons (pressed/not pressed).
Digital inputs and outputs are abstracted within the embedded-hal digital module and
[nrf52833-hal] does have an implementation for them.
(spoilers reading the binary state of switches / buttons is not as straightforward as it sounds ;-) )
Analog-to-Digital Converters (ADC)
There are a lot of digital sensors out there. You can use a protocol like I2C and SPI to read them. But analog sensors also exist! These sensors just output a reading to the CPU of the voltage they are sensing at an ADC input pin.
The ADC peripheral can thus be used to measure an "analog" voltage level — for example, 1.25 Volts
— as a "digital" number — for example, 24824 — that the processor can use in its calculations.
There were generic ADC traits in embedded-hal, but they were removed for embedded-hal 1.0: see
issue #377. The nrf52833-hal crate provides a nice interface to the specific ADC built into the
nRF52833.
Digital-to-Analog Converters (DAC)
As you might expect a DAC is exactly the opposite of ADC. You can write some digital number into a register to produce a specific voltage on some analog output pin. When this analog output pin is connected to some appropriate electronics and the register is written to quickly with the right values you can do things like produce sounds or music.
Neither the nRF52833 nor the MB2 board has a dedicated DAC. One typically gets a kind of DAC effect by outputting PWM and using a bit of electronics on the output (RC filter) to "smooth" out the PWM waveform.
Real Time Clock
A Real-Time Clock peripheral keeps track of time under its own power, usually in "human format": seconds, minutes, hours, days, months and years. Some Real-Time Clocks even handle leap years and Daylight Saving Time automatically.
Neither the nRF52833 nor the MB2 board contains a Real-Time Clock. The nRF52833 does contain
"Real-Time Counter" (RTC), a low-frequency ticking clock that is supported by nrf52833-hal. This
counter can be dedicated to serve as a synthesized real-time clock. The key requirement, of course,
is to keep the RTC peripheral powered even when the MB2 is not in use. While the MB2 does not have
an on-board battery, the RTC should be able to run for a long time (possibly years) with a battery
plugged into the battery port on the MB2 (for example, the battery pack provided with the micro::bit
Go kit).
Other communication protocols
- SPI: The "Serial Peripheral Interface" is a high-speed communications interface similar in some
ways to I2C. SPI is abstracted within the
embedded-halspimodule and implemented by [nrf52-hal]. - I2S: The "Inter-IC Sound" protocol is a variant of I2C customized for audio transmission.
I2S is currently not abstracted within
embedded-hal, but is implemented by [nrf52-hal]. - Ethernet: there does exist a small TCP/IP stack named
smoltcpwhich is implemented for some chips. The MB2 does not have an Ethernet peripheral - USB: there is some experimental work on this, for example with the
usb-devicecrate. For the MB2, the USB port is managed by the interface MCU rather than the host MCU, making it difficult to do custom USB things. - Bluetooth: the
nrf-softdevicewrapper provided by the Embassy MB2 runtime is probably the easiest entry into MB2 Bluetooth. Embassy also sports the Rust-nativeTrouBLEBLE host crate. - CAN, SMBUS, IrDA, etc: All kinds of specialty interfaces exist in the world; Rust sometimes has support for them. Please investigate the current situation for the interface you need
Different applications use different communication protocols. User facing applications usually have a USB connector because USB is a ubiquitous protocol in PCs and smartphones. Whereas inside cars you'll find plenty of CAN buses. Some digital sensors use SPI, I2C or SMBUS.
If you happen to be interested in developing abstractions in the embedded-hal or implementations
of peripherals in general, don't be shy to open an issue in the HAL repositories. Alternatively you
could also join the Rust Embedded matrix channel and get into contact with most of the people who
built the stuff from above.
General Embedded-Relevant Topics
These topics cover items that are not specific to our device, or the hardware on it. Instead, they discuss useful techniques that could be used on embedded systems.
Most of the hardware we will discuss here is not available on the MB2 — but much of it could easily be added by connecting a cheap piece of hardware to the MB2 edge-card connector, either driving it directly or using something like SPI or I2C to control it.
Multitasking
Most of our programs executed a single task. How could we achieve multitasking in a system with no OS, and thus no threads? There are two main approaches to multitasking: preemptive multitasking and cooperative multitasking.
In preemptive multitasking a task that's currently being executed can, at any point in time, be preempted (interrupted) by another task. On preemption, the first task will be suspended and the processor will instead execute the second task. At some point the first task will be resumed. Microcontrollers provide hardware support for preemption in the form of interrupts. We were introduced to interrupts when we built our snake game in chapter 16.
In cooperative multitasking a task that's being executed will run until it reaches a suspension point. When the processor reaches that suspension point it will stop executing the current task and instead go and execute a different task. At some point the first task will be resumed. The main difference between these two approaches to multitasking is that in cooperative multitasking yields execution control at known suspension points instead of being forcefully preempted at any point of its execution.
Gyroscopes
As part of our Punch-o-meter exercise, we used the Accelerometer to measure changes in acceleration in three dimensions. But there are other motion sensors such as gyroscopes, which allows us to measure changes in "spin" in three dimensions.
This can be very useful when trying to build certain systems, such as a robot that wants to avoid tipping over. Additionally, the data from a sensor like a gyroscope can also be combined with data from accelerometer using a technique called Sensor Fusion (see below for more information).
Servo and Stepper Motors
While some motors are used primarily just to spin in one direction or the other, for example driving a remote control car forwards or backwards, it is sometimes useful to measure more precisely how a motor rotates.
A microcontroller can be used to drive Servo or Stepper motors, which allow for more precise control of how many turns are being made by the motor, or can even position the motor in one specific place, for example if we wanted to move the arms of a clock to a particular direction.
Sensor fusion
The micro:bit contains two motion sensors: an accelerometer and a magnetometer. On their own these measure (proper) acceleration and (the Earth's) magnetic field. But these magnitudes can be "fused" into something more useful: a "robust" measurement of the orientation of the board, with less measurement error than that of any single sensor.
This idea of deriving more reliable data from different sources is known as sensor fusion.
So where to next?
First and foremost, join us on the Rust Embedded matrix channel. Lots of people who contribute or
work on embedded software hang out there, including, for example, the people who wrote the
microbit BSP, the nrf52-hal crate, the embedded-hal crates, etc. We are happy to help you get
started or move on with embedded programming in Rust!
There are many other options:
- You could check out the examples in the
microbit-v2board support crate. All those examples work for the micro:bit board you have.
- If you are looking for a general overview of what is available in Rust Embedded right now check out the Awesome Rust Embedded list.
- You could check out Embassy. This is a modern efficient cooperative multitasking framework that
supports concurrent execution using Rust
async/await.
- You could check out Real-Time Interrupt-driven Concurrency RTIC. RTIC is a very efficient preemptive multitasking framework that supports task prioritization and deadlock-free execution.
- You could check out more abstractions of the
embedded-halproject and maybe even try and write your own platform agnostic driver based on it.
- You could try running Rust on a different development board. Popular boards such as the ESP-32, Raspberry Pi, or Arduino have their own active Rust developer communities.