diff --git a/.github/bors.toml b/.github/bors.toml index 183d1f0..64eb89c 100644 --- a/.github/bors.toml +++ b/.github/bors.toml @@ -4,5 +4,5 @@ required_approvals = 1 status = [ "CI (stable, x86_64-unknown-linux-gnu)", "CI (stable, armv7-unknown-linux-gnueabihf)", - "CI (1.36.0, x86_64-unknown-linux-gnu)", + "CI (1.46.0, x86_64-unknown-linux-gnu)", ] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a84d4fd..ba91b86 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: include: # Test MSRV - - rust: 1.36.0 + - rust: 1.46.0 TARGET: x86_64-unknown-linux-gnu # Test nightly but don't fail diff --git a/CHANGELOG.md b/CHANGELOG.md index 2270baf..566dc86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Modified `OutputPin` behavior for active-low pins to match `InputPin` behavior. - Set default features to build both sysfs and cdev pin types. - Removed `Pin` export, use `CdevPin` or `SysfsPin`. -- Increased the Minimum Supported Rust Version to `1.36.0` due to an update of `gpio_cdev`. -- Adapted to `embedded-hal` `1.0.0-alpha.3` release. +- Increased the Minimum Supported Rust Version to `1.46.0` due to an update of `bitflags`. +- Adapted to `embedded-hal` `1.0.0-alpha.5` release. - Updated `nb` to version `1`. ## [v0.3.0] - 2019-11-25 diff --git a/Cargo.toml b/Cargo.toml index ae62c40..7ff56ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,15 +20,15 @@ async-tokio = ["gpio-cdev/async-tokio"] default = [ "gpio_cdev", "gpio_sysfs" ] [dependencies] -embedded-hal = "=1.0.0-alpha.4" -gpio-cdev = { version = "0.4", optional = true } -sysfs_gpio = { version = "0.5", optional = true } +embedded-hal = "=1.0.0-alpha.5" +gpio-cdev = { version = "0.5", optional = true } +sysfs_gpio = { version = "0.6", optional = true } -i2cdev = "0.4.3" +i2cdev = "0.5" nb = "1" serial-core = "0.4.0" serial-unix = "0.4.0" -spidev = "0.4" +spidev = "0.5" [dev-dependencies] openpty = "0.1.0" @@ -36,4 +36,4 @@ openpty = "0.1.0" [dependencies.cast] # we don't need the `Error` implementation default-features = false -version = "0.2.2" +version = "0.3" diff --git a/README.md b/README.md index 4a7c42b..949e3c3 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ linux-embedded-hal = { version = "0.3", features = ["gpio_cdev"] } ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.36.0 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.46.0 and up. It *might* compile with older versions but that may change in any new patch release. ## License diff --git a/examples/transactional-i2c.rs b/examples/transactional-i2c.rs index 451ae43..a8697e1 100644 --- a/examples/transactional-i2c.rs +++ b/examples/transactional-i2c.rs @@ -1,6 +1,4 @@ -extern crate embedded_hal; -extern crate linux_embedded_hal; -use embedded_hal::blocking::i2c::{Operation as I2cOperation, Transactional}; +use embedded_hal::i2c::blocking::{Operation as I2cOperation, Transactional}; use linux_embedded_hal::I2cdev; const ADDR: u8 = 0x12; @@ -23,7 +21,7 @@ where I2cOperation::Write(&[0xAB]), I2cOperation::Read(&mut read_buffer), ]; - self.i2c.try_exec(ADDR, &mut ops).and(Ok(read_buffer[0])) + self.i2c.exec(ADDR, &mut ops).and(Ok(read_buffer[0])) } } diff --git a/src/cdev_pin.rs b/src/cdev_pin.rs index b35e50e..6af3031 100644 --- a/src/cdev_pin.rs +++ b/src/cdev_pin.rs @@ -1,4 +1,6 @@ -//! Linux CDev pin type +//! Implementation of [`embedded-hal`] digital input/output traits using a Linux CDev pin +//! +//! [`embedded-hal`]: https://docs.rs/embedded-hal /// Newtype around [`gpio_cdev::LineHandle`] that implements the `embedded-hal` traits /// @@ -15,10 +17,10 @@ impl CdevPin { } } -impl embedded_hal::digital::OutputPin for CdevPin { +impl embedded_hal::digital::blocking::OutputPin for CdevPin { type Error = gpio_cdev::errors::Error; - fn try_set_low(&mut self) -> Result<(), Self::Error> { + fn set_low(&mut self) -> Result<(), Self::Error> { if self.1 { self.0.set_value(1) } else { @@ -26,7 +28,7 @@ impl embedded_hal::digital::OutputPin for CdevPin { } } - fn try_set_high(&mut self) -> Result<(), Self::Error> { + fn set_high(&mut self) -> Result<(), Self::Error> { if self.1 { self.0.set_value(0) } else { @@ -35,10 +37,10 @@ impl embedded_hal::digital::OutputPin for CdevPin { } } -impl embedded_hal::digital::InputPin for CdevPin { +impl embedded_hal::digital::blocking::InputPin for CdevPin { type Error = gpio_cdev::errors::Error; - fn try_is_high(&self) -> Result { + fn is_high(&self) -> Result { if !self.1 { self.0.get_value().map(|val| val != 0) } else { @@ -46,8 +48,8 @@ impl embedded_hal::digital::InputPin for CdevPin { } } - fn try_is_low(&self) -> Result { - self.try_is_high().map(|val| !val) + fn is_low(&self) -> Result { + self.is_high().map(|val| !val) } } diff --git a/src/delay.rs b/src/delay.rs new file mode 100644 index 0000000..878d062 --- /dev/null +++ b/src/delay.rs @@ -0,0 +1,90 @@ +//! Implementation of [`embedded-hal`] delay traits +//! +//! [`embedded-hal`]: https://docs.rs/embedded-hal + +use cast::{u32, u64}; +use core::convert::Infallible; +use embedded_hal::delay::blocking::{DelayMs, DelayUs}; +use std::thread; +use std::time::Duration; + +/// Empty struct that provides delay functionality on top of `thread::sleep` +pub struct Delay; + +impl DelayUs for Delay { + type Error = Infallible; + + fn delay_us(&mut self, n: u8) -> Result<(), Self::Error> { + thread::sleep(Duration::new(0, u32(n) * 1000)); + Ok(()) + } +} + +impl DelayUs for Delay { + type Error = Infallible; + + fn delay_us(&mut self, n: u16) -> Result<(), Self::Error> { + thread::sleep(Duration::new(0, u32(n) * 1000)); + Ok(()) + } +} + +impl DelayUs for Delay { + type Error = Infallible; + + fn delay_us(&mut self, n: u32) -> Result<(), Self::Error> { + let secs = n / 1_000_000; + let nsecs = (n % 1_000_000) * 1_000; + + thread::sleep(Duration::new(u64(secs), nsecs)); + Ok(()) + } +} + +impl DelayUs for Delay { + type Error = Infallible; + + fn delay_us(&mut self, n: u64) -> Result<(), Self::Error> { + let secs = n / 1_000_000; + let nsecs = ((n % 1_000_000) * 1_000) as u32; + + thread::sleep(Duration::new(secs, nsecs)); + Ok(()) + } +} + +impl DelayMs for Delay { + type Error = Infallible; + + fn delay_ms(&mut self, n: u8) -> Result<(), Self::Error> { + thread::sleep(Duration::from_millis(u64(n))); + Ok(()) + } +} + +impl DelayMs for Delay { + type Error = Infallible; + + fn delay_ms(&mut self, n: u16) -> Result<(), Self::Error> { + thread::sleep(Duration::from_millis(u64(n))); + Ok(()) + } +} + +impl DelayMs for Delay { + type Error = Infallible; + + fn delay_ms(&mut self, n: u32) -> Result<(), Self::Error> { + thread::sleep(Duration::from_millis(u64(n))); + Ok(()) + } +} + +impl DelayMs for Delay { + type Error = Infallible; + + fn delay_ms(&mut self, n: u64) -> Result<(), Self::Error> { + thread::sleep(Duration::from_millis(n)); + Ok(()) + } +} diff --git a/src/i2c.rs b/src/i2c.rs new file mode 100644 index 0000000..c93e794 --- /dev/null +++ b/src/i2c.rs @@ -0,0 +1,119 @@ +//! Implementation of [`embedded-hal`] I2C traits +//! +//! [`embedded-hal`]: https://docs.rs/embedded-hal + +use std::ops; +use std::path::{Path, PathBuf}; + +/// Newtype around [`i2cdev::linux::LinuxI2CDevice`] that implements the `embedded-hal` traits +/// +/// [`i2cdev::linux::LinuxI2CDevice`]: https://docs.rs/i2cdev/0.3.1/i2cdev/linux/struct.LinuxI2CDevice.html +pub struct I2cdev { + inner: i2cdev::linux::LinuxI2CDevice, + path: PathBuf, + address: Option, +} + +impl I2cdev { + /// See [`i2cdev::linux::LinuxI2CDevice::new`][0] for details. + /// + /// [0]: https://docs.rs/i2cdev/0.3.1/i2cdev/linux/struct.LinuxI2CDevice.html#method.new + pub fn new

