#register #i2c #spi #embedded-hal-driver #traits-structs

no-std bit-byte-structs

A bus-agnostic trait and structs for interfacing with register based peripherals

1 unstable release

0.0.3 Sep 29, 2021

#1160 in Embedded development


Used in icm20948

MIT license

24KB
291 lines

Bit Byte Structs

A crate to provide boiler plate for creating bus agnostic drivers for peripherals with register based configuration.

This crate helps reduce boiler plates by providing traits and structs to help libs creating peripheral libraries. These can be bus agonistic and are currently implementing I2C and SPI traits of the embedded-hal lib.

This is very much a first cut and still has some ruff edges.

Documentation

Currently the docs can be found at https://pointswaves.gitlab.io/bit-byte-structures/bit_byte_structs/index.html

License

This is currently licensed under the MIT license.

Interactions

Please feel free to create a issue to report a problem, or to discuses a new PR before spending too much time on it.


lib.rs:

Bit Byte Structs

Bus Agnostic code for Register Bank style chips

Many peripheral IC chips are configured by a bank of registers. Setting these registers so the chip performs as expected can take up a lot of effort and code. This library implements the required boiler plate so device specific libs don't need to.

About this crate

This create provides a convent interface trait with implementations for I2C and SPI. It then implements a set of basic structures representing numbers made up of sub, hole or multiple bytes, using this trait.

There is no reason this could not be implemented for other buses. The current implementations are based around the embedded hal traits

The provided structures are there to make it quick and easy to create drivers for peripheral chips rather than to help optimise drivers for some aspect of performance.

The lib is loosely inspired by Adafruits BusIO but dose not share any IP or code.

Like other users of the above lib it is assumed that a driver author may use this lib to interact with basic configuration registers with this lib but may well implement there own functions for the performance critical or less standard registers.

The lib has a few key goals: * Make it easy to make drivers * Not to take permanent ownership of the bus * Not to make it easy for authors to pick and chose which registers they use this lib to interact with * Minimize the boiler plate in down stream users of this struct.

To use in a library

This lib helps remove boiler plate for the libs that use

These examples are illustrative only. We are working on getting working ones.

use core::marker::PhantomData;
use bitbytestructs::bus::{Interface, InterfaceError};
use bitbytestructs::registers::{BitStruct, BitByteStructError};

pub struct PeripheralDevice<InterfaceThing: ?Sized, E> {
   phantom: PhantomData<InterfaceThing>,
   low_power_bit: BitStruct<dyn Interface<Error = InterfaceError<E>>>,
}

impl<I2C, E> PeripheralDevice<I2C, E>
where
    I2C: Read<Error = E> + Write<Error = E> + WriteRead<Error = E>,
{
    pub fn new() -> Result<Self, BitByteStructError<InterfaceError<E>>> {

       let low_power_bit =
           BitStruct::<dyn Interface<Error = InterfaceError<E>>>::new(0x23, 1, 5)?;


        Ok(ICMCommon::<I, E> {
            phantom: PhantomData,
            low_power_bit,
        })
    }

   /// Set low power mode
   pub fn set_low_power(
       &mut self,
       i2c_bus: &mut I2C,
   ) -> Result<(), BitByteStructError<InterfaceError<E>>> {
        let mut interface = bus::SPIPeripheral::<I2C, E>::new(spi_bus, cs);
        
        if low_power {
           self.low_power_bit.write(interface, 1)?;
        } else {
           self.low_power_bit.write(interface, 0)?;
        }
        Ok(())
    }
}

The lib can be used to reduce boiler plate when writing libs for peripherals that use a single bus further more they can also be used to help with peripherals that also have spi and i2c.

Use of libs that use this lib

fn main() {
    let spi_mode = Mode {
        polarity: Polarity::IdleLow,
        phase: Phase::CaptureOnFirstTransition,
    };

    let mut spi = Spi::spi2(dp.SPI2, pins, spi_mode, 500.khz(), clocks, &mut rcc.apb1);

    let mut delay_obj = Delay::new(cp.SYST, clocks);
    let mut peripheral_A = PeripheralDevice::new(&mut spi, cs_a).unwrap();
    peripheral_A.init(&mut spi, &mut delay_obj).unwrap();

    let mut peripheral_B = PeripheralDevice::new(&mut spi, cs_b).unwrap();
    peripheral_B.init(&mut spi, &mut delay_obj).unwrap();

    loop {
        let results = peripheral_A.get_values_accel_gyro(&mut spi).unwrap();
        let (xa, ya, za, xg, yg, zg) =
            results;
        hprintln!(
            "results values from chip A {:?} {:?} {:?} {:?} {:?} {:?}",
            xa,
            ya,
            za,
            xg,
            yg,
            zg
        )
        .unwrap();
        let results = peripheral_B.get_values_accel_gyro(&mut spi).unwrap();
        let (xa, ya, za, xg, yg, zg) =
            results;
        hprintln!(
            "results values from chip B{:?} {:?} {:?} {:?} {:?} {:?}",
            xa,
            ya,
            za,
            xg,
            yg,
            zg
        )
        .unwrap();
        delay_obj.delay_ms(500 as u16);
    }
}

By borrowing the bus per operation we use the type system to make sure that the bus is not accessed when being used by a different peripheral.

The peripheral struct could have a functions that creates a sub struct that keeps the buss for a life time until ist time to give the bus back and let a different peripheral use it.

Dependencies

~195KB