Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for reading back values of ON/OFF states #12

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
67 changes: 64 additions & 3 deletions src/channels.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
use crate::{hal, Channel, Error, Pca9685, Register};

// Only the 12 low bits of the ON/OFF registers contain the current value.
static ON_OFF_BITMASK: u16 = 0b0000111111111111;
UhhhWaitWhat marked this conversation as resolved.
Show resolved Hide resolved
// The next bit then contains the overriding "full" state for the register.
static FULL_ON_OFF_BITMASK: u16 = 0b0001000000000000;
UhhhWaitWhat marked this conversation as resolved.
Show resolved Hide resolved

impl<I2C, E> Pca9685<I2C>
where
I2C: hal::blocking::i2c::Write<Error = E> + hal::blocking::i2c::WriteRead<Error = E>,
Expand All @@ -17,6 +22,12 @@ where
self.write_double_register(reg, value)
}

/// Get the `ON` counter for the selected channel.
pub fn get_channel_on(&mut self, channel: Channel) -> Result<u16, Error<E>> {
let reg = get_register_on(channel);
Ok(self.read_double_register(reg)? & ON_OFF_BITMASK)
}

/// Set the `OFF` counter for the selected channel.
pub fn set_channel_off(&mut self, channel: Channel, value: u16) -> Result<(), Error<E>> {
if value > 4095 {
Expand All @@ -26,6 +37,12 @@ where
self.write_double_register(reg, value)
}

/// Get the `OFF` counter for the selected channel.
pub fn get_channel_off(&mut self, channel: Channel) -> Result<u16, Error<E>> {
let reg = get_register_off(channel);
Ok(self.read_double_register(reg)? & ON_OFF_BITMASK)
}

/// Set the `ON` and `OFF` counters for the selected channel.
///
/// Note that the full off setting takes precedence over the `on` settings.
Expand All @@ -44,6 +61,14 @@ where
self.write_two_double_registers(reg, on, off)
}

/// Get the `ON` and `OFF` counters for the selected channel.
pub fn get_channel_on_off(&mut self, channel: Channel) -> Result<(u16, u16), Error<E>> {
let reg = get_register_on(channel);
let (on, off) = self.read_two_double_registers(reg)?;

Ok((on & ON_OFF_BITMASK, off & ON_OFF_BITMASK))
}

/// Set the channel always on.
///
/// The turning on is delayed by the value argument.
Expand All @@ -56,10 +81,16 @@ where
return Err(Error::InvalidInputData);
}
let reg = get_register_on(channel);
let value = value | 0b0001_0000_0000_0000;
let value = value | FULL_ON_OFF_BITMASK;
self.write_double_register(reg, value)
}

/// Get the channels always on state.
pub fn get_channel_full_on(&mut self, channel: Channel) -> Result<bool, Error<E>> {
let reg = get_register_on(channel);
Ok(self.read_double_register(reg)? & FULL_ON_OFF_BITMASK == FULL_ON_OFF_BITMASK)
}

/// Set the channel always off.
///
/// This takes precedence over the `on` settings and can be cleared by setting
Expand All @@ -69,8 +100,13 @@ where
/// further details.
pub fn set_channel_full_off(&mut self, channel: Channel) -> Result<(), Error<E>> {
let reg = get_register_off(channel);
let value = 0b0001_0000_0000_0000;
self.write_double_register(reg, value)
self.write_double_register(reg, FULL_ON_OFF_BITMASK)
}

/// Get the channels always off state.
pub fn get_channel_full_off(&mut self, channel: Channel) -> Result<bool, Error<E>> {
let reg = get_register_off(channel);
Ok(self.read_double_register(reg)? & FULL_ON_OFF_BITMASK == FULL_ON_OFF_BITMASK)
}

/// Set the `ON` and `OFF` counter for each channel at once.
Expand All @@ -94,6 +130,31 @@ where
self.enable_auto_increment()?;
self.i2c.write(self.address, &data).map_err(Error::I2C)
}

/// Get the `ON` and `OFF` counter for each channel at once.
///
/// The index of the value in the arrays corresponds to the channel: 0-15.
/// Note that the full off setting takes precedence over the `on` settings.
/// See section 7.3.3 "LED output and PWM control" of the datasheet for
/// further details.
pub fn get_all_on_off(&mut self) -> Result<([u16; 16], [u16; 16]), Error<E>> {
let mut data = [0; 64];

self.enable_auto_increment()?;
self.i2c
.write_read(self.address, &[Register::C0_ON_L], &mut data)
.map_err(Error::I2C)?;

let mut on = [0u16; 16];
let mut off = [0u16; 16];

for (i, chunk) in data.chunks(4).enumerate() {
on[i] = u16::from_le_bytes([chunk[0], chunk[1]]) & ON_OFF_BITMASK;
off[i] = u16::from_le_bytes([chunk[2], chunk[3]]) & ON_OFF_BITMASK;
}

Ok((on, off))
}
}

macro_rules! get_register {
Expand Down
28 changes: 28 additions & 0 deletions src/register_access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,32 @@ where
.map_err(Error::I2C)
.and(Ok(data[0]))
}

pub(crate) fn read_double_register(&mut self, address: u8) -> Result<u16, Error<E>> {
let mut data = [0; 2];

self.enable_auto_increment()?;
self.i2c
.write_read(self.address, &[address], &mut data)
.map_err(Error::I2C)?;

Ok(u16::from_le_bytes(data))
}

