#driver #embedded-hal-driver #no-std

no-std device-register

A no_std library to describe the registers permissions of a device to ease driver development

6 releases

0.3.1 Jan 16, 2023
0.3.0 Nov 29, 2022
0.2.0 Oct 12, 2022
0.1.2 Sep 28, 2022
0.1.1 Aug 22, 2022

#172 in Embedded development

Download history 32/week @ 2023-06-07 29/week @ 2023-06-14 24/week @ 2023-06-21 12/week @ 2023-06-28 16/week @ 2023-07-05 7/week @ 2023-07-12 14/week @ 2023-07-19 15/week @ 2023-07-26 21/week @ 2023-08-02 20/week @ 2023-08-09 23/week @ 2023-08-16 18/week @ 2023-08-23 8/week @ 2023-08-30 23/week @ 2023-09-06 30/week @ 2023-09-13 28/week @ 2023-09-20

95 downloads per month
Used in device-register-async


88 lines


crates.io documentation

A no_std library to describe the registers permissions of a device to ease driver development.

  • no_std support
  • Zero cost, no use of dyn
  • No dsl, just a derive macro and impl a trait.
  • Error passthrough


Simply derive using XXRegister, where XX is the premission. The following permissions are supported

To define a register, simply derive using the desired permission.

Then use the register attribute to define it's address, type for the address and the error.

#[register( addr = "42", ty = "u8")]
pub struct Register0(pub u16);

Then, your driver only need to implement the RegisterInterface to have access to the read/write/edit traits.

Complete example

Here is a complete example. See the tests folder for more, or checkout the tmp117 driver for actual usage.

use std::collections::HashMap;
use device_register::*;

// The type of the address used by the driver
struct Address(pub u8);

// We define the register with Read/Write permission
// Then we pass the address type, value and error type of the driveer
#[derive(Debug, Copy, PartialEq, Eq, Clone, RWRegister)]
#[register( addr = "Address(1)", ty = "Address")]
struct Register0(pub u16);

// Mock of the device driver
struct DeviceDriver {
    // Simulate reading from the device
    pub registers: HashMap<u8, u16>,

// Implement a method directly, by passing the trait for specific usecases like async
impl DeviceDriver {
    pub async fn read_async<R>(&self) -> R
        R: ReadableRegister<Address = Address> + From<u16>,
        async {
            let bytes = self.registers.get(&R::ADDRESS.0).unwrap();

// We implement the required interface
impl<R> RegisterInterface<R, Address> for DeviceDriver
    R: Register<Address = Address> + Clone + From<u16>,
    u16: From<R>,
    // The type of the error, lets have none for now,
    type Error = ();

    fn read_register(&mut self) -> Result<R, Self::Error> {
        let bytes = self.registers.get(&R::ADDRESS.0).unwrap();

    fn write_register(&mut self, register: &R) -> Result<(), Self::Error> {
        self.registers.insert(R::ADDRESS.0, register.clone().into());

let mut device = DeviceDriver{
    registers:  HashMap::new(),
// We can the Read/Write/Edit the registers that uses the Address type.
let write = Register0(42);

let read: Register0 = device.read().unwrap();

assert_eq!(read, write);

device.edit(|r: &mut Register0| {
    r.0 = 43;

let read: Register0 = device.read().unwrap();
assert_eq!(read, Register0(43));

// Custom implementation, async is an example of usecase for custom implements
tokio_test::block_on( async {
    let read_async: Register0 = device.read_async().await;
    assert_eq!(read, Register0(43));
} );


Licensed under either of

at your option.


Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

License: MIT OR Apache-2.0


~37K SLoC