#register #peripheral #embedded #byte-array #spi #i2c #traits

regiface

A crate to ease development of interfacing with registers on embedded peripherals

5 releases

0.1.4 Feb 12, 2024
0.1.3 Feb 12, 2024
0.1.2 Feb 12, 2024
0.1.1 Jan 29, 2024
0.1.0 Jan 29, 2024

#614 in Hardware support

45 downloads per month

MIT/Apache

25KB
346 lines

This crate provides a handful of utility types for writing abstractions for interfacing with register based devices. Most commonly, this would be utilized when writing drivers for external peripherals within an embedded environment. As such, some utility functions are provided for reading and writing registers on devices across I2C or SPI buses.

This crate provides a single trait to be implemented by all types that represent a value that is stored within an addressable register, aptly named Register. This trait provides nothing more than a method for retrieving the ID associated with the given register.

Readable Registers

A register in which values can be retrieved from, or read from, is represented as any type that implements the ReadableRegister trait. This trait is very little more than just a marker trait, but it represents a type that is both a Register and that can be created from a byte array through the FromByteArray trait. The bulk of the work in writing a type that can be read from a register will be in implementing the FromByteArray trait.

A type that implements the ReadableRegister trait can then be used with provided utility methods such as those provided by the i2c or spi modules.

Register Implementation Example

use regiface::{Register, ReadableRegister, FromByteArray};

// A type we will use to represent some fictional register struct 
MyRegister {    
    value: u8
}

// Implement the Register trait, and specify it has an ID of 42u8
impl Register for MyRegister {    
    type IdType = u8;

    fn id() -> Self::IdType {
        42    
    }
}

// Implement the FromByteArray trait, and specify it can be converted from a 1-byte array 
impl FromByteArray for MyRegister {
    type Error = core::convert::Infallible;
    type Array = [u8; 1];

    fn from_bytes(bytes: Self::Array) -> Result<Self, Self::Error> {
        Ok(Self {
            value: bytes[0]        
        })    
    }
}

// Indicate this is a readable register!
impl ReadableRegister for MyRegister {}

Writable Registers

A register in which values can be written to is represented as any type that implements the WritableRegister trait. This trait is very little more than just a marker trait,but it represents a type that is both a Register and that can be serialized into a byte array through the ToByteArray trait. The bulk of the work in writing a type that can be written to a register will be in implementing the ToByteArray trait.

A type that implements the WritableRegister trait can then be used with provided utility methods such as those provided by the i2c or spi modules.

Register Implementation Example

use regiface::{Register, WritableRegister, ToByteArray};

// A type we will use to represent some fictional register 
struct MyRegister {    
    value: u8
}

// Implement the Register trait, and specify it has an ID of 42u8
impl Register for MyRegister {    
    type IdType = u8;

    fn id() -> Self::IdType {        
        42    
    }
}

// Implement the ToByteArray trait, and specify it can be converted to a 1-byte array 
impl ToByteArray for MyRegister {    
    type Error = core::convert::Infallible;    
    type Array = [u8; 1];

    fn to_bytes(self) -> Result<Self::Array, Self::Error> {        
        Ok([self.value])    
    }
}

// Indicate this is a readable register!
impl WritableRegister for MyRegister {}

Dependencies

~0.3–1MB
~22K SLoC