pololu_tic 0.2.0-beta.7

A driver to control the Tic series of stepper motor controllers created by Pololu, including the T500, T834, T825, T249, and 36v4.
Documentation
//! I2C interface to a Tic motor driver board.

use embedded_hal::{
    delay::DelayNs,
    i2c::{Error, I2c as ExternalI2c},
};

use crate::{
    Command, HandlerError, Product,
    base::{TicBase, communication::TicCommunication},
};

/// I2C interface to a Tic board.
///
/// ### Usage Example
/// ```rust,ignore
/// use pololu_tic::{TicBase as _, I2c as TicI2C, Product};
///
/// let mut tic = TicI2C::new_with_address(
///     <i2c_bus>,
///     Product::Tic36v4,
///     <delay>,
///     14
/// );
///
/// tic.set_target_velocity(2000000);
///
/// loop {
///     tic.reset_command_timeout();
/// }
/// ```
pub struct I2c<I: ExternalI2c, D: DelayNs> {
    address: u8,
    i2c: I,
    product: Product,
    delay: D,
}

impl<I: ExternalI2c, D: DelayNs> I2c<I, D> {
    const DEFAULT_ADDR: u8 = 14;

    /// Create a new Tic as an I²C device with default values for the address.
    pub const fn new_default(i2c: I, product: Product, delay: D) -> Self {
        Self {
            address: Self::DEFAULT_ADDR,
            i2c,
            product,
            delay,
        }
    }

    /// Create a new Tic as an I²C device with a custom address value.
    pub const fn new_with_address(i2c: I, product: Product, delay: D, address: u8) -> Self {
        Self {
            address,
            i2c,
            product,
            delay,
        }
    }

    /// Get the current I²C address value.
    pub const fn get_address(&self) -> u8 {
        self.address
    }
}

impl<I: ExternalI2c, D: DelayNs> TicCommunication for I2c<I, D> {
    fn command_quick(&mut self, cmd: Command) -> Result<(), HandlerError> {
        self.i2c
            .write(self.address, &[cmd as u8])
            .map_err(|e| HandlerError::I2cError(e.kind()))?;
        Ok(())
    }

    fn command_w32(&mut self, cmd: Command, val: u32) -> Result<(), HandlerError> {
        let mut data = [0u8; 5];
        data[0] = cmd as u8;
        data[1..5].copy_from_slice(&val.to_le_bytes());
        self.i2c
            .write(self.address, &data)
            .map_err(|e| HandlerError::I2cError(e.kind()))?;
        Ok(())
    }

    fn command_w7(&mut self, cmd: Command, val: u8) -> Result<(), HandlerError> {
        let mut data = [0u8; 2];
        data[0] = cmd as u8;
        data[1] = val & 0x7F;
        self.i2c
            .write(self.address, &data)
            .map_err(|e| HandlerError::I2cError(e.kind()))?;
        Ok(())
    }

    fn block_read(
        &mut self,
        cmd: Command,
        offset: u8,
        buffer: &mut [u8],
    ) -> Result<(), HandlerError> {
        let data = [cmd as u8, offset];
        self.i2c
            .write_read(self.address, &data, buffer)
            .map_err(|e| HandlerError::I2cError(e.kind()))?;
        Ok(())
    }
}

impl<I: ExternalI2c, D: DelayNs> TicBase for I2c<I, D> {
    fn product(&self) -> Product {
        self.product
    }

    fn delay(&mut self, delay: core::time::Duration) {
        self.delay.delay_ns(delay.as_nanos() as u32);
    }
}

impl From<embedded_hal::i2c::ErrorKind> for HandlerError {
    fn from(err: embedded_hal::i2c::ErrorKind) -> Self {
        Self::I2cError(err)
    }
}