pub(crate) fn read_two_double_registers(
&mut self,
address: u8,
) -> Result<(u16, u16), Error<E>> {
let mut data = [0; 4];

self.enable_auto_increment()?;
self.i2c
.write_read(self.address, &[address], &mut data)
.map_err(Error::I2C)?;

Ok((
u16::from_le_bytes([data[0], data[1]]),
u16::from_le_bytes([data[2], data[3]]),
))
}
}
109 changes: 109 additions & 0 deletions tests/channels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,17 @@ macro_rules! channels_test {
destroy(pwm);
}

#[test]
fn can_get_channel_on_min() {
let trans = [
I2cTrans::write(DEV_ADDR, vec![Register::MODE1, MODE1_AI]),
I2cTrans::write_read(DEV_ADDR, vec![Register::$reg_on], vec![0, 0])
];
let mut pwm = new(&trans);
assert_eq!(pwm.get_channel_on(Channel::$channel).unwrap(), 0);
destroy(pwm);
}

#[test]

fn can_set_channel_on_max() {
Expand All @@ -144,6 +155,17 @@ macro_rules! channels_test {
destroy(pwm);
}

#[test]
fn can_get_channel_on_max() {
let trans = [
I2cTrans::write(DEV_ADDR, vec![Register::MODE1, MODE1_AI]),
I2cTrans::write_read(DEV_ADDR, vec![Register::$reg_on], vec![255, 255])
];
let mut pwm = new(&trans);
assert_eq!(pwm.get_channel_on(Channel::$channel).unwrap(), 4095);
destroy(pwm);
}

#[test]
fn can_set_channel_off_min() {
let trans = [
Expand All @@ -155,6 +177,17 @@ macro_rules! channels_test {
destroy(pwm);
}

#[test]
fn can_get_channel_off_min() {
let trans = [
I2cTrans::write(DEV_ADDR, vec![Register::MODE1, MODE1_AI]),
I2cTrans::write_read(DEV_ADDR, vec![Register::$reg_off], vec![0, 0])
];
let mut pwm = new(&trans);
assert_eq!(pwm.get_channel_off(Channel::$channel).unwrap(), 0);
destroy(pwm);
}

#[test]
fn can_set_channel_off_max() {
let trans = [
Expand All @@ -166,6 +199,17 @@ macro_rules! channels_test {
destroy(pwm);
}

#[test]
fn can_get_channel_off_max() {
let trans = [
I2cTrans::write(DEV_ADDR, vec![Register::MODE1, MODE1_AI]),
I2cTrans::write_read(DEV_ADDR, vec![Register::$reg_off], vec![255, 255])
];
let mut pwm = new(&trans);
assert_eq!(pwm.get_channel_off(Channel::$channel).unwrap(), 4095);
destroy(pwm);
}

#[test]

fn can_set_channel_full_on_min() {
Expand All @@ -190,6 +234,17 @@ macro_rules! channels_test {
destroy(pwm);
}

#[test]
fn can_get_channel_full_on() {
let trans = [
I2cTrans::write(DEV_ADDR, vec![Register::MODE1, MODE1_AI]),
I2cTrans::write_read(DEV_ADDR, vec![Register::$reg_on], vec![0, 0b0001_0000])
];
let mut pwm = new(&trans);
assert!(pwm.get_channel_full_on(Channel::$channel).unwrap());
destroy(pwm);
}

#[test]

fn can_set_channel_full_off() {
Expand All @@ -213,6 +268,17 @@ macro_rules! channels_test {
pwm.set_channel_on_off(Channel::$channel, 0x102, 0x304).unwrap();
destroy(pwm);
}

#[test]
fn can_get_channel_full_off() {
let trans = [
I2cTrans::write(DEV_ADDR, vec![Register::MODE1, MODE1_AI]),
I2cTrans::write_read(DEV_ADDR, vec![Register::$reg_off], vec![0, 0b0001_0000])
];
let mut pwm = new(&trans);
assert!(pwm.get_channel_full_off(Channel::$channel).unwrap());
destroy(pwm);
}
}
)*
};
Expand Down Expand Up @@ -360,3 +426,46 @@ fn can_set_all_on_off() {
pwm.set_all_on_off(&on, &off).unwrap();
destroy(pwm);
}

#[test]
fn can_get_all_on_off() {
#[rustfmt::skip]
let trans = [
I2cTrans::write(DEV_ADDR, vec![Register::MODE1, MODE1_AI]),
I2cTrans::write_read(
DEV_ADDR,
vec![Register::C0_ON_L],
vec![
0, 0, 255, 255,
1, 0, 0, 0,
0, 0, 1, 0,
255, 255, 0, 0,
0, 0, 255, 255,
0, 0, 0, 0,
0, 0, 1, 0,
0, 0, 0, 0,
255, 255, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
],
),
];

let mut pwm = new(&trans);

#[rustfmt::skip]
assert_eq!(
pwm.get_all_on_off().unwrap(),
(
[ 0, 1, 0, 4095, 0, 0, 0, 0, 4095, 0, 0, 0, 0, 0, 0, 0],
[4095, 0, 1, 0, 4095, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
)
);

destroy(pwm);
}