Skip to content

Commit

Permalink
Add async support
Browse files Browse the repository at this point in the history
  • Loading branch information
nils-van-zuijlen authored and eldruin committed Feb 2, 2024
1 parent 12fc793 commit 0715e71
Show file tree
Hide file tree
Showing 8 changed files with 231 additions and 65 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
rust: [stable, 1.62.0]
rust: [stable, 1.75.0]
TARGET:
- x86_64-unknown-linux-gnu
- x86_64-unknown-linux-musl
Expand Down Expand Up @@ -108,4 +108,4 @@ jobs:
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
path-to-lcov: './lcov.info'
path-to-lcov: './lcov.info'
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Added
- 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.

### Changed
- [breaking-change] Removed `Default` implementation for `Pca9685` struct.
- Raised MSRV to 1.62.0.
- Raised MSRV to 1.75.0.
- [breaking-change] Updated `embedded-hal` dependency to 1.0.

## [0.3.1] - 2021-07-14
Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ edition = "2018"
[dependencies]
embedded-hal = "1.0"
nb = "1"
embedded-hal-async = { version = "1", optional = true }
maybe-async-cfg = "0.2.3"

[dev-dependencies]
linux-embedded-hal = "0.4"
Expand All @@ -33,3 +35,4 @@ lto = true

[features]
std = []
async = ["dep:embedded-hal-async"]
51 changes: 50 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@

