-
Notifications
You must be signed in to change notification settings - Fork 17
Added asynchronous pwm functionality #25
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
Open
lucavezoc
wants to merge
20
commits into
rust-embedded:master
Choose a base branch
from
lucavezoc:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 19 commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
bd96def
added get capture
1b03a0b
changed capture function to return tuple
5a0ea58
moved async functions to separate file
lucavezoc 2421fca
added missing await
lucavezoc 62cbb5d
added more functions to async_pwm
lucavezoc 9fa6a6d
added sync all to functions with write all
lucavezoc f5c02fa
added sync all to functions with write all
lucavezoc 613ca49
Some trivial simplifications
eldruin a1c9a96
Remove implementation of deprecated Error::description function
eldruin f3ea21a
Update URLs
eldruin cc21cdb
Improve metadata
eldruin 4827262
Update readme
eldruin 0e27540
Add CI
eldruin af5d6bb
Remove Travis CI
eldruin 5dca9a2
Fix .github/CODEOWNERS
eldruin 0bf20f7
Fix bors
eldruin e67f6ae
Switch to GHMQ
eldruin 9b74df2
Merge branch 'rust-embedded:master' into master
lucavezoc f78a342
edition 2021
lucavezoc 6e8bd12
use only necessary tokio features
lucavezoc File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,252 @@ | ||
use std::str::FromStr; | ||
use tokio::fs; | ||
use tokio::fs::File; | ||
use tokio::fs::OpenOptions; | ||
use tokio::io::AsyncReadExt; | ||
use tokio::io::AsyncWriteExt; | ||
use tokio::macros::support::Future; | ||
|
||
use crate::error; | ||
use crate::Error; | ||
|
||
#[derive(Debug)] | ||
pub struct PwmAsync { | ||
chip: PwmChipAsync, | ||
number: u32, | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct PwmChipAsync { | ||
pub number: u32, | ||
} | ||
|
||
/// Open the specified entry name as a writable file | ||
async fn pwm_file_wo(chip: &PwmChipAsync, pin: u32, name: &str) -> Result<File, error::Error> { | ||
let f = OpenOptions::new() | ||
.write(true) | ||
.open(format!( | ||
"/sys/class/pwm/pwmchip{}/pwm{}/{}", | ||
chip.number, pin, name | ||
)) | ||
.await?; | ||
Ok(f) | ||
} | ||
|
||
/// Open the specified entry name as a readable file | ||
async fn pwm_file_ro(chip: &PwmChipAsync, pin: u32, name: &str) -> Result<File, error::Error> { | ||
let f = File::open(format!( | ||
"/sys/class/pwm/pwmchip{}/pwm{}/{}", | ||
chip.number, pin, name | ||
)) | ||
.await?; | ||
Ok(f) | ||
} | ||
|
||
/// Get the u32 value from the given entry | ||
async fn pwm_file_parse<T: FromStr>( | ||
chip: &PwmChipAsync, | ||
pin: u32, | ||
name: &str, | ||
) -> Result<T, error::Error> { | ||
let mut s = String::with_capacity(10); | ||
let mut f = pwm_file_ro(chip, pin, name).await?; | ||
f.read_to_string(&mut s).await?; | ||
match s.trim().parse::<T>() { | ||
Ok(r) => Ok(r), | ||
Err(_) => Err(Error::Unexpected(format!( | ||
"Unexpeted value file contents: {:?}", | ||
s | ||
))), | ||
} | ||
} | ||
|
||
/// Get the two u32 from capture file descriptor | ||
async fn pwm_capture_parse<T: FromStr>( | ||
chip: &PwmChipAsync, | ||
pin: u32, | ||
name: &str, | ||
) -> Result<Vec<T>, error::Error> { | ||
let mut s = String::with_capacity(10); | ||
let mut f = pwm_file_ro(chip, pin, name).await?; | ||
f.read_to_string(&mut s).await?; | ||
s = s.trim().to_string(); | ||
let capture = s.split_whitespace().collect::<Vec<_>>(); | ||
let mut vec: Vec<T> = vec![]; | ||
for s in capture.iter() { | ||
if let Ok(j) = s.parse::<T>() { | ||
vec.push(j); | ||
} | ||
} | ||
Ok(vec) | ||
} | ||
|
||
impl PwmChipAsync { | ||
pub async fn new(number: u32) -> Result<PwmChipAsync, error::Error> { | ||
fs::metadata(&format!("/sys/class/pwm/pwmchip{}", number)).await?; | ||
Ok(PwmChipAsync { number: number }) | ||
} | ||
|
||
pub async fn count(&self) -> Result<u32, error::Error> { | ||
let npwm_path = format!("/sys/class/pwm/pwmchip{}/npwm", self.number); | ||
let mut npwm_file = File::open(&npwm_path).await?; | ||
let mut s = String::new(); | ||
npwm_file.read_to_string(&mut s).await?; | ||
match s.parse::<u32>() { | ||
Ok(n) => Ok(n), | ||
Err(_) => Err(Error::Unexpected(format!( | ||
"Unexpected npwm contents: {:?}", | ||
s | ||
))), | ||
} | ||
} | ||
|
||
pub async fn export(&self, number: u32) -> Result<(), error::Error> { | ||
// only export if not already exported | ||
if fs::metadata(&format!( | ||
"/sys/class/pwm/pwmchip{}/pwm{}", | ||
self.number, number | ||
)) | ||
.await | ||
.is_err() | ||
{ | ||
let path = format!("/sys/class/pwm/pwmchip{}/export", self.number); | ||
let mut export_file = File::create(&path).await?; | ||
let _ = export_file | ||
.write_all(format!("{}", number).as_bytes()) | ||
.await; | ||
let _ = export_file.sync_all().await; | ||
} | ||
Ok(()) | ||
} | ||
|
||
pub async fn unexport(&self, number: u32) -> Result<(), error::Error> { | ||
if fs::metadata(&format!( | ||
"/sys/class/pwm/pwmchip{}/pwm{}", | ||
self.number, number | ||
)) | ||
.await | ||
.is_ok() | ||
{ | ||
let path = format!("/sys/class/pwm/pwmchip{}/unexport", self.number); | ||
let mut export_file = File::create(&path).await?; | ||
let _ = export_file | ||
.write_all(format!("{}", number).as_bytes()) | ||
.await; | ||
let _ = export_file.sync_all().await; | ||
} | ||
Ok(()) | ||
} | ||
} | ||
impl PwmAsync { | ||
/// Create a new Pwm wiht the provided chip/number | ||
/// | ||
/// This function does not export the Pwm pin | ||
pub async fn new(chip: u32, number: u32) -> Result<PwmAsync, error::Error> { | ||
let chip: PwmChipAsync = PwmChipAsync::new(chip).await?; | ||
Ok(PwmAsync { | ||
chip: chip, | ||
number: number, | ||
}) | ||
} | ||
|
||
/// Run a closure with the GPIO exported | ||
#[inline] | ||
pub async fn with_exported<F>( | ||
&self, | ||
closure: impl Future<Output = F>, | ||
) -> Result<(), error::Error> | ||
where | ||
F: FnOnce() -> Result<(), error::Error>, | ||
{ | ||
self.export().await?; | ||
let y = closure.await; | ||
match y() { | ||
Ok(()) => self.unexport().await, | ||
Err(e) => match self.unexport().await { | ||
Ok(()) => Err(e), | ||
Err(ue) => Err(error::Error::Unexpected(format!( | ||
"Failed unexporting due to:\n{}\nwhile handling:\n{}", | ||
ue, e | ||
))), | ||
}, | ||
} | ||
} | ||
|
||
/// Export the Pwm for use | ||
pub async fn export(&self) -> Result<(), error::Error> { | ||
self.chip.export(self.number).await | ||
} | ||
|
||
/// Unexport the PWM | ||
pub async fn unexport(&self) -> Result<(), error::Error> { | ||
self.chip.unexport(self.number).await | ||
} | ||
|
||
/// Query the state of enable for a given PWM pin | ||
pub async fn get_enabled(&self) -> Result<bool, error::Error> { | ||
pwm_file_parse::<u32>(&self.chip, self.number, "enable") | ||
.await | ||
.map(|enable_state| match enable_state { | ||
1 => true, | ||
0 => false, | ||
_ => panic!("enable != 1|0 should be unreachable"), | ||
}) | ||
} | ||
|
||
/// Get the capture | ||
pub async fn get_capture(&self) -> Result<(u32, u32), error::Error> { | ||
let t = pwm_capture_parse::<u32>(&self.chip, self.number, "capture").await?; | ||
if t.len() == 2 { | ||
Ok((t[0], t[1])) | ||
} else { | ||
Err(error::Error::Unexpected(format!("Failed exporting"))) | ||
} | ||
} | ||
|
||
/// Get the currently configured duty_cycle as percentage of period | ||
pub async fn get_duty_cycle_async(&self) -> Result<f32, error::Error> { | ||
Ok((self.get_duty_cycle_ns().await? as f32) / (self.get_period_ns().await? as f32)) | ||
} | ||
|
||
/// Get the currently configured duty_cycle in nanoseconds | ||
pub async fn get_duty_cycle_ns(&self) -> Result<u32, error::Error> { | ||
pwm_file_parse::<u32>(&self.chip, self.number, "duty_cycle").await | ||
} | ||
|
||
/// Get the currently configured period in nanoseconds | ||
pub async fn get_period_ns(&self) -> Result<u32, error::Error> { | ||
pwm_file_parse::<u32>(&self.chip, self.number, "period").await | ||
} | ||
|
||
/// The period of the PWM signal in Nanoseconds | ||
pub async fn set_period_ns(&self, period_ns: u32) -> Result<(), error::Error> { | ||
let mut period_file = pwm_file_wo(&self.chip, self.number, "period").await?; | ||
period_file | ||
.write_all(format!("{}", period_ns).as_bytes()) | ||
.await?; | ||
let _ = period_file.sync_all().await; | ||
Ok(()) | ||
} | ||
|
||
/// The active time of the PWM signal | ||
/// | ||
/// Value is in nanoseconds and must be less than the period. | ||
pub async fn set_duty_cycle_ns(&self, duty_cycle_ns: u32) -> Result<(), error::Error> { | ||
// we'll just let the kernel do the validation | ||
let mut duty_cycle_file = pwm_file_wo(&self.chip, self.number, "duty_cycle").await?; | ||
duty_cycle_file | ||
.write_all(format!("{}", duty_cycle_ns).as_bytes()) | ||
.await?; | ||
let _ = duty_cycle_file.sync_all().await; | ||
Ok(()) | ||
} | ||
|
||
/// Enable/Disable the PWM Signal | ||
pub async fn enable(&self, enable: bool) -> Result<(), error::Error> { | ||
let mut enable_file = pwm_file_wo(&self.chip, self.number, "enable").await?; | ||
let contents = if enable { "1" } else { "0" }; | ||
enable_file.write_all(contents.as_bytes()).await?; | ||
let _ = enable_file.sync_all().await; | ||
Ok(()) | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using the
full
feature set is an antipattern in libraries per the tokio docs. Could you please reduce this to just the needed features?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed!