-
Notifications
You must be signed in to change notification settings - Fork 819
Home
Embassy is a project to make async
the preferred option for embedded development.
Software written without async
blocks on I/O operations. In a std
environment, such as a PC, software handles this problem with threads. When one
thread blocks on an I/O operation, another is able to take its place. However, even on a PC, threads are relatively heavy, and therefore some
programming languages, such as Go, have implemented a concept called coroutines
or 'goroutines' that are much lighter and less-intensive than threads.
In rust, this concept is implemented as async
. async
works by transforming each async
function into an object called a future
. When a future
blocks on I/O the future yields, and the scheduler, called an executor
, can select a different future to execute. Compared to alternatives such as an
RTOS, async
can yield better performance and lower power consumption because the executor
doesn't have to guess when a future
is ready to execute.
However, program size may be higher than other alternatives, which may be a problem for certain space-constrained devices with very low memory. On the
devices Embassy supports, such as stm32 and nrf, memory is generally large enough to accommodate the modestly-increased program size.
Embassy is an executor
and a Hardware Access Layer (HAL). The executor
is a scheduler that generally executes a fixed number of tasks,
allocated at startup, though more can be added later. The HAL is an API that you can use to access certain blocking functionality, such as USART,
UART, I2C, SPI, CAN, and USB. Embassy doesn't provide access to non-blocking functionality, such as GPIO, because non-blocking functionality doesn't
require special treatment to work with Embassy. For such functionality, existing HALs are recommended.
Embassy also provides a delay trait that can be used to delay a task by a fixed number of microseconds or milliseconds. For less than one microsecond,
blocking delays should be used because the cost of context-switching is too high and the executor
will be unable to provide accurate timing.
For most I/O in embedded devices, the peripheral doesn't directly support the transmission of multiple bits at once, with CAN being a notable exception. Instead, the MCU must write each byte, one at a time, and then wait until the peripheral is ready to send the next. For high I/O rates, this can pose a problem if the MCU must devote an increasing portion of its time handling each byte. The solution to this problem is to use the Direct Memory Access controller.
The Direct Memory Access controller (DMA) is a controller that is present in MCUs that Embassy supports, including stm32 and nrf. The DMA allows the MCU to set up a transfer, either send or receive, and then wait for the transfer to complete. With DMA, once started, no MCU intervention is required until the transfer is complete, meaning that the MCU can perform other computation, or set up other I/O while the transfer is in progress. For high I/O rates, DMA can cut the time that the MCU spends handling I/O by over half. However, because DMA is more complex to set-up, it is less widely used in the embedded community. Embassy aims to change that by making DMA the first choice rather than the last. Using Embassy, there's no additional tuning required once I/O rates increase because your application is already set-up to handle them.
If Embassy sounds exciting to you, then click Getting Started to get started!