[![crates.io](https://img.shields.io/crates/v/pwm-pca9685.svg)](https://crates.io/crates/pwm-pca9685)
[![Docs](https://docs.rs/pwm-pca9685/badge.svg)](https://docs.rs/pwm-pca9685)
![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.62+-blue.svg)
![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.75+-blue.svg)
[![Build Status](https://github.com/eldruin/pwm-pca9685-rs/workflows/Build/badge.svg)](https://github.com/eldruin/pwm-pca9685-rs/actions?query=workflow%3ABuild)
[![Coverage Status](https://coveralls.io/repos/github/eldruin/pwm-pca9685-rs/badge.svg?branch=master)](https://coveralls.io/github/eldruin/pwm-pca9685-rs?branch=master)

This is a platform agnostic Rust driver for the PCA9685 PWM/Servo/LED
controller, based on the [`embedded-hal`] traits.
This driver also supports the [`embedded-hal-async`] traits if the `async` feature is enabled.

[`embedded-hal`]: https://github.com/rust-embedded/embedded-hal
[`embedded-hal-async`]: https://github.com/rust-embedded/embedded-hal-async

This driver allows you to:
- Enable/disable the device. See: `enable()`.
Expand Down Expand Up @@ -91,6 +93,53 @@ fn main() {
}
```

The same settings, but async with the [Embassy](https://embassy.dev/) framework on an RP2040:

```toml
# Cargo.toml
pwm-pca9685 = { version = "0.3.1", features = ["async"] }
```

```rust
#![no_std]
#![no_main]

use embassy_executor::Spawner;
use embassy_rp::{bind_interrupts, i2c};
use embassy_rp::peripherals::I2C0;
use embassy_time::Timer;
use panic_halt as _;
use pwm_pca9685::{Address, Channel, Pca9685};

bind_interrupts!(struct Irqs {
I2C0_IRQ => i2c::InterruptHandler<I2C0>;
});

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_rp::init(Default::default());
let i2c = i2c::I2c::new_async(p.I2C0, p.PIN_1, p.PIN_0, Irqs, i2c::Config::default());

let address = Address::default();
let mut pwm = Pca9685::new(dev, address).await.unwrap();

// This corresponds to a frequency of 60 Hz.
pwm.set_prescale(100).await.unwrap();

// It is necessary to enable the device.
pwm.enable().await.unwrap();

// Turn on channel 0 at 0.
pwm.set_channel_on(Channel::C0, 0).await.unwrap();

// Turn off channel 0 at 2047, which is 50% in
// the range `[0..4095]`.
pwm.set_channel_off(Channel::C0, 2047).await.unwrap();

let _dev = pwm.destroy(); // Get the I2C device back
}
```

## Support

For questions, issues, feature requests, and other changes, please file an
Expand Down
54 changes: 39 additions & 15 deletions src/channels.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,50 @@
use crate::{hal, Channel, Error, Pca9685, Register};
use crate::{Channel, Error, Pca9685, Register};

#[cfg(not(feature = "async"))]
use embedded_hal::i2c::I2c;
#[cfg(feature = "async")]
use embedded_hal_async::i2c::I2c as AsyncI2c;

#[maybe_async_cfg::maybe(
sync(
cfg(not(feature = "async")),
self = "Pca9685",
idents(AsyncI2c(sync = "I2c"))
),
async(feature = "async", keep_self)
)]
impl<I2C, E> Pca9685<I2C>
where
I2C: hal::i2c::I2c<Error = E>,
I2C: AsyncI2c<Error = E>,
{
/// Set the `ON` counter for the selected channel.
///
/// 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 set_channel_on(&mut self, channel: Channel, value: u16) -> Result<(), Error<E>> {
pub async fn set_channel_on(&mut self, channel: Channel, value: u16) -> Result<(), Error<E>> {
if value > 4095 {
return Err(Error::InvalidInputData);
}
let reg = get_register_on(channel);
self.write_double_register(reg, value)
self.write_double_register(reg, value).await
}

/// Set the `OFF` counter for the selected channel.
pub fn set_channel_off(&mut self, channel: Channel, value: u16) -> Result<(), Error<E>> {
pub async fn set_channel_off(&mut self, channel: Channel, value: u16) -> Result<(), Error<E>> {
if value > 4095 {
return Err(Error::InvalidInputData);
}
let reg = get_register_off(channel);
self.write_double_register(reg, value)
self.write_double_register(reg, value).await
}

/// Set the `ON` and `OFF` counters for the selected channel.
///
/// 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 set_channel_on_off(
pub async fn set_channel_on_off(
&mut self,
channel: Channel,
on: u16,
Expand All @@ -41,7 +54,7 @@ where
return Err(Error::InvalidInputData);
}
let reg = get_register_on(channel);
self.write_two_double_registers(reg, on, off)
self.write_two_double_registers(reg, on, off).await
}

/// Set the channel always on.
Expand All @@ -51,13 +64,17 @@ where
///
/// See section 7.3.3 "LED output and PWM control" of the datasheet for
/// further details.
pub fn set_channel_full_on(&mut self, channel: Channel, value: u16) -> Result<(), Error<E>> {
pub async fn set_channel_full_on(
&mut self,
channel: Channel,
value: u16,
) -> Result<(), Error<E>> {
if value > 4095 {
return Err(Error::InvalidInputData);
}
let reg = get_register_on(channel);
let value = value | 0b0001_0000_0000_0000;
self.write_double_register(reg, value)
self.write_double_register(reg, value).await
}

/// Set the channel always off.
Expand All @@ -67,10 +84,10 @@ where
///
/// See section 7.3.3 "LED output and PWM control" of the datasheet for
/// further details.
pub fn set_channel_full_off(&mut self, channel: Channel) -> Result<(), Error<E>> {
pub async 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, value).await
}

/// Set the `ON` and `OFF` counter for each channel at once.
Expand All @@ -79,7 +96,11 @@ where
/// 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 set_all_on_off(&mut self, on: &[u16; 16], off: &[u16; 16]) -> Result<(), Error<E>> {
pub async fn set_all_on_off(
&mut self,
on: &[u16; 16],
off: &[u16; 16],
) -> Result<(), Error<E>> {
let mut data = [0; 65];
data[0] = Register::C0_ON_L;
for (i, (on, off)) in on.iter().zip(off).enumerate() {
Expand All @@ -91,8 +112,11 @@ where
data[i * 4 + 3] = *off as u8;
data[i * 4 + 4] = (*off >> 8) as u8;
}
self.enable_auto_increment()?;
self.i2c.write(self.address, &data).map_err(Error::I2C)
self.enable_auto_increment().await?;
self.i2c
.write(self.address, &data)
.await
.map_err(Error::I2C)
}
}

Expand Down
Loading

0 comments on commit 0715e71

Please sign in to comment.