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 set_all_channels() for setting on, off counter values and full-on and full-off in one transaction #18

Merged
merged 6 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Added `Display`, `Error` and common trait implementations for `Error<E>`.
- Added common trait implementations for types.
- Async support based on `embedded-hal-async` 1.0 behind `async` feature flag.
- Added `set_all_channels()`.

### Changed
- [breaking-change] Removed `Default` implementation for `Pca9685` struct.
Expand Down
35 changes: 34 additions & 1 deletion src/channels.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Channel, Error, Pca9685, Register};
use crate::{types::ChannelOnOffControl, Channel, Error, Pca9685, Register};

#[cfg(not(feature = "async"))]
use embedded_hal::i2c::I2c;
Expand Down Expand Up @@ -118,6 +118,39 @@ where
.await
.map_err(Error::I2C)
}

/// Set the PWM control registers for each channel at once.
///
/// This allows to set all `on` and `off` counter values, as well as the
/// full-on and full-off bit in a single I2C transaction.
/// The index of the value in the array corresponds to the channel: 0-15.
///
/// See section 7.3.3 "LED output and PWM control" of the datasheet for
/// further details.
pub async fn set_all_channels(
&mut self,
values: &[ChannelOnOffControl; 16],
) -> Result<(), Error<E>> {
const FULL_ON_OFF: u8 = 0b0001_0000;
let mut data = [0; 65];
data[0] = Register::C0_ON_L;
for (i, channel_value) in values.iter().enumerate() {
if channel_value.on > 4095 || channel_value.off > 4095 {
return Err(Error::InvalidInputData);
}
data[i * 4 + 1] = channel_value.on as u8;
data[i * 4 + 2] =
(channel_value.on >> 8) as u8 | (FULL_ON_OFF * channel_value.full_on as u8);
data[i * 4 + 3] = channel_value.off as u8;
data[i * 4 + 4] =
(channel_value.off >> 8) as u8 | (FULL_ON_OFF * channel_value.full_off as u8);
}
self.enable_auto_increment().await?;
self.i2c
.write(self.address, &data)
.await
.map_err(Error::I2C)
}
}

macro_rules! get_register {
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ mod channels;
mod device_impl;
mod types;
pub use crate::types::{
Address, Channel, DisabledOutputValue, Error, OutputDriver, OutputLogicState,
OutputStateChange, Pca9685, ProgrammableAddress,
Address, Channel, ChannelOnOffControl, DisabledOutputValue, Error, OutputDriver,
OutputLogicState, OutputStateChange, Pca9685, ProgrammableAddress,
};
pub use nb;
13 changes: 13 additions & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,19 @@ impl From<(bool, bool, bool, bool, bool, bool)> for Address {
}
}

/// PWM control values for a single channel
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ChannelOnOffControl {
/// Counter value to switch the channel on during each PWM cycle
pub on: u16,
/// Counter value to switch the channel off during each PWM cycle
pub off: u16,
/// Set the channel to full-on. In this case, the `off` value is ignored.
pub full_on: bool,
/// Set the channel to full-off. Takes precedence over `on` an `full_on`.
pub full_off: bool,
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
199 changes: 198 additions & 1 deletion tests/channels.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use embedded_hal_mock::eh1::i2c::Transaction as I2cTrans;
use pwm_pca9685::Channel;
use pwm_pca9685::{Channel, ChannelOnOffControl};
use std::convert::TryFrom;

mod common;
Expand Down Expand Up @@ -95,6 +95,24 @@ invalid_test!(
&[4096; 16]
);

invalid_test!(
cannot_set_all_channels_invalid_value_on,
set_all_channels,
&[ChannelOnOffControl {
on: 4096,
..Default::default()
}; 16]
);

invalid_test!(
cannot_set_all_channels_invalid_value_off,
set_all_channels,
&[ChannelOnOffControl {
off: 4096,
..Default::default()
}; 16]
);

#[test]
fn sets_autoincrement_just_once() {
let trans = [
Expand Down Expand Up @@ -360,3 +378,182 @@ fn can_set_all_on_off() {
pwm.set_all_on_off(&on, &off).unwrap();
destroy(pwm);
}

#[test]

fn can_set_all_channels() {
const FULL_ON_OFF: u8 = 0b0001_0000;
let trans = [
I2cTrans::write(DEV_ADDR, vec![Register::MODE1, MODE1_AI]),
I2cTrans::write(
DEV_ADDR,
vec![
Register::C0_ON_L,
1,
1,
3,
3,
2,
1,
4,
3,
3,
1,
5,
3,
4,
1,
6,
3,
5,
1,
7,
3,
6,
1,
8,
3,
7,
1,
9,
3,
8,
1,
0,
4,
9,
1,
1,
4,
0,
2,
2,
4,
1,
2,
3,
4,
2,
2,
4,
4,
3,
2,
5,
4,
4,
2,
6,
4 | FULL_ON_OFF,
5,
2 | FULL_ON_OFF,
7,
4,
6,
2 | FULL_ON_OFF,
8,
4 | FULL_ON_OFF,
],
),
];
let mut pwm = new(&trans);
let values = [
ChannelOnOffControl {
on: 0x101,
off: 0x303,
..Default::default()
},
ChannelOnOffControl {
on: 0x102,
off: 0x304,
full_on: false,
full_off: false,
},
ChannelOnOffControl {
on: 0x103,
off: 0x305,
full_on: false,
full_off: false,
},
ChannelOnOffControl {
on: 0x104,
off: 0x306,
full_on: false,
full_off: false,
},
ChannelOnOffControl {
on: 0x105,
off: 0x307,
full_on: false,
full_off: false,
},
ChannelOnOffControl {
on: 0x106,
off: 0x308,
full_on: false,
full_off: false,
},
ChannelOnOffControl {
on: 0x107,
off: 0x309,
full_on: false,
full_off: false,
},
ChannelOnOffControl {
on: 0x108,
off: 0x400,
full_on: false,
full_off: false,
},
ChannelOnOffControl {
on: 0x109,
off: 0x401,
full_on: false,
full_off: false,
},
ChannelOnOffControl {
on: 0x200,
off: 0x402,
full_on: false,
full_off: false,
},
ChannelOnOffControl {
on: 0x201,
off: 0x403,
full_on: false,
full_off: false,
},
ChannelOnOffControl {
on: 0x202,
off: 0x404,
full_on: false,
full_off: false,
},
ChannelOnOffControl {
on: 0x203,
off: 0x405,
full_on: false,
full_off: false,
},
ChannelOnOffControl {
on: 0x204,
off: 0x406,
full_on: false,
full_off: true,
},
ChannelOnOffControl {
on: 0x205,
off: 0x407,
full_on: true,
full_off: false,
},
ChannelOnOffControl {
on: 0x206,
off: 0x408,
full_on: true,
full_off: true,
},
];
pwm.set_all_channels(&values).unwrap();
destroy(pwm);
}