mcp320x 0.1.1

Platform agnostic driver written using embedded-hal traits to interface with the MCP320X ADC's
Documentation
//! A platform agnostic driver to interface with the MCP320X ADC's.
//!
//! This driver was built using [`embedded-hal`] traits.
//!
//! [`embedded-hal`]: https://docs.rs/embedded-hal/
//!

#![deny(missing_docs)]
#![deny(
    future_incompatible,
    let_underscore,
    keyword_idents,
    elided_lifetimes_in_paths,
    meta_variable_misuse,
    noop_method_call,
    pointer_structural_match,
    unused_lifetimes,
    unused_qualifications,
    unsafe_op_in_unsafe_fn,
    clippy::undocumented_unsafe_blocks,
    clippy::debug_assert_with_mut_call,
    clippy::empty_line_after_outer_attr,
    clippy::panic,
    clippy::unwrap_used,
    clippy::expect_used,
    clippy::redundant_field_names,
    clippy::rest_pat_in_fully_bound_structs,
    clippy::unneeded_field_pattern,
    clippy::useless_let_if_seq,
    clippy::default_union_representation,
    clippy::checked_conversions,
    clippy::dbg_macro
)]
#![warn(clippy::cloned_instead_of_copied, clippy::cognitive_complexity)]
#![allow(
    clippy::must_use_candidate,
    clippy::doc_markdown,
    clippy::missing_errors_doc,
    clippy::implicit_hasher
)]
#![feature(unsize)]
#![no_std]

extern crate embedded_hal as hal;

use hal::blocking::spi::Transfer;
use hal::digital::v2::OutputPin;
use hal::spi::{Mode, Phase, Polarity};

/// SPI mode
pub const MODE: Mode = Mode {
    phase: Phase::CaptureOnFirstTransition,
    polarity: Polarity::IdleLow,
};

/// mcp320X driver
pub struct Mcp320X<SPI, CS> {
    spi: SPI,
    cs: CS,
}

impl<SPI, CS, E> Mcp320X<SPI, CS>
where
    SPI: Transfer<u8, Error = E>,
    CS: OutputPin,
{
    /// Creates a new driver from an SPI peripheral and a chip select
    /// digital I/O pin.
    pub fn new(spi: SPI, cs: CS) -> Mcp320X<SPI, CS> {
        Mcp320X { spi, cs }
    }

    /// Read a mcp320X ADC channel and return the 10 bit value as a u16
    pub fn read_channel<C: Into<u8>>(
        &mut self,
        ch: C,
    ) -> Result<u16, McpError<SPI::Error, CS::Error>> {
        self.cs.set_low().map_err(McpError::CSError)?;

        let ch = ch.into();
        let mut buffer = [0u8; 3];
        buffer[0] = 1 << 2 | 1 << 1 | (ch & 4) >> 1;
        buffer[1] = (ch & 3) << 6;

        self.spi.transfer(&mut buffer).map_err(McpError::SPIError)?;

        self.cs.set_high().map_err(McpError::CSError)?;

        let r = (((buffer[1] as u16) << 8) | (buffer[2] as u16)) & 0x3ff;
        Ok(r)
    }
}
/// Possible errors for MCP320X
pub enum McpError<SE, CE> {
    /// SPI communication error
    SPIError(SE),
    /// Error setting CS pin
    CSError(CE),
}

impl<SE, CE> core::fmt::Debug for McpError<SE, CE>
where
    SE: core::fmt::Debug,
    CE: core::fmt::Debug,
{
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            Self::SPIError(err) => err.fmt(f),
            Self::CSError(err) => err.fmt(f),
        }
    }
}

/// Channel list for MCP3208
#[derive(Clone, Copy)]
#[allow(missing_docs)]
pub enum Channels8 {
    CH0,
    CH1,
    CH2,
    CH3,
    CH4,
    CH5,
    CH6,
    CH7,
}

impl From<Channels8> for u8 {
    fn from(c: Channels8) -> u8 {
        c as u8
    }
}

impl Channels8 {
    /// List all channels
    pub fn list() -> [Channels8; 8] {
        [
            Self::CH0,
            Self::CH1,
            Self::CH2,
            Self::CH3,
            Self::CH4,
            Self::CH5,
            Self::CH6,
            Self::CH7,
        ]
    }
}

/// Channel list for MCP3204
#[derive(Clone, Copy)]
#[allow(missing_docs)]
pub enum Channels4 {
    CH0,
    CH1,
    CH2,
    CH3,
}

impl From<Channels4> for u8 {
    fn from(c: Channels4) -> u8 {
        c as u8
    }
}

impl Channels4 {
    /// List all channels
    pub fn list() -> [Channels4; 4] {
        [Self::CH0, Self::CH1, Self::CH2, Self::CH3]
    }
}