(path: P) -> Result + where + P: AsRef, + { + let dev = I2cdev { + path: path.as_ref().to_path_buf(), + inner: i2cdev::linux::LinuxI2CDevice::new(path, 0)?, + address: None, + }; + Ok(dev) + } + + fn set_address(&mut self, address: u8) -> Result<(), i2cdev::linux::LinuxI2CError> { + if self.address != Some(address) { + self.inner = i2cdev::linux::LinuxI2CDevice::new(&self.path, u16::from(address))?; + self.address = Some(address); + } + Ok(()) + } +} + +impl ops::Deref for I2cdev { + type Target = i2cdev::linux::LinuxI2CDevice; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl ops::DerefMut for I2cdev { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +mod embedded_hal_impl { + use super::*; + use embedded_hal::i2c::blocking::{ + Operation as I2cOperation, Read, Transactional, Write, WriteRead, + }; + use i2cdev::core::{I2CDevice, I2CMessage, I2CTransfer}; + use i2cdev::linux::LinuxI2CMessage; + + impl Read for I2cdev { + type Error = i2cdev::linux::LinuxI2CError; + + fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.set_address(address)?; + self.inner.read(buffer) + } + } + + impl Write for I2cdev { + type Error = i2cdev::linux::LinuxI2CError; + + fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { + self.set_address(address)?; + self.inner.write(bytes) + } + } + + impl WriteRead for I2cdev { + type Error = i2cdev::linux::LinuxI2CError; + + fn write_read( + &mut self, + address: u8, + bytes: &[u8], + buffer: &mut [u8], + ) -> Result<(), Self::Error> { + self.set_address(address)?; + let mut messages = [LinuxI2CMessage::write(bytes), LinuxI2CMessage::read(buffer)]; + self.inner.transfer(&mut messages).map(drop) + } + } + + impl Transactional for I2cdev { + type Error = i2cdev::linux::LinuxI2CError; + + fn exec( + &mut self, + address: u8, + operations: &mut [I2cOperation], + ) -> Result<(), Self::Error> { + // Map operations from generic to linux objects + let mut messages: Vec<_> = operations + .as_mut() + .iter_mut() + .map(|a| match a { + I2cOperation::Write(w) => LinuxI2CMessage::write(w), + I2cOperation::Read(r) => LinuxI2CMessage::read(r), + }) + .collect(); + + self.set_address(address)?; + self.inner.transfer(&mut messages).map(drop) + } + } +} diff --git a/src/lib.rs b/src/lib.rs index f03c925..ffb3e87 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,6 @@ #![deny(missing_docs)] -use cast; pub use i2cdev; pub use nb; pub use serial_core; @@ -24,25 +23,6 @@ pub use sysfs_gpio; #[cfg(feature = "gpio_cdev")] pub use gpio_cdev; - -use core::convert::Infallible; -use std::io::{self, Write}; -use std::path::{Path, PathBuf}; -use std::time::Duration; -use std::{ops, thread}; - -use cast::{u32, u64}; -use embedded_hal::blocking::i2c::Operation as I2cOperation; -use i2cdev::core::{I2CDevice, I2CMessage, I2CTransfer}; -use i2cdev::linux::LinuxI2CMessage; -use spidev::SpidevTransfer; - -mod serial; -mod timer; - -pub use serial::Serial; -pub use timer::SysTimer; - #[cfg(feature = "gpio_sysfs")] /// Sysfs Pin wrapper module mod sysfs_pin; @@ -54,273 +34,19 @@ mod cdev_pin; #[cfg(feature = "gpio_cdev")] /// Cdev pin re-export pub use cdev_pin::CdevPin; + #[cfg(feature = "gpio_sysfs")] /// Sysfs pin re-export pub use sysfs_pin::SysfsPin; -/// Empty struct that provides delay functionality on top of `thread::sleep` -pub struct Delay; - -impl embedded_hal::blocking::delay::DelayUs for Delay { - type Error = Infallible; - - fn try_delay_us(&mut self, n: u8) -> Result<(), Self::Error> { - thread::sleep(Duration::new(0, u32(n) * 1000)); - Ok(()) - } -} - -impl embedded_hal::blocking::delay::DelayUs for Delay { - type Error = Infallible; - - fn try_delay_us(&mut self, n: u16) -> Result<(), Self::Error> { - thread::sleep(Duration::new(0, u32(n) * 1000)); - Ok(()) - } -} - -impl embedded_hal::blocking::delay::DelayUs for Delay { - type Error = Infallible; - - fn try_delay_us(&mut self, n: u32) -> Result<(), Self::Error> { - let secs = n / 1_000_000; - let nsecs = (n % 1_000_000) * 1_000; - - thread::sleep(Duration::new(u64(secs), nsecs)); - Ok(()) - } -} - -impl embedded_hal::blocking::delay::DelayUs for Delay { - type Error = Infallible; - - fn try_delay_us(&mut self, n: u64) -> Result<(), Self::Error> { - let secs = n / 1_000_000; - let nsecs = ((n % 1_000_000) * 1_000) as u32; - - thread::sleep(Duration::new(secs, nsecs)); - Ok(()) - } -} - -impl embedded_hal::blocking::delay::DelayMs for Delay { - type Error = Infallible; - - fn try_delay_ms(&mut self, n: u8) -> Result<(), Self::Error> { - thread::sleep(Duration::from_millis(u64(n))); - Ok(()) - } -} - -impl embedded_hal::blocking::delay::DelayMs for Delay { - type Error = Infallible; - - fn try_delay_ms(&mut self, n: u16) -> Result<(), Self::Error> { - thread::sleep(Duration::from_millis(u64(n))); - Ok(()) - } -} - -impl embedded_hal::blocking::delay::DelayMs for Delay { - type Error = Infallible; - - fn try_delay_ms(&mut self, n: u32) -> Result<(), Self::Error> { - thread::sleep(Duration::from_millis(u64(n))); - Ok(()) - } -} - -impl embedded_hal::blocking::delay::DelayMs for Delay { - type Error = Infallible; - - fn try_delay_ms(&mut self, n: u64) -> Result<(), Self::Error> { - thread::sleep(Duration::from_millis(n)); - Ok(()) - } -} - -/// Newtype around [`i2cdev::linux::LinuxI2CDevice`] that implements the `embedded-hal` traits -/// -/// [`i2cdev::linux::LinuxI2CDevice`]: https://docs.rs/i2cdev/0.3.1/i2cdev/linux/struct.LinuxI2CDevice.html -pub struct I2cdev { - inner: i2cdev::linux::LinuxI2CDevice, - path: PathBuf, - address: Option, -} - -impl I2cdev { - /// See [`i2cdev::linux::LinuxI2CDevice::new`][0] for details. - /// - /// [0]: https://docs.rs/i2cdev/0.3.1/i2cdev/linux/struct.LinuxI2CDevice.html#method.new - pub fn new

(path: P) -> Result - where - P: AsRef, - { - let dev = I2cdev { - path: path.as_ref().to_path_buf(), - inner: i2cdev::linux::LinuxI2CDevice::new(path, 0)?, - address: None, - }; - Ok(dev) - } - - fn set_address(&mut self, address: u8) -> Result<(), i2cdev::linux::LinuxI2CError> { - if self.address != Some(address) { - self.inner = i2cdev::linux::LinuxI2CDevice::new(&self.path, u16::from(address))?; - self.address = Some(address); - } - Ok(()) - } -} - -impl embedded_hal::blocking::i2c::Read for I2cdev { - type Error = i2cdev::linux::LinuxI2CError; - - fn try_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.set_address(address)?; - self.inner.read(buffer) - } -} - -impl embedded_hal::blocking::i2c::Write for I2cdev { - type Error = i2cdev::linux::LinuxI2CError; - - fn try_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { - self.set_address(address)?; - self.inner.write(bytes) - } -} - -impl embedded_hal::blocking::i2c::WriteRead for I2cdev { - type Error = i2cdev::linux::LinuxI2CError; - - fn try_write_read( - &mut self, - address: u8, - bytes: &[u8], - buffer: &mut [u8], - ) -> Result<(), Self::Error> { - self.set_address(address)?; - let mut messages = [LinuxI2CMessage::write(bytes), LinuxI2CMessage::read(buffer)]; - self.inner.transfer(&mut messages).map(drop) - } -} - -impl embedded_hal::blocking::i2c::Transactional for I2cdev { - type Error = i2cdev::linux::LinuxI2CError; - - fn try_exec( - &mut self, - address: u8, - operations: &mut [I2cOperation], - ) -> Result<(), Self::Error> { - // Map operations from generic to linux objects - let mut messages: Vec<_> = operations - .as_mut() - .iter_mut() - .map(|a| match a { - I2cOperation::Write(w) => LinuxI2CMessage::write(w), - I2cOperation::Read(r) => LinuxI2CMessage::read(r), - }) - .collect(); - - self.set_address(address)?; - self.inner.transfer(&mut messages).map(drop) - } -} - -impl ops::Deref for I2cdev { - type Target = i2cdev::linux::LinuxI2CDevice; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -impl ops::DerefMut for I2cdev { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner - } -} - -/// Newtype around [`spidev::Spidev`] that implements the `embedded-hal` traits -/// -/// [`spidev::Spidev`]: https://docs.rs/spidev/0.4.0/spidev/struct.Spidev.html -pub struct Spidev(pub spidev::Spidev); - -impl Spidev { - /// See [`spidev::Spidev::open`][0] for details. - /// - /// [0]: https://docs.rs/spidev/0.4.0/spidev/struct.Spidev.html#method.open - pub fn open

(path: P) -> io::Result - where - P: AsRef, - { - spidev::Spidev::open(path).map(Spidev) - } -} - -impl embedded_hal::blocking::spi::Transfer for Spidev { - type Error = io::Error; - - fn try_transfer<'b>(&mut self, buffer: &'b mut [u8]) -> io::Result<&'b [u8]> { - let tx = buffer.to_owned(); - self.0 - .transfer(&mut SpidevTransfer::read_write(&tx, buffer))?; - Ok(buffer) - } -} - -impl embedded_hal::blocking::spi::Write for Spidev { - type Error = io::Error; - - fn try_write(&mut self, buffer: &[u8]) -> io::Result<()> { - self.0.write_all(buffer) - } -} - -pub use embedded_hal::blocking::spi::Operation as SpiOperation; - -/// Transactional implementation batches SPI operations into a single transaction -impl embedded_hal::blocking::spi::Transactional for Spidev { - type Error = io::Error; - - fn try_exec<'a>(&mut self, operations: &mut [SpiOperation<'a, u8>]) -> Result<(), Self::Error> { - // Map types from generic to linux objects - let mut messages: Vec<_> = operations - .iter_mut() - .map(|a| { - match a { - SpiOperation::Write(w) => SpidevTransfer::write(w), - SpiOperation::Transfer(r) => { - // Clone read to write pointer - // SPIdev is okay with having w == r but this is tricky to achieve in safe rust - let w = unsafe { - let p = r.as_ptr(); - std::slice::from_raw_parts(p, r.len()) - }; - - SpidevTransfer::read_write(w, r) - } - } - }) - .collect(); - - // Execute transfer - self.0.transfer_multiple(&mut messages) - } -} - -impl ops::Deref for Spidev { - type Target = spidev::Spidev; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} +mod delay; +mod i2c; +mod serial; +mod spi; +mod timer; -impl ops::DerefMut for Spidev { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} +pub use crate::delay::Delay; +pub use crate::i2c::I2cdev; +pub use crate::serial::Serial; +pub use crate::spi::Spidev; +pub use crate::timer::SysTimer; diff --git a/src/serial.rs b/src/serial.rs index 102849c..46bb61d 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -1,12 +1,12 @@ -//! Implementation of [`Serial`](https://docs.rs/embedded-hal/0.2.1/embedded_hal/serial/index.html) - -use std::io::{ErrorKind as IoErrorKind, Read, Write}; -use std::path::Path; +//! Implementation of [`embedded-hal`] serial traits +//! +//! [`embedded-hal`]: https://docs.rs/embedded-hal use nb; - use serial_core; use serial_unix::TTYPort; +use std::io::{ErrorKind as IoErrorKind, Read, Write}; +use std::path::Path; /// Newtype around [`serial_unix::TTYPort`] that implements /// the `embedded-hal` traits. @@ -29,10 +29,10 @@ fn translate_io_errors(err: std::io::Error) -> nb::Error { } } -impl embedded_hal::serial::Read for Serial { +impl embedded_hal::serial::nb::Read for Serial { type Error = IoErrorKind; - fn try_read(&mut self) -> nb::Result { + fn read(&mut self) -> nb::Result { let mut buffer = [0; 1]; let bytes_read = self.0.read(&mut buffer).map_err(translate_io_errors)?; if bytes_read == 1 { @@ -43,15 +43,15 @@ impl embedded_hal::serial::Read for Serial { } } -impl embedded_hal::serial::Write for Serial { +impl embedded_hal::serial::nb::Write for Serial { type Error = IoErrorKind; - fn try_write(&mut self, word: u8) -> nb::Result<(), Self::Error> { + fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { self.0.write(&[word]).map_err(translate_io_errors)?; Ok(()) } - fn try_flush(&mut self) -> nb::Result<(), Self::Error> { + fn flush(&mut self) -> nb::Result<(), Self::Error> { self.0.flush().map_err(translate_io_errors) } } @@ -60,7 +60,7 @@ impl embedded_hal::serial::Write for Serial { mod test { use std::path::Path; - use embedded_hal::serial::{Read, Write}; + use embedded_hal::serial::nb::{Read, Write}; use std::io::{Read as IoRead, Write as IoWrite}; use super::*; @@ -75,20 +75,20 @@ mod test { #[test] fn test_empty_read() { let (mut _master, mut serial) = create_pty_and_serial(); - assert_eq!(Err(nb::Error::WouldBlock), serial.try_read()); + assert_eq!(Err(nb::Error::WouldBlock), serial.read()); } #[test] fn test_read() { let (mut master, mut serial) = create_pty_and_serial(); master.write(&[1]).expect("Write failed"); - assert_eq!(Ok(1), serial.try_read()); + assert_eq!(Ok(1), serial.read()); } #[test] fn test_write() { let (mut master, mut serial) = create_pty_and_serial(); - serial.try_write(2).expect("Write failed"); + serial.write(2).expect("Write failed"); let mut buf = [0; 2]; assert_eq!(1, master.read(&mut buf).unwrap()); assert_eq!(buf, [2, 0]); diff --git a/src/spi.rs b/src/spi.rs new file mode 100644 index 0000000..2306a53 --- /dev/null +++ b/src/spi.rs @@ -0,0 +1,94 @@ +//! Implementation of [`embedded-hal`] SPI traits +//! +//! [`embedded-hal`]: https://docs.rs/embedded-hal +//! + +use std::io; +use std::ops; +use std::path::Path; + +/// Newtype around [`spidev::Spidev`] that implements the `embedded-hal` traits +/// +/// [`spidev::Spidev`]: https://docs.rs/spidev/0.4.0/spidev/struct.Spidev.html +pub struct Spidev(pub spidev::Spidev); + +impl Spidev { + /// See [`spidev::Spidev::open`][0] for details. + /// + /// [0]: https://docs.rs/spidev/0.4.0/spidev/struct.Spidev.html#method.open + pub fn open

(path: P) -> io::Result + where + P: AsRef, + { + spidev::Spidev::open(path).map(Spidev) + } +} + +impl ops::Deref for Spidev { + type Target = spidev::Spidev; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl ops::DerefMut for Spidev { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +mod embedded_hal_impl { + use super::*; + use embedded_hal::spi::blocking::{Operation as SpiOperation, Transactional, Transfer, Write}; + use spidev::SpidevTransfer; + use std::io::Write as _; + + impl Transfer for Spidev { + type Error = io::Error; + + fn transfer<'b>(&mut self, buffer: &'b mut [u8]) -> io::Result<()> { + let tx = buffer.to_owned(); + self.0 + .transfer(&mut SpidevTransfer::read_write(&tx, buffer)) + } + } + + impl Write for Spidev { + type Error = io::Error; + + fn write(&mut self, buffer: &[u8]) -> io::Result<()> { + self.0.write_all(buffer) + } + } + + /// Transactional implementation batches SPI operations into a single transaction + impl Transactional for Spidev { + type Error = io::Error; + + fn exec<'a>(&mut self, operations: &mut [SpiOperation<'a, u8>]) -> Result<(), Self::Error> { + // Map types from generic to linux objects + let mut messages: Vec<_> = operations + .iter_mut() + .map(|a| { + match a { + SpiOperation::Write(w) => SpidevTransfer::write(w), + SpiOperation::Transfer(r) => { + // Clone read to write pointer + // SPIdev is okay with having w == r but this is tricky to achieve in safe rust + let w = unsafe { + let p = r.as_ptr(); + std::slice::from_raw_parts(p, r.len()) + }; + + SpidevTransfer::read_write(w, r) + } + } + }) + .collect(); + + // Execute transfer + self.0.transfer_multiple(&mut messages) + } + } +} diff --git a/src/sysfs_pin.rs b/src/sysfs_pin.rs index b585912..5734f22 100644 --- a/src/sysfs_pin.rs +++ b/src/sysfs_pin.rs @@ -1,4 +1,6 @@ -//! Linux Sysfs pin type +//! Implementation of [`embedded-hal`] digital input/output traits using a Linux Sysfs pin +//! +//! [`embedded-hal`]: https://docs.rs/embedded-hal use std::path::Path; @@ -26,10 +28,10 @@ impl SysfsPin { } } -impl embedded_hal::digital::OutputPin for SysfsPin { +impl embedded_hal::digital::blocking::OutputPin for SysfsPin { type Error = sysfs_gpio::Error; - fn try_set_low(&mut self) -> Result<(), Self::Error> { + fn set_low(&mut self) -> Result<(), Self::Error> { if self.0.get_active_low()? { self.0.set_value(1) } else { @@ -37,7 +39,7 @@ impl embedded_hal::digital::OutputPin for SysfsPin { } } - fn try_set_high(&mut self) -> Result<(), Self::Error> { + fn set_high(&mut self) -> Result<(), Self::Error> { if self.0.get_active_low()? { self.0.set_value(0) } else { @@ -46,10 +48,10 @@ impl embedded_hal::digital::OutputPin for SysfsPin { } } -impl embedded_hal::digital::InputPin for SysfsPin { +impl embedded_hal::digital::blocking::InputPin for SysfsPin { type Error = sysfs_gpio::Error; - fn try_is_high(&self) -> Result { + fn is_high(&self) -> Result { if !self.0.get_active_low()? { self.0.get_value().map(|val| val != 0) } else { @@ -57,8 +59,8 @@ impl embedded_hal::digital::InputPin for SysfsPin { } } - fn try_is_low(&self) -> Result { - self.try_is_high().map(|val| !val) + fn is_low(&self) -> Result { + self.is_high().map(|val| !val) } } diff --git a/src/timer.rs b/src/timer.rs index 0b26b19..6235f72 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -1,9 +1,11 @@ -//! Timers. +//! Implementation of [`embedded-hal`] timer traits +//! +//! [`embedded-hal`]: https://docs.rs/embedded-hal use core::convert::Infallible; use std::time::{Duration, Instant}; -use embedded_hal::timer::{CountDown, Periodic}; +use embedded_hal::timer::{nb::CountDown, Periodic}; /// A periodic timer based on [`std::time::Instant`][instant], which is a /// monotonically nondecreasing clock. @@ -31,7 +33,7 @@ impl CountDown for SysTimer { type Error = Infallible; type Time = Duration; - fn try_start(&mut self, count: T) -> Result<(), Self::Error> + fn start(&mut self, count: T) -> Result<(), Self::Error> where T: Into, { @@ -40,7 +42,7 @@ impl CountDown for SysTimer { Ok(()) } - fn try_wait(&mut self) -> nb::Result<(), Self::Error> { + fn wait(&mut self) -> nb::Result<(), Self::Error> { if (Instant::now() - self.start) >= self.duration { // Restart the timer to fulfill the contract by `Periodic` self.start = Instant::now(); @@ -63,8 +65,8 @@ mod tests { fn test_delay() { let mut timer = SysTimer::new(); let before = Instant::now(); - timer.try_start(Duration::from_millis(100)).unwrap(); - nb::block!(timer.try_wait()).unwrap(); + timer.start(Duration::from_millis(100)).unwrap(); + nb::block!(timer.wait()).unwrap(); let after = Instant::now(); let duration_ms = (after - before).as_millis(); assert!(duration_ms >= 100); @@ -76,13 +78,13 @@ mod tests { fn test_periodic() { let mut timer = SysTimer::new(); let before = Instant::now(); - timer.try_start(Duration::from_millis(100)).unwrap(); - nb::block!(timer.try_wait()).unwrap(); + timer.start(Duration::from_millis(100)).unwrap(); + nb::block!(timer.wait()).unwrap(); let after1 = Instant::now(); let duration_ms_1 = (after1 - before).as_millis(); assert!(duration_ms_1 >= 100); assert!(duration_ms_1 < 500); - nb::block!(timer.try_wait()).unwrap(); + nb::block!(timer.wait()).unwrap(); let after2 = Instant::now(); let duration_ms_2 = (after2 - after1).as_millis(); assert!(duration_ms_2 >= 100);