Skip to content

Commit

Permalink
Merge pull request #4 from ryankurte/fix-async-add-addr
Browse files Browse the repository at this point in the history
Fix `restart_nonblocking` error, add integer address mode
  • Loading branch information
eldruin committed Sep 2, 2020
2 parents 9db5039 + 36f758c commit 2cb2cec
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 98 deletions.
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -67,11 +67,11 @@ on channel 0.
```rust
extern crate linux_embedded_hal as hal;
extern crate pwm_pca9685 as pca9685;
use pca9685::{ Channel, Pca9685, SlaveAddr };
use pca9685::{ Channel, Pca9685, Address };

fn main() {
let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
let address = SlaveAddr::default();
let address = Address::default();
let mut pwm = Pca9685::new(dev, address);
// This corresponds to a frequency of 60 Hz.
pwm.set_prescale(100).unwrap();
Expand Down
6 changes: 3 additions & 3 deletions examples/linux.rs
Expand Up @@ -3,12 +3,12 @@ extern crate linux_embedded_hal;
extern crate pwm_pca9685 as pca9685;

use linux_embedded_hal::I2cdev;
use pca9685::{Channel, Pca9685, SlaveAddr};
use pca9685::{Channel, Pca9685, Address};

fn main() {
let dev = I2cdev::new("/dev/i2c-1").unwrap();
let address = SlaveAddr::default();
let mut pwm = Pca9685::new(dev, address);
let address = Address::default();
let mut pwm = Pca9685::new(dev, address).unwrap();

// This corresponds to a frequency of 60 Hz.
pwm.set_prescale(100).unwrap();
Expand Down
41 changes: 25 additions & 16 deletions src/device_impl.rs
Expand Up @@ -2,7 +2,7 @@ use crate::{
config::{BitFlagMode1, BitFlagMode2, Config},
hal::{blocking::delay::DelayUs, blocking::i2c},
DisabledOutputValue, Error, OutputDriver, OutputLogicState, OutputStateChange, Pca9685,
ProgrammableAddress, Register, SlaveAddr,
ProgrammableAddress, Register, Address,
};
use nb;

Expand All @@ -11,12 +11,16 @@ where
I2C: i2c::Write<Error = E> + i2c::WriteRead<Error = E>,
{
/// Create a new instance of the device.
pub fn new(i2c: I2C, address: SlaveAddr) -> Self {
Pca9685 {
pub fn new<A: Into<Address>>(i2c: I2C, address: A) -> Result<Self, Error<E>> {
let a = address.into();

Self::check_address(a.0)?;

Ok(Pca9685 {
i2c,
address: address.address(),
address: a.0,
config: Config::default(),
}
})
}

/// Destroy driver instance, return I²C bus instance.
Expand Down Expand Up @@ -72,17 +76,17 @@ where
/// least 500us after the receiving the first `WouldBlock` error before
/// calling again to continue.
pub fn restart_nonblocking(&mut self) -> nb::Result<(), Error<E>> {
let mode1 = self.read_register(Register::MODE1)?;
let mode1 = self.read_register(Register::MODE1).map_err(nb::Error::Other)?;
let restart_high = (mode1 & BitFlagMode1::Restart as u8) != 0;
let sleep_high = (mode1 & BitFlagMode1::Sleep as u8) != 0;
if restart_high {
if sleep_high {
self.enable()?;
self.enable().map_err(nb::Error::Other)?;
return Err(nb::Error::WouldBlock);
} else {
let previous = self.config;
let config = previous.with_high(BitFlagMode1::Restart);
self.write_mode1(config)?;
self.write_mode1(config).map_err(nb::Error::Other)?;
self.config = previous;
}
}
Expand All @@ -94,20 +98,22 @@ where
/// Initially these are not enabled. Once you set this, you can call
/// `enable_programmable_address()` and then use `set_address()` to configure
/// the driver to use the new address.
pub fn set_programmable_address(
pub fn set_programmable_address<A: Into<Address>>(
&mut self,
address_type: ProgrammableAddress,
address: u8,
address: A,
) -> Result<(), Error<E>> {
self.check_address(address)?;
let a = address.into();

Self::check_address(a.0)?;
let reg = match address_type {
ProgrammableAddress::Subaddress1 => Register::SUBADDR1,
ProgrammableAddress::Subaddress2 => Register::SUBADDR2,
ProgrammableAddress::Subaddress3 => Register::SUBADDR3,
ProgrammableAddress::AllCall => Register::ALL_CALL_ADDR,
};
self.i2c
.write(self.address, &[reg, address])
.write(self.address, &[reg, a.0])
.map_err(Error::I2C)
}

Expand Down Expand Up @@ -145,13 +151,16 @@ where
/// This does not have any effect on the hardware and is useful when
/// switching between programmable addresses and the fixed hardware address
/// for communication.
pub fn set_address(&mut self, address: u8) -> Result<(), Error<E>> {
self.check_address(address)?;
self.address = address;
pub fn set_address<A: Into<Address>>(&mut self, address: A) -> Result<(), Error<E>> {
let a = address.into();

Self::check_address(a.0)?;
self.address = a.0;

Ok(())
}

fn check_address(&self, address: u8) -> Result<(), Error<E>> {
fn check_address(address: u8) -> Result<(), Error<E>> {
const LED_ALL_CALL: u8 = 0b111_0000;
// const SW_RESET: u8 = 0b000_0011; this gets absorbed by the high speed mode test
const HIGH_SPEED_MODE: u8 = 0b000_111;
Expand Down
50 changes: 25 additions & 25 deletions src/lib.rs
Expand Up @@ -76,12 +76,12 @@
//! ```no_run
//! extern crate linux_embedded_hal as hal;
//! extern crate pwm_pca9685 as pca9685;
//! use pca9685::{ Pca9685, SlaveAddr };
//! use pca9685::{ Pca9685, Address };
//!
//! # fn main() {
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let address = SlaveAddr::default();
//! let pwm = Pca9685::new(dev, address);
//! let address = Address::default();
//! let pwm = Pca9685::new(dev, address).unwrap();
//! // do something...
//!
//! // get the I2C device back
Expand All @@ -94,13 +94,13 @@
//! ```no_run
//! extern crate linux_embedded_hal as hal;
//! extern crate pwm_pca9685 as pca9685;
//! use pca9685::{ Pca9685, SlaveAddr };
//! use pca9685::{ Pca9685, Address };
//!
//! # fn main() {
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let (a5, a4, a3, a2, a1, a0) = (false, true, false, true, true, false);
//! let address = SlaveAddr::Alternative(a5, a4, a3, a2, a1, a0);
//! let pwm = Pca9685::new(dev, address);
//! let address = (a5, a4, a3, a2, a1, a0);
//! let pwm = Pca9685::new(dev, address).unwrap();
//! # }
//! ```
//!
Expand All @@ -115,12 +115,12 @@
//! ```no_run
//! extern crate linux_embedded_hal as hal;
//! extern crate pwm_pca9685 as pca9685;
//! use pca9685::{ Channel, Pca9685, SlaveAddr };
//! use pca9685::{ Channel, Pca9685, Address };
//!
//! # fn main() {
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let address = SlaveAddr::default();
//! let mut pwm = Pca9685::new(dev, address);
//! let address = Address::default();
//! let mut pwm = Pca9685::new(dev, address).unwrap();
//! pwm.set_prescale(100).unwrap();
//!
//! // Turn on channel 0 at 0 and off at 2047, which is 50% in the range `[0..4095]`.
Expand All @@ -144,12 +144,12 @@
//! ```no_run
//! extern crate linux_embedded_hal as hal;
//! extern crate pwm_pca9685 as pca9685;
//! use pca9685::{ Channel, Pca9685, SlaveAddr };
//! use pca9685::{ Channel, Pca9685, Address };
//!
//! # fn main() {
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let address = SlaveAddr::default();
//! let mut pwm = Pca9685::new(dev, address);
//! let address = Address::default();
//! let mut pwm = Pca9685::new(dev, address).unwrap();
//! pwm.set_prescale(100).unwrap();
//!
//! // Turn on channel 0 at 0
Expand All @@ -173,12 +173,12 @@
//! ```no_run
//! extern crate linux_embedded_hal as hal;
//! extern crate pwm_pca9685 as pca9685;
//! use pca9685::{ Channel, Pca9685, SlaveAddr };
//! use pca9685::{ Channel, Pca9685, Address };
//!
//! # fn main() {
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let address = SlaveAddr::default();
//! let mut pwm = Pca9685::new(dev, address);
//! let address = Address::default();
//! let mut pwm = Pca9685::new(dev, address).unwrap();
//!
//! // Turn channel 0 full on at 1024
//! pwm.set_channel_full_on(Channel::C0, 1024).unwrap();
Expand All @@ -203,12 +203,12 @@
//! ```no_run
//! extern crate linux_embedded_hal as hal;
//! extern crate pwm_pca9685 as pca9685;
//! use pca9685::{ Channel, Pca9685, SlaveAddr };
//! use pca9685::{ Channel, Pca9685, Address };
//!
//! # fn main() {
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let address = SlaveAddr::default();
//! let mut pwm = Pca9685::new(dev, address);
//! let address = Address::default();
//! let mut pwm = Pca9685::new(dev, address).unwrap();
//!
//! let mut on = [0; 16];
//! let mut off = [2047; 16];
Expand All @@ -227,12 +227,12 @@
//! ```no_run
//! extern crate linux_embedded_hal as hal;
//! extern crate pwm_pca9685 as pca9685;
//! use pca9685::{Channel, Pca9685, SlaveAddr, ProgrammableAddress};
//! use pca9685::{Channel, Pca9685, Address, ProgrammableAddress};
//!
//! # fn main() {
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let hardware_address = SlaveAddr::default();
//! let mut pwm = Pca9685::new(dev, hardware_address);
//! let hardware_address = Address::default();
//! let mut pwm = Pca9685::new(dev, hardware_address).unwrap();
//!
//! let subaddr1 = 0x71;
//! pwm.set_programmable_address(ProgrammableAddress::Subaddress1, subaddr1).unwrap();
Expand All @@ -243,7 +243,7 @@
//! pwm.set_channel_on_off(Channel::C0, 0, 2047).unwrap();
//!
//! // The device will also respond to the hardware address:
//! pwm.set_address(hardware_address.address()).unwrap();
//! pwm.set_address(hardware_address).unwrap();
//! pwm.set_channel_on_off(Channel::C0, 2047, 4095).unwrap();
//!
//! // when done you can also disable responding to the additional address:
Expand All @@ -256,11 +256,11 @@
//! ```no_run
//! extern crate linux_embedded_hal as hal;
//! extern crate pwm_pca9685 as pca9685;
//! use pca9685::{Channel, Pca9685, SlaveAddr};
//! use pca9685::{Channel, Pca9685, Address};
//!
//! # fn main() {
//! let dev = hal::I2cdev::new("/dev/i2c-1").unwrap();
//! let mut pwm = Pca9685::new(dev, SlaveAddr::default());
//! let mut pwm = Pca9685::new(dev, Address::default()).unwrap();
//!
//! pwm.set_channel_on_off(Channel::C0, 0, 2047).unwrap();
//! // Prepare for restart and put the device to sleep
Expand All @@ -285,5 +285,5 @@ mod device_impl;
mod types;
pub use crate::types::{
Channel, DisabledOutputValue, Error, OutputDriver, OutputLogicState, OutputStateChange,
Pca9685, ProgrammableAddress, SlaveAddr,
Pca9685, ProgrammableAddress, Address,
};
59 changes: 28 additions & 31 deletions src/types.rs
Expand Up @@ -176,43 +176,40 @@ pub enum ProgrammableAddress {
AllCall,
}

