embedded-hal

repository·master·Indexed 25 days ago

https://github.com/rust-embedded/embedded-hal

A collection of Hardware Abstraction Layer (HAL) traits for the Rust embedded ecosystem, enabling platform-agnostic drivers. The ecosystem includes crates for different execution models: embedded-hal (blocking), embedded-hal-async (async/await), embedded-hal-nb (polling), embedded-hal-bus (SPI/I2C bus sharing), embedded-can (CAN bus), and embedded-io/embedded-io-async for byte-oriented I/O streams.

Tokens
20.8K
Snippets
28
Records
140
Agent score
82%

What's inside embedded-hal

  1. Overview of embedded-hal ecosystem

    master

    embedded-hal provides a Hardware Abstraction Layer (HAL) for embedded systems, serving as a foundation for platform-agnostic drivers.

    By writing drivers using embedded-hal traits, driver authors can support multiple target platforms (e.g., Cortex-M, AVR, embedded Linux). Application developers can then use these drivers on any platform that implements the required embedded-hal traits.

  2. Understand the design shift in embedded-hal 1.0

    master

    The transition from embedded-hal 0.2.x to 1.0.0 represents a fundamental shift in purpose. While 0.2.x attempted to both standardize HAL APIs and provide traits for generic drivers, 1.0.0 focuses exclusively on providing traits for writing generic drivers that work on any HAL.

    Key implications for developers:

    • Reduced Complexity: The project has dropped the goal of standardizing all HAL APIs, as hardware capabilities vary too widely.
    • Interoperability: The focus is on ensuring generic drivers can work across the entire Rust Embedded ecosystem out of the box.
    • Execution Models: Blocking traits are the primary focus of the core crate. Other execution models (async, non-blocking) have been moved to companion crates.
  3. Use `embedded-hal-nb` for non-blocking HAL traits

    master
    embedded-hal-nb provides non-blocking versions of certain embedded-hal traits. It utilizes the nb crate to implement non-blocking patterns, making it suitable for embedded systems where operations might not be immediately ready. It shares the same scope and design goals as the core embedded-hal crate.
  4. Use `embedded-io-adapters` to convert between I/O traits

    master

    The embedded-io-adapters crate provides structs that wrap an I/O stream to implement different families of I/O traits. This allows you to bridge the gap between embedded-io/embedded-io-async and other common I/O ecosystems.

    Instead of separate adapters for Read, ReadBuf, or Write, a single adapter struct implements the appropriate traits based on the capabilities of the wrapped inner type. This enables seamless usage when working with trait combinations like Read + Write.

  5. Use `embedded-can` for CAN bus abstraction

    master
    embedded-can provides a generic abstraction layer for Controller Area Network (CAN) interfaces in embedded Rust. It defines traits that allow hardware abstraction layers (HALs) to implement CAN driver functionality, enabling drivers for CAN peripherals to be written generically and used across different microcontrollers.
  6. Understand `embedded-io` vs `std::io`

    master

    The embedded-io crate provides I/O traits for no_std embedded systems where std::io is unavailable due to its reliance on allocation (e.g., std::io::Error).

    Key differences include:

    • Error Handling: Error is an associated type rather than a fixed type. This allows implementors to return custom error types without using dyn or Box, following the embedded-hal pattern.
    • Blocking Semantics: Unlike std::io where Read/Write can be blocking or non-blocking based on runtime state, embedded-io's Read and Write traits are always blocking. For non-blocking operations, use the ReadReady and WriteReady traits.
  7. Understand the MSRV upgrade policy

    master

    The embedded-hal ecosystem follows a specific policy regarding Minimum Supported Rust Version (MSRV) upgrades. This policy ensures that patch releases remain stable for existing users, while major and minor releases may introduce higher MSRV requirements.

    Upgrade Rules:

    • Patch releases (_._.*Z*): The MSRV will not be updated. Patch releases are guaranteed to maintain compatibility with the existing MSRV.
    • Major or Minor releases (*X*.*Y*._): The MSRV may be upgraded.
    • Preliminary releases (e.g., -alpha): The MSRV may be upgraded as part of preparation for a final release.
    • Changelog: All MSRV upgrades will be explicitly documented in the changelog.

    This policy applies to both 0.x.x and >=1.x.x versions.

    Recommendation for users with strict MSRV requirements: If your project requires a specific Rust version and cannot accommodate MSRV upgrades, you should pin the dependency version in your Cargo.toml to a specific version rather than using version ranges.

  8. Migrate SPI usage from SpiBus to SpiDevice

    master

    In embedded-hal 1.0, SPI traits are split into SpiBus (the raw bus with SCK, MOSI, MISO) and SpiDevice (a single device on a bus managed by a CS pin).

    For HAL Implementors

    • If you do not manage a CS pin automatically, implement SpiBus.
    • If your API does manage a CS pin automatically, implement SpiDevice.
    • Never implement both SpiBus and SpiDevice on the same struct.

    For Driver Authors

    • If your device has a CS pin, use SpiDevice. Do not take the CS pin as a separate OutputPin; SpiDevice manages it for you.
    • If your device only has SCK, MOSI, MISO, use SpiBus.
    • If using SPI to bitbang non-SPI protocols (e.g., WS2812), use SpiBus.

    For End Users (Converting SpiBus to SpiDevice)

    If your HAL provides SpiBus but your driver requires SpiDevice, wrap the bus using embedded_hal_bus::spi::ExclusiveDevice along with a CS pin.

    use embedded_hal_bus::spi::{ExclusiveDevice, NoDelay};
    
    // Create the SPI from the HAL. This implements SpiBus, not SpiDevice!
    let spi_bus = my_hal::spi::Spi::new(...);
    // Create the CS. This must implement OutputPin.
    let cs = my_hal::gpio::Output::new(...);
    
    // Combine the SPI bus and the CS pin into a SPI device. This now does implement SpiDevice!
    let spi_device = ExclusiveDevice::new(spi_bus, cs, NoDelay);
    
    // Now you can create your driver with it!
    let driver = my_driver::Driver::new(spi_device, ...);
  9. Share SPI buses using `embedded-hal-bus`

    master

    To share a single SPI bus among multiple devices, use the mechanisms provided by embedded-hal-bus to connect an SpiBus (representing the physical bus) to multiple SpiDevice instances (representing individual devices on that bus).

    Important for Driver Authors: Device drivers should use the SpiDevice traits rather than SpiBus traits whenever possible. This ensures compatibility with bus-sharing adapters and allows multiple devices to coexist on the same hardware bus.

  10. Use embedded-hal-compat for interoperability shims

    master
    If you are working with a HAL implementation that has not yet been updated to 1.0, or if you need to use a driver that requires a different version than your HAL provides, use the embedded-hal-compat crate. It provides shims to allow (generally) seamless interoperability between embedded-hal v0.2 and v1.0 without requiring manual code alterations.