1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! Embedded SPI helper and testing package
//! This is intended to try out some possible trait improvements prior to proposing chanegs
//! to embedded-hal

extern crate embedded_hal;

use embedded_hal::blocking::spi::{Transfer, Write};
use embedded_hal::digital::v2::OutputPin;

/// Transaction trait provides higher level, transaction-based, SPI constructs
/// These are executed in a single SPI transaction (without de-asserting CS).
trait Transactional {
    type Error;

    /// Read writes the prefix buffer then reads into the input buffer 
    fn read(&mut self, prefix: &[u8], data: &mut [u8]) -> Result<(), Self::Error>;
    /// Write writes the prefix buffer then writes the output buffer
    fn write(&mut self, prefix: &[u8], data: &[u8]) -> Result<(), Self::Error>;
    /// Exec allows 'Transaction' objects to be chained together into a single transaction
    fn exec(&mut self, transactions: &mut [Transaction]) -> Result<(), Self::Error>;
}

/// Convenience error type combining SPI and Pin errors
#[derive(Debug, Clone, PartialEq)]
pub enum Error<SpiError, PinError> {
    Spi(SpiError),
    Pin(PinError),
    Aborted,
}

//#[derive(Debug, PartialEq)]
pub enum Transaction<'a> {
    Write(&'a [u8]),
    Read(&'a mut [u8]),
}

/// TransactionalSpi wraps an Spi and Pin object to support transactions
#[derive(Debug, Clone, PartialEq)]
pub struct TransactionalSpi<Spi, Pin> {
    spi: Spi,
    cs: Pin,
}

impl <Spi, SpiError, Pin, PinError> TransactionalSpi<Spi, Pin> 
where
    Spi: Transfer<u8, Error = SpiError> + Write<u8, Error = SpiError>,
    Pin: OutputPin<Error = PinError>,
{
    pub fn new(spi: Spi, cs: Pin) -> Self {
        Self{spi, cs}
    }
}

impl <Spi, SpiError, Pin, PinError> Transactional for TransactionalSpi<Spi, Pin> 
where
    Spi: Transfer<u8, Error = SpiError> + Write<u8, Error = SpiError>,
    Pin: OutputPin<Error = PinError>,
{
    type Error = Error<SpiError, PinError>;

    /// Read data from a specified address
    /// This consumes the provided input data array and returns a reference to this on success
    fn read<'a>(&mut self, prefix: &[u8], mut data: &'a mut [u8]) -> Result<(), Error<SpiError, PinError>> {
        // Assert CS
        self.cs.set_low().map_err(|e| Error::Pin(e) )?;

        // Write command
        let mut res = self.spi.write(&prefix);

        // Read incoming data
        if res.is_ok() {
            res = self.spi.transfer(&mut data).map(|_r| () );
        }

        // Clear CS
        self.cs.set_high().map_err(|e| Error::Pin(e) )?;

        // Return result (contains returned data)
        match res {
            Err(e) => Err(Error::Spi(e)),
            Ok(_) => Ok(()),
        }
    }

    /// Write data to a specified register address
    fn write(&mut self, prefix: &[u8], data: &[u8]) -> Result<(), Self::Error> {
        // Assert CS
        self.cs.set_low().map_err(|e| Error::Pin(e) )?;

        // Write command
        let mut res = self.spi.write(&prefix);

        // Read incoming data
        if res.is_ok() {
            res = self.spi.write(&data);
        }

        // Clear CS
        self.cs.set_high().map_err(|e| Error::Pin(e) )?;

        // Return result
        match res {
            Err(e) => Err(Error::Spi(e)),
            Ok(_) => Ok(()),
        }
    }

    /// Execute the provided transactions
    fn exec(&mut self, transactions: &mut [Transaction]) -> Result<(), Self::Error> {
        let mut res = Ok(());

        // Assert CS
        self.cs.set_low().map_err(|e| Error::Pin(e) )?;

        for i in 0..transactions.len() {
            let mut t = &mut transactions[i];

            res = match &mut t {
                Transaction::Write(d) => self.spi.write(d),
                Transaction::Read(d) =>  self.spi.transfer(d).map(|_r| () ),
            }.map_err(|e| Error::Spi(e) );

            if res.is_err() {
                break;
            }
        }

        // Assert CS
        self.cs.set_low().map_err(|e| Error::Pin(e) )?;

        res
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}