/// Possible slave addresses
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum SlaveAddr {
/// Default slave address
Default,
/// Alternative slave address providing bit values for A5, A4, A3, A2, A1 and A0
Alternative(bool, bool, bool, bool, bool, bool),
}
/// I2C device address
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Address(pub(crate) u8);

impl Default for SlaveAddr {
/// Default slave address
/// Default device address
impl Default for Address {
fn default() -> Self {
SlaveAddr::Default
Address(DEVICE_BASE_ADDRESS)
}
}

impl SlaveAddr {
/// Get the I2C slave address
///
/// This is useful when switching between programmable addresses and the
/// fixed hardware slave address.
pub fn address(self) -> u8 {
match self {
SlaveAddr::Default => DEVICE_BASE_ADDRESS,
SlaveAddr::Alternative(a5, a4, a3, a2, a1, a0) => {
DEVICE_BASE_ADDRESS
| ((a5 as u8) << 5)
| ((a4 as u8) << 4)
| ((a3 as u8) << 3)
| ((a2 as u8) << 2)
| ((a1 as u8) << 1)
| a0 as u8
}
}
/// Support custom (integer) addresses
impl From<u8> for Address {
fn from(a: u8) -> Self {
Address(a)
}
}

/// Compute device address from address bits
impl From<(bool, bool, bool, bool, bool, bool)> for Address {
fn from(a: (bool, bool, bool, bool, bool, bool)) -> Self {
Address(
DEVICE_BASE_ADDRESS
| ((a.0 as u8) << 5)
| ((a.1 as u8) << 4)
| ((a.2 as u8) << 3)
| ((a.3 as u8) << 2)
| ((a.4 as u8) << 1)
| a.5 as u8
)
}
}


#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -233,7 +230,7 @@ mod tests {

#[test]
fn can_get_default_address() {
let addr = SlaveAddr::default();
assert_eq!(DEVICE_BASE_ADDRESS, addr.address());
let addr = Address::default();
assert_eq!(DEVICE_BASE_ADDRESS, addr.0);
}
}

0 comments on commit 2cb2cec

Please sign in